From 7b0bcbb04a248bf2ca2aec22ef9250768ecaf2b9 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Tue, 24 Sep 2024 09:27:09 +0200 Subject: [PATCH 001/154] Allow page to not exist. (#7498) --- .../Extensions/PagingResultExtensions.cs | 6 ++++-- .../Pagination/src/Pagination.Primitives/Page.cs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/HotChocolate/Pagination/src/Pagination.Mappings/Extensions/PagingResultExtensions.cs b/src/HotChocolate/Pagination/src/Pagination.Mappings/Extensions/PagingResultExtensions.cs index d0f8f66ebe6..89871942676 100644 --- a/src/HotChocolate/Pagination/src/Pagination.Mappings/Extensions/PagingResultExtensions.cs +++ b/src/HotChocolate/Pagination/src/Pagination.Mappings/Extensions/PagingResultExtensions.cs @@ -20,7 +20,7 @@ public static class PagingResultExtensions /// Returns a relay connection. /// public static async Task> ToConnectionAsync( - this Task> resultPromise) + this Task?> resultPromise) where T : class { var result = await resultPromise; @@ -64,8 +64,10 @@ public static Connection ToConnection( where T : class => CreateConnection(result); - private static Connection CreateConnection(Page page) where T : class + private static Connection CreateConnection(Page? page) where T : class { + page ??= Page.Empty; + return new Connection( page.Items.Select(t => new Edge(t, page.CreateCursor)).ToArray(), new ConnectionPageInfo( diff --git a/src/HotChocolate/Pagination/src/Pagination.Primitives/Page.cs b/src/HotChocolate/Pagination/src/Pagination.Primitives/Page.cs index ea4b4864ea7..764f7584376 100644 --- a/src/HotChocolate/Pagination/src/Pagination.Primitives/Page.cs +++ b/src/HotChocolate/Pagination/src/Pagination.Primitives/Page.cs @@ -24,7 +24,7 @@ namespace HotChocolate.Pagination; /// /// The type of the items. /// -public readonly struct Page( +public sealed class Page( ImmutableArray items, bool hasNextPage, bool hasPreviousPage, From fcf00de1eb67771791bd1d7d12be74f52c370b6f Mon Sep 17 00:00:00 2001 From: Tobias Tengler <45513122+tobias-tengler@users.noreply.github.com> Date: Tue, 24 Sep 2024 09:34:15 +0200 Subject: [PATCH 002/154] Enable EnsureAllNodesCanBeResolved by default (#7494) --- dictionary.txt | 1 + .../Core/src/Types/SchemaOptions.cs | 2 +- .../CodeFirstAuthorizationTests.cs | 1 + .../Types.Tests/Types/Relay/NodeTypeTests.cs | 14 ++++++++++++- .../v14/migrating/migrate-from-13-to-14.md | 20 +++++++++++++++---- 5 files changed, 32 insertions(+), 6 deletions(-) diff --git a/dictionary.txt b/dictionary.txt index 016169a8236..3f8fd70b00a 100644 --- a/dictionary.txt +++ b/dictionary.txt @@ -125,6 +125,7 @@ quox quux Rafi reencode +refetched relayjs reprojected resheader diff --git a/src/HotChocolate/Core/src/Types/SchemaOptions.cs b/src/HotChocolate/Core/src/Types/SchemaOptions.cs index cfa9d970e8e..800c9e0f827 100644 --- a/src/HotChocolate/Core/src/Types/SchemaOptions.cs +++ b/src/HotChocolate/Core/src/Types/SchemaOptions.cs @@ -146,7 +146,7 @@ public FieldBindingFlags DefaultFieldBindingFlags /// /// Defines if the schema building process shall validate that all nodes are resolvable through `node`. /// - public bool EnsureAllNodesCanBeResolved { get; set; } + public bool EnsureAllNodesCanBeResolved { get; set; } = true; /// /// Defines if flag enums should be inferred as object value nodes diff --git a/src/HotChocolate/Core/test/Authorization.Tests/CodeFirstAuthorizationTests.cs b/src/HotChocolate/Core/test/Authorization.Tests/CodeFirstAuthorizationTests.cs index a956ff3100b..711db8faa6c 100644 --- a/src/HotChocolate/Core/test/Authorization.Tests/CodeFirstAuthorizationTests.cs +++ b/src/HotChocolate/Core/test/Authorization.Tests/CodeFirstAuthorizationTests.cs @@ -496,6 +496,7 @@ private static IServiceProvider CreateServices( .AddGraphQLServer() .AddQueryType() .AddGlobalObjectIdentification() + .ModifyOptions(o => o.EnsureAllNodesCanBeResolved = false) .AddAuthorizationHandler(_ => handler) .ModifyAuthorizationOptions(configure ?? (_ => { })) .Services diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/NodeTypeTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/NodeTypeTests.cs index 7baf4c7c29c..cce2e15eafa 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/NodeTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/NodeTypeTests.cs @@ -236,7 +236,6 @@ public async Task NodeResolver_Is_Missing() async Task Error() => await new ServiceCollection() .AddGraphQL() .AddQueryType() - .ModifyOptions(o => o.EnsureAllNodesCanBeResolved = true) .AddGlobalObjectIdentification() .BuildSchemaAsync(); @@ -245,6 +244,19 @@ public async Task NodeResolver_Is_Missing() error.Message.MatchSnapshot(); } + [Fact] + public async Task NodeResolver_Is_Missing_EnsureAllNodesCanBeResolved_False() + { + var schema = await new ServiceCollection() + .AddGraphQL() + .AddQueryType() + .ModifyOptions(o => o.EnsureAllNodesCanBeResolved = false) + .AddGlobalObjectIdentification() + .BuildSchemaAsync(); + + Assert.NotNull(schema); + } + public class Query { [NodeResolver] diff --git a/website/src/docs/hotchocolate/v14/migrating/migrate-from-13-to-14.md b/website/src/docs/hotchocolate/v14/migrating/migrate-from-13-to-14.md index 952ee412084..cef75cca68e 100644 --- a/website/src/docs/hotchocolate/v14/migrating/migrate-from-13-to-14.md +++ b/website/src/docs/hotchocolate/v14/migrating/migrate-from-13-to-14.md @@ -56,6 +56,18 @@ services .AddGlobalObjectIdentification(); ``` +## Node Resolver validation + +We now enforce that each object type implementing the `Node` interface also defines a resolver, so that the object can be refetched through the `node(id: ID!)` field. + +You can opt out of this new behavior by setting the `EnsureAllNodesCanBeResolved` option to `false`. + +```csharp +services + .AddGraphQLServer() + .ModifyOptions(o => o.EnsureAllNodesCanBeResolved = false) +``` + ## Builder APIs We have aligned all builder APIs to be more consistent and easier to use. Builders can now be created by using the static method `Builder.New()` and the `Build()` method to create the final object. @@ -96,7 +108,7 @@ Please ensure that your clients are sending date/time strings in the correct for ### Packages renamed | Old package name | New package name | -|------------------------------------------|---------------------------------------------| +| ---------------------------------------- | ------------------------------------------- | | HotChocolate.PersistedQueries.FileSystem | HotChocolate.PersistedOperations.FileSystem | | HotChocolate.PersistedQueries.InMemory | HotChocolate.PersistedOperations.InMemory | | HotChocolate.PersistedQueries.Redis | HotChocolate.PersistedOperations.Redis | @@ -104,13 +116,13 @@ Please ensure that your clients are sending date/time strings in the correct for ### Interfaces renamed | Old interface name | New interface name | -|--------------------------------|------------------------------------| +| ------------------------------ | ---------------------------------- | | IPersistedQueryOptionsAccessor | IPersistedOperationOptionsAccessor | ### Methods renamed | Old method name | New method name | -|-------------------------------------|----------------------------------------| +| ----------------------------------- | -------------------------------------- | | UsePersistedQueryPipeline | UsePersistedOperationPipeline | | UseAutomaticPersistedQueryPipeline | UseAutomaticPersistedOperationPipeline | | AddFileSystemQueryStorage | AddFileSystemOperationDocumentStorage | @@ -126,7 +138,7 @@ Please ensure that your clients are sending date/time strings in the correct for ### Defaults changed | Parameter | Old default | New default | -|----------------|---------------------|------------------------| +| -------------- | ------------------- | ---------------------- | | cacheDirectory | "persisted_queries" | "persisted_operations" | # Deprecations From 87f3b70e9d7234af43b3e6a50266e93ed131a418 Mon Sep 17 00:00:00 2001 From: Jordan Dominion Date: Tue, 24 Sep 2024 03:36:06 -0400 Subject: [PATCH 003/154] Removed duplicate call to `GetCustomAttributes` (#7497) --- .../Types/Types/Descriptors/Conventions/DefaultTypeInspector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/DefaultTypeInspector.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/DefaultTypeInspector.cs index 53b8981b2d0..7c93af9c09d 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/DefaultTypeInspector.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/DefaultTypeInspector.cs @@ -420,7 +420,7 @@ public void ApplyAttributes( var temp = ArrayPool.Shared.Rent(attributes.Length); var i = 0; - foreach (var attribute in attributeProvider.GetCustomAttributes(true)) + foreach (var attribute in attributes) { if (attribute is DescriptorAttribute casted) { From c4b8ca10c95e5a529d9ace3eddc765a96456347d Mon Sep 17 00:00:00 2001 From: Glen Date: Tue, 24 Sep 2024 09:43:19 +0200 Subject: [PATCH 004/154] Fixed typo in Build.Environment.cs (#7495) --- .build/Build.Environment.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.build/Build.Environment.cs b/.build/Build.Environment.cs index c029cca66cc..39d62a7e379 100644 --- a/.build/Build.Environment.cs +++ b/.build/Build.Environment.cs @@ -17,7 +17,7 @@ partial class Build AbsolutePath OutputDirectory => RootDirectory / "output"; AbsolutePath TestResultDirectory => OutputDirectory / "test-results"; - AbsolutePath CoverageReportDirectory => OutputDirectory / "coberage-reports"; + AbsolutePath CoverageReportDirectory => OutputDirectory / "coverage-reports"; AbsolutePath PackageDirectory => OutputDirectory / "packages"; AbsolutePath StarWarsTemplateNuSpec => RootDirectory / "templates" / "StarWars" / "HotChocolate.Templates.StarWars.nuspec"; AbsolutePath HotChocolateDirectoryBuildProps => SourceDirectory / "HotChocolate" / "Directory.Build.Props"; From c54a61440ae5b6d49b6aa0432e9cc5281d711f61 Mon Sep 17 00:00:00 2001 From: Glen Date: Tue, 24 Sep 2024 14:54:18 +0200 Subject: [PATCH 005/154] Disabled FS3261 (#7501) --- .../Core/src/Types.FSharp/HotChocolate.Types.FSharp.fsproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/HotChocolate/Core/src/Types.FSharp/HotChocolate.Types.FSharp.fsproj b/src/HotChocolate/Core/src/Types.FSharp/HotChocolate.Types.FSharp.fsproj index 64338ada9ff..5a760d01f18 100644 --- a/src/HotChocolate/Core/src/Types.FSharp/HotChocolate.Types.FSharp.fsproj +++ b/src/HotChocolate/Core/src/Types.FSharp/HotChocolate.Types.FSharp.fsproj @@ -1,5 +1,9 @@ + + FS3261 + + From 0b064d7a2a9356761c0958817c768893fdf2a8c2 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 25 Sep 2024 12:08:01 +0200 Subject: [PATCH 006/154] Fixed LoadRequired for multiple keys. (#7507) (cherry picked from commit 4a8548417783b17eaca951533894b99db829f3d9) --- .../src/Core/DataLoaderExtensions.cs | 47 ++++++++++++++- .../Core.Tests/DataLoaderListBatchTests.cs | 57 +++++++++++++++++++ 2 files changed, 102 insertions(+), 2 deletions(-) diff --git a/src/GreenDonut/src/Core/DataLoaderExtensions.cs b/src/GreenDonut/src/Core/DataLoaderExtensions.cs index 9a64e0c52aa..b6d967fb590 100644 --- a/src/GreenDonut/src/Core/DataLoaderExtensions.cs +++ b/src/GreenDonut/src/Core/DataLoaderExtensions.cs @@ -1,4 +1,6 @@ +using System.Buffers; using System.Collections.Immutable; +using System.Text; namespace GreenDonut; @@ -85,14 +87,55 @@ public static async Task> LoadRequiredAsync( var values = await dataLoader.LoadAsync(keys, cancellationToken).ConfigureAwait(false); - if(values.Count != keys.Count) + if(values.Any(t => t is null)) { - throw new KeyNotFoundException("Not all keys could be resolved."); + throw new KeyNotFoundException(CreateMissingKeyValueMessage(keys, values)); } return values!; } + private static string CreateMissingKeyValueMessage( + IReadOnlyCollection keys, + IReadOnlyList values) + { + var buffer = new StringBuilder(); + + var i = 0; + var first = true; + var multipleMissing = false; + + foreach (var key in keys) + { + if (values[i] == null) + { + if(!first) + { + multipleMissing = true; + buffer.Append(", "); + } + + buffer.Append(key); + first = false; + } + + i++; + } + + if (multipleMissing) + { + buffer.Insert(0, "The keys `"); + } + else + { + buffer.Insert(0, "The key `"); + } + + buffer.Append("` could not be resolved."); + + return buffer.ToString(); + } + /// /// Adds a new entry to the cache if not already exists. /// diff --git a/src/GreenDonut/test/Core.Tests/DataLoaderListBatchTests.cs b/src/GreenDonut/test/Core.Tests/DataLoaderListBatchTests.cs index 80adebad064..410a7e7207b 100644 --- a/src/GreenDonut/test/Core.Tests/DataLoaderListBatchTests.cs +++ b/src/GreenDonut/test/Core.Tests/DataLoaderListBatchTests.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.Components.Web; using Microsoft.Extensions.DependencyInjection; using Xunit; @@ -58,6 +59,44 @@ public static async Task Ensure_Multiple_Large_Batches_Can_Be_Enqueued_Concurren await Task.WhenAll(tasks); } + [Fact] + public static async Task Ensure_Required_Keys_Are_Returned_One_Missing() + { + // arrange + using var cts = new CancellationTokenSource(5000); + var services = new ServiceCollection() + .AddDataLoader() + .BuildServiceProvider(); + var dataLoader = services.GetRequiredService(); + + // act + async Task Error() + => await dataLoader.LoadRequiredAsync([1, 2], cts.Token); + + // assert + var ex = await Assert.ThrowsAsync(Error); + Assert.Equal("The key `1` could not be resolved.", ex.Message); + } + + [Fact] + public static async Task Ensure_Required_Keys_Are_Returned_Two_Missing() + { + // arrange + using var cts = new CancellationTokenSource(5000); + var services = new ServiceCollection() + .AddDataLoader() + .BuildServiceProvider(); + var dataLoader = services.GetRequiredService(); + + // act + async Task Error() + => await dataLoader.LoadRequiredAsync([1, 2, 3], cts.Token); + + // assert + var ex = await Assert.ThrowsAsync(Error); + Assert.Equal("The keys `1, 3` could not be resolved.", ex.Message); + } + [Fact] public static async Task Ensure_No_Buffer_Leakage_When_Batch_Is_Overrun_Async() { @@ -151,4 +190,22 @@ protected override async Task> LoadBatchAsync( .ToDictionary(t => t.Item1, t => t.Item2.ToArray()); } } + + private sealed class UnresolvedTestDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options) + : BatchDataLoader(batchScheduler, options) + { + protected override async Task> LoadBatchAsync( + IReadOnlyList runNumbers, + CancellationToken cancellationToken) + { + await Task.Delay(300, cancellationToken).ConfigureAwait(false); + + return runNumbers + .Select(runNumber => (runNumber, Enumerable.Range(0, 500))) + .Where(t => t.runNumber % 2 == 0) + .ToDictionary(t => t.runNumber, t => t.Item2.ToArray()); + } + } } From bcecf68cb7fff160b984c1100acdb89836be82e3 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 25 Sep 2024 12:10:37 +0200 Subject: [PATCH 007/154] Fixed typo (cherry picked from commit 05152d04e78e9feb8ae29cadb6f4a946fea5121b) --- .../CostAnalysis/src/CostAnalysis/Options/CostOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/Options/CostOptions.cs b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Options/CostOptions.cs index b5e029185c7..6d84907069b 100644 --- a/src/HotChocolate/CostAnalysis/src/CostAnalysis/Options/CostOptions.cs +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Options/CostOptions.cs @@ -26,7 +26,7 @@ public sealed class CostOptions public bool ApplyCostDefaults { get; set; } = true; /// - /// Gets or sets the default cost for a async resolver pipeline. + /// Gets or sets the default cost for an async resolver pipeline. /// public double? DefaultResolverCost { get; set; } = 10.0; From 2c43bd6e5c1d2b70f2ce4ee1c41ee4324e6f48ff Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 25 Sep 2024 14:09:04 +0200 Subject: [PATCH 008/154] Ignore nullability for ToConnection helper. (#7508) (cherry picked from commit ef128b4910793510610b59598a231f300c69dd4a) --- .../Pagination.Mappings/Extensions/PagingResultExtensions.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/HotChocolate/Pagination/src/Pagination.Mappings/Extensions/PagingResultExtensions.cs b/src/HotChocolate/Pagination/src/Pagination.Mappings/Extensions/PagingResultExtensions.cs index 89871942676..9c70360d861 100644 --- a/src/HotChocolate/Pagination/src/Pagination.Mappings/Extensions/PagingResultExtensions.cs +++ b/src/HotChocolate/Pagination/src/Pagination.Mappings/Extensions/PagingResultExtensions.cs @@ -19,13 +19,15 @@ public static class PagingResultExtensions /// /// Returns a relay connection. /// +#nullable disable public static async Task> ToConnectionAsync( - this Task?> resultPromise) + this Task> resultPromise) where T : class { var result = await resultPromise; return CreateConnection(result); } +#nullable restore /// /// Converts a to a . From e3dd137ed257523fcc8dd0bfebe202fb2df4521d Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Wed, 25 Sep 2024 08:17:58 -0400 Subject: [PATCH 009/154] Fixed a case where a schema path with unix paths (/) would cause the schema file to be added twice on windows machines (#7505) (cherry picked from commit bc437c45f308fefa4f493002f479fd1052cfef93) --- .../src/dotnet-graphql/GeneratorHelpers.cs | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/StrawberryShake/Tooling/src/dotnet-graphql/GeneratorHelpers.cs b/src/StrawberryShake/Tooling/src/dotnet-graphql/GeneratorHelpers.cs index e0cbee0ec5e..c542701250d 100644 --- a/src/StrawberryShake/Tooling/src/dotnet-graphql/GeneratorHelpers.cs +++ b/src/StrawberryShake/Tooling/src/dotnet-graphql/GeneratorHelpers.cs @@ -23,14 +23,20 @@ public static string[] GetGraphQLDocuments( IReadOnlyList buildArtifacts, string schemaPath) { - var files = patterns - .SelectMany(pattern => Files(path, pattern)) - .Select(t => Combine(path, t)) - .ToHashSet(); - files.Add(Combine(path, schemaPath)); + IEnumerable files = + [ + .. + patterns + .SelectMany(pattern => Files(path, pattern)) + .Select(t => Combine(path, t)), + Combine(path, schemaPath) + ]; - files.ExceptWith(buildArtifacts); - return files.ToArray(); + return files + .Select(GetFullPath) + .Distinct() + .ExceptBy(buildArtifacts, GetFullPath) + .ToArray(); } public static IReadOnlyList GetBuildArtifacts(string path) From 54ce846ed955325044a6e1884a981f54be9f0045 Mon Sep 17 00:00:00 2001 From: Daniel Rebouta <34064225+CronKz@users.noreply.github.com> Date: Wed, 25 Sep 2024 17:33:31 +0200 Subject: [PATCH 010/154] Fixed EfQueryableCursorPagingHandler (hasNextPage & hasPreviousPage) (#7506) (cherry picked from commit 114ad6f2e502af104e4a47d52e170ca921423e6b) --- .../EfQueryableCursorPagingHandler.cs | 14 ++-- .../IntegrationTests.cs | 72 ++++++++++++++++++- ...rst_10_With_Default_Sorting_HasNextPage.md | 50 +++++++++++++ ...10_With_Default_Sorting_HasPreviousPage.md | 50 +++++++++++++ 4 files changed, 179 insertions(+), 7 deletions(-) create mode 100644 src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/__snapshots__/IntegrationTests.Paging_First_10_With_Default_Sorting_HasNextPage.md create mode 100644 src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/__snapshots__/IntegrationTests.Paging_Last_10_With_Default_Sorting_HasPreviousPage.md diff --git a/src/HotChocolate/Data/src/EntityFramework/Pagination/EfQueryableCursorPagingHandler.cs b/src/HotChocolate/Data/src/EntityFramework/Pagination/EfQueryableCursorPagingHandler.cs index 74a1ebe1437..e15e027f651 100644 --- a/src/HotChocolate/Data/src/EntityFramework/Pagination/EfQueryableCursorPagingHandler.cs +++ b/src/HotChocolate/Data/src/EntityFramework/Pagination/EfQueryableCursorPagingHandler.cs @@ -62,7 +62,7 @@ private async ValueTask SliceAsync( if (arguments.Last is not null) { - query = query.Reverse().Take(arguments.Last.Value); + query = query.Reverse().Take(arguments.Last.Value + 1); requestedCount = arguments.Last.Value; } @@ -103,14 +103,15 @@ private async ValueTask SliceAsync( .ToAsyncEnumerable(context.RequestAborted) .ConfigureAwait(false)) { - builder.Add(new Edge(item.Item, CursorFormatter.Format(item.Item, keys))); - totalCount ??= item.TotalCount; fetchCount++; - if (fetchCount >= requestedCount) + if (fetchCount > requestedCount) { break; } + + builder.Add(new Edge(item.Item, CursorFormatter.Format(item.Item, keys))); + totalCount ??= item.TotalCount; } } else @@ -120,13 +121,14 @@ private async ValueTask SliceAsync( .ToAsyncEnumerable(context.RequestAborted) .ConfigureAwait(false)) { - builder.Add(new Edge(item, CursorFormatter.Format(item, keys))); fetchCount++; - if (fetchCount >= requestedCount) + if (fetchCount > requestedCount) { break; } + + builder.Add(new Edge(item, CursorFormatter.Format(item, keys))); } } diff --git a/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/IntegrationTests.cs b/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/IntegrationTests.cs index 104f4f7fb45..a236f7fb47c 100644 --- a/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/IntegrationTests.cs +++ b/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/IntegrationTests.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; using System.Runtime.CompilerServices; using CookieCrumble; using HotChocolate.Data.Sorting; @@ -246,6 +246,76 @@ public async Task Paging_Next_2_With_User_Sorting() #endif } + [Fact] + public async Task Paging_First_10_With_Default_Sorting_HasNextPage() + { + var connectionString = CreateConnectionString(); + await SeedAsync(connectionString); + + var executor = await new ServiceCollection() + .AddScoped(_ => new CatalogContext(connectionString)) + .AddGraphQLServer() + .AddQueryType() + .AddSorting() + .AddDbContextCursorPagingProvider() + .BuildRequestExecutorAsync(); + + var result = await executor.ExecuteAsync(q => q + .SetDocument( + """ + { + brands(first: 10) { + nodes { + name + } + pageInfo { + hasNextPage + hasPreviousPage + endCursor + } + } + } + """) + .SetGlobalState("printSQL", true)); + + result.MatchMarkdownSnapshot(); + } + + [Fact] + public async Task Paging_Last_10_With_Default_Sorting_HasPreviousPage() + { + var connectionString = CreateConnectionString(); + await SeedAsync(connectionString); + + var executor = await new ServiceCollection() + .AddScoped(_ => new CatalogContext(connectionString)) + .AddGraphQLServer() + .AddQueryType() + .AddSorting() + .AddDbContextCursorPagingProvider() + .BuildRequestExecutorAsync(); + + var result = await executor.ExecuteAsync(q => q + .SetDocument( + """ + { + brands(last: 10) { + nodes { + name + } + pageInfo { + hasNextPage + hasPreviousPage + endCursor + } + } + } + """) + .SetGlobalState("printSQL", true)); + + result.MatchMarkdownSnapshot(); + } + public class Query { [UsePaging] diff --git a/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/__snapshots__/IntegrationTests.Paging_First_10_With_Default_Sorting_HasNextPage.md b/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/__snapshots__/IntegrationTests.Paging_First_10_With_Default_Sorting_HasNextPage.md new file mode 100644 index 00000000000..39b490dbcaf --- /dev/null +++ b/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/__snapshots__/IntegrationTests.Paging_First_10_With_Default_Sorting_HasNextPage.md @@ -0,0 +1,50 @@ +# Paging_First_10_With_Default_Sorting_HasNextPage + +```json +{ + "data": { + "brands": { + "nodes": [ + { + "name": "Brand0" + }, + { + "name": "Brand1" + }, + { + "name": "Brand2" + }, + { + "name": "Brand3" + }, + { + "name": "Brand4" + }, + { + "name": "Brand5" + }, + { + "name": "Brand6" + }, + { + "name": "Brand7" + }, + { + "name": "Brand8" + }, + { + "name": "Brand9" + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "endCursor": "MTA=" + } + } + }, + "extensions": { + "sql": "-- @__p_0='11'\nSELECT b.\"Id\", b.\"AlwaysNull\", b.\"DisplayName\", b.\"Name\", b.\"BrandDetails_Country_Name\"\nFROM \"Brands\" AS b\nORDER BY b.\"Id\"\nLIMIT @__p_0" + } +} +``` diff --git a/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/__snapshots__/IntegrationTests.Paging_Last_10_With_Default_Sorting_HasPreviousPage.md b/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/__snapshots__/IntegrationTests.Paging_Last_10_With_Default_Sorting_HasPreviousPage.md new file mode 100644 index 00000000000..55672524000 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/__snapshots__/IntegrationTests.Paging_Last_10_With_Default_Sorting_HasPreviousPage.md @@ -0,0 +1,50 @@ +# Paging_Last_10_With_Default_Sorting_HasPreviousPage + +```json +{ + "data": { + "brands": { + "nodes": [ + { + "name": "Brand90" + }, + { + "name": "Brand91" + }, + { + "name": "Brand92" + }, + { + "name": "Brand93" + }, + { + "name": "Brand94" + }, + { + "name": "Brand95" + }, + { + "name": "Brand96" + }, + { + "name": "Brand97" + }, + { + "name": "Brand98" + }, + { + "name": "Brand99" + } + ], + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": true, + "endCursor": "MTAw" + } + } + }, + "extensions": { + "sql": "-- @__p_0='11'\nSELECT b.\"Id\", b.\"AlwaysNull\", b.\"DisplayName\", b.\"Name\", b.\"BrandDetails_Country_Name\"\nFROM \"Brands\" AS b\nORDER BY b.\"Id\" DESC\nLIMIT @__p_0" + } +} +``` From a90360d8d6d03013792fded6aa4411779480e0a1 Mon Sep 17 00:00:00 2001 From: PascalSenn Date: Wed, 25 Sep 2024 21:38:31 +0200 Subject: [PATCH 011/154] Allow Access To Nitro (#7513) (cherry picked from commit 791a7976eee00d7920919545a631b64fb50757f5) --- src/HotChocolate/Fusion/src/Core/HotChocolate.Fusion.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/src/HotChocolate/Fusion/src/Core/HotChocolate.Fusion.csproj b/src/HotChocolate/Fusion/src/Core/HotChocolate.Fusion.csproj index d6773f527e7..7fe4f027e57 100644 --- a/src/HotChocolate/Fusion/src/Core/HotChocolate.Fusion.csproj +++ b/src/HotChocolate/Fusion/src/Core/HotChocolate.Fusion.csproj @@ -13,6 +13,7 @@ + From 05ebd88f2bb082748ca5327572f94e426a237fe7 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 25 Sep 2024 22:05:24 +0200 Subject: [PATCH 012/154] Fixed issue with the DataLoader registration. (#7514) --- .../DataLoaderRegistrar.cs | 46 +++++++++++++++++++ .../DataLoaderServiceCollectionExtensions.cs | 28 +++-------- .../Core.Tests/DataLoaderListBatchTests.cs | 1 - .../src/Fetching/DataLoaderScopeHolder.cs | 35 ++------------ .../src/Fetching/ExecutionDataLoaderScope.cs | 9 +--- 5 files changed, 57 insertions(+), 62 deletions(-) create mode 100644 src/GreenDonut/src/Core/DependencyInjection/DataLoaderRegistrar.cs diff --git a/src/GreenDonut/src/Core/DependencyInjection/DataLoaderRegistrar.cs b/src/GreenDonut/src/Core/DependencyInjection/DataLoaderRegistrar.cs new file mode 100644 index 00000000000..20f49973846 --- /dev/null +++ b/src/GreenDonut/src/Core/DependencyInjection/DataLoaderRegistrar.cs @@ -0,0 +1,46 @@ +#if NET8_0_OR_GREATER +using System.Collections.Frozen; +#endif + +namespace GreenDonut.DependencyInjection; + +/// +/// Provides access to the DataLoader registrations. +/// +public sealed class DataLoaderRegistrar +{ + /// + /// Initializes a new instance of the class. + /// + /// + /// The DataLoader registrations. + /// + public DataLoaderRegistrar(IEnumerable registrations) + { + var map = new Dictionary(); + + foreach (var registration in registrations) + { + map[registration.ServiceType] = registration; + } + + foreach (var registration in map.Values.ToList()) + { + if (registration.ServiceType != registration.InstanceType && + !map.ContainsKey(registration.InstanceType)) + { + map[registration.InstanceType] = registration; + } + } +#if NET8_0_OR_GREATER + Registrations = map.ToFrozenDictionary(); +#else + Registrations = map; +#endif + } + + /// + /// Gets the DataLoader registrations. + /// + public IReadOnlyDictionary Registrations { get; } +} diff --git a/src/GreenDonut/src/Core/DependencyInjection/DataLoaderServiceCollectionExtensions.cs b/src/GreenDonut/src/Core/DependencyInjection/DataLoaderServiceCollectionExtensions.cs index 647e04b88fa..3e6f24daa58 100644 --- a/src/GreenDonut/src/Core/DependencyInjection/DataLoaderServiceCollectionExtensions.cs +++ b/src/GreenDonut/src/Core/DependencyInjection/DataLoaderServiceCollectionExtensions.cs @@ -1,7 +1,4 @@ using System.Collections.Concurrent; -#if NET8_0_OR_GREATER -using System.Collections.Frozen; -#endif using GreenDonut; using GreenDonut.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -49,6 +46,7 @@ public static IServiceCollection TryAddDataLoaderCore( { services.TryAddSingleton(); + services.TryAddSingleton(); services.AddSingleton(); services.TryAddScoped(sp => sp.GetRequiredService().CreateScope(sp)); services.TryAddScoped(); @@ -94,30 +92,18 @@ public static T GetDataLoader(this IServiceProvider services) where T : IData internal sealed class DataLoaderScopeFactory { -#if NET8_0_OR_GREATER - private readonly FrozenDictionary _registrations; -#else - private readonly Dictionary _registrations; -#endif - - public DataLoaderScopeFactory(IEnumerable dataLoaderRegistrations) -#if NET8_0_OR_GREATER - => _registrations = dataLoaderRegistrations.ToFrozenDictionary(t => t.ServiceType); -#else - => _registrations = dataLoaderRegistrations.ToDictionary(t => t.ServiceType); -#endif + private readonly DataLoaderRegistrar _registrar; + + public DataLoaderScopeFactory(DataLoaderRegistrar registrar) + => _registrar = registrar; public IDataLoaderScope CreateScope(IServiceProvider scopedServiceProvider) - => new DefaultDataLoaderScope(scopedServiceProvider, _registrations); + => new DefaultDataLoaderScope(scopedServiceProvider, _registrar.Registrations); } file sealed class DefaultDataLoaderScope( IServiceProvider serviceProvider, -#if NET8_0_OR_GREATER - FrozenDictionary registrations) -#else - Dictionary registrations) -#endif + IReadOnlyDictionary registrations) : IDataLoaderScope { private readonly ConcurrentDictionary _dataLoaders = new(); diff --git a/src/GreenDonut/test/Core.Tests/DataLoaderListBatchTests.cs b/src/GreenDonut/test/Core.Tests/DataLoaderListBatchTests.cs index 410a7e7207b..c6922301d63 100644 --- a/src/GreenDonut/test/Core.Tests/DataLoaderListBatchTests.cs +++ b/src/GreenDonut/test/Core.Tests/DataLoaderListBatchTests.cs @@ -1,4 +1,3 @@ -using Microsoft.AspNetCore.Components.Web; using Microsoft.Extensions.DependencyInjection; using Xunit; diff --git a/src/HotChocolate/Core/src/Fetching/DataLoaderScopeHolder.cs b/src/HotChocolate/Core/src/Fetching/DataLoaderScopeHolder.cs index 3821f19a64b..5dfc23b2aec 100644 --- a/src/HotChocolate/Core/src/Fetching/DataLoaderScopeHolder.cs +++ b/src/HotChocolate/Core/src/Fetching/DataLoaderScopeHolder.cs @@ -1,6 +1,3 @@ -#if NET8_0_OR_GREATER -using System.Collections.Frozen; -#endif using GreenDonut; using GreenDonut.DependencyInjection; using Microsoft.Extensions.DependencyInjection; @@ -13,36 +10,10 @@ namespace HotChocolate.Fetching; public sealed class DataLoaderScopeHolder { private static readonly AsyncLocal _currentScope = new(); -#if NET8_0_OR_GREATER - private readonly FrozenDictionary _registrations; -#else - private readonly Dictionary _registrations; -#endif + private readonly IReadOnlyDictionary _registrations; - public DataLoaderScopeHolder(IEnumerable registrations) - { -#if NET8_0_OR_GREATER - _registrations = CreateRegistrations().ToFrozenDictionary(t => t.Item1, t => t.Item2); -#else - _registrations = CreateRegistrations().ToDictionary(t => t.Item1, t => t.Item2); -#endif - - IEnumerable<(Type, DataLoaderRegistration)> CreateRegistrations() - { - foreach (var reg in registrations) - { - if (reg.ServiceType == reg.InstanceType) - { - yield return (reg.ServiceType, reg); - } - else - { - yield return (reg.ServiceType, reg); - yield return (reg.InstanceType, reg); - } - } - } - } + public DataLoaderScopeHolder(DataLoaderRegistrar registrar) + => _registrations = registrar.Registrations; /// /// Creates and pins a new . diff --git a/src/HotChocolate/Core/src/Fetching/ExecutionDataLoaderScope.cs b/src/HotChocolate/Core/src/Fetching/ExecutionDataLoaderScope.cs index 506bf8dab60..4e7b4277166 100644 --- a/src/HotChocolate/Core/src/Fetching/ExecutionDataLoaderScope.cs +++ b/src/HotChocolate/Core/src/Fetching/ExecutionDataLoaderScope.cs @@ -1,7 +1,4 @@ using System.Collections.Concurrent; -#if NET8_0_OR_GREATER -using System.Collections.Frozen; -#endif using GreenDonut; using GreenDonut.DependencyInjection; using HotChocolate.Fetching.Properties; @@ -12,11 +9,7 @@ namespace HotChocolate.Fetching; internal sealed class ExecutionDataLoaderScope( IServiceProvider serviceProvider, IBatchScheduler batchScheduler, -#if NET8_0_OR_GREATER - FrozenDictionary registrations) -#else - Dictionary registrations) -#endif + IReadOnlyDictionary registrations) : IDataLoaderScope { private readonly ConcurrentDictionary _dataLoaders = new(); From ba1c1b800b44c849edc2916477d2a023d5cf0532 Mon Sep 17 00:00:00 2001 From: Glen Date: Wed, 25 Sep 2024 17:35:27 +0200 Subject: [PATCH 013/154] Added a generic version of the ID descriptor method (#7503) (cherry picked from commit 86a9c8144142412bbdbe2bd59d2b4696e0f34ed3) --- .../Extensions/RelayIdFieldExtensions.cs | 59 +++++++++++++++++-- .../Types/Relay/IdDescriptorTests.cs | 41 ++++++++++--- .../IdDescriptorTests.Id_On_Objects.snap | 7 ++- ...orTests.Id_Type_Is_Correctly_Inferred.snap | 5 +- 4 files changed, 97 insertions(+), 15 deletions(-) diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/Extensions/RelayIdFieldExtensions.cs b/src/HotChocolate/Core/src/Types/Types/Relay/Extensions/RelayIdFieldExtensions.cs index 9bdba582f94..2c1152be3c6 100644 --- a/src/HotChocolate/Core/src/Types/Types/Relay/Extensions/RelayIdFieldExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Types/Relay/Extensions/RelayIdFieldExtensions.cs @@ -61,7 +61,7 @@ namespace HotChocolate.Types; public static class RelayIdFieldExtensions { /// - /// the the descriptor + /// the descriptor /// /// Sets the type name of the relay id /// @@ -80,7 +80,24 @@ public static IInputFieldDescriptor ID( } /// - /// the the descriptor + /// the descriptor + /// + /// the type from which the type name is derived + /// + public static IInputFieldDescriptor ID(this IInputFieldDescriptor descriptor) + { + if (descriptor is null) + { + throw new ArgumentNullException(nameof(descriptor)); + } + + RelayIdFieldHelpers.ApplyIdToField(descriptor, typeof(T).Name); + + return descriptor; + } + + /// + /// the descriptor /// /// Sets the type name of the relay id /// @@ -99,7 +116,24 @@ public static IArgumentDescriptor ID( } /// - /// the the descriptor + /// the descriptor + /// + /// the type from which the type name is derived + /// + public static IArgumentDescriptor ID(this IArgumentDescriptor descriptor) + { + if (descriptor is null) + { + throw new ArgumentNullException(nameof(descriptor)); + } + + RelayIdFieldHelpers.ApplyIdToField(descriptor, typeof(T).Name); + + return descriptor; + } + + /// + /// the descriptor /// /// Sets the type name of the relay id /// @@ -118,7 +152,24 @@ public static IObjectFieldDescriptor ID( } /// - /// the the descriptor + /// the descriptor + /// + /// the type from which the type name is derived + /// + public static IObjectFieldDescriptor ID(this IObjectFieldDescriptor descriptor) + { + if (descriptor is null) + { + throw new ArgumentNullException(nameof(descriptor)); + } + + RelayIdFieldHelpers.ApplyIdToField(descriptor, typeof(T).Name); + + return descriptor; + } + + /// + /// the descriptor public static IInterfaceFieldDescriptor ID(this IInterfaceFieldDescriptor descriptor) { if (descriptor is null) diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/IdDescriptorTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/IdDescriptorTests.cs index 35e14735155..c7dc943408a 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/IdDescriptorTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/IdDescriptorTests.cs @@ -12,7 +12,7 @@ public async Task Id_On_Arguments() // arrange var intId = Convert.ToBase64String("Query:1"u8); var stringId = Convert.ToBase64String("Query:abc"u8); - var guidId = Convert.ToBase64String(Combine("Query:"u8, Guid.Empty.ToByteArray())); + var guidId = Convert.ToBase64String(Combine("Another:"u8, Guid.Empty.ToByteArray())); // act var result = @@ -47,6 +47,7 @@ public async Task Id_On_Objects() { // arrange var someId = Convert.ToBase64String("Some:1"u8); + var anotherId = Convert.ToBase64String("Another:1"u8); // act var result = @@ -58,12 +59,19 @@ public async Task Id_On_Objects() .ExecuteRequestAsync( OperationRequestBuilder.New() .SetDocument( - @"query foo ($someId: ID!) { - foo(input: { someId: $someId }) { + """ + query foo ($someId: ID!, $anotherId: ID!) { + foo(input: { someId: $someId, anotherId: $anotherId }) { someId + anotherId } - }") - .SetVariableValues(new Dictionary { { "someId", someId }, }) + } + """) + .SetVariableValues(new Dictionary + { + { "someId", someId }, + { "anotherId", anotherId } + }) .Build()); // assert @@ -71,6 +79,7 @@ public async Task Id_On_Objects() { result = result.ToJson(), someId, + anotherId }.MatchSnapshot(); } @@ -107,7 +116,7 @@ protected override void Configure(IObjectTypeDescriptor descriptor) descriptor .Field(t => t.GuidId(default)) - .Argument("id", a => a.ID()); + .Argument("id", a => a.ID()); descriptor .Field(t => t.Foo(default)) @@ -123,6 +132,10 @@ protected override void Configure(IInputObjectTypeDescriptor descripto descriptor .Field(t => t.SomeId) .ID("Some"); + + descriptor + .Field(t => t.AnotherId) + .ID(); } } @@ -135,6 +148,10 @@ protected override void Configure(IObjectTypeDescriptor descriptor) descriptor .Field(t => t.SomeId) .ID("Bar"); + + descriptor + .Field(t => t.AnotherId) + .ID(); } } @@ -145,6 +162,10 @@ protected override void Configure(IInterfaceTypeDescriptor descript descriptor .Field(t => t.SomeId) .ID(); + + descriptor + .Field(t => t.AnotherId) + .ID(); } } @@ -153,21 +174,27 @@ public class Query public string IntId(int id) => id.ToString(); public string StringId(string id) => id; public string GuidId(Guid id) => id.ToString(); - public IFooPayload Foo(FooInput input) => new FooPayload { SomeId = input.SomeId, }; + public IFooPayload Foo(FooInput input) + => new FooPayload { SomeId = input.SomeId, AnotherId = input.AnotherId }; } public class FooInput { public string SomeId { get; set; } + public string AnotherId { get; set; } } public class FooPayload : IFooPayload { public string SomeId { get; set; } + public string AnotherId { get; set; } } public interface IFooPayload { string SomeId { get; set; } + string AnotherId { get; set; } } + + private class Another; } diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdDescriptorTests.Id_On_Objects.snap b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdDescriptorTests.Id_On_Objects.snap index 1ef53d6fda2..a8023318c6c 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdDescriptorTests.Id_On_Objects.snap +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdDescriptorTests.Id_On_Objects.snap @@ -1,4 +1,5 @@ -{ - "result": "{\n \"data\": {\n \"foo\": {\n \"someId\": \"QmFyOjE=\"\n }\n }\n}", - "someId": "U29tZTox" +{ + "result": "{\n \"data\": {\n \"foo\": {\n \"someId\": \"QmFyOjE=\",\n \"anotherId\": \"QW5vdGhlcjox\"\n }\n }\n}", + "someId": "U29tZTox", + "anotherId": "QW5vdGhlcjox" } diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdDescriptorTests.Id_Type_Is_Correctly_Inferred.snap b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdDescriptorTests.Id_Type_Is_Correctly_Inferred.snap index b5be55322d7..20c520646a7 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdDescriptorTests.Id_Type_Is_Correctly_Inferred.snap +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdDescriptorTests.Id_Type_Is_Correctly_Inferred.snap @@ -1,13 +1,15 @@ -schema { +schema { query: Query } interface IFooPayload { someId: ID + anotherId: ID } type FooPayload implements IFooPayload { someId: ID + anotherId: ID } type Query { @@ -19,4 +21,5 @@ type Query { input FooInput { someId: ID + anotherId: ID } From e94f7fd7e88d0fc4de0436a774c9515e80bda51e Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Fri, 27 Sep 2024 08:35:31 +0200 Subject: [PATCH 014/154] Install Mono in Build Process --- .github/workflows/release.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 50d8c5f4417..5655a176c6b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,6 +16,21 @@ jobs: with: show-progress: false + - name: Install Mono GPG Key and Repository + run: | + sudo apt update + sudo apt install -y gnupg ca-certificates + sudo apt-key adv --homedir /tmp/apt-key --keyserver keyserver.ubuntu.com --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF + echo "deb https://download.mono-project.com/repo/ubuntu stable-jammy main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list + + - name: Install Mono + run: | + sudo apt update + sudo apt install -y mono-complete + + - name: Check Mono Version + run: mono --version + - name: Install .NET uses: actions/setup-dotnet@v4 with: From 4287f39714e53d46d62aeeb3e76ac2a9ccf91fc5 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Fri, 27 Sep 2024 08:38:16 +0200 Subject: [PATCH 015/154] Switch Runner Version --- .github/workflows/release.yml | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5655a176c6b..f5e1df809bc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,7 @@ on: jobs: release: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 permissions: contents: write steps: @@ -16,21 +16,6 @@ jobs: with: show-progress: false - - name: Install Mono GPG Key and Repository - run: | - sudo apt update - sudo apt install -y gnupg ca-certificates - sudo apt-key adv --homedir /tmp/apt-key --keyserver keyserver.ubuntu.com --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF - echo "deb https://download.mono-project.com/repo/ubuntu stable-jammy main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list - - - name: Install Mono - run: | - sudo apt update - sudo apt install -y mono-complete - - - name: Check Mono Version - run: mono --version - - name: Install .NET uses: actions/setup-dotnet@v4 with: From 398f60b74363f9e329699085191b73a1bbece528 Mon Sep 17 00:00:00 2001 From: Sunghwan Bang Date: Fri, 27 Sep 2024 17:47:45 +0900 Subject: [PATCH 016/154] Upgraded MongoDB Driver to 2.29.0 (#7517) (cherry picked from commit fe84aae16586d19b34245b4ff8fad74ebb05e16e) --- src/Directory.Packages.props | 2 +- .../Data/Driver/FilterDefinitionExtensions.cs | 27 +++++----- .../Data/Driver/MongoDbFilterDefinition.cs | 52 ++++--------------- .../Driver/MongoDbProjectionDefinition.cs | 44 +++------------- .../src/Data/Driver/MongoDbSortDefinition.cs | 44 +++------------- .../Data/Driver/SortDefinitionExtensions.cs | 28 +++++----- .../Data/Extensions/BsonDocumentExtensions.cs | 10 +++- 7 files changed, 60 insertions(+), 147 deletions(-) diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 79733f3ded1..57ed7ee2f19 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -35,7 +35,7 @@ - + diff --git a/src/HotChocolate/MongoDb/src/Data/Driver/FilterDefinitionExtensions.cs b/src/HotChocolate/MongoDb/src/Data/Driver/FilterDefinitionExtensions.cs index 3f9bf278609..14839cc3bc7 100644 --- a/src/HotChocolate/MongoDb/src/Data/Driver/FilterDefinitionExtensions.cs +++ b/src/HotChocolate/MongoDb/src/Data/Driver/FilterDefinitionExtensions.cs @@ -7,30 +7,29 @@ namespace HotChocolate.Data.MongoDb; public static class FilterDefinitionExtensions { public static MongoDbFilterDefinition Wrap( - this FilterDefinition filterDefinition) => - new FilterDefinitionWrapper(filterDefinition); + this FilterDefinition filterDefinition) + => new FilterDefinitionWrapper(filterDefinition); - private sealed class FilterDefinitionWrapper : MongoDbFilterDefinition + private sealed class FilterDefinitionWrapper( + FilterDefinition filter) + : MongoDbFilterDefinition { - private readonly FilterDefinition _filter; - - public FilterDefinitionWrapper(FilterDefinition filter) - { - _filter = filter; - } - public override BsonDocument Render( IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) { if (documentSerializer is IBsonSerializer typedSerializer) { - return _filter.Render(typedSerializer, serializerRegistry); + return filter.Render( + new RenderArgs( + typedSerializer, + serializerRegistry)); } - return _filter.Render( - serializerRegistry.GetSerializer(), - serializerRegistry); + return filter.Render( + new RenderArgs( + serializerRegistry.GetSerializer(), + serializerRegistry)); } } } diff --git a/src/HotChocolate/MongoDb/src/Data/Driver/MongoDbFilterDefinition.cs b/src/HotChocolate/MongoDb/src/Data/Driver/MongoDbFilterDefinition.cs index 17331ae597a..bb8b8e02ab0 100644 --- a/src/HotChocolate/MongoDb/src/Data/Driver/MongoDbFilterDefinition.cs +++ b/src/HotChocolate/MongoDb/src/Data/Driver/MongoDbFilterDefinition.cs @@ -1,7 +1,6 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Driver; -using MongoDB.Driver.Linq; namespace HotChocolate.Data.MongoDb; @@ -12,61 +11,30 @@ public abstract class MongoDbFilterDefinition : FilterDefinition /// /// Gets an empty filter. An empty filter matches everything. /// - public static new MongoDbFilterDefinition Empty => MongoDbFilterDefinition._empty; + public new static MongoDbFilterDefinition Empty => _empty; public abstract BsonDocument Render( IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry); - public override BsonDocument Render( - IBsonSerializer documentSerializer, - IBsonSerializerRegistry serializerRegistry) - { - return Render(documentSerializer, serializerRegistry); - } - - public override BsonDocument Render( - IBsonSerializer documentSerializer, - IBsonSerializerRegistry serializerRegistry, - LinqProvider provider) - { - return Render(documentSerializer, serializerRegistry); - } + public override BsonDocument Render(RenderArgs args) + => Render(args.DocumentSerializer, args.SerializerRegistry); public FilterDefinition ToFilterDefinition() => new FilterDefinitionWrapper(this); - private sealed class FilterDefinitionWrapper : FilterDefinition + private sealed class FilterDefinitionWrapper( + MongoDbFilterDefinition filter) + : FilterDefinition { - private readonly MongoDbFilterDefinition _filter; - - public FilterDefinitionWrapper(MongoDbFilterDefinition filter) - { - _filter = filter; - } - - public override BsonDocument Render( - IBsonSerializer documentSerializer, - IBsonSerializerRegistry serializerRegistry) - { - return _filter.Render(documentSerializer, serializerRegistry); - } - - public override BsonDocument Render( - IBsonSerializer documentSerializer, - IBsonSerializerRegistry serializerRegistry, - LinqProvider provider) - { - return Render(documentSerializer, serializerRegistry); - } + public override BsonDocument Render(RenderArgs args) + => filter.Render(args.DocumentSerializer, args.SerializerRegistry); } - internal sealed class MongoDbEmptyFilterDefinition : MongoDbFilterDefinition + private sealed class MongoDbEmptyFilterDefinition : MongoDbFilterDefinition { public override BsonDocument Render( IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) - { - return new BsonDocument(); - } + => new(); } } diff --git a/src/HotChocolate/MongoDb/src/Data/Driver/MongoDbProjectionDefinition.cs b/src/HotChocolate/MongoDb/src/Data/Driver/MongoDbProjectionDefinition.cs index 04327f19469..2232c401953 100644 --- a/src/HotChocolate/MongoDb/src/Data/Driver/MongoDbProjectionDefinition.cs +++ b/src/HotChocolate/MongoDb/src/Data/Driver/MongoDbProjectionDefinition.cs @@ -1,7 +1,6 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Driver; -using MongoDB.Driver.Linq; namespace HotChocolate.Data.MongoDb; @@ -11,46 +10,17 @@ public abstract BsonDocument Render( IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry); - public override BsonDocument Render( - IBsonSerializer documentSerializer, - IBsonSerializerRegistry serializerRegistry) - { - return Render(documentSerializer, serializerRegistry); - } - - public override BsonDocument Render( - IBsonSerializer documentSerializer, - IBsonSerializerRegistry serializerRegistry, - LinqProvider provider) - { - return Render(documentSerializer, serializerRegistry); - } + public override BsonDocument Render(RenderArgs args) + => Render(args.DocumentSerializer, args.SerializerRegistry); public ProjectionDefinition ToProjectionDefinition() => new ProjectionDefinitionWrapper(this); - private sealed class ProjectionDefinitionWrapper : ProjectionDefinition + private sealed class ProjectionDefinitionWrapper( + MongoDbProjectionDefinition filter) + : ProjectionDefinition { - private readonly MongoDbProjectionDefinition _filter; - - public ProjectionDefinitionWrapper(MongoDbProjectionDefinition filter) - { - _filter = filter; - } - - public override BsonDocument Render( - IBsonSerializer documentSerializer, - IBsonSerializerRegistry serializerRegistry) - { - return _filter.Render(documentSerializer, serializerRegistry); - } - - public override BsonDocument Render( - IBsonSerializer documentSerializer, - IBsonSerializerRegistry serializerRegistry, - LinqProvider provider) - { - return Render(documentSerializer, serializerRegistry); - } + public override BsonDocument Render(RenderArgs args) + => filter.Render(args.DocumentSerializer, args.SerializerRegistry); } } diff --git a/src/HotChocolate/MongoDb/src/Data/Driver/MongoDbSortDefinition.cs b/src/HotChocolate/MongoDb/src/Data/Driver/MongoDbSortDefinition.cs index f62fd986ad4..38995101458 100644 --- a/src/HotChocolate/MongoDb/src/Data/Driver/MongoDbSortDefinition.cs +++ b/src/HotChocolate/MongoDb/src/Data/Driver/MongoDbSortDefinition.cs @@ -1,7 +1,6 @@ using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Driver; -using MongoDB.Driver.Linq; namespace HotChocolate.Data.MongoDb; @@ -11,45 +10,16 @@ public abstract BsonDocument Render( IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry); - public override BsonDocument Render( - IBsonSerializer documentSerializer, - IBsonSerializerRegistry serializerRegistry) - { - return Render(documentSerializer, serializerRegistry); - } - - public override BsonDocument Render( - IBsonSerializer documentSerializer, - IBsonSerializerRegistry serializerRegistry, - LinqProvider provider) - { - return Render(documentSerializer, serializerRegistry); - } + public override BsonDocument Render(RenderArgs args) + => Render(args.DocumentSerializer, args.SerializerRegistry); public SortDefinition ToSortDefinition() => new SortDefinitionWrapper(this); - private sealed class SortDefinitionWrapper : SortDefinition + private sealed class SortDefinitionWrapper( + MongoDbSortDefinition sort) + : SortDefinition { - private readonly MongoDbSortDefinition _sort; - - public SortDefinitionWrapper(MongoDbSortDefinition sort) - { - _sort = sort; - } - - public override BsonDocument Render( - IBsonSerializer documentSerializer, - IBsonSerializerRegistry serializerRegistry) - { - return _sort.Render(documentSerializer, serializerRegistry); - } - - public override BsonDocument Render( - IBsonSerializer documentSerializer, - IBsonSerializerRegistry serializerRegistry, - LinqProvider provider) - { - return Render(documentSerializer, serializerRegistry); - } + public override BsonDocument Render(RenderArgs args) + => sort.Render(args.DocumentSerializer, args.SerializerRegistry); } } diff --git a/src/HotChocolate/MongoDb/src/Data/Driver/SortDefinitionExtensions.cs b/src/HotChocolate/MongoDb/src/Data/Driver/SortDefinitionExtensions.cs index a27b0eae18e..b4fac551e1b 100644 --- a/src/HotChocolate/MongoDb/src/Data/Driver/SortDefinitionExtensions.cs +++ b/src/HotChocolate/MongoDb/src/Data/Driver/SortDefinitionExtensions.cs @@ -6,30 +6,30 @@ namespace HotChocolate.Data.MongoDb; public static class SortDefinitionExtensions { - public static MongoDbSortDefinition Wrap(this SortDefinition sortDefinition) => - new SortDefinitionWrapper(sortDefinition); + public static MongoDbSortDefinition Wrap( + this SortDefinition sortDefinition) + => new SortDefinitionWrapper(sortDefinition); - private sealed class SortDefinitionWrapper : MongoDbSortDefinition + private sealed class SortDefinitionWrapper( + SortDefinition sort) + : MongoDbSortDefinition { - private readonly SortDefinition _sort; - - public SortDefinitionWrapper(SortDefinition sort) - { - _sort = sort; - } - public override BsonDocument Render( IBsonSerializer documentSerializer, IBsonSerializerRegistry serializerRegistry) { if (documentSerializer is IBsonSerializer typedSerializer) { - return _sort.Render(typedSerializer, serializerRegistry); + return sort.Render( + new RenderArgs( + typedSerializer, + serializerRegistry)); } - return _sort.Render( - serializerRegistry.GetSerializer(), - serializerRegistry); + return sort.Render( + new RenderArgs( + serializerRegistry.GetSerializer(), + serializerRegistry)); } } } diff --git a/src/HotChocolate/MongoDb/src/Data/Extensions/BsonDocumentExtensions.cs b/src/HotChocolate/MongoDb/src/Data/Extensions/BsonDocumentExtensions.cs index f090200cc18..48931801964 100644 --- a/src/HotChocolate/MongoDb/src/Data/Extensions/BsonDocumentExtensions.cs +++ b/src/HotChocolate/MongoDb/src/Data/Extensions/BsonDocumentExtensions.cs @@ -14,9 +14,15 @@ public static class BsonDocumentExtensions public static BsonDocument DefaultRender( this FilterDefinition bsonQuery) - => bsonQuery.Render(_documentSerializer, _serializerRegistry); + => bsonQuery.Render( + new RenderArgs( + _documentSerializer, + _serializerRegistry)); public static BsonDocument DefaultRender( this SortDefinition bsonQuery) - => bsonQuery.Render(_documentSerializer, _serializerRegistry); + => bsonQuery.Render( + new RenderArgs( + _documentSerializer, + _serializerRegistry)); } From 1c47117a0110326a53e6ca6ee09b815ac0732497 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Fri, 27 Sep 2024 23:17:55 +0200 Subject: [PATCH 017/154] Reworked Default Security Policies (#7524) --- ...olateAspNetCoreHostingBuilderExtensions.cs | 8 +- ...teAspNetCoreServiceCollectionExtensions.cs | 24 +- .../Caching/test/Caching.Tests/SchemaTests.cs | 2 +- .../Core/src/Abstractions/ErrorCodes.cs | 10 + .../Authorization/AuthorizeValidationRule.cs | 2 + ...estExecutorBuilderExtensions.Validation.cs | 49 ++++- .../Core/src/Validation/CoordinateLimit.cs | 27 +++ .../Core/src/Validation/DocumentValidator.cs | 8 + .../Validation/DocumentValidatorContext.cs | 6 + .../src/Validation/DocumentValidatorRule.cs | 8 +- .../Core/src/Validation/ErrorHelper.cs | 29 +++ .../ValidationBuilderExtensions.Rules.cs | 62 +++++- .../Extensions/ValidationBuilderExtensions.cs | 14 +- .../ValidationServiceCollectionExtensions.cs | 1 + .../src/Validation/FieldDepthCycleTracker.cs | 89 ++++++++ .../Validation/IDocumentValidatorContext.cs | 16 +- .../src/Validation/IDocumentValidatorRule.cs | 5 + .../src/Validation/Rules/ArgumentVisitor.cs | 12 +- .../Core/src/Validation/Rules/DocumentRule.cs | 16 +- .../Rules/IntrospectionDepthVisitor.cs | 77 +++++++ .../Validation/Rules/IntrospectionVisitor.cs | 29 +-- .../Rules/MaxAllowedFieldCycleDepthVisitor.cs | 75 +++++++ .../Rules/MaxExecutionDepthVisitor.cs | 17 +- ...cutorBuilderExtensions_Validation.Tests.cs | 1 + .../Types.Analyzers.Tests/IntegrationTests.cs | 2 +- .../DocumentValidatorTests.cs | 6 + .../HotChocolate.Validation.Tests.csproj | 3 + .../IntrospectionDepthRuleTests.cs | 205 ++++++++++++++++++ .../introspection_with_cycle.graphql | 93 ++++++++ .../introspection_without_cycle.graphql | 90 ++++++++ ...torTests.Introspection_Cycle_Detected.snap | 41 ++++ .../FusionRequestExecutorBuilderExtensions.cs | 2 +- .../Fusion/test/Shared/DemoProject.cs | 22 +- .../Language/src/Language/ListExtensions.cs | 2 +- .../Data.Raven.Tests/AnnotationBasedTests.cs | 2 +- 35 files changed, 957 insertions(+), 98 deletions(-) create mode 100644 src/HotChocolate/Core/src/Validation/CoordinateLimit.cs create mode 100644 src/HotChocolate/Core/src/Validation/FieldDepthCycleTracker.cs create mode 100644 src/HotChocolate/Core/src/Validation/Rules/IntrospectionDepthVisitor.cs create mode 100644 src/HotChocolate/Core/src/Validation/Rules/MaxAllowedFieldCycleDepthVisitor.cs create mode 100644 src/HotChocolate/Core/test/Validation.Tests/IntrospectionDepthRuleTests.cs create mode 100644 src/HotChocolate/Core/test/Validation.Tests/__resources__/introspection_with_cycle.graphql create mode 100644 src/HotChocolate/Core/test/Validation.Tests/__resources__/introspection_without_cycle.graphql create mode 100644 src/HotChocolate/Core/test/Validation.Tests/__snapshots__/DocumentValidatorTests.Introspection_Cycle_Detected.snap diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/HotChocolateAspNetCoreHostingBuilderExtensions.cs b/src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/HotChocolateAspNetCoreHostingBuilderExtensions.cs index c7334698d5d..0a50113b1e7 100644 --- a/src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/HotChocolateAspNetCoreHostingBuilderExtensions.cs +++ b/src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/HotChocolateAspNetCoreHostingBuilderExtensions.cs @@ -22,15 +22,15 @@ public static class HotChocolateAspNetCoreHostingBuilderExtensions /// /// The max allowed GraphQL request size. /// - /// - /// Defines if the cost analyzer should be disabled. + /// + /// Defines if the default security policy should be disabled. /// /// public static IRequestExecutorBuilder AddGraphQL( this IHostApplicationBuilder builder, string? schemaName = default, int maxAllowedRequestSize = MaxAllowedRequestSize, - bool disableCostAnalyzer = false) - => builder.Services.AddGraphQLServer(schemaName, maxAllowedRequestSize, disableCostAnalyzer); + bool disableDefaultSecurity = false) + => builder.Services.AddGraphQLServer(schemaName, maxAllowedRequestSize, disableDefaultSecurity); } #endif diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/HotChocolateAspNetCoreServiceCollectionExtensions.cs b/src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/HotChocolateAspNetCoreServiceCollectionExtensions.cs index 0bcd6c26278..53dfcda1156 100644 --- a/src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/HotChocolateAspNetCoreServiceCollectionExtensions.cs +++ b/src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/HotChocolateAspNetCoreServiceCollectionExtensions.cs @@ -94,8 +94,8 @@ public static IServiceCollection AddGraphQLServerCore( /// /// The max allowed GraphQL request size. /// - /// - /// Defines if the cost analyzer should be disabled. + /// + /// Defines if the default security policy should be disabled. /// /// /// Returns the so that configuration can be chained. @@ -104,7 +104,7 @@ public static IRequestExecutorBuilder AddGraphQLServer( this IServiceCollection services, string? schemaName = default, int maxAllowedRequestSize = MaxAllowedRequestSize, - bool disableCostAnalyzer = false) + bool disableDefaultSecurity = false) { var builder = services .AddGraphQLServerCore(maxAllowedRequestSize) @@ -112,14 +112,20 @@ public static IRequestExecutorBuilder AddGraphQLServer( .AddDefaultHttpRequestInterceptor() .AddSubscriptionServices(); - if (!disableCostAnalyzer) + if (!disableDefaultSecurity) { builder.AddCostAnalyzer(); builder.AddIntrospectionAllowedRule( (sp, _) => { var environment = sp.GetService(); - return (environment?.IsDevelopment() ?? true) == false; + return environment?.IsDevelopment() == false; + }); + builder.AddMaxAllowedFieldCycleDepthRule( + isEnabled: (sp, _) => + { + var environment = sp.GetService(); + return environment?.IsDevelopment() == false; }); } @@ -135,8 +141,8 @@ public static IRequestExecutorBuilder AddGraphQLServer( /// /// The name of the schema. Use explicit schema names if you host multiple schemas. /// - /// - /// Defines if the cost analyzer should be disabled. + /// + /// Defines if the default security policy should be disabled. /// /// /// Returns the so that configuration can be chained. @@ -144,8 +150,8 @@ public static IRequestExecutorBuilder AddGraphQLServer( public static IRequestExecutorBuilder AddGraphQLServer( this IRequestExecutorBuilder builder, string? schemaName = default, - bool disableCostAnalyzer = false) - => builder.Services.AddGraphQLServer(schemaName, disableCostAnalyzer: disableCostAnalyzer); + bool disableDefaultSecurity = false) + => builder.Services.AddGraphQLServer(schemaName, disableDefaultSecurity: disableDefaultSecurity); #else /// /// Adds a GraphQL server configuration to the DI. diff --git a/src/HotChocolate/Caching/test/Caching.Tests/SchemaTests.cs b/src/HotChocolate/Caching/test/Caching.Tests/SchemaTests.cs index 5921ef6f64d..7d919cd2fe0 100644 --- a/src/HotChocolate/Caching/test/Caching.Tests/SchemaTests.cs +++ b/src/HotChocolate/Caching/test/Caching.Tests/SchemaTests.cs @@ -14,7 +14,7 @@ public async Task Allow_CacheControl_On_FieldDefinition() var schema = await new ServiceCollection() #if NET7_0_OR_GREATER - .AddGraphQLServer(disableCostAnalyzer: true) + .AddGraphQLServer(disableDefaultSecurity: true) #else .AddGraphQLServer() #endif diff --git a/src/HotChocolate/Core/src/Abstractions/ErrorCodes.cs b/src/HotChocolate/Core/src/Abstractions/ErrorCodes.cs index 933414ead22..dad7fd68e68 100644 --- a/src/HotChocolate/Core/src/Abstractions/ErrorCodes.cs +++ b/src/HotChocolate/Core/src/Abstractions/ErrorCodes.cs @@ -310,6 +310,16 @@ public static class Validation /// The introspection is not allowed for the current request /// public const string IntrospectionNotAllowed = "HC0046"; + + /// + /// The maximum allowed introspection depth was exceeded. + /// + public const string MaxIntrospectionDepthOverflow = "HC0086"; + + /// + /// The maximum allowed coordinate cycle depth was exceeded. + /// + public const string MaxCoordinateCycleDepthOverflow = "HC0087"; } /// diff --git a/src/HotChocolate/Core/src/Authorization/AuthorizeValidationRule.cs b/src/HotChocolate/Core/src/Authorization/AuthorizeValidationRule.cs index c990879a054..4b95641c9f5 100644 --- a/src/HotChocolate/Core/src/Authorization/AuthorizeValidationRule.cs +++ b/src/HotChocolate/Core/src/Authorization/AuthorizeValidationRule.cs @@ -8,6 +8,8 @@ internal sealed class AuthorizeValidationRule(AuthorizationCache cache) : IDocum private readonly AuthorizeValidationVisitor _visitor = new(); private readonly AuthorizationCache _cache = cache ?? throw new ArgumentNullException(nameof(cache)); + public ushort Priority => ushort.MaxValue; + public bool IsCacheable => false; public void Validate(IDocumentValidatorContext context, DocumentNode document) diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Validation.cs b/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Validation.cs index 07cf4be931e..ab5066b6719 100644 --- a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Validation.cs +++ b/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Validation.cs @@ -1,3 +1,4 @@ +using HotChocolate; using HotChocolate.Execution.Configuration; using HotChocolate.Validation; using HotChocolate.Validation.Options; @@ -187,6 +188,9 @@ public static IRequestExecutorBuilder AddValidationResultAggregator( /// /// Defines if request depth overrides are allowed on a per request basis. /// + /// + /// Defines if the validation rule is enabled. + /// /// /// Returns the for configuration chaining. /// @@ -194,7 +198,8 @@ public static IRequestExecutorBuilder AddMaxExecutionDepthRule( this IRequestExecutorBuilder builder, int maxAllowedExecutionDepth, bool skipIntrospectionFields = false, - bool allowRequestOverrides = false) + bool allowRequestOverrides = false, + Func? isEnabled = null) { if (builder is null) { @@ -206,7 +211,8 @@ public static IRequestExecutorBuilder AddMaxExecutionDepthRule( b => b.AddMaxExecutionDepthRule( maxAllowedExecutionDepth, skipIntrospectionFields, - allowRequestOverrides)); + allowRequestOverrides, + isEnabled)); return builder; } @@ -280,6 +286,45 @@ public static IRequestExecutorBuilder SetMaxAllowedValidationErrors( return builder; } + /// + /// Adds a validation rule that restricts the coordinate cycle depth in a GraphQL operation. + /// + public static IRequestExecutorBuilder AddMaxAllowedFieldCycleDepthRule( + this IRequestExecutorBuilder builder, + ushort? defaultCycleLimit = 3, + (SchemaCoordinate Coordinate, ushort MaxAllowed)[]? coordinateCycleLimits = null, + Func? isEnabled = null) + { + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + ConfigureValidation( + builder, + b => b.AddMaxAllowedFieldCycleDepthRule( + defaultCycleLimit, + coordinateCycleLimits, + isEnabled)); + + return builder; + } + + /// + /// Removes the validation rule that restricts the coordinate cycle depth in a GraphQL operation. + /// + public static IRequestExecutorBuilder RemoveMaxAllowedFieldCycleDepthRule( + this IRequestExecutorBuilder builder) + { + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + ConfigureValidation(builder, b => b.RemoveMaxAllowedFieldCycleDepthRule()); + return builder; + } + private static IRequestExecutorBuilder ConfigureValidation( IRequestExecutorBuilder builder, Action configure) diff --git a/src/HotChocolate/Core/src/Validation/CoordinateLimit.cs b/src/HotChocolate/Core/src/Validation/CoordinateLimit.cs new file mode 100644 index 00000000000..2717b7cc7bd --- /dev/null +++ b/src/HotChocolate/Core/src/Validation/CoordinateLimit.cs @@ -0,0 +1,27 @@ +namespace HotChocolate.Validation; + +internal sealed class CoordinateLimit +{ + public ushort MaxAllowed { get; private set; } + + public ushort Count { get; private set; } + + public bool Add() + { + if (Count < MaxAllowed) + { + Count++; + return true; + } + + return false; + } + + public void Remove() => Count--; + + public void Reset(ushort maxAllowed) + { + MaxAllowed = maxAllowed; + Count = 0; + } +} diff --git a/src/HotChocolate/Core/src/Validation/DocumentValidator.cs b/src/HotChocolate/Core/src/Validation/DocumentValidator.cs index 4ce27a4755f..4c4d3cd99ee 100644 --- a/src/HotChocolate/Core/src/Validation/DocumentValidator.cs +++ b/src/HotChocolate/Core/src/Validation/DocumentValidator.cs @@ -54,6 +54,9 @@ public DocumentValidator( _nonCacheableRules = _allRules.Where(t => !t.IsCacheable).ToArray(); _aggregators = resultAggregators.ToArray(); _maxAllowedErrors = errorOptions.MaxAllowedErrors; + + Array.Sort(_allRules, (a, b) => a.Priority.CompareTo(b.Priority)); + Array.Sort(_nonCacheableRules, (a, b) => a.Priority.CompareTo(b.Priority)); } /// @@ -106,6 +109,11 @@ public ValueTask ValidateAsync( for (var i = 0; i < length; i++) { Unsafe.Add(ref start, i).Validate(context, document); + + if (context.FatalErrorDetected) + { + break; + } } if (_aggregators.Length == 0) diff --git a/src/HotChocolate/Core/src/Validation/DocumentValidatorContext.cs b/src/HotChocolate/Core/src/Validation/DocumentValidatorContext.cs index 9b645c35b54..f0c913368a1 100644 --- a/src/HotChocolate/Core/src/Validation/DocumentValidatorContext.cs +++ b/src/HotChocolate/Core/src/Validation/DocumentValidatorContext.cs @@ -96,6 +96,8 @@ public IOutputType NonNullString public bool UnexpectedErrorsDetected { get; set; } + public bool FatalErrorDetected { get; set; } + public int Count { get; set; } public int Max { get; set; } @@ -110,6 +112,8 @@ public IOutputType NonNullString public HashSet ProcessedFieldPairs { get; } = []; + public FieldDepthCycleTracker FieldDepth { get; } = new(); + public IList RentFieldInfoList() { var buffer = _buffers.Peek(); @@ -166,7 +170,9 @@ public void Clear() CurrentFieldPairs.Clear(); NextFieldPairs.Clear(); ProcessedFieldPairs.Clear(); + FieldDepth.Reset(); UnexpectedErrorsDetected = false; + FatalErrorDetected = false; Count = 0; Max = 0; Allowed = 0; diff --git a/src/HotChocolate/Core/src/Validation/DocumentValidatorRule.cs b/src/HotChocolate/Core/src/Validation/DocumentValidatorRule.cs index 49fac8dd123..c6935a7654b 100644 --- a/src/HotChocolate/Core/src/Validation/DocumentValidatorRule.cs +++ b/src/HotChocolate/Core/src/Validation/DocumentValidatorRule.cs @@ -8,12 +8,18 @@ public class DocumentValidatorRule { private readonly TVisitor _visitor; - public DocumentValidatorRule(TVisitor visitor, bool isCacheable = true) + public DocumentValidatorRule( + TVisitor visitor, + bool isCacheable = true, + ushort property = ushort.MaxValue) { _visitor = visitor; IsCacheable = isCacheable; + Priority = property; } + public ushort Priority { get; } + public bool IsCacheable { get; } public void Validate(IDocumentValidatorContext context, DocumentNode document) diff --git a/src/HotChocolate/Core/src/Validation/ErrorHelper.cs b/src/HotChocolate/Core/src/Validation/ErrorHelper.cs index e20a4671a79..e1984388a14 100644 --- a/src/HotChocolate/Core/src/Validation/ErrorHelper.cs +++ b/src/HotChocolate/Core/src/Validation/ErrorHelper.cs @@ -707,4 +707,33 @@ public static IError StreamOnNonListField( .SpecifiedBy("sec-Stream-Directives-Are-Used-On-List-Fields") .SetPath(context.CreateErrorPath()) .Build(); + + public static void ReportMaxIntrospectionDepthOverflow( + this IDocumentValidatorContext context, + ISyntaxNode selection) + { + context.FatalErrorDetected = true; + context.ReportError( + ErrorBuilder.New() + .SetMessage("Maximum allowed introspection depth exceeded.") + .SetCode(ErrorCodes.Validation.MaxIntrospectionDepthOverflow) + .SetLocations([selection]) + .SetPath(context.CreateErrorPath()) + .Build()); + } + + public static void ReportMaxCoordinateCycleDepthOverflow( + this IDocumentValidatorContext context, + ISyntaxNode selection) + { + context.FatalErrorDetected = true; + + context.ReportError( + ErrorBuilder.New() + .SetMessage("Maximum allowed coordinate cycle depth was exceeded.") + .SetCode(ErrorCodes.Validation.MaxIntrospectionDepthOverflow) + .SetLocations([selection]) + .SetPath(context.CreateErrorPath()) + .Build()); + } } diff --git a/src/HotChocolate/Core/src/Validation/Extensions/ValidationBuilderExtensions.Rules.cs b/src/HotChocolate/Core/src/Validation/Extensions/ValidationBuilderExtensions.Rules.cs index e8ddc3f5cb2..c0439f486da 100644 --- a/src/HotChocolate/Core/src/Validation/Extensions/ValidationBuilderExtensions.Rules.cs +++ b/src/HotChocolate/Core/src/Validation/Extensions/ValidationBuilderExtensions.Rules.cs @@ -1,3 +1,5 @@ +using System.Collections.Immutable; +using HotChocolate; using HotChocolate.Validation.Options; using HotChocolate.Validation.Rules; @@ -111,7 +113,7 @@ public static IValidationBuilder AddDocumentRules( /// Field selections on scalars or enums are never allowed, /// because they are the leaf nodes of any GraphQL query. /// - /// Conversely the leaf field selections of GraphQL queries + /// Conversely, the leaf field selections of GraphQL queries /// must be of type scalar or enum. Leaf selections on objects, /// interfaces, and unions without subfields are disallowed. /// @@ -169,7 +171,7 @@ public static IValidationBuilder AddFieldRules( /// AND /// /// The graph of fragment spreads must not form any cycles including - /// spreading itself. Otherwise an operation could infinitely spread or + /// spreading itself. Otherwise, an operation could infinitely spread or /// infinitely execute on cycles in the underlying data. /// /// https://spec.graphql.org/June2018/#sec-Fragment-spreads-must-not-form-cycles @@ -312,7 +314,10 @@ public static IValidationBuilder AddOperationRules( /// Specifies if depth analysis is skipped for introspection queries. /// /// - /// Defines if request depth overrides are allowed on a per request basis. + /// Defines if request depth overrides are allowed on a per-request basis. + /// + /// + /// A delegate that defines if the rule is enabled. /// /// /// Returns the for configuration chaining. @@ -321,12 +326,15 @@ public static IValidationBuilder AddMaxExecutionDepthRule( this IValidationBuilder builder, int maxAllowedExecutionDepth, bool skipIntrospectionFields = false, - bool allowRequestOverrides = false) + bool allowRequestOverrides = false, + Func? isEnabled = null) { return builder .TryAddValidationVisitor( (_, o) => new MaxExecutionDepthVisitor(o), - isCacheable: !allowRequestOverrides) + priority: 2, + isCacheable: !allowRequestOverrides, + isEnabled: isEnabled) .ModifyValidationOptions(o => { o.MaxAllowedExecutionDepth = maxAllowedExecutionDepth; @@ -334,6 +342,13 @@ public static IValidationBuilder AddMaxExecutionDepthRule( }); } + /// + /// Removes a validation rule that restricts the depth of a GraphQL request. + /// + public static IValidationBuilder RemoveMaxExecutionDepthRule( + this IValidationBuilder builder) + => builder.TryRemoveValidationVisitor(); + /// /// Adds a validation rule that only allows requests to use `__schema` or `__type` /// if the request carries an introspection allowed flag. @@ -341,14 +356,47 @@ public static IValidationBuilder AddMaxExecutionDepthRule( public static IValidationBuilder AddIntrospectionAllowedRule( this IValidationBuilder builder, Func? isEnabled = null) - => builder.TryAddValidationVisitor((_, _) => new IntrospectionVisitor(), false, isEnabled); + => builder.TryAddValidationVisitor( + (_, _) => new IntrospectionVisitor(), + priority: 0, + isCacheable: false, + isEnabled: isEnabled); /// /// Removes a validation rule that only allows requests to use `__schema` or `__type` /// if the request carries an introspection allowed flag. /// public static IValidationBuilder RemoveIntrospectionAllowedRule( + this IValidationBuilder builder) + => builder.TryRemoveValidationVisitor(); + + /// + /// Adds a validation rule that restricts the depth of a GraphQL introspection request. + /// + public static IValidationBuilder AddIntrospectionDepthRule( + this IValidationBuilder builder) + => builder.TryAddValidationVisitor(priority: 1); + + /// + /// Adds a validation rule that restricts the depth of coordinate cycles in GraphQL operations. + /// + public static IValidationBuilder AddMaxAllowedFieldCycleDepthRule( this IValidationBuilder builder, + ushort? defaultCycleLimit = 3, + (SchemaCoordinate Coordinate, ushort MaxAllowed)[]? coordinateCycleLimits = null, Func? isEnabled = null) - => builder.TryRemoveValidationVisitor(); + => builder.TryAddValidationVisitor( + (_, _) => new MaxAllowedFieldCycleDepthVisitor( + coordinateCycleLimits?.ToImmutableArray() + ?? ImmutableArray<(SchemaCoordinate, ushort)>.Empty, + defaultCycleLimit), + priority: 3, + isEnabled: isEnabled); + + /// + /// Removes a validation rule that restricts the depth of coordinate cycles in GraphQL operations. + /// + public static IValidationBuilder RemoveMaxAllowedFieldCycleDepthRule( + this IValidationBuilder builder) + => builder.TryRemoveValidationVisitor(); } diff --git a/src/HotChocolate/Core/src/Validation/Extensions/ValidationBuilderExtensions.cs b/src/HotChocolate/Core/src/Validation/Extensions/ValidationBuilderExtensions.cs index 477835a3e3d..887fb2b555d 100644 --- a/src/HotChocolate/Core/src/Validation/Extensions/ValidationBuilderExtensions.cs +++ b/src/HotChocolate/Core/src/Validation/Extensions/ValidationBuilderExtensions.cs @@ -110,13 +110,17 @@ internal static IValidationBuilder ModifyValidationOptions( /// Specifies if the validation visitor`s results are cacheable or /// if the visitor needs to be rerun on every request. /// + /// + /// The priority of the validation visitor. The lower the value the earlier the visitor is executed. + /// /// The validation visitor type. /// /// Returns the validation builder for configuration chaining. /// public static IValidationBuilder TryAddValidationVisitor( this IValidationBuilder builder, - bool isCacheable = true) + bool isCacheable = true, + ushort priority = ushort.MaxValue) where T : DocumentValidatorVisitor, new() { return builder.ConfigureValidation(m => @@ -124,7 +128,7 @@ public static IValidationBuilder TryAddValidationVisitor( { if (o.Rules.All(t => t.GetType() != typeof(DocumentValidatorRule))) { - o.Rules.Add(new DocumentValidatorRule(new T(), isCacheable)); + o.Rules.Add(new DocumentValidatorRule(new T(), isCacheable, priority)); } })); } @@ -143,6 +147,9 @@ public static IValidationBuilder TryAddValidationVisitor( /// Specifies if the validation visitor`s results are cacheable or /// if the visitor needs to be rerun on every request. /// + /// + /// The priority of the validation visitor. The lower the value the earlier the visitor is executed. + /// /// /// A delegate to determine if the validation visitor and should be added. /// @@ -154,6 +161,7 @@ public static IValidationBuilder TryAddValidationVisitor( this IValidationBuilder builder, Func factory, bool isCacheable = true, + ushort priority = ushort.MaxValue, Func? isEnabled = null) where T : DocumentValidatorVisitor { @@ -163,7 +171,7 @@ public static IValidationBuilder TryAddValidationVisitor( if (o.Rules.All(t => t.GetType() != typeof(DocumentValidatorRule)) && (isEnabled?.Invoke(s, o) ?? true)) { - o.Rules.Add(new DocumentValidatorRule(factory(s, o), isCacheable)); + o.Rules.Add(new DocumentValidatorRule(factory(s, o), isCacheable, priority)); } })); } diff --git a/src/HotChocolate/Core/src/Validation/Extensions/ValidationServiceCollectionExtensions.cs b/src/HotChocolate/Core/src/Validation/Extensions/ValidationServiceCollectionExtensions.cs index 5ab7538b971..308c8800bea 100644 --- a/src/HotChocolate/Core/src/Validation/Extensions/ValidationServiceCollectionExtensions.cs +++ b/src/HotChocolate/Core/src/Validation/Extensions/ValidationServiceCollectionExtensions.cs @@ -28,6 +28,7 @@ public static IValidationBuilder AddValidation( var builder = new DefaultValidationBuilder(schemaName, services); builder + .AddIntrospectionDepthRule() .AddDocumentRules() .AddOperationRules() .AddFieldRules() diff --git a/src/HotChocolate/Core/src/Validation/FieldDepthCycleTracker.cs b/src/HotChocolate/Core/src/Validation/FieldDepthCycleTracker.cs new file mode 100644 index 00000000000..7e001ba2073 --- /dev/null +++ b/src/HotChocolate/Core/src/Validation/FieldDepthCycleTracker.cs @@ -0,0 +1,89 @@ +using HotChocolate.Language; + +namespace HotChocolate.Validation; + +/// +/// Allows to track field cycle depths in a GraphQL query. +/// +public sealed class FieldDepthCycleTracker +{ + private readonly Dictionary _coordinates = new(); + private readonly List _limits = new(); + private ushort? _defaultMaxAllowed; + + /// + /// Adds a field coordinate to the tracker. + /// + /// + /// A field coordinate. + /// + /// + /// true if the field coordinate has not reached its cycle depth limit; + /// otherwise, false. + /// + public bool Add(SchemaCoordinate coordinate) + { + if (_coordinates.TryGetValue(coordinate, out var limit)) + { + return limit.Add(); + } + + if(_defaultMaxAllowed.HasValue) + { + _limits.TryPop(out limit); + limit ??= new CoordinateLimit(); + limit.Reset(_defaultMaxAllowed.Value); + _coordinates.Add(coordinate, limit); + return limit.Add(); + } + + return true; + } + + /// + /// Removes a field coordinate from the tracker. + /// + /// + /// A field coordinate. + /// + public void Remove(SchemaCoordinate coordinate) + { + if (_coordinates.TryGetValue(coordinate, out var limit)) + { + limit.Remove(); + } + } + + /// + /// Initializes the field depth tracker with the specified limits. + /// + /// + /// A collection of field coordinates and their cycle depth limits. + /// + /// + /// The default cycle depth limit for coordinates that were not explicitly defined. + /// + public void Initialize( + IEnumerable<(SchemaCoordinate Coordinate, ushort MaxAllowed)> limits, + ushort? defaultMaxAllowed = null) + { + foreach (var (coordinate, maxAllowed) in limits) + { + _limits.TryPop(out var limit); + limit ??= new CoordinateLimit(); + limit.Reset(maxAllowed); + _coordinates.Add(coordinate, limit); + } + + _defaultMaxAllowed = defaultMaxAllowed; + } + + /// + /// Resets the field depth tracker. + /// + public void Reset() + { + _limits.AddRange(_coordinates.Values); + _coordinates.Clear(); + } +} diff --git a/src/HotChocolate/Core/src/Validation/IDocumentValidatorContext.cs b/src/HotChocolate/Core/src/Validation/IDocumentValidatorContext.cs index b923a9e6c94..99402f7ad9a 100644 --- a/src/HotChocolate/Core/src/Validation/IDocumentValidatorContext.cs +++ b/src/HotChocolate/Core/src/Validation/IDocumentValidatorContext.cs @@ -153,6 +153,11 @@ public interface IDocumentValidatorContext /// bool UnexpectedErrorsDetected { get; set; } + /// + /// Defines that a fatal error was detected and that the analyzer will be aborted. + /// + bool FatalErrorDetected { get; set; } + /// /// A map to store arbitrary visitor data. /// @@ -161,17 +166,22 @@ public interface IDocumentValidatorContext /// /// When processing field merging this list holds the field pairs that are processed. /// - List CurrentFieldPairs { get; } + List CurrentFieldPairs { get; } /// /// When processing field merging this list holds the field pairs that are processed next. /// - List NextFieldPairs { get; } + List NextFieldPairs { get; } /// /// When processing field merging this set represents the already processed field pairs. /// - HashSet ProcessedFieldPairs { get; } + HashSet ProcessedFieldPairs { get; } + + /// + /// Gets the field depth cycle tracker. + /// + FieldDepthCycleTracker FieldDepth { get; } /// /// Rents a list of field infos. diff --git a/src/HotChocolate/Core/src/Validation/IDocumentValidatorRule.cs b/src/HotChocolate/Core/src/Validation/IDocumentValidatorRule.cs index 42eb261eeb2..17162a75005 100644 --- a/src/HotChocolate/Core/src/Validation/IDocumentValidatorRule.cs +++ b/src/HotChocolate/Core/src/Validation/IDocumentValidatorRule.cs @@ -7,6 +7,11 @@ namespace HotChocolate.Validation; /// public interface IDocumentValidatorRule { + /// + /// Gets the priority of this rule. Rules with a lower priority are executed first. + /// + ushort Priority { get; } + /// /// Defines if the result of this rule can be cached and reused on consecutive /// validations of the same GraphQL request document. diff --git a/src/HotChocolate/Core/src/Validation/Rules/ArgumentVisitor.cs b/src/HotChocolate/Core/src/Validation/Rules/ArgumentVisitor.cs index 0355dfc9e76..93989e8c3ed 100644 --- a/src/HotChocolate/Core/src/Validation/Rules/ArgumentVisitor.cs +++ b/src/HotChocolate/Core/src/Validation/Rules/ArgumentVisitor.cs @@ -30,16 +30,10 @@ namespace HotChocolate.Validation.Rules; /// /// http://facebook.github.io/graphql/June2018/#sec-Required-Arguments /// -internal sealed class ArgumentVisitor : TypeDocumentValidatorVisitor +internal sealed class ArgumentVisitor() + : TypeDocumentValidatorVisitor( + new SyntaxVisitorOptions { VisitDirectives = true, }) { - public ArgumentVisitor() - : base(new SyntaxVisitorOptions - { - VisitDirectives = true, - }) - { - } - protected override ISyntaxVisitorAction Enter( FieldNode node, IDocumentValidatorContext context) diff --git a/src/HotChocolate/Core/src/Validation/Rules/DocumentRule.cs b/src/HotChocolate/Core/src/Validation/Rules/DocumentRule.cs index beb698e48b5..c8ce2210229 100644 --- a/src/HotChocolate/Core/src/Validation/Rules/DocumentRule.cs +++ b/src/HotChocolate/Core/src/Validation/Rules/DocumentRule.cs @@ -3,22 +3,28 @@ namespace HotChocolate.Validation.Rules; /// +/// /// GraphQL execution will only consider the executable definitions /// Operation and Fragment. -/// +/// +/// /// Type system definitions and extensions are not executable, /// and are not considered during execution. -/// +/// +/// /// To avoid ambiguity, a document containing TypeSystemDefinition /// is invalid for execution. -/// +/// +/// /// GraphQL documents not intended to be directly executed may /// include TypeSystemDefinition. -/// -/// https://spec.graphql.org/June2018/#sec-Executable-Definitions +/// +/// https://spec.graphql.org/June2018/#sec-Executable-Definitions /// internal sealed class DocumentRule : IDocumentValidatorRule { + public ushort Priority => ushort.MaxValue; + public bool IsCacheable => true; public void Validate(IDocumentValidatorContext context, DocumentNode document) diff --git a/src/HotChocolate/Core/src/Validation/Rules/IntrospectionDepthVisitor.cs b/src/HotChocolate/Core/src/Validation/Rules/IntrospectionDepthVisitor.cs new file mode 100644 index 00000000000..c274f1ab743 --- /dev/null +++ b/src/HotChocolate/Core/src/Validation/Rules/IntrospectionDepthVisitor.cs @@ -0,0 +1,77 @@ +using HotChocolate.Language; +using HotChocolate.Language.Visitors; +using HotChocolate.Types; +using HotChocolate.Types.Introspection; +using HotChocolate.Utilities; + +namespace HotChocolate.Validation.Rules; + +/// +/// This rules ensures that recursive introspection fields cannot be used +/// to create endless cycles. +/// +internal sealed class IntrospectionDepthVisitor : TypeDocumentValidatorVisitor +{ + private readonly (SchemaCoordinate Coordinate, ushort MaxAllowed)[] _limits = + [ + (new SchemaCoordinate("__Type", "fields"), 1), + (new SchemaCoordinate("__Type", "inputFields"), 1), + (new SchemaCoordinate("__Type", "interfaces"), 1), + (new SchemaCoordinate("__Type", "possibleTypes"), 1), + (new SchemaCoordinate("__Type", "ofType"), 8) + ]; + + protected override ISyntaxVisitorAction Enter( + DocumentNode node, + IDocumentValidatorContext context) + { + context.FieldDepth.Initialize(_limits); + return base.Enter(node, context); + } + + protected override ISyntaxVisitorAction Enter( + FieldNode node, + IDocumentValidatorContext context) + { + if (IntrospectionFields.TypeName.EqualsOrdinal(node.Name.Value)) + { + return Skip; + } + + if (context.Types.TryPeek(out var type) + && type.NamedType() is IComplexOutputType ot + && ot.Fields.TryGetField(node.Name.Value, out var of)) + { + // we are only interested in fields if the root field is either + // __type or __schema. + if (context.OutputFields.Count == 0 + && !of.IsIntrospectionField) + { + return Skip; + } + + if (!context.FieldDepth.Add(of.Coordinate)) + { + context.ReportMaxIntrospectionDepthOverflow(node); + return Break; + } + + context.OutputFields.Push(of); + context.Types.Push(of.Type); + return Continue; + } + + context.UnexpectedErrorsDetected = true; + return Skip; + } + + protected override ISyntaxVisitorAction Leave( + FieldNode node, + IDocumentValidatorContext context) + { + context.FieldDepth.Remove(context.OutputFields.Peek().Coordinate); + context.Types.Pop(); + context.OutputFields.Pop(); + return Continue; + } +} diff --git a/src/HotChocolate/Core/src/Validation/Rules/IntrospectionVisitor.cs b/src/HotChocolate/Core/src/Validation/Rules/IntrospectionVisitor.cs index a907972b97d..b0b576f534c 100644 --- a/src/HotChocolate/Core/src/Validation/Rules/IntrospectionVisitor.cs +++ b/src/HotChocolate/Core/src/Validation/Rules/IntrospectionVisitor.cs @@ -36,7 +36,6 @@ protected override ISyntaxVisitorAction Enter( if (context.Types.TryPeek(out var type)) { var namedType = type.NamedType(); - if (context.Schema.QueryType == namedType && (IntrospectionFields.Schema.EqualsOrdinal(node.Name.Value) || IntrospectionFields.Type.EqualsOrdinal(node.Name.Value))) @@ -45,36 +44,10 @@ protected override ISyntaxVisitorAction Enter( return Break; } - if (namedType is IComplexOutputType ct) - { - if (ct.Fields.TryGetField(node.Name.Value, out var of)) - { - if (node.SelectionSet is null || - node.SelectionSet.Selections.Count == 0 || - of.Type.NamedType().IsLeafType()) - { - return Skip; - } - - context.OutputFields.Push(of); - context.Types.Push(of.Type); - return Continue; - } - - return Skip; - } + return Skip; } context.UnexpectedErrorsDetected = true; return Skip; } - - protected override ISyntaxVisitorAction Leave( - FieldNode node, - IDocumentValidatorContext context) - { - context.OutputFields.Pop(); - context.Types.Pop(); - return Continue; - } } diff --git a/src/HotChocolate/Core/src/Validation/Rules/MaxAllowedFieldCycleDepthVisitor.cs b/src/HotChocolate/Core/src/Validation/Rules/MaxAllowedFieldCycleDepthVisitor.cs new file mode 100644 index 00000000000..a59ee506cc7 --- /dev/null +++ b/src/HotChocolate/Core/src/Validation/Rules/MaxAllowedFieldCycleDepthVisitor.cs @@ -0,0 +1,75 @@ +using System.Collections.Immutable; +using HotChocolate.Language; +using HotChocolate.Language.Visitors; +using HotChocolate.Types; +using HotChocolate.Types.Introspection; +using HotChocolate.Utilities; + +namespace HotChocolate.Validation.Rules; + +/// +/// This rules allows to limit cycles across unique field coordinates. +/// +/// +/// Specifies specific coordinate cycle limits. +/// +/// +/// Specifies the default coordinate cycle limit. +/// +internal sealed class MaxAllowedFieldCycleDepthVisitor( + ImmutableArray<(SchemaCoordinate Coordinate, ushort MaxAllowed)> coordinateCycleLimits, + ushort? defaultCycleLimit) + : TypeDocumentValidatorVisitor +{ + protected override ISyntaxVisitorAction Enter( + DocumentNode node, + IDocumentValidatorContext context) + { + context.FieldDepth.Initialize(coordinateCycleLimits, defaultCycleLimit); + return base.Enter(node, context); + } + + protected override ISyntaxVisitorAction Enter( + FieldNode node, + IDocumentValidatorContext context) + { + if (IntrospectionFields.TypeName.EqualsOrdinal(node.Name.Value)) + { + return Skip; + } + + if (context.Types.TryPeek(out var type) + && type.NamedType() is IComplexOutputType ot + && ot.Fields.TryGetField(node.Name.Value, out var of)) + { + // we are ignoring introspection fields in this visitor. + if (of.IsIntrospectionField) + { + return Skip; + } + + if (!context.FieldDepth.Add(of.Coordinate)) + { + context.ReportMaxCoordinateCycleDepthOverflow(node); + return Break; + } + + context.OutputFields.Push(of); + context.Types.Push(of.Type); + return Continue; + } + + context.UnexpectedErrorsDetected = true; + return Skip; + } + + protected override ISyntaxVisitorAction Leave( + FieldNode node, + IDocumentValidatorContext context) + { + context.FieldDepth.Remove(context.OutputFields.Peek().Coordinate); + context.Types.Pop(); + context.OutputFields.Pop(); + return Continue; + } +} diff --git a/src/HotChocolate/Core/src/Validation/Rules/MaxExecutionDepthVisitor.cs b/src/HotChocolate/Core/src/Validation/Rules/MaxExecutionDepthVisitor.cs index b1f5802892f..e9fa0eb7d67 100644 --- a/src/HotChocolate/Core/src/Validation/Rules/MaxExecutionDepthVisitor.cs +++ b/src/HotChocolate/Core/src/Validation/Rules/MaxExecutionDepthVisitor.cs @@ -5,15 +5,10 @@ namespace HotChocolate.Validation.Rules; -internal sealed class MaxExecutionDepthVisitor : DocumentValidatorVisitor +internal sealed class MaxExecutionDepthVisitor( + IMaxExecutionDepthOptionsAccessor options) + : DocumentValidatorVisitor { - private readonly IMaxExecutionDepthOptionsAccessor _options; - - public MaxExecutionDepthVisitor(IMaxExecutionDepthOptionsAccessor options) - { - _options = options; - } - protected override ISyntaxVisitorAction Enter( DocumentNode node, IDocumentValidatorContext context) @@ -33,9 +28,9 @@ protected override ISyntaxVisitorAction Enter( } // otherwise we will go with the configured value - else if(_options.MaxAllowedExecutionDepth.HasValue) + else if(options.MaxAllowedExecutionDepth.HasValue) { - context.Allowed = _options.MaxAllowedExecutionDepth.Value; + context.Allowed = options.MaxAllowedExecutionDepth.Value; } // if there is no configured value we will just stop traversing the graph @@ -80,7 +75,7 @@ protected override ISyntaxVisitorAction Enter( FieldNode node, IDocumentValidatorContext context) { - if (_options.SkipIntrospectionFields && + if (options.SkipIntrospectionFields && node.Name.Value.StartsWith("__")) { return Skip; diff --git a/src/HotChocolate/Core/test/Execution.Tests/DependencyInjection/RequestExecutorBuilderExtensions_Validation.Tests.cs b/src/HotChocolate/Core/test/Execution.Tests/DependencyInjection/RequestExecutorBuilderExtensions_Validation.Tests.cs index a4f29ced6be..39b96d83efa 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/DependencyInjection/RequestExecutorBuilderExtensions_Validation.Tests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/DependencyInjection/RequestExecutorBuilderExtensions_Validation.Tests.cs @@ -221,6 +221,7 @@ public class MockVisitor : DocumentValidatorVisitor; public class MockRule : IDocumentValidatorRule { + public ushort Priority => ushort.MaxValue; public bool IsCacheable => true; public void Validate(IDocumentValidatorContext context, DocumentNode document) diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/IntegrationTests.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/IntegrationTests.cs index 80bd0bd36d4..497e0886829 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/IntegrationTests.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/IntegrationTests.cs @@ -146,7 +146,7 @@ private static IServiceProvider CreateApplicationServices( .AddSingleton(); serviceCollection - .AddGraphQLServer(disableCostAnalyzer: true) + .AddGraphQLServer(disableDefaultSecurity: true) .AddCustomModule() .AddGlobalObjectIdentification() .AddMutationConventions(); diff --git a/src/HotChocolate/Core/test/Validation.Tests/DocumentValidatorTests.cs b/src/HotChocolate/Core/test/Validation.Tests/DocumentValidatorTests.cs index bea9c2ece04..9744480ffe7 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/DocumentValidatorTests.cs +++ b/src/HotChocolate/Core/test/Validation.Tests/DocumentValidatorTests.cs @@ -861,6 +861,12 @@ public async Task Produce_Many_Errors_50000_query() await ExpectErrors(FileResource.Open("50000_query.graphql")); } + [Fact] + public async Task Introspection_Cycle_Detected() + { + await ExpectErrors(FileResource.Open("introspection_with_cycle.graphql")); + } + private Task ExpectValid(string sourceText) => ExpectValid(null, null, sourceText); private async Task ExpectValid(ISchema? schema, IDocumentValidator? validator, string sourceText) diff --git a/src/HotChocolate/Core/test/Validation.Tests/HotChocolate.Validation.Tests.csproj b/src/HotChocolate/Core/test/Validation.Tests/HotChocolate.Validation.Tests.csproj index ca7f3e84ba0..33e66b35bc2 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/HotChocolate.Validation.Tests.csproj +++ b/src/HotChocolate/Core/test/Validation.Tests/HotChocolate.Validation.Tests.csproj @@ -18,5 +18,8 @@ Always + + Always + diff --git a/src/HotChocolate/Core/test/Validation.Tests/IntrospectionDepthRuleTests.cs b/src/HotChocolate/Core/test/Validation.Tests/IntrospectionDepthRuleTests.cs new file mode 100644 index 00000000000..4f682209691 --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/IntrospectionDepthRuleTests.cs @@ -0,0 +1,205 @@ +using CookieCrumble; +using HotChocolate.Language; +using HotChocolate.Validation.Options; +using Microsoft.Extensions.DependencyInjection; + +namespace HotChocolate.Validation; + +public class IntrospectionDepthRuleTests() + : DocumentValidatorVisitorTestBase(b => b.AddIntrospectionDepthRule()) +{ + [Fact] public void Introspection_With_Cycles_Will_Fail() + { + // arrange + IDocumentValidatorContext context = ValidationUtils.CreateContext(); + + var query = Utf8GraphQLParser.Parse(FileResource.Open("introspection_with_cycle.graphql")); + context.Prepare(query); + + // act + Rule.Validate(context, query); + + // assert + Assert.Equal( + "Maximum allowed introspection depth exceeded.", + Assert.Single(context.Errors).Message); + } + + [Fact] + public void Introspection_Without_Cycles() + { + // arrange + IDocumentValidatorContext context = ValidationUtils.CreateContext(); + + var query = Utf8GraphQLParser.Parse(FileResource.Open("introspection_without_cycle.graphql")); + context.Prepare(query); + + // act + Rule.Validate(context, query); + + // assert + Assert.Empty(context.Errors); + } +} + +public class MaxAllowedFieldCycleDepthRuleTests() + : DocumentValidatorVisitorTestBase(b => b.AddMaxAllowedFieldCycleDepthRule()) +{ + [Fact] + public void Max_3_Cycles_Allowed_Success() + { + // arrange + IDocumentValidatorContext context = ValidationUtils.CreateContext(); + + var query = Utf8GraphQLParser.Parse( + """ + { + human { + relatives { + relatives { + relatives { + name + } + } + } + } + } + """); + context.Prepare(query); + + // act + Rule.Validate(context, query); + + // assert + Assert.Empty(context.Errors); + Assert.False(context.FatalErrorDetected); + Assert.False(context.UnexpectedErrorsDetected); + } + + [Fact] + public void Max_3_Cycles_Allowed_Fail() + { + // arrange + IDocumentValidatorContext context = ValidationUtils.CreateContext(); + + var query = Utf8GraphQLParser.Parse( + """ + { + human { + relatives { + relatives { + relatives { + relatives { + name + } + } + } + } + } + } + """); + context.Prepare(query); + + // act + Rule.Validate(context, query); + + // assert + Assert.Equal( + "Maximum allowed coordinate cycle depth was exceeded.", + Assert.Single(context.Errors).Message); + Assert.True(context.FatalErrorDetected); + Assert.False(context.UnexpectedErrorsDetected); + } + + [Fact] + public void Max_2_Relative_Field_Allowed_Success() + { + // arrange + var serviceCollection = new ServiceCollection(); + + var builder = serviceCollection + .AddValidation() + .ConfigureValidation(c => c.Modifiers.Add(o => o.Rules.Clear())) + .ModifyValidationOptions(o => o.MaxAllowedErrors = int.MaxValue); + builder.AddMaxAllowedFieldCycleDepthRule( + null, + [(new SchemaCoordinate("Human", "relatives"), 2)]); + + IServiceProvider services = serviceCollection.BuildServiceProvider(); + + var rule = services + .GetRequiredService() + .GetRules(Schema.DefaultName).First(); + + IDocumentValidatorContext context = ValidationUtils.CreateContext(); + + var query = Utf8GraphQLParser.Parse( + """ + { + human { + relatives { + relatives { + name + } + } + } + } + """); + context.Prepare(query); + + // act + rule.Validate(context, query); + + // assert + Assert.Empty(context.Errors); + Assert.False(context.FatalErrorDetected); + Assert.False(context.UnexpectedErrorsDetected); + } + + [Fact] + public void Max_1_Relative_Field_Allowed_Fail() + { + // arrange + var serviceCollection = new ServiceCollection(); + + var builder = serviceCollection + .AddValidation() + .ConfigureValidation(c => c.Modifiers.Add(o => o.Rules.Clear())) + .ModifyValidationOptions(o => o.MaxAllowedErrors = int.MaxValue); + builder.AddMaxAllowedFieldCycleDepthRule( + null, + [(new SchemaCoordinate("Human", "relatives"), 1)]); + + IServiceProvider services = serviceCollection.BuildServiceProvider(); + + var rule = services + .GetRequiredService() + .GetRules(Schema.DefaultName).First(); + + IDocumentValidatorContext context = ValidationUtils.CreateContext(); + + var query = Utf8GraphQLParser.Parse( + """ + { + human { + relatives { + relatives { + name + } + } + } + } + """); + context.Prepare(query); + + // act + rule.Validate(context, query); + + // assert + Assert.Equal( + "Maximum allowed coordinate cycle depth was exceeded.", + Assert.Single(context.Errors).Message); + Assert.True(context.FatalErrorDetected); + Assert.False(context.UnexpectedErrorsDetected); + } +} diff --git a/src/HotChocolate/Core/test/Validation.Tests/__resources__/introspection_with_cycle.graphql b/src/HotChocolate/Core/test/Validation.Tests/__resources__/introspection_with_cycle.graphql new file mode 100644 index 00000000000..ac0a3e54237 --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__resources__/introspection_with_cycle.graphql @@ -0,0 +1,93 @@ +query IntrospectionQuery { + __schema { + queryType { + name + } + mutationType { + name + } + types { + ... FullType + } + directives { + name + description + args { + ... InputValue + } + onOperation + onFragment + onField + } + } +} + +fragment FullType on __Type { + kind + name + description + fields(includeDeprecated: true) { + name + description + args { + ... InputValue + } + type { + ... TypeRef + fields { + name + } + } + isDeprecated + deprecationReason + } + inputFields { + ... InputValue + } + interfaces { + ... TypeRef + } + enumValues(includeDeprecated: true) { + name + description + isDeprecated + deprecationReason + } + possibleTypes { + ... TypeRef + } +} + +fragment InputValue on __InputValue { + name + description + type { + ... TypeRef + } + defaultValue +} + +fragment TypeRef on __Type { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + } + } + } + } + } +} diff --git a/src/HotChocolate/Core/test/Validation.Tests/__resources__/introspection_without_cycle.graphql b/src/HotChocolate/Core/test/Validation.Tests/__resources__/introspection_without_cycle.graphql new file mode 100644 index 00000000000..bab1d210a34 --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__resources__/introspection_without_cycle.graphql @@ -0,0 +1,90 @@ +query IntrospectionQuery { + __schema { + queryType { + name + } + mutationType { + name + } + types { + ... FullType + } + directives { + name + description + args { + ... InputValue + } + onOperation + onFragment + onField + } + } +} + +fragment FullType on __Type { + kind + name + description + fields(includeDeprecated: true) { + name + description + args { + ... InputValue + } + type { + ... TypeRef + } + isDeprecated + deprecationReason + } + inputFields { + ... InputValue + } + interfaces { + ... TypeRef + } + enumValues(includeDeprecated: true) { + name + description + isDeprecated + deprecationReason + } + possibleTypes { + ... TypeRef + } +} + +fragment InputValue on __InputValue { + name + description + type { + ... TypeRef + } + defaultValue +} + +fragment TypeRef on __Type { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + } + } + } + } + } +} diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/DocumentValidatorTests.Introspection_Cycle_Detected.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/DocumentValidatorTests.Introspection_Cycle_Detected.snap new file mode 100644 index 00000000000..5599ef7c648 --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/DocumentValidatorTests.Introspection_Cycle_Detected.snap @@ -0,0 +1,41 @@ +[ + { + "Message": "Maximum allowed introspection depth exceeded.", + "Code": "HC0086", + "Path": { + "Name": "type", + "Parent": { + "Name": "fields", + "Parent": { + "Name": "types", + "Parent": { + "Name": "__schema", + "Parent": { + "Parent": null, + "Length": 0, + "IsRoot": true + }, + "Length": 1, + "IsRoot": false + }, + "Length": 2, + "IsRoot": false + }, + "Length": 3, + "IsRoot": false + }, + "Length": 4, + "IsRoot": false + }, + "Locations": [ + { + "Line": 37, + "Column": 7 + } + ], + "Extensions": { + "code": "HC0086" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Fusion/src/Core/DependencyInjection/FusionRequestExecutorBuilderExtensions.cs b/src/HotChocolate/Fusion/src/Core/DependencyInjection/FusionRequestExecutorBuilderExtensions.cs index bf78691069c..39fd20bca4e 100644 --- a/src/HotChocolate/Fusion/src/Core/DependencyInjection/FusionRequestExecutorBuilderExtensions.cs +++ b/src/HotChocolate/Fusion/src/Core/DependencyInjection/FusionRequestExecutorBuilderExtensions.cs @@ -53,7 +53,7 @@ public static FusionGatewayBuilder AddFusionGatewayServer( sp.GetRequiredService())); var builder = services - .AddGraphQLServer(graphName, disableCostAnalyzer: true) + .AddGraphQLServer(graphName, disableDefaultSecurity: true) .UseField(next => next) .AddOperationCompilerOptimizer() .AddOperationCompilerOptimizer() diff --git a/src/HotChocolate/Fusion/test/Shared/DemoProject.cs b/src/HotChocolate/Fusion/test/Shared/DemoProject.cs index a2889806c47..a5abf239d40 100644 --- a/src/HotChocolate/Fusion/test/Shared/DemoProject.cs +++ b/src/HotChocolate/Fusion/test/Shared/DemoProject.cs @@ -92,7 +92,7 @@ public static async Task CreateAsync(CancellationToken ct = default s => s .AddRouting() .AddSingleton() - .AddGraphQLServer(disableCostAnalyzer: true) + .AddGraphQLServer(disableDefaultSecurity: true) .AddQueryType() .AddMutationType() .AddSubscriptionType() @@ -115,7 +115,7 @@ public static async Task CreateAsync(CancellationToken ct = default s => s .AddRouting() .AddSingleton() - .AddGraphQLServer(disableCostAnalyzer: true) + .AddGraphQLServer(disableDefaultSecurity: true) .AddQueryType() .AddMutationType() .AddSubscriptionType() @@ -138,7 +138,7 @@ public static async Task CreateAsync(CancellationToken ct = default s => s .AddRouting() .AddSingleton() - .AddGraphQLServer(disableCostAnalyzer: true) + .AddGraphQLServer(disableDefaultSecurity: true) .AddQueryType() .AddMutationType() .AddMutationConventions() @@ -159,7 +159,7 @@ public static async Task CreateAsync(CancellationToken ct = default s => s .AddRouting() .AddSingleton() - .AddGraphQLServer(disableCostAnalyzer: true) + .AddGraphQLServer(disableDefaultSecurity: true) .AddQueryType() .AddMutationType() .AddGlobalObjectIdentification() @@ -180,7 +180,7 @@ public static async Task CreateAsync(CancellationToken ct = default var shipping = testServerFactory.Create( s => s .AddRouting() - .AddGraphQLServer(disableCostAnalyzer: true) + .AddGraphQLServer(disableDefaultSecurity: true) .AddQueryType() .ConfigureSchema(b => b.SetContextData(GlobalIdSupportEnabled, 1)) .AddConvention(_ => new DefaultNamingConventions()), @@ -198,7 +198,7 @@ public static async Task CreateAsync(CancellationToken ct = default var shipping2 = testServerFactory.Create( s => s .AddRouting() - .AddGraphQLServer(disableCostAnalyzer: true) + .AddGraphQLServer(disableDefaultSecurity: true) .AddQueryType() .ConfigureSchema(b => b.SetContextData(GlobalIdSupportEnabled, 1)) .AddConvention(_ => new DefaultNamingConventions()), @@ -216,7 +216,7 @@ public static async Task CreateAsync(CancellationToken ct = default var appointment = testServerFactory.Create( s => s .AddRouting() - .AddGraphQLServer(disableCostAnalyzer: true) + .AddGraphQLServer(disableDefaultSecurity: true) .AddQueryType() .AddObjectType() .AddObjectType() @@ -236,7 +236,7 @@ public static async Task CreateAsync(CancellationToken ct = default var patient1 = testServerFactory.Create( s => s .AddRouting() - .AddGraphQLServer(disableCostAnalyzer: true) + .AddGraphQLServer(disableDefaultSecurity: true) .AddQueryType() .AddGlobalObjectIdentification() .AddConvention(_ => new DefaultNamingConventions()), @@ -254,7 +254,7 @@ public static async Task CreateAsync(CancellationToken ct = default var books = testServerFactory.Create( s => s .AddRouting() - .AddGraphQLServer(disableCostAnalyzer: true) + .AddGraphQLServer(disableDefaultSecurity: true) .AddQueryType() .AddConvention(_ => new DefaultNamingConventions()), c => c @@ -271,7 +271,7 @@ public static async Task CreateAsync(CancellationToken ct = default var authors = testServerFactory.Create( s => s .AddRouting() - .AddGraphQLServer(disableCostAnalyzer: true) + .AddGraphQLServer(disableDefaultSecurity: true) .AddQueryType() .AddConvention(_ => new DefaultNamingConventions()), c => c @@ -288,7 +288,7 @@ public static async Task CreateAsync(CancellationToken ct = default var resale = testServerFactory.Create( s => s .AddRouting() - .AddGraphQLServer(disableCostAnalyzer: true) + .AddGraphQLServer(disableDefaultSecurity: true) .AddQueryType() .AddGlobalObjectIdentification() .AddMutationConventions() diff --git a/src/HotChocolate/Language/src/Language/ListExtensions.cs b/src/HotChocolate/Language/src/Language/ListExtensions.cs index c4e6bb5cfe3..92ba6f4ab10 100644 --- a/src/HotChocolate/Language/src/Language/ListExtensions.cs +++ b/src/HotChocolate/Language/src/Language/ListExtensions.cs @@ -16,7 +16,7 @@ public static T Pop(this IList list) return p; } - public static bool TryPop(this IList list, [NotNullWhen(true)] out T item) + public static bool TryPop(this IList list, [MaybeNullWhen(false)] out T item) { if (list.Count > 0) { diff --git a/src/HotChocolate/Raven/test/Data.Raven.Tests/AnnotationBasedTests.cs b/src/HotChocolate/Raven/test/Data.Raven.Tests/AnnotationBasedTests.cs index 285a44018a0..16552270dc6 100644 --- a/src/HotChocolate/Raven/test/Data.Raven.Tests/AnnotationBasedTests.cs +++ b/src/HotChocolate/Raven/test/Data.Raven.Tests/AnnotationBasedTests.cs @@ -243,7 +243,7 @@ public async Task Executable_Should_Work() public ValueTask CreateExecutorAsync() => new ServiceCollection() .AddSingleton(CreateDocumentStore()) #if NET7_0_OR_GREATER - .AddGraphQLServer(disableCostAnalyzer: true) + .AddGraphQLServer(disableDefaultSecurity: true) #else .AddGraphQLServer() #endif From d6b5f7ec8a108f42d97495c8a1335b7d8ffdb90e Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Sat, 28 Sep 2024 21:19:17 +0200 Subject: [PATCH 018/154] Fixed XML Documentation (cherry picked from commit 13c7aca68669e538850dc54791005be91c590d06) --- src/HotChocolate/Primitives/src/Primitives/SchemaCoordinate.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HotChocolate/Primitives/src/Primitives/SchemaCoordinate.cs b/src/HotChocolate/Primitives/src/Primitives/SchemaCoordinate.cs index 574b72d70d5..d704771dd52 100644 --- a/src/HotChocolate/Primitives/src/Primitives/SchemaCoordinate.cs +++ b/src/HotChocolate/Primitives/src/Primitives/SchemaCoordinate.cs @@ -8,7 +8,7 @@ namespace HotChocolate; /// /// -/// A is a human readable string that uniquely identifies a +/// A is a human-readable string that uniquely identifies a /// schema element within a GraphQL Schema. /// A schema element is a specific instance of a named type, field, input field, enum value, /// field argument, directive, or directive argument. From 955d8a228eb75a6149f85443a1867434603500e6 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Sat, 28 Sep 2024 21:19:55 +0200 Subject: [PATCH 019/154] Fixed error code for MaxCoordinateCycleDepthOverflow (cherry picked from commit 99946fe42bdfea3ed57a088c61a748128c8cd204) --- src/HotChocolate/Core/src/Validation/ErrorHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HotChocolate/Core/src/Validation/ErrorHelper.cs b/src/HotChocolate/Core/src/Validation/ErrorHelper.cs index e1984388a14..1417160af04 100644 --- a/src/HotChocolate/Core/src/Validation/ErrorHelper.cs +++ b/src/HotChocolate/Core/src/Validation/ErrorHelper.cs @@ -731,7 +731,7 @@ public static void ReportMaxCoordinateCycleDepthOverflow( context.ReportError( ErrorBuilder.New() .SetMessage("Maximum allowed coordinate cycle depth was exceeded.") - .SetCode(ErrorCodes.Validation.MaxIntrospectionDepthOverflow) + .SetCode(ErrorCodes.Validation.MaxCoordinateCycleDepthOverflow) .SetLocations([selection]) .SetPath(context.CreateErrorPath()) .Build()); From 8c4826951cc09dbeae3a682af5d2670b9c2fa656 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Sun, 29 Sep 2024 18:19:49 +0200 Subject: [PATCH 020/154] Fixed parser error when selection-set is missing an EndBrace. (#7526) (cherry picked from commit 33696439445c6c2142b4fca002ef5cd0e93d1e85) --- .../ErrorBehaviorTests.SyntaxError.json | 2 +- .../Language.Utf8/Utf8GraphQLParser.Operations.cs | 3 ++- .../test/Language.Tests/Parser/ErrorTests.cs | 13 +++++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 src/HotChocolate/Language/test/Language.Tests/Parser/ErrorTests.cs diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.SyntaxError.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.SyntaxError.json index 363698cb9f2..513b6e4200c 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.SyntaxError.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.SyntaxError.json @@ -1,7 +1,7 @@ { "errors": [ { - "message": "Expected a `Name`-token, but found a `EndOfFile`-token.", + "message": "Expected a `RightBrace`-token, but found a `EndOfFile`-token.", "locations": [ { "line": 1, diff --git a/src/HotChocolate/Language/src/Language.Utf8/Utf8GraphQLParser.Operations.cs b/src/HotChocolate/Language/src/Language.Utf8/Utf8GraphQLParser.Operations.cs index d29a3892fa5..a82e452346c 100644 --- a/src/HotChocolate/Language/src/Language.Utf8/Utf8GraphQLParser.Operations.cs +++ b/src/HotChocolate/Language/src/Language.Utf8/Utf8GraphQLParser.Operations.cs @@ -180,7 +180,8 @@ private SelectionSetNode ParseSelectionSet() // skip opening token MoveNext(); - while (_reader.Kind != TokenKind.RightBrace) + while (_reader.Kind != TokenKind.RightBrace + && _reader.Kind != TokenKind.EndOfFile) { selections.Add(ParseSelection()); } diff --git a/src/HotChocolate/Language/test/Language.Tests/Parser/ErrorTests.cs b/src/HotChocolate/Language/test/Language.Tests/Parser/ErrorTests.cs new file mode 100644 index 00000000000..69c20f84a02 --- /dev/null +++ b/src/HotChocolate/Language/test/Language.Tests/Parser/ErrorTests.cs @@ -0,0 +1,13 @@ +using Xunit; + +namespace HotChocolate.Language; + +public class ErrorTests +{ + [Fact] + public void Missing_EndBrace_For_SelectionSet() + { + var ex = Assert.Throws(() => Utf8GraphQLParser.Parse("query { x")); + Assert.Equal("Expected a `RightBrace`-token, but found a `EndOfFile`-token.", ex.Message); + } +} From 6bb769c49d21c2ff18d0b29ccb3530b5bf74add9 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Sun, 29 Sep 2024 19:22:37 +0200 Subject: [PATCH 021/154] Fixed Test Assertion (cherry picked from commit 1f6d1ed761f7658baf63c0c9f3260fea8658f027) --- .../test/CodeGeneration.CSharp.Tests/ErrorGeneratorTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/ErrorGeneratorTests.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/ErrorGeneratorTests.cs index 528ce789416..1af2a362e0d 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/ErrorGeneratorTests.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/ErrorGeneratorTests.cs @@ -26,7 +26,7 @@ public void Generate_SyntaxError() { Assert.Equal("SS0001", error.Code); Assert.Equal( - "Expected a `Name`-token, but found a `EndOfFile`-token.", + "Expected a `RightBrace`-token, but found a `EndOfFile`-token.", error.Message); }); } From b7c55f5d02dacb4eee05e53d06dd819c63563003 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Sun, 29 Sep 2024 22:22:55 +0200 Subject: [PATCH 022/154] Added DataLoader group generator. (#7528) (cherry picked from commit c174e8620b4314d3d29df1f2f9cb0df137011a28) --- .../Attributes/DataLoaderGroupAttribute.cs | 20 +++ .../FileBuilders/DataLoaderFileBuilder.cs | 87 +++++++++- .../DataLoaderModuleFileBuilder.cs | 8 + .../FileBuilders/ModuleFileBuilder.cs | 6 + .../Generators/DataLoaderGenerator.cs | 20 +++ .../Generators/DataLoaderModuleGenerator.cs | 18 ++ .../Generators/TypeModuleSyntaxGenerator.cs | 18 ++ .../Helpers/DataLoaderAttributeHelper.cs | 46 ++++++ .../Types.Analyzers/Models/DataLoaderInfo.cs | 17 +- .../Models/GroupedDataLoaderInfo.cs | 20 +++ .../Types.Analyzers.Tests/DataLoaderTests.cs | 31 ++++ .../Types/DataLoaders.cs | 3 + ...chDataLoader_With_Group_MatchesSnapshot.md | 156 ++++++++++++++++++ 13 files changed, 440 insertions(+), 10 deletions(-) create mode 100644 src/GreenDonut/src/Core/Attributes/DataLoaderGroupAttribute.cs create mode 100644 src/HotChocolate/Core/src/Types.Analyzers/Models/GroupedDataLoaderInfo.cs create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_MatchesSnapshot.md diff --git a/src/GreenDonut/src/Core/Attributes/DataLoaderGroupAttribute.cs b/src/GreenDonut/src/Core/Attributes/DataLoaderGroupAttribute.cs new file mode 100644 index 00000000000..4aad4e63fe6 --- /dev/null +++ b/src/GreenDonut/src/Core/Attributes/DataLoaderGroupAttribute.cs @@ -0,0 +1,20 @@ +namespace GreenDonut; + +/// +/// Allows to group multiple DataLoaders together into a context class +/// that can be used to inject multiple DataLoader at once into classes. +/// +/// +/// The group names that are used to group multiple DataLoaders together. +/// +[AttributeUsage( + AttributeTargets.Method + | AttributeTargets.Class, + AllowMultiple = true)] +public class DataLoaderGroupAttribute(params string[] groupNames) : Attribute +{ + /// + /// Gets the group names that are used to group multiple DataLoaders together. + /// + public string[] GroupNames { get; } = groupNames; +} diff --git a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs index 5f283518d5b..88ee006496b 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs @@ -1,4 +1,5 @@ using System.Collections.Immutable; +using System.Security.Cryptography.X509Certificates; using System.Text; using HotChocolate.Types.Analyzers.Helpers; using HotChocolate.Types.Analyzers.Inspectors; @@ -225,8 +226,8 @@ public void WriteDataLoaderLoadMethod( value.ToFullyQualified(), kind is DataLoaderKind.Group ? "[]" : string.Empty, value.IsValueType ? string.Empty : "?"); - _writer.WriteIndentedLine( - "global::{0}<{1}{2}> context,", + _writer.WriteIndentedLine( + "global::{0}<{1}{2}> context,", WellKnownTypes.DataLoaderFetchContext, value.ToFullyQualified(), kind is DataLoaderKind.Group ? "[]" : string.Empty); @@ -277,7 +278,6 @@ public void WriteDataLoaderLoadMethod( parameter.Type.PrintNullRefQualifier(), parameter.StateKey, defaultValueString); - } else if (parameter.Type.IsNullableType()) { @@ -484,6 +484,87 @@ kind is DataLoaderKind.Cache _writer.WriteLine(").ConfigureAwait(false);"); } + public void WriteDataLoaderGroupClass( + string groupClassName, + IReadOnlyList dataLoaders) + { + _writer.WriteIndentedLine("public interface I{0}", groupClassName); + _writer.WriteIndentedLine("{"); + _writer.IncreaseIndent(); + + foreach (var dataLoader in dataLoaders) + { + _writer.WriteIndentedLine("{0} {1} {{ get; }}", dataLoader.InterfaceName, dataLoader.Name); + } + + _writer.DecreaseIndent(); + _writer.WriteIndentedLine("}"); + + _writer.WriteIndentedLine("public sealed class {0} : I{0}", groupClassName); + _writer.WriteIndentedLine("{"); + _writer.IncreaseIndent(); + + _writer.WriteIndentedLine("private readonly IServiceProvider _services;"); + + foreach (var dataLoader in dataLoaders) + { + _writer.WriteIndentedLine("private {0}? {1};", dataLoader.InterfaceName, dataLoader.FieldName); + } + + _writer.WriteLine(); + _writer.WriteIndentedLine("public {0}(IServiceProvider services)", groupClassName); + _writer.WriteIndentedLine("{"); + _writer.IncreaseIndent(); + _writer.WriteIndentedLine("_services = services"); + _writer.IncreaseIndent(); + _writer.WriteIndentedLine("?? throw new ArgumentNullException(nameof(services));"); + _writer.DecreaseIndent(); + _writer.DecreaseIndent(); + _writer.WriteIndentedLine("}"); + + foreach (var dataLoader in dataLoaders) + { + _writer.WriteIndentedLine( + "public {0} {1}", + dataLoader.InterfaceName, + dataLoader.Name); + + _writer.WriteIndentedLine("{"); + _writer.IncreaseIndent(); + + _writer.WriteIndentedLine("get"); + + _writer.WriteIndentedLine("{"); + _writer.IncreaseIndent(); + + _writer.WriteIndentedLine( + "if ({0} is null)", + dataLoader.FieldName); + + _writer.WriteIndentedLine("{"); + _writer.IncreaseIndent(); + + _writer.WriteIndentedLine( + "{0} = _services.GetRequiredService<{1}>();", + dataLoader.FieldName, + dataLoader.InterfaceName); + + _writer.DecreaseIndent(); + _writer.WriteIndentedLine("}"); + _writer.WriteLine(); + _writer.WriteIndentedLine("return {0}!;", dataLoader.FieldName); + + _writer.DecreaseIndent(); + _writer.WriteIndentedLine("}"); + + _writer.DecreaseIndent(); + _writer.WriteIndentedLine("}"); + } + + _writer.DecreaseIndent(); + _writer.WriteIndentedLine("}"); + } + public void WriteLine() => _writer.WriteLine(); private static ITypeSymbol ExtractMapType(ITypeSymbol returnType) diff --git a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderModuleFileBuilder.cs b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderModuleFileBuilder.cs index 1285bd15f1a..237d98ebc5e 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderModuleFileBuilder.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderModuleFileBuilder.cs @@ -93,6 +93,14 @@ public void WriteAddDataLoader(string dataLoaderType, string dataLoaderInterface dataLoaderType); } + public void WriteAddDataLoaderGroup(string groupType, string groupInterfaceType) + { + _writer.WriteIndentedLine( + "services.AddScoped();", + groupInterfaceType, + groupType); + } + public override string ToString() => _sb.ToString(); diff --git a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ModuleFileBuilder.cs b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ModuleFileBuilder.cs index 041585490be..2c8470bb124 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ModuleFileBuilder.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ModuleFileBuilder.cs @@ -269,6 +269,12 @@ public void WriteRegisterDataLoader(string typeName) public void WriteRegisterDataLoader(string typeName, string interfaceTypeName) => _writer.WriteIndentedLine("builder.AddDataLoader();", interfaceTypeName, typeName); + public void WriteRegisterDataLoaderGroup(string typeName, string interfaceTypeName) + => _writer.WriteIndentedLine( + "builder.Services.AddScoped();", + interfaceTypeName, + typeName); + public void WriteTryAddOperationType(OperationType type) { _writer.WriteIndentedLine("builder.ConfigureSchema("); diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Generators/DataLoaderGenerator.cs b/src/HotChocolate/Core/src/Types.Analyzers/Generators/DataLoaderGenerator.cs index 07718defca2..d9e8be27124 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Generators/DataLoaderGenerator.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Generators/DataLoaderGenerator.cs @@ -92,6 +92,26 @@ private static void WriteDataLoader( hasDataLoaders = true; } + List? buffer = null; + foreach (var dataLoaderGroup in group + .Where(d => d.Groups.Count > 0) + .SelectMany(d => d.Groups, (d, g) => new { DataLoader = d, Group = g }) + .GroupBy(t => t.Group, t => t.DataLoader, StringComparer.Ordinal) + .OrderBy(t => t.Key, StringComparer.Ordinal)) + { + var isPublic = defaults.IsInterfacePublic ?? true; + var groups = group.Select( + t => new GroupedDataLoaderInfo( + t.NameWithoutSuffix, + t.InterfaceName, + t.IsInterfacePublic ?? isPublic)); + + buffer ??= new(); + buffer.Clear(); + buffer.AddRange(groups); + generator.WriteDataLoaderGroupClass(dataLoaderGroup.Key, buffer); + } + generator.WriteEndNamespace(); } diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Generators/DataLoaderModuleGenerator.cs b/src/HotChocolate/Core/src/Types.Analyzers/Generators/DataLoaderModuleGenerator.cs index 4d010079fcb..2f9de065f56 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Generators/DataLoaderModuleGenerator.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Generators/DataLoaderModuleGenerator.cs @@ -19,6 +19,7 @@ public void Generate( return; } + HashSet<(string InterfaceName, string ClassName)>? groups = null; var generator = new DataLoaderModuleFileBuilder(module.ModuleName); generator.WriteHeader(); @@ -43,10 +44,27 @@ public void Generate( var typeName = $"{dataLoader.Namespace}.{dataLoader.Name}"; var interfaceTypeName = $"{dataLoader.Namespace}.{dataLoader.InterfaceName}"; generator.WriteAddDataLoader(typeName, interfaceTypeName); + + if(dataLoader.Groups.Count > 0) + { + groups ??= []; + foreach (var groupName in dataLoader.Groups) + { + groups.Add(($"{dataLoader.Namespace}.I{groupName}", $"{dataLoader.Namespace}.{groupName}")); + } + } break; } } + if (groups is not null) + { + foreach (var (interfaceName, className) in groups.OrderBy(t => t.ClassName)) + { + generator.WriteAddDataLoaderGroup(className, interfaceName); + } + } + generator.WriteEndRegistrationMethod(); generator.WriteEndClass(); generator.WriteEndNamespace(); diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Generators/TypeModuleSyntaxGenerator.cs b/src/HotChocolate/Core/src/Types.Analyzers/Generators/TypeModuleSyntaxGenerator.cs index d9e3cfbdd9c..411f029f456 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Generators/TypeModuleSyntaxGenerator.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Generators/TypeModuleSyntaxGenerator.cs @@ -49,6 +49,7 @@ private static void WriteConfiguration( List syntaxInfos, ModuleInfo module) { + HashSet<(string InterfaceName, string ClassName)>? groups = null; using var generator = new ModuleFileBuilder(module.ModuleName, "Microsoft.Extensions.DependencyInjection"); generator.WriteHeader(); @@ -110,6 +111,15 @@ private static void WriteConfiguration( generator.WriteRegisterDataLoader(typeName, interfaceTypeName); hasConfigurations = true; + + if(dataLoader.Groups.Count > 0) + { + groups ??= []; + foreach (var groupName in dataLoader.Groups) + { + groups.Add(($"{dataLoader.Namespace}.I{groupName}", $"{dataLoader.Namespace}.{groupName}")); + } + } } break; @@ -174,6 +184,14 @@ private static void WriteConfiguration( hasConfigurations = true; } + if (groups is not null) + { + foreach (var (interfaceName, className) in groups.OrderBy(t => t.ClassName)) + { + generator.WriteRegisterDataLoaderGroup(className, interfaceName); + } + } + generator.WriteEndRegistrationMethod(); if (hasObjectTypeExtensions) diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Helpers/DataLoaderAttributeHelper.cs b/src/HotChocolate/Core/src/Types.Analyzers/Helpers/DataLoaderAttributeHelper.cs index b8314bb4455..71b79a2a35e 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Helpers/DataLoaderAttributeHelper.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Helpers/DataLoaderAttributeHelper.cs @@ -1,3 +1,4 @@ +using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -14,6 +15,51 @@ public static AttributeData GetDataLoaderAttribute( "DataLoaderAttribute", StringComparison.Ordinal)); + public static ImmutableHashSet GetDataLoaderGroupKeys(this IMethodSymbol methodSymbol) + { + var groupNamesBuilder = ImmutableHashSet.CreateBuilder(StringComparer.Ordinal); + AddGroupNames(groupNamesBuilder, methodSymbol.GetAttributes()); + AddGroupNames(groupNamesBuilder, methodSymbol.ContainingType.GetAttributes()); + return groupNamesBuilder.Count == 0 ? ImmutableHashSet.Empty : groupNamesBuilder.ToImmutable(); + + static void AddGroupNames(ImmutableHashSet.Builder builder, IEnumerable attributes) + { + foreach (var attribute in attributes) + { + if (IsDataLoaderGroupAttribute(attribute.AttributeClass)) + { + foreach (var arg in attribute.ConstructorArguments.FirstOrDefault().Values) + { + if (arg.Value is string groupName) + { + builder.Add(groupName); + } + } + } + } + } + + static bool IsDataLoaderGroupAttribute(INamedTypeSymbol? attributeClass) + { + if (attributeClass == null) + { + return false; + } + + while (attributeClass != null) + { + if (attributeClass.Name == "DataLoaderGroupAttribute") + { + return true; + } + + attributeClass = attributeClass.BaseType; + } + + return false; + } + } + public static string? GetDataLoaderStateKey( this IParameterSymbol parameter) { diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderInfo.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderInfo.cs index 9f8992030aa..a44e139599a 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderInfo.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderInfo.cs @@ -26,7 +26,8 @@ public DataLoaderInfo( _lookups = attribute.GetLookups(); var declaringType = methodSymbol.ContainingType; - Name = GetDataLoaderName(methodSymbol.Name, attribute); + NameWithoutSuffix = GetDataLoaderName(methodSymbol.Name, attribute); + Name = NameWithoutSuffix + "DataLoader"; InterfaceName = $"I{Name}"; Namespace = methodSymbol.ContainingNamespace.ToDisplayString(); FullName = $"{Namespace}.{Name}"; @@ -38,12 +39,17 @@ public DataLoaderInfo( KeyParameter = MethodSymbol.Parameters[0]; ContainingType = declaringType.ToDisplayString(); Parameters = CreateParameters(methodSymbol); + Groups = methodSymbol.GetDataLoaderGroupKeys(); } public string Name { get; } + public string NameWithoutSuffix { get; } + public string FullName { get; } + public ImmutableHashSet Groups { get; } + public string Namespace { get; } public string InterfaceName { get; } @@ -274,11 +280,8 @@ private static string GetDataLoaderName(string name, AttributeData attribute) name = name.Substring(0, name.Length - 5); } - if (name.EndsWith("DataLoader")) - { - return name; - } - - return name + "DataLoader"; + return name.EndsWith("DataLoader") + ? name.Substring(0, name.Length - 10) + : name; } } diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/GroupedDataLoaderInfo.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/GroupedDataLoaderInfo.cs new file mode 100644 index 00000000000..93fe0580cb4 --- /dev/null +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/GroupedDataLoaderInfo.cs @@ -0,0 +1,20 @@ +namespace HotChocolate.Types.Analyzers.FileBuilders; + +public class GroupedDataLoaderInfo +{ + public GroupedDataLoaderInfo(string name, string interfaceName, bool isPublic) + { + Name = name; + InterfaceName = interfaceName; + IsPublic = isPublic; + FieldName = "_" + name.Substring(0, 1).ToLowerInvariant() + name.Substring(1); + } + + public string Name { get; } + + public string InterfaceName { get; } + + public string FieldName { get; } + + public bool IsPublic { get; } +} diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/DataLoaderTests.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/DataLoaderTests.cs index 39fa67c25ca..10db23ff707 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/DataLoaderTests.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/DataLoaderTests.cs @@ -31,6 +31,37 @@ public class Entity """).MatchMarkdownAsync(); } + [Fact] + public async Task GenerateSource_BatchDataLoader_With_Group_MatchesSnapshot() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using HotChocolate; + using GreenDonut; + + namespace TestNamespace; + + [DataLoaderGroup("Group1")] + internal static class TestClass + { + [DataLoader] + [DataLoaderGroup("Group2")] + public static Task> GetEntityByIdAsync( + IReadOnlyList entityIds, + CancellationToken cancellationToken) + => default!; + } + + public class Entity + { + public int Id { get; set; } + } + """).MatchMarkdownAsync(); + } + [Fact] public async Task GenerateSource_GroupedDataLoader_MatchesSnapshot() { diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/Types/DataLoaders.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/Types/DataLoaders.cs index a1835731951..e05996f78f9 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/Types/DataLoaders.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/Types/DataLoaders.cs @@ -1,9 +1,11 @@ using GreenDonut; using GreenDonut.Projections; using HotChocolate.Pagination; +using Microsoft.Extensions.DependencyInjection; namespace HotChocolate.Types; +[DataLoaderGroup("Group1DataLoader", "Group2DataLoader")] public static class DataLoaders { [DataLoader(Lookups = [nameof(CreateLookupKey)])] @@ -22,6 +24,7 @@ public static Task> GetSomeInfoGroupedById( IReadOnlyList keys) => default!; + [DataLoaderGroup("Group3DataLoader", "Group2DataLoader")] [DataLoader] public static Task GetSomeInfoCacheById( int key) diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_MatchesSnapshot.md new file mode 100644 index 00000000000..acef934fa4c --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_MatchesSnapshot.md @@ -0,0 +1,156 @@ +# GenerateSource_BatchDataLoader_With_Group_MatchesSnapshot + +## GreenDonutDataLoader.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using Microsoft.Extensions.DependencyInjection; +using GreenDonut; + +namespace TestNamespace +{ + public interface IEntityByIdDataLoader + : global::GreenDonut.IDataLoader + { + } + + public sealed class EntityByIdDataLoader + : global::GreenDonut.DataLoaderBase + , IEntityByIdDataLoader + { + private readonly global::System.IServiceProvider _services; + + public EntityByIdDataLoader( + global::System.IServiceProvider services, + global::GreenDonut.IBatchScheduler batchScheduler, + global::GreenDonut.DataLoaderOptions options) + : base(batchScheduler, options) + { + _services = services ?? + throw new global::System.ArgumentNullException(nameof(services)); + } + + protected override async global::System.Threading.Tasks.ValueTask FetchAsync( + global::System.Collections.Generic.IReadOnlyList keys, + global::System.Memory> results, + global::GreenDonut.DataLoaderFetchContext context, + global::System.Threading.CancellationToken ct) + { + var temp = await TestNamespace.TestClass.GetEntityByIdAsync(keys, ct).ConfigureAwait(false); + CopyResults(keys, results.Span, temp); + } + + private void CopyResults( + global::System.Collections.Generic.IReadOnlyList keys, + global::System.Span> results, + global::System.Collections.Generic.IReadOnlyDictionary resultMap) + { + for (var i = 0; i < keys.Count; i++) + { + var key = keys[i]; + if (resultMap.TryGetValue(key, out var value)) + { + results[i] = global::GreenDonut.Result.Resolve(value); + } + else + { + results[i] = global::GreenDonut.Result.Resolve(default(global::TestNamespace.Entity)); + } + } + } + } + public interface IGroup1 + { + IEntityByIdDataLoader EntityById { get; } + } + public sealed class Group1 : IGroup1 + { + private readonly IServiceProvider _services; + private IEntityByIdDataLoader? _entityById; + + public Group1(IServiceProvider services) + { + _services = services + ?? throw new ArgumentNullException(nameof(services)); + } + public IEntityByIdDataLoader EntityById + { + get + { + if (_entityById is null) + { + _entityById = _services.GetRequiredService(); + } + + return _entityById!; + } + } + } + public interface IGroup2 + { + IEntityByIdDataLoader EntityById { get; } + } + public sealed class Group2 : IGroup2 + { + private readonly IServiceProvider _services; + private IEntityByIdDataLoader? _entityById; + + public Group2(IServiceProvider services) + { + _services = services + ?? throw new ArgumentNullException(nameof(services)); + } + public IEntityByIdDataLoader EntityById + { + get + { + if (_entityById is null) + { + _entityById = _services.GetRequiredService(); + } + + return _entityById!; + } + } + } +} + + +``` + +## HotChocolateTypeModule.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + builder.AddDataLoader(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + return builder; + } + } +} + +``` + From 0c9789a38cb0053f4484908dc43788f71849af63 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Sun, 29 Sep 2024 23:53:59 +0200 Subject: [PATCH 023/154] Added DataLoader group refinements. (#7529) (cherry picked from commit 3eac31b61249801d83dd9158f43d2e95709f9f62) --- .../FileBuilders/DataLoaderFileBuilder.cs | 1 + .../Helpers/DataLoaderAttributeHelper.cs | 10 +- .../Types.Analyzers/Models/DataLoaderInfo.cs | 3 +- .../Types.Analyzers.Tests/DataLoaderTests.cs | 60 ++++++++ ...chDataLoader_With_Group_MatchesSnapshot.md | 2 + ...ith_Group_Only_On_Class_MatchesSnapshot.md | 129 ++++++++++++++++++ ...th_Group_Only_On_Method_MatchesSnapshot.md | 129 ++++++++++++++++++ 7 files changed, 330 insertions(+), 4 deletions(-) create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_Only_On_Class_MatchesSnapshot.md create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_Only_On_Method_MatchesSnapshot.md diff --git a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs index 88ee006496b..cbbbb4a5aea 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs @@ -499,6 +499,7 @@ public void WriteDataLoaderGroupClass( _writer.DecreaseIndent(); _writer.WriteIndentedLine("}"); + _writer.WriteLine(); _writer.WriteIndentedLine("public sealed class {0} : I{0}", groupClassName); _writer.WriteIndentedLine("{"); diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Helpers/DataLoaderAttributeHelper.cs b/src/HotChocolate/Core/src/Types.Analyzers/Helpers/DataLoaderAttributeHelper.cs index 71b79a2a35e..97b5523a85b 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Helpers/DataLoaderAttributeHelper.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Helpers/DataLoaderAttributeHelper.cs @@ -28,11 +28,15 @@ static void AddGroupNames(ImmutableHashSet.Builder builder, IEnumerable< { if (IsDataLoaderGroupAttribute(attribute.AttributeClass)) { - foreach (var arg in attribute.ConstructorArguments.FirstOrDefault().Values) + var constructorArguments = attribute.ConstructorArguments; + if (constructorArguments.Length > 0) { - if (arg.Value is string groupName) + foreach (var arg in constructorArguments[0].Values) { - builder.Add(groupName); + if (arg.Value is string groupName) + { + builder.Add(groupName); + } } } } diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderInfo.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderInfo.cs index a44e139599a..ebc066f20cf 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderInfo.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderInfo.cs @@ -258,7 +258,8 @@ public override bool Equals(SyntaxInfo obj) private bool Equals(DataLoaderInfo other) => AttributeSyntax.IsEquivalentTo(other.AttributeSyntax) - && MethodSyntax.IsEquivalentTo(other.MethodSyntax); + && MethodSyntax.IsEquivalentTo(other.MethodSyntax) + && Groups.SequenceEqual(other.Groups, StringComparer.Ordinal); public override int GetHashCode() => HashCode.Combine(AttributeSyntax, MethodSyntax); diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/DataLoaderTests.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/DataLoaderTests.cs index 10db23ff707..413e8156841 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/DataLoaderTests.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/DataLoaderTests.cs @@ -62,6 +62,66 @@ public class Entity """).MatchMarkdownAsync(); } + [Fact] + public async Task GenerateSource_BatchDataLoader_With_Group_Only_On_Class_MatchesSnapshot() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using HotChocolate; + using GreenDonut; + + namespace TestNamespace; + + [DataLoaderGroup("Group1")] + internal static class TestClass + { + [DataLoader] + public static Task> GetEntityByIdAsync( + IReadOnlyList entityIds, + CancellationToken cancellationToken) + => default!; + } + + public class Entity + { + public int Id { get; set; } + } + """).MatchMarkdownAsync(); + } + + [Fact] + public async Task GenerateSource_BatchDataLoader_With_Group_Only_On_Method_MatchesSnapshot() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using HotChocolate; + using GreenDonut; + + namespace TestNamespace; + + internal static class TestClass + { + [DataLoaderGroup("Group1")] + [DataLoader] + public static Task> GetEntityByIdAsync( + IReadOnlyList entityIds, + CancellationToken cancellationToken) + => default!; + } + + public class Entity + { + public int Id { get; set; } + } + """).MatchMarkdownAsync(); + } + [Fact] public async Task GenerateSource_GroupedDataLoader_MatchesSnapshot() { diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_MatchesSnapshot.md index acef934fa4c..ce52a9eb22c 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_MatchesSnapshot.md @@ -69,6 +69,7 @@ namespace TestNamespace { IEntityByIdDataLoader EntityById { get; } } + public sealed class Group1 : IGroup1 { private readonly IServiceProvider _services; @@ -96,6 +97,7 @@ namespace TestNamespace { IEntityByIdDataLoader EntityById { get; } } + public sealed class Group2 : IGroup2 { private readonly IServiceProvider _services; diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_Only_On_Class_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_Only_On_Class_MatchesSnapshot.md new file mode 100644 index 00000000000..7927e10cd18 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_Only_On_Class_MatchesSnapshot.md @@ -0,0 +1,129 @@ +# GenerateSource_BatchDataLoader_With_Group_Only_On_Class_MatchesSnapshot + +## GreenDonutDataLoader.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using Microsoft.Extensions.DependencyInjection; +using GreenDonut; + +namespace TestNamespace +{ + public interface IEntityByIdDataLoader + : global::GreenDonut.IDataLoader + { + } + + public sealed class EntityByIdDataLoader + : global::GreenDonut.DataLoaderBase + , IEntityByIdDataLoader + { + private readonly global::System.IServiceProvider _services; + + public EntityByIdDataLoader( + global::System.IServiceProvider services, + global::GreenDonut.IBatchScheduler batchScheduler, + global::GreenDonut.DataLoaderOptions options) + : base(batchScheduler, options) + { + _services = services ?? + throw new global::System.ArgumentNullException(nameof(services)); + } + + protected override async global::System.Threading.Tasks.ValueTask FetchAsync( + global::System.Collections.Generic.IReadOnlyList keys, + global::System.Memory> results, + global::GreenDonut.DataLoaderFetchContext context, + global::System.Threading.CancellationToken ct) + { + var temp = await TestNamespace.TestClass.GetEntityByIdAsync(keys, ct).ConfigureAwait(false); + CopyResults(keys, results.Span, temp); + } + + private void CopyResults( + global::System.Collections.Generic.IReadOnlyList keys, + global::System.Span> results, + global::System.Collections.Generic.IReadOnlyDictionary resultMap) + { + for (var i = 0; i < keys.Count; i++) + { + var key = keys[i]; + if (resultMap.TryGetValue(key, out var value)) + { + results[i] = global::GreenDonut.Result.Resolve(value); + } + else + { + results[i] = global::GreenDonut.Result.Resolve(default(global::TestNamespace.Entity)); + } + } + } + } + public interface IGroup1 + { + IEntityByIdDataLoader EntityById { get; } + } + + public sealed class Group1 : IGroup1 + { + private readonly IServiceProvider _services; + private IEntityByIdDataLoader? _entityById; + + public Group1(IServiceProvider services) + { + _services = services + ?? throw new ArgumentNullException(nameof(services)); + } + public IEntityByIdDataLoader EntityById + { + get + { + if (_entityById is null) + { + _entityById = _services.GetRequiredService(); + } + + return _entityById!; + } + } + } +} + + +``` + +## HotChocolateTypeModule.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + builder.AddDataLoader(); + builder.Services.AddScoped(); + return builder; + } + } +} + +``` + diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_Only_On_Method_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_Only_On_Method_MatchesSnapshot.md new file mode 100644 index 00000000000..fe1ac6df85e --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_Only_On_Method_MatchesSnapshot.md @@ -0,0 +1,129 @@ +# GenerateSource_BatchDataLoader_With_Group_Only_On_Method_MatchesSnapshot + +## GreenDonutDataLoader.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using Microsoft.Extensions.DependencyInjection; +using GreenDonut; + +namespace TestNamespace +{ + public interface IEntityByIdDataLoader + : global::GreenDonut.IDataLoader + { + } + + public sealed class EntityByIdDataLoader + : global::GreenDonut.DataLoaderBase + , IEntityByIdDataLoader + { + private readonly global::System.IServiceProvider _services; + + public EntityByIdDataLoader( + global::System.IServiceProvider services, + global::GreenDonut.IBatchScheduler batchScheduler, + global::GreenDonut.DataLoaderOptions options) + : base(batchScheduler, options) + { + _services = services ?? + throw new global::System.ArgumentNullException(nameof(services)); + } + + protected override async global::System.Threading.Tasks.ValueTask FetchAsync( + global::System.Collections.Generic.IReadOnlyList keys, + global::System.Memory> results, + global::GreenDonut.DataLoaderFetchContext context, + global::System.Threading.CancellationToken ct) + { + var temp = await TestNamespace.TestClass.GetEntityByIdAsync(keys, ct).ConfigureAwait(false); + CopyResults(keys, results.Span, temp); + } + + private void CopyResults( + global::System.Collections.Generic.IReadOnlyList keys, + global::System.Span> results, + global::System.Collections.Generic.IReadOnlyDictionary resultMap) + { + for (var i = 0; i < keys.Count; i++) + { + var key = keys[i]; + if (resultMap.TryGetValue(key, out var value)) + { + results[i] = global::GreenDonut.Result.Resolve(value); + } + else + { + results[i] = global::GreenDonut.Result.Resolve(default(global::TestNamespace.Entity)); + } + } + } + } + public interface IGroup1 + { + IEntityByIdDataLoader EntityById { get; } + } + + public sealed class Group1 : IGroup1 + { + private readonly IServiceProvider _services; + private IEntityByIdDataLoader? _entityById; + + public Group1(IServiceProvider services) + { + _services = services + ?? throw new ArgumentNullException(nameof(services)); + } + public IEntityByIdDataLoader EntityById + { + get + { + if (_entityById is null) + { + _entityById = _services.GetRequiredService(); + } + + return _entityById!; + } + } + } +} + + +``` + +## HotChocolateTypeModule.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + builder.AddDataLoader(); + builder.Services.AddScoped(); + return builder; + } + } +} + +``` + From 9a1f448c36b0e024c8e9775e0f17f64355b39d2b Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Mon, 30 Sep 2024 01:36:05 +0200 Subject: [PATCH 024/154] Added Helper to convert a ISelection to an Expression. (#7531) (cherry picked from commit 72704749f7dc5af180d38dfd264b2b3fc214604d) --- ...otChocolateExecutionSelectionExtensions.cs | 72 +++++++++++++++++++ ...tChocolateExecutionDataLoaderExtensions.cs | 62 ++-------------- 2 files changed, 79 insertions(+), 55 deletions(-) create mode 100644 src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs b/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs new file mode 100644 index 00000000000..cc26612f1d7 --- /dev/null +++ b/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs @@ -0,0 +1,72 @@ +using System.Buffers.Text; +using System.Linq.Expressions; +using System.Text; +using System.Runtime.CompilerServices; +using HotChocolate.Execution.Projections; + +// ReSharper disable once CheckNamespace +namespace HotChocolate.Execution.Processing; + +/// +/// Provides extension methods to work with selections. +/// +public static class HotChocolateExecutionSelectionExtensions +{ + private static readonly SelectionExpressionBuilder _builder = new(); + + /// + /// Creates a selector expression from a GraphQL selection. + /// + /// + /// The selection that shall be converted into a selector expression. + /// + /// + /// The type of the value that is returned by the . + /// + /// + /// Returns a selector expression that can be used for data projections. + /// + public static Expression> ToSelectorExpression( + this ISelection selection) + => GetOrCreateExpression(selection); + + private static Expression> GetOrCreateExpression( + ISelection selection) + { + return selection.DeclaringOperation.GetOrAddState( + CreateExpressionKey(selection.Id), + static (_, ctx) => ctx._builder.BuildExpression(ctx.selection), + (_builder, selection)); + } + + private static string CreateExpressionKey(int key) + { + var keyPrefix = GetKeyPrefix(); + var requiredBufferSize = EstimateIntLength(key) + keyPrefix.Length; + Span span = stackalloc byte[requiredBufferSize]; + keyPrefix.CopyTo(span); + Utf8Formatter.TryFormat(key, span.Slice(keyPrefix.Length), out var written, 'D'); + return Encoding.UTF8.GetString(span.Slice(0, written + keyPrefix.Length)); + } + + private static ReadOnlySpan GetKeyPrefix() + => "hc-dataloader-expr-"u8; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int EstimateIntLength(int value) + { + if (value == 0) + { + // to print 0 we need still 1 digit + return 1; + } + + // if the number is negative we need one more digit for the sign + var length = (value < 0) ? 1 : 0; + + // we add the number of digits the number has to the length of the number. + length += (int)Math.Floor(Math.Log10(Math.Abs(value)) + 1); + + return length; + } +} diff --git a/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs b/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs index c07ef5ee662..297b9ebe770 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs @@ -2,14 +2,9 @@ #nullable enable using System.Buffers; -using System.Buffers.Text; using System.Diagnostics.CodeAnalysis; -using System.Linq.Expressions; -using System.Runtime.CompilerServices; -using System.Text; using HotChocolate.Execution; using HotChocolate.Execution.Processing; -using HotChocolate.Execution.Projections; using HotChocolate.Pagination; using HotChocolate.Types; using HotChocolate.Types.Descriptors.Definitions; @@ -26,8 +21,6 @@ namespace GreenDonut.Projections; #endif public static class HotChocolateExecutionDataLoaderExtensions { - private static readonly SelectionExpressionBuilder _builder = new(); - /// /// Selects the fields that where selected in the GraphQL selection tree. /// @@ -51,7 +44,7 @@ public static ISelectionDataLoader Select( ISelection selection) where TKey : notnull { - var expression = GetOrCreateExpression(selection); + var expression = selection.ToSelectorExpression(); return dataLoader.Select(expression); } @@ -86,8 +79,8 @@ public static IPagingDataLoader> Select( var count = GetConnectionSelections(selection, buffer); for (var i = 0; i < count; i++) { - var expression = GetOrCreateExpression(buffer[i]); - HotChocolatePaginationBatchingDataLoaderExtensions.Select(dataLoader, expression); + var expression = buffer[i].ToSelectorExpression(); + dataLoader.Select(expression); } ArrayPool.Shared.Return(buffer); } @@ -97,15 +90,15 @@ public static IPagingDataLoader> Select( var count = GetCollectionSelections(selection, buffer); for (var i = 0; i < count; i++) { - var expression = GetOrCreateExpression(buffer[i]); - HotChocolatePaginationBatchingDataLoaderExtensions.Select(dataLoader, expression); + var expression = buffer[i].ToSelectorExpression(); + dataLoader.Select(expression); } ArrayPool.Shared.Return(buffer); } else { - var expression = GetOrCreateExpression(selection); - HotChocolatePaginationBatchingDataLoaderExtensions.Select(dataLoader, expression); + var expression = selection.ToSelectorExpression(); + dataLoader.Select(expression); } return dataLoader; @@ -172,46 +165,5 @@ private static int GetCollectionSelections(ISelection selection, Span> GetOrCreateExpression( - ISelection selection) - where TKey : notnull - { - return selection.DeclaringOperation.GetOrAddState( - CreateExpressionKey(selection.Id), - static (_, ctx) => ctx._builder.BuildExpression(ctx.selection), - (_builder, selection)); - } - - private static string CreateExpressionKey(int key) - { - var keyPrefix = GetKeyPrefix(); - var requiredBufferSize = EstimateIntLength(key) + keyPrefix.Length; - Span span = stackalloc byte[requiredBufferSize]; - keyPrefix.CopyTo(span); - Utf8Formatter.TryFormat(key, span.Slice(keyPrefix.Length), out var written, 'D'); - return Encoding.UTF8.GetString(span.Slice(0, written + keyPrefix.Length)); - } - - private static ReadOnlySpan GetKeyPrefix() - => "hc-dataloader-expr-"u8; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int EstimateIntLength(int value) - { - if (value == 0) - { - // to print 0 we need still 1 digit - return 1; - } - - // if the number is negative we need one more digit for the sign - var length = (value < 0) ? 1 : 0; - - // we add the number of digits the number has to the length of the number. - length += (int)Math.Floor(Math.Log10(Math.Abs(value)) + 1); - - return length; - } } #endif From f74e284a03ef6ff34f65119edfac9f87a4ff6900 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Mon, 30 Sep 2024 16:11:48 +0200 Subject: [PATCH 025/154] Added more security policy options. (#7534) (cherry picked from commit 17a0b7777dfea3b50bafde1195670cde0eb61ffd) --- ...teAspNetCoreServiceCollectionExtensions.cs | 2 +- .../AspNetCore.Tests/IntrospectionTests.cs | 151 +++++++++++++++++- ...estExecutorBuilderExtensions.Validation.cs | 103 +++++++++--- ...otChocolateExecutionSelectionExtensions.cs | 2 + .../DefaultDocumentValidatorFactory.cs | 4 +- .../ValidationBuilderExtensions.Rules.cs | 31 ++-- .../Extensions/ValidationBuilderExtensions.cs | 56 ++++--- .../ValidationServiceCollectionExtensions.cs | 1 + .../Options/IIntrospectionOptionsAccessor.cs | 29 ++++ .../Options/ValidationConfiguration.cs | 34 ++-- .../Validation/Options/ValidationOptions.cs | 34 ++-- .../Options/ValidationOptionsModifiers.cs | 3 + .../Options/ValidationRulesOptions.cs | 19 +++ .../Rules/IntrospectionDepthVisitor.cs | 15 +- ...cutorBuilderExtensions_Validation.Tests.cs | 7 + .../DocumentValidatorVisitorTestBase.cs | 2 +- .../IntrospectionDepthRuleTests.cs | 4 +- .../IntrospectionRuleTests.cs | 112 +++++++------ .../Core/test/Validation.Tests/TestHelper.cs | 4 +- ....IntrospectionNotAllowed_Schema_Field.snap | 14 +- ...otAllowed_Schema_Field_Custom_Message.snap | 14 +- ...ed_Schema_Field_Custom_MessageFactory.snap | 14 +- ...ts.IntrospectionNotAllowed_Type_Field.snap | 14 +- .../TestHelper/TestServerHelper.cs | 2 +- 24 files changed, 490 insertions(+), 181 deletions(-) create mode 100644 src/HotChocolate/Core/src/Validation/Options/IIntrospectionOptionsAccessor.cs create mode 100644 src/HotChocolate/Core/src/Validation/Options/ValidationRulesOptions.cs diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/HotChocolateAspNetCoreServiceCollectionExtensions.cs b/src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/HotChocolateAspNetCoreServiceCollectionExtensions.cs index 53dfcda1156..610bae912a5 100644 --- a/src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/HotChocolateAspNetCoreServiceCollectionExtensions.cs +++ b/src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/HotChocolateAspNetCoreServiceCollectionExtensions.cs @@ -115,7 +115,7 @@ public static IRequestExecutorBuilder AddGraphQLServer( if (!disableDefaultSecurity) { builder.AddCostAnalyzer(); - builder.AddIntrospectionAllowedRule( + builder.DisableIntrospection( (sp, _) => { var environment = sp.GetService(); diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/IntrospectionTests.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/IntrospectionTests.cs index aa2a3a76e7f..373c57753ed 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/IntrospectionTests.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/IntrospectionTests.cs @@ -1,5 +1,7 @@ +using System.Net; using CookieCrumble; using HotChocolate.AspNetCore.Tests.Utilities; +using HotChocolate.Transport; using HotChocolate.Transport.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -77,6 +79,152 @@ public async Task Introspection_Request_With_Rule_Removed_Fail(string environmen } #endif + + [Fact] + public async Task Introspection_OfType_Depth_1_BadRequest() + { + // arrange + var server = CreateStarWarsServer( + configureServices: s => s + .AddGraphQL() + .SetIntrospectionAllowedDepth( + maxAllowedOfTypeDepth: 1, + maxAllowedListRecursiveDepth: 1)); + + var request = new GraphQLHttpRequest( + new OperationRequest( + """ + { + __schema { + types { + ofType { + ofType { + name + } + } + } + } + } + """), + new Uri("http://localhost:5000/graphql")); + + // act + var client = new DefaultGraphQLHttpClient(server.CreateClient()); + using var response = await client.SendAsync(request); + + // assert + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + } + + [Fact] + public async Task Introspection_OfType_Depth_1_Depth_Analysis_Disabled() + { + // arrange + var server = CreateStarWarsServer( + configureServices: s => s + .AddGraphQL() + .SetIntrospectionAllowedDepth( + maxAllowedOfTypeDepth: 1, + maxAllowedListRecursiveDepth: 1) + .Services + .AddValidation() + .ConfigureValidation(b => b.Modifiers.Add(o => o.DisableDepthRule = true))); + + var request = new GraphQLHttpRequest( + new OperationRequest( + """ + { + __schema { + types { + ofType { + ofType { + name + } + } + } + } + } + """), + new Uri("http://localhost:5000/graphql")); + + // act + var client = new DefaultGraphQLHttpClient(server.CreateClient()); + using var response = await client.SendAsync(request); + + // assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } + + [Fact] + public async Task Introspection_Disabled_BadRequest() + { + // arrange + var server = CreateStarWarsServer( + configureServices: s => s + .AddGraphQL() + .DisableIntrospection()); + + var request = new GraphQLHttpRequest( + new OperationRequest( + """ + { + __schema { + types { + ofType { + ofType { + name + } + } + } + } + } + """), + new Uri("http://localhost:5000/graphql")); + + // act + var client = new DefaultGraphQLHttpClient(server.CreateClient()); + using var response = await client.SendAsync(request); + + // assert + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + } + + [Fact] + public async Task Introspection_OfType_Depth_2_OK() + { + // arrange + var server = CreateStarWarsServer( + configureServices: s => s + .AddGraphQL() + .SetIntrospectionAllowedDepth( + maxAllowedOfTypeDepth: 2, + maxAllowedListRecursiveDepth: 1)); + + var request = new GraphQLHttpRequest( + new OperationRequest( + """ + { + __schema { + types { + ofType { + ofType { + name + } + } + } + } + } + """), + new Uri("http://localhost:5000/graphql")); + + // act + var client = new DefaultGraphQLHttpClient(server.CreateClient()); + using var response = await client.SendAsync(request); + + // assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } + private GraphQLHttpClient GetClient(string environment, bool removeRule = false) { var server = CreateStarWarsServer( @@ -85,7 +233,8 @@ private GraphQLHttpClient GetClient(string environment, bool removeRule = false) { if (removeRule) { - s.AddGraphQL().RemoveIntrospectionAllowedRule(); + s.AddGraphQL() + .DisableIntrospection(disable: false); } }); return new DefaultGraphQLHttpClient(server.CreateClient()); diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Validation.cs b/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Validation.cs index ab5066b6719..699d65bbe02 100644 --- a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Validation.cs +++ b/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.Validation.cs @@ -186,7 +186,7 @@ public static IRequestExecutorBuilder AddValidationResultAggregator( /// Specifies if depth analysis is skipped for introspection queries. /// /// - /// Defines if request depth overrides are allowed on a per request basis. + /// Defines if request depth overrides are allowed on a per-request basis. /// /// /// Defines if the validation rule is enabled. @@ -220,21 +220,13 @@ public static IRequestExecutorBuilder AddMaxExecutionDepthRule( /// Adds a validation rule that only allows requests to use `__schema` or `__type` /// if the request carries an introspection allowed flag. /// + [Obsolete("Use `DisableIntrospection` instead.")] public static IRequestExecutorBuilder AddIntrospectionAllowedRule( - this IRequestExecutorBuilder builder, - Func? isEnabled = null) - => ConfigureValidation(builder, b => b.AddIntrospectionAllowedRule(isEnabled)); - - /// - /// Removes a validation rule that only allows requests to use `__schema` or `__type` - /// if the request carries an introspection allowed flag. - /// - public static IRequestExecutorBuilder RemoveIntrospectionAllowedRule( this IRequestExecutorBuilder builder) - => ConfigureValidation(builder, b => b.RemoveIntrospectionAllowedRule()); + => DisableIntrospection(builder); /// - /// Toggle whether introspection is allow or not. + /// Toggle whether introspection is allowed or not. /// /// /// The . @@ -244,17 +236,49 @@ public static IRequestExecutorBuilder RemoveIntrospectionAllowedRule( /// If `false` introspection is disallowed, except for requests /// that carry an introspection allowed flag. /// + [Obsolete("Use `DisableIntrospection` instead.")] public static IRequestExecutorBuilder AllowIntrospection( this IRequestExecutorBuilder builder, bool allow) - { - if (!allow) - { - builder.AddIntrospectionAllowedRule(); - } + => DisableIntrospection(builder, disable: !allow); - return builder; - } + /// + /// Toggle whether introspection is disabled or not. + /// + /// + /// The . + /// + /// + /// If `true` introspection is disabled, except for requests + /// that carry an introspection allowed flag. + /// If `false` introspection is enabled. + /// + public static IRequestExecutorBuilder DisableIntrospection( + this IRequestExecutorBuilder builder, + bool disable = true) + => ConfigureValidation( + builder, + b => b.ModifyValidationOptions( + o => o.DisableIntrospection = disable)); + + /// + /// Toggle whether introspection is disabled or not. + /// + /// + /// The . + /// + /// + /// If `true` introspection is disabled, except for requests + /// that carry an introspection allowed flag. + /// If `false` introspection is enabled. + /// + public static IRequestExecutorBuilder DisableIntrospection( + this IRequestExecutorBuilder builder, + Func disable) + => ConfigureValidation( + builder, + b => b.ModifyValidationOptions( + (s, o) => o.DisableIntrospection = disable(s, o))); /// /// Sets the max allowed document validation errors. @@ -283,6 +307,47 @@ public static IRequestExecutorBuilder SetMaxAllowedValidationErrors( builder, b => b.ConfigureValidation( c => c.Modifiers.Add(o => o.MaxAllowedErrors = maxAllowedValidationErrors))); + + return builder; + } + + /// + /// Sets the max allowed depth for introspection queries. + /// + /// + /// The . + /// + /// + /// The max allowed ofType depth for introspection queries. + /// + /// + /// The max allowed list recursive depth for introspection queries. + /// + /// + /// Returns an that can be used to chain + /// + /// + /// is null. + /// + public static IRequestExecutorBuilder SetIntrospectionAllowedDepth( + this IRequestExecutorBuilder builder, + ushort maxAllowedOfTypeDepth, + ushort maxAllowedListRecursiveDepth) + { + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + ConfigureValidation( + builder, + b => b.ConfigureValidation( + c => c.Modifiers.Add(o => + { + o.MaxAllowedOfTypeDepth = maxAllowedOfTypeDepth; + o.MaxAllowedListRecursiveDepth = maxAllowedListRecursiveDepth; + }))); + return builder; } diff --git a/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs b/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs index cc26612f1d7..2c7c85881b2 100644 --- a/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs +++ b/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs @@ -1,3 +1,4 @@ +#if NET6_0_OR_GREATER using System.Buffers.Text; using System.Linq.Expressions; using System.Text; @@ -70,3 +71,4 @@ private static int EstimateIntLength(int value) return length; } } +#endif diff --git a/src/HotChocolate/Core/src/Validation/DefaultDocumentValidatorFactory.cs b/src/HotChocolate/Core/src/Validation/DefaultDocumentValidatorFactory.cs index 2f8677f4207..76fdca737ee 100644 --- a/src/HotChocolate/Core/src/Validation/DefaultDocumentValidatorFactory.cs +++ b/src/HotChocolate/Core/src/Validation/DefaultDocumentValidatorFactory.cs @@ -19,6 +19,8 @@ public IDocumentValidator CreateValidator(string? schemaName = default) { schemaName ??= Schema.DefaultName; var options = _configuration.GetOptions(schemaName); - return new DocumentValidator(_contextPool, options.Rules, options.ResultAggregators, options); + var rules = _configuration.GetRules(schemaName); + var aggregators = _configuration.GetResultAggregators(schemaName); + return new DocumentValidator(_contextPool, rules, aggregators, options); } } diff --git a/src/HotChocolate/Core/src/Validation/Extensions/ValidationBuilderExtensions.Rules.cs b/src/HotChocolate/Core/src/Validation/Extensions/ValidationBuilderExtensions.Rules.cs index c0439f486da..2a4c85e0a7b 100644 --- a/src/HotChocolate/Core/src/Validation/Extensions/ValidationBuilderExtensions.Rules.cs +++ b/src/HotChocolate/Core/src/Validation/Extensions/ValidationBuilderExtensions.Rules.cs @@ -93,11 +93,11 @@ public static IValidationBuilder AddDocumentRules( this IValidationBuilder builder) { return builder.ConfigureValidation( - m => m.Modifiers.Add(o => + m => m.RulesModifiers.Add((_, r) => { - if (o.Rules.All(t => t.GetType() != typeof(DocumentRule))) + if (r.Rules.All(t => t.GetType() != typeof(DocumentRule))) { - o.Rules.Add(new DocumentRule()); + r.Rules.Add(new DocumentRule()); } })); } @@ -342,40 +342,27 @@ public static IValidationBuilder AddMaxExecutionDepthRule( }); } - /// - /// Removes a validation rule that restricts the depth of a GraphQL request. - /// - public static IValidationBuilder RemoveMaxExecutionDepthRule( - this IValidationBuilder builder) - => builder.TryRemoveValidationVisitor(); - /// /// Adds a validation rule that only allows requests to use `__schema` or `__type` /// if the request carries an introspection allowed flag. /// public static IValidationBuilder AddIntrospectionAllowedRule( - this IValidationBuilder builder, - Func? isEnabled = null) + this IValidationBuilder builder) => builder.TryAddValidationVisitor( (_, _) => new IntrospectionVisitor(), priority: 0, isCacheable: false, - isEnabled: isEnabled); - - /// - /// Removes a validation rule that only allows requests to use `__schema` or `__type` - /// if the request carries an introspection allowed flag. - /// - public static IValidationBuilder RemoveIntrospectionAllowedRule( - this IValidationBuilder builder) - => builder.TryRemoveValidationVisitor(); + isEnabled: (_, o) => o.DisableIntrospection); /// /// Adds a validation rule that restricts the depth of a GraphQL introspection request. /// public static IValidationBuilder AddIntrospectionDepthRule( this IValidationBuilder builder) - => builder.TryAddValidationVisitor(priority: 1); + => builder.TryAddValidationVisitor( + priority: 1, + factory: (_, o) => new IntrospectionDepthVisitor(o), + isEnabled: (_, o) => !o.DisableDepthRule); /// /// Adds a validation rule that restricts the depth of coordinate cycles in GraphQL operations. diff --git a/src/HotChocolate/Core/src/Validation/Extensions/ValidationBuilderExtensions.cs b/src/HotChocolate/Core/src/Validation/Extensions/ValidationBuilderExtensions.cs index 887fb2b555d..dcc9a0c2dbd 100644 --- a/src/HotChocolate/Core/src/Validation/Extensions/ValidationBuilderExtensions.cs +++ b/src/HotChocolate/Core/src/Validation/Extensions/ValidationBuilderExtensions.cs @@ -2,6 +2,7 @@ using HotChocolate.Validation.Options; using Microsoft.Extensions.Options; +// ReSharper disable once CheckNamespace namespace Microsoft.Extensions.DependencyInjection; /// @@ -94,11 +95,28 @@ public static IValidationBuilder ConfigureValidation( /// /// Returns the validation builder for configuration chaining. /// - internal static IValidationBuilder ModifyValidationOptions( + public static IValidationBuilder ModifyValidationOptions( this IValidationBuilder builder, Action configure) => builder.ConfigureValidation(m => m.Modifiers.Add(configure)); + /// + /// Modifies the validation options object. + /// + /// + /// The validation builder. + /// + /// + /// The delegate to mutate the validation options. + /// + /// + /// Returns the validation builder for configuration chaining. + /// + public static IValidationBuilder ModifyValidationOptions( + this IValidationBuilder builder, + Action configure) + => builder.ConfigureValidation((s, m) => m.Modifiers.Add(o => configure(s, o))); + /// /// Registers the specified validation visitor, /// if the same type of validation visitor was not yet registered. @@ -124,11 +142,11 @@ public static IValidationBuilder TryAddValidationVisitor( where T : DocumentValidatorVisitor, new() { return builder.ConfigureValidation(m => - m.Modifiers.Add(o => + m.RulesModifiers.Add((_, r) => { - if (o.Rules.All(t => t.GetType() != typeof(DocumentValidatorRule))) + if (r.Rules.All(t => t.GetType() != typeof(DocumentValidatorRule))) { - o.Rules.Add(new DocumentValidatorRule(new T(), isCacheable, priority)); + r.Rules.Add(new DocumentValidatorRule(new T(), isCacheable, priority)); } })); } @@ -166,12 +184,12 @@ public static IValidationBuilder TryAddValidationVisitor( where T : DocumentValidatorVisitor { return builder.ConfigureValidation((s, m) => - m.Modifiers.Add(o => + m.RulesModifiers.Add((o, r) => { - if (o.Rules.All(t => t.GetType() != typeof(DocumentValidatorRule)) + if (r.Rules.All(t => t.GetType() != typeof(DocumentValidatorRule)) && (isEnabled?.Invoke(s, o) ?? true)) { - o.Rules.Add(new DocumentValidatorRule(factory(s, o), isCacheable, priority)); + r.Rules.Add(new DocumentValidatorRule(factory(s, o), isCacheable, priority)); } })); } @@ -184,12 +202,12 @@ public static IValidationBuilder TryRemoveValidationVisitor( where T : DocumentValidatorVisitor { return builder.ConfigureValidation((_, m) => - m.Modifiers.Add(o => + m.RulesModifiers.Add((_, r) => { - var entries = o.Rules.Where(t => t.GetType() == typeof(DocumentValidatorRule)).ToList(); + var entries = r.Rules.Where(t => t.GetType() == typeof(DocumentValidatorRule)).ToList(); foreach (var entry in entries) { - o.Rules.Remove(entry); + r.Rules.Remove(entry); } })); } @@ -210,11 +228,11 @@ public static IValidationBuilder TryAddValidationRule( where T : class, IDocumentValidatorRule, new() { return builder.ConfigureValidation(m => - m.Modifiers.Add(o => + m.RulesModifiers.Add((_, r) => { - if (o.Rules.All(t => t.GetType() != typeof(T))) + if (r.Rules.All(t => t.GetType() != typeof(T))) { - o.Rules.Add(new T()); + r.Rules.Add(new T()); } })); } @@ -239,12 +257,12 @@ public static IValidationBuilder TryAddValidationRule( where T : class, IDocumentValidatorRule { return builder.ConfigureValidation((s, m) => - m.Modifiers.Add(o => + m.RulesModifiers.Add((o, r) => { var instance = factory(s, o); - if (o.Rules.All(t => t.GetType() != instance.GetType())) + if (r.Rules.All(t => t.GetType() != instance.GetType())) { - o.Rules.Add(instance); + r.Rules.Add(instance); } })); } @@ -269,12 +287,12 @@ public static IValidationBuilder TryAddValidationResultAggregator( where T : class, IValidationResultAggregator { return builder.ConfigureValidation((s, m) => - m.Modifiers.Add(o => + m.RulesModifiers.Add((o, r) => { var instance = factory(s, o); - if (o.ResultAggregators.All(t => t.GetType() != instance.GetType())) + if (r.ResultAggregators.All(t => t.GetType() != instance.GetType())) { - o.ResultAggregators.Add(instance); + r.ResultAggregators.Add(instance); } })); } diff --git a/src/HotChocolate/Core/src/Validation/Extensions/ValidationServiceCollectionExtensions.cs b/src/HotChocolate/Core/src/Validation/Extensions/ValidationServiceCollectionExtensions.cs index 308c8800bea..f45aee12809 100644 --- a/src/HotChocolate/Core/src/Validation/Extensions/ValidationServiceCollectionExtensions.cs +++ b/src/HotChocolate/Core/src/Validation/Extensions/ValidationServiceCollectionExtensions.cs @@ -28,6 +28,7 @@ public static IValidationBuilder AddValidation( var builder = new DefaultValidationBuilder(schemaName, services); builder + .AddIntrospectionAllowedRule() .AddIntrospectionDepthRule() .AddDocumentRules() .AddOperationRules() diff --git a/src/HotChocolate/Core/src/Validation/Options/IIntrospectionOptionsAccessor.cs b/src/HotChocolate/Core/src/Validation/Options/IIntrospectionOptionsAccessor.cs new file mode 100644 index 00000000000..44ab26a2e36 --- /dev/null +++ b/src/HotChocolate/Core/src/Validation/Options/IIntrospectionOptionsAccessor.cs @@ -0,0 +1,29 @@ +namespace HotChocolate.Validation.Options; + +/// +/// Represents the options for introspection rules. +/// +public interface IIntrospectionOptionsAccessor +{ + /// + /// Defines if introspection is disabled. + /// + bool DisableIntrospection { get; } + + /// + /// Defines if the introspection depth rule is disabled. + /// + bool DisableDepthRule { get; } + + /// + /// Specifies the maximum allowed `ofType` field depth + /// when running the introspection depth rule. + /// + ushort MaxAllowedOfTypeDepth { get; } + + /// + /// Specifies the maximum allowed list recursive depth + /// when running the introspection depth rule. + /// + ushort MaxAllowedListRecursiveDepth { get; } +} diff --git a/src/HotChocolate/Core/src/Validation/Options/ValidationConfiguration.cs b/src/HotChocolate/Core/src/Validation/Options/ValidationConfiguration.cs index 97730ada474..2040708c1d0 100644 --- a/src/HotChocolate/Core/src/Validation/Options/ValidationConfiguration.cs +++ b/src/HotChocolate/Core/src/Validation/Options/ValidationConfiguration.cs @@ -3,36 +3,42 @@ namespace HotChocolate.Validation.Options; -public class ValidationConfiguration : IValidationConfiguration +public class ValidationConfiguration( + IOptionsMonitor optionsMonitor) + : IValidationConfiguration { - private readonly ConcurrentDictionary _optionsCache = new(); - private readonly IOptionsMonitor _optionsMonitor; - - public ValidationConfiguration(IOptionsMonitor optionsMonitor) - { - _optionsMonitor = optionsMonitor - ?? throw new ArgumentNullException(nameof(optionsMonitor)); - } + private readonly ConcurrentDictionary _optionsCache = new(); + private readonly IOptionsMonitor _optionsMonitor = optionsMonitor + ?? throw new ArgumentNullException(nameof(optionsMonitor)); public IEnumerable GetRules(string schemaName) - => GetOptions(schemaName).Rules; + => GetRulesOptions(schemaName).Rules; public IEnumerable GetResultAggregators(string schemaName) - => GetOptions(schemaName).ResultAggregators; + => GetRulesOptions(schemaName).ResultAggregators; public ValidationOptions GetOptions(string schemaName) - => _optionsCache.GetOrAdd(schemaName, CreateOptions); + => _optionsCache.GetOrAdd(schemaName, CreateOptions).Item1; + + private ValidationRulesOptions GetRulesOptions(string schemaName) + => _optionsCache.GetOrAdd(schemaName, CreateOptions).Item2; - private ValidationOptions CreateOptions(string schemaName) + private (ValidationOptions, ValidationRulesOptions) CreateOptions(string schemaName) { var modifiers = _optionsMonitor.Get(schemaName); var options = new ValidationOptions(); + var rulesOptions = new ValidationRulesOptions(); for (var i = 0; i < modifiers.Modifiers.Count; i++) { modifiers.Modifiers[i](options); } - return options; + for (var i = 0; i < modifiers.RulesModifiers.Count; i++) + { + modifiers.RulesModifiers[i](options, rulesOptions); + } + + return (options, rulesOptions); } } diff --git a/src/HotChocolate/Core/src/Validation/Options/ValidationOptions.cs b/src/HotChocolate/Core/src/Validation/Options/ValidationOptions.cs index c55b6246db7..f9496ae1a84 100644 --- a/src/HotChocolate/Core/src/Validation/Options/ValidationOptions.cs +++ b/src/HotChocolate/Core/src/Validation/Options/ValidationOptions.cs @@ -3,24 +3,15 @@ namespace HotChocolate.Validation.Options; /// /// The validation options. /// -public class ValidationOptions +public sealed class ValidationOptions : IMaxExecutionDepthOptionsAccessor , IErrorOptionsAccessor + , IIntrospectionOptionsAccessor { private int? _maxAllowedExecutionDepth; private int _maxErrors = 5; - - /// - /// Gets the document rules of the validation. - /// - public IList Rules { get; } = - new List(); - - /// - /// Gets the document rules that run async logic after the initial validators have run.. - /// - public IList ResultAggregators { get; } = - new List(); + private ushort _maxAllowedOfTypeDepth = 16; + private ushort _maxAllowedListRecursiveDepth = 1; /// /// Gets the maximum allowed depth of a query. The default value is @@ -54,7 +45,24 @@ public int MaxAllowedErrors { value = 5; } + _maxErrors = value; } } + + public bool DisableIntrospection { get; set; } + + public bool DisableDepthRule { get; set; } + + public ushort MaxAllowedOfTypeDepth + { + get => _maxAllowedOfTypeDepth; + set => _maxAllowedOfTypeDepth = value > 0 ? value : (ushort)1; + } + + public ushort MaxAllowedListRecursiveDepth + { + get => _maxAllowedListRecursiveDepth; + set => _maxAllowedListRecursiveDepth = value > 0 ? value : (ushort)16; + } } diff --git a/src/HotChocolate/Core/src/Validation/Options/ValidationOptionsModifiers.cs b/src/HotChocolate/Core/src/Validation/Options/ValidationOptionsModifiers.cs index c368fb616e8..e2dd55b2d0d 100644 --- a/src/HotChocolate/Core/src/Validation/Options/ValidationOptionsModifiers.cs +++ b/src/HotChocolate/Core/src/Validation/Options/ValidationOptionsModifiers.cs @@ -4,4 +4,7 @@ public class ValidationOptionsModifiers { public IList> Modifiers { get; } = new List>(); + + public IList> RulesModifiers { get; } = + new List>(); } diff --git a/src/HotChocolate/Core/src/Validation/Options/ValidationRulesOptions.cs b/src/HotChocolate/Core/src/Validation/Options/ValidationRulesOptions.cs new file mode 100644 index 00000000000..521c24d18e4 --- /dev/null +++ b/src/HotChocolate/Core/src/Validation/Options/ValidationRulesOptions.cs @@ -0,0 +1,19 @@ +namespace HotChocolate.Validation.Options; + +/// +/// The validation rules options. +/// +public sealed class ValidationRulesOptions +{ + /// + /// Gets the document rules of the validation. + /// + public IList Rules { get; } = + new List(); + + /// + /// Gets the document rules that run async logic after the initial validators have run.. + /// + public IList ResultAggregators { get; } = + new List(); +} diff --git a/src/HotChocolate/Core/src/Validation/Rules/IntrospectionDepthVisitor.cs b/src/HotChocolate/Core/src/Validation/Rules/IntrospectionDepthVisitor.cs index c274f1ab743..21a5c294d77 100644 --- a/src/HotChocolate/Core/src/Validation/Rules/IntrospectionDepthVisitor.cs +++ b/src/HotChocolate/Core/src/Validation/Rules/IntrospectionDepthVisitor.cs @@ -3,6 +3,7 @@ using HotChocolate.Types; using HotChocolate.Types.Introspection; using HotChocolate.Utilities; +using HotChocolate.Validation.Options; namespace HotChocolate.Validation.Rules; @@ -10,15 +11,17 @@ namespace HotChocolate.Validation.Rules; /// This rules ensures that recursive introspection fields cannot be used /// to create endless cycles. /// -internal sealed class IntrospectionDepthVisitor : TypeDocumentValidatorVisitor +internal sealed class IntrospectionDepthVisitor( + IIntrospectionOptionsAccessor options) + : TypeDocumentValidatorVisitor { private readonly (SchemaCoordinate Coordinate, ushort MaxAllowed)[] _limits = [ - (new SchemaCoordinate("__Type", "fields"), 1), - (new SchemaCoordinate("__Type", "inputFields"), 1), - (new SchemaCoordinate("__Type", "interfaces"), 1), - (new SchemaCoordinate("__Type", "possibleTypes"), 1), - (new SchemaCoordinate("__Type", "ofType"), 8) + (new SchemaCoordinate("__Type", "fields"), options.MaxAllowedListRecursiveDepth), + (new SchemaCoordinate("__Type", "inputFields"), options.MaxAllowedListRecursiveDepth), + (new SchemaCoordinate("__Type", "interfaces"), options.MaxAllowedListRecursiveDepth), + (new SchemaCoordinate("__Type", "possibleTypes"), options.MaxAllowedListRecursiveDepth), + (new SchemaCoordinate("__Type", "ofType"), options.MaxAllowedOfTypeDepth) ]; protected override ISyntaxVisitorAction Enter( diff --git a/src/HotChocolate/Core/test/Execution.Tests/DependencyInjection/RequestExecutorBuilderExtensions_Validation.Tests.cs b/src/HotChocolate/Core/test/Execution.Tests/DependencyInjection/RequestExecutorBuilderExtensions_Validation.Tests.cs index 39b96d83efa..5fc5677f833 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/DependencyInjection/RequestExecutorBuilderExtensions_Validation.Tests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/DependencyInjection/RequestExecutorBuilderExtensions_Validation.Tests.cs @@ -70,6 +70,7 @@ public void AddValidationRule_2_Factory_Is_Null() } [Fact] + [Obsolete] public async Task AddIntrospectionAllowedRule_IntegrationTest_NotAllowed() { Snapshot.FullName(); @@ -87,6 +88,7 @@ public async Task AddIntrospectionAllowedRule_IntegrationTest_NotAllowed() } [Fact] + [Obsolete] public async Task AllowIntrospection_IntegrationTest_NotAllowed() { Snapshot.FullName(); @@ -104,6 +106,7 @@ public async Task AllowIntrospection_IntegrationTest_NotAllowed() } [Fact] + [Obsolete] public async Task AllowIntrospection_IntegrationTest_Allowed() { Snapshot.FullName(); @@ -121,6 +124,7 @@ public async Task AllowIntrospection_IntegrationTest_Allowed() } [Fact] + [Obsolete] public async Task AllowIntrospection_IntegrationTest_NotAllowed_CustomMessage() { Snapshot.FullName(); @@ -139,6 +143,7 @@ public async Task AllowIntrospection_IntegrationTest_NotAllowed_CustomMessage() } [Fact] + [Obsolete] public async Task AddIntrospectionAllowedRule_IntegrationTest_NotAllowed_CustomMessageFact() { Snapshot.FullName(); @@ -157,6 +162,7 @@ public async Task AddIntrospectionAllowedRule_IntegrationTest_NotAllowed_CustomM } [Fact] + [Obsolete] public async Task AddIntrospectionAllowedRule_IntegrationTest_NotAllowed_CustomMessage() { Snapshot.FullName(); @@ -175,6 +181,7 @@ public async Task AddIntrospectionAllowedRule_IntegrationTest_NotAllowed_CustomM } [Fact] + [Obsolete] public async Task AddIntrospectionAllowedRule_IntegrationTest_Allowed() { Snapshot.FullName(); diff --git a/src/HotChocolate/Core/test/Validation.Tests/DocumentValidatorVisitorTestBase.cs b/src/HotChocolate/Core/test/Validation.Tests/DocumentValidatorVisitorTestBase.cs index e3fc0c2559b..bb2d2d7ab8b 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/DocumentValidatorVisitorTestBase.cs +++ b/src/HotChocolate/Core/test/Validation.Tests/DocumentValidatorVisitorTestBase.cs @@ -14,7 +14,7 @@ protected DocumentValidatorVisitorTestBase(Action configure) var builder = serviceCollection .AddValidation() - .ConfigureValidation(c => c.Modifiers.Add(o => o.Rules.Clear())) + .ConfigureValidation(c => c.RulesModifiers.Add((_, r) => r.Rules.Clear())) .ModifyValidationOptions(o => o.MaxAllowedErrors = int.MaxValue); configure(builder); diff --git a/src/HotChocolate/Core/test/Validation.Tests/IntrospectionDepthRuleTests.cs b/src/HotChocolate/Core/test/Validation.Tests/IntrospectionDepthRuleTests.cs index 4f682209691..4a8e6aa15cf 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/IntrospectionDepthRuleTests.cs +++ b/src/HotChocolate/Core/test/Validation.Tests/IntrospectionDepthRuleTests.cs @@ -119,7 +119,7 @@ public void Max_2_Relative_Field_Allowed_Success() var builder = serviceCollection .AddValidation() - .ConfigureValidation(c => c.Modifiers.Add(o => o.Rules.Clear())) + .ConfigureValidation(c => c.RulesModifiers.Add((_, r) => r.Rules.Clear())) .ModifyValidationOptions(o => o.MaxAllowedErrors = int.MaxValue); builder.AddMaxAllowedFieldCycleDepthRule( null, @@ -164,7 +164,7 @@ public void Max_1_Relative_Field_Allowed_Fail() var builder = serviceCollection .AddValidation() - .ConfigureValidation(c => c.Modifiers.Add(o => o.Rules.Clear())) + .ConfigureValidation(c => c.RulesModifiers.Add((_, r) => r.Rules.Clear())) .ModifyValidationOptions(o => o.MaxAllowedErrors = int.MaxValue); builder.AddMaxAllowedFieldCycleDepthRule( null, diff --git a/src/HotChocolate/Core/test/Validation.Tests/IntrospectionRuleTests.cs b/src/HotChocolate/Core/test/Validation.Tests/IntrospectionRuleTests.cs index b24019ee484..f5c02993881 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/IntrospectionRuleTests.cs +++ b/src/HotChocolate/Core/test/Validation.Tests/IntrospectionRuleTests.cs @@ -11,11 +11,13 @@ public void IntrospectionNotAllowed_Schema_Field() { ExpectErrors( CreateSchema(), - b => b.AddIntrospectionAllowedRule(), - @" - { - __schema - }"); + b => b.AddIntrospectionAllowedRule() + .ModifyValidationOptions(o => o.DisableIntrospection = true), + """ + { + __schema + } + """); } [Fact] @@ -23,15 +25,16 @@ public void IntrospectionNotAllowed_Schema_Field_Custom_MessageFactory() { ExpectErrors( CreateSchema(), - b => b.AddIntrospectionAllowedRule(), - @" - { - __schema - }", - new KeyValuePair[] + b => b.AddIntrospectionAllowedRule() + .ModifyValidationOptions(o => o.DisableIntrospection = true), + """ { - new(WellKnownContextData.IntrospectionMessage, new Func(() => "Bar")), - }); + __schema + } + """, + [ + new(WellKnownContextData.IntrospectionMessage, new Func(() => "Bar")) + ]); } [Fact] @@ -39,15 +42,16 @@ public void IntrospectionNotAllowed_Schema_Field_Custom_Message() { ExpectErrors( CreateSchema(), - b => b.AddIntrospectionAllowedRule(), - @" - { - __schema - }", - new KeyValuePair[] + b => b.AddIntrospectionAllowedRule() + .ModifyValidationOptions(o => o.DisableIntrospection = true), + """ { - new(WellKnownContextData.IntrospectionMessage, "Baz"), - }); + __schema + } + """, + [ + new(WellKnownContextData.IntrospectionMessage, "Baz") + ]); } [Fact] @@ -55,11 +59,13 @@ public void IntrospectionNotAllowed_Type_Field() { ExpectErrors( CreateSchema(), - b => b.AddIntrospectionAllowedRule(), - @" - { - __type(name: ""foo"") - }"); + b => b.AddIntrospectionAllowedRule() + .ModifyValidationOptions(o => o.DisableIntrospection = true), + """ + { + __type(name: "foo") + } + """); } [Fact] @@ -67,11 +73,13 @@ public void IntrospectionAllowed_Typename_Field() { ExpectValid( CreateSchema(), - b => b.AddIntrospectionAllowedRule(), - @" - { - __typename - }"); + b => b.AddIntrospectionAllowedRule() + .ModifyValidationOptions(o => o.DisableIntrospection = true), + """ + { + __typename + } + """); } [Fact] @@ -79,16 +87,18 @@ public void IntrospectionAllowed_Schema_Field() { ExpectValid( CreateSchema(), - b => b.AddIntrospectionAllowedRule(), - @"{ + b => b.AddIntrospectionAllowedRule() + .ModifyValidationOptions(o => o.DisableIntrospection = true), + """ + { __schema { name } - }", - new KeyValuePair[] - { - new(WellKnownContextData.IntrospectionAllowed, null), - }); + } + """, + [ + new(WellKnownContextData.IntrospectionAllowed, null) + ]); } [Fact] @@ -96,22 +106,22 @@ public void IntrospectionAllowed_Type_Field() { ExpectValid( CreateSchema(), - b => b.AddIntrospectionAllowedRule(), - @" - { - __type(name: ""foo"") - }", - new KeyValuePair[] + b => b.AddIntrospectionAllowedRule() + .ModifyValidationOptions(o => o.DisableIntrospection = true), + """ { - new(WellKnownContextData.IntrospectionAllowed, null), - }); + __type(name: "foo") + } + """, + [ + new(WellKnownContextData.IntrospectionAllowed, null) + ]); } - private ISchema CreateSchema() - { - return SchemaBuilder.New() - .AddDocumentFromString(FileResource.Open("IntrospectionSchema.graphql")) + private static ISchema CreateSchema() + => SchemaBuilder.New() + .AddDocumentFromString( + FileResource.Open("IntrospectionSchema.graphql")) .Use(_ => _ => default) .Create(); - } } diff --git a/src/HotChocolate/Core/test/Validation.Tests/TestHelper.cs b/src/HotChocolate/Core/test/Validation.Tests/TestHelper.cs index 3593af31c68..9776202112d 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/TestHelper.cs +++ b/src/HotChocolate/Core/test/Validation.Tests/TestHelper.cs @@ -30,7 +30,7 @@ public static void ExpectValid( var builder = serviceCollection .AddValidation() - .ConfigureValidation(c => c.Modifiers.Add(o => o.Rules.Clear())); + .ConfigureValidation(c => c.RulesModifiers.Add((_, r) => r.Rules.Clear())); configure(builder); IServiceProvider services = serviceCollection.BuildServiceProvider(); @@ -86,7 +86,7 @@ public static void ExpectErrors( var builder = serviceCollection .AddValidation() - .ConfigureValidation(c => c.Modifiers.Add(o => o.Rules.Clear())); + .ConfigureValidation(c => c.RulesModifiers.Add((_, r) => r.Rules.Clear())); configure(builder); IServiceProvider services = serviceCollection.BuildServiceProvider(); diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/IntrospectionRuleTests.IntrospectionNotAllowed_Schema_Field.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/IntrospectionRuleTests.IntrospectionNotAllowed_Schema_Field.snap index feb6f685f92..fb8497ba68a 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/IntrospectionRuleTests.IntrospectionNotAllowed_Schema_Field.snap +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/IntrospectionRuleTests.IntrospectionNotAllowed_Schema_Field.snap @@ -1,22 +1,22 @@ -[ +[ { "Message": "Introspection is not allowed for the current request.", "Code": "HC0046", "Path": null, "Locations": [ { - "Line": 3, - "Column": 21 + "Line": 2, + "Column": 5 } ], "Extensions": { "field": { "Kind": "Name", "Location": { - "Start": 39, - "End": 65, - "Line": 3, - "Column": 21 + "Start": 6, + "End": 16, + "Line": 2, + "Column": 5 }, "Value": "__schema" }, diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/IntrospectionRuleTests.IntrospectionNotAllowed_Schema_Field_Custom_Message.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/IntrospectionRuleTests.IntrospectionNotAllowed_Schema_Field_Custom_Message.snap index eccb208ea09..66f6cce3e58 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/IntrospectionRuleTests.IntrospectionNotAllowed_Schema_Field_Custom_Message.snap +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/IntrospectionRuleTests.IntrospectionNotAllowed_Schema_Field_Custom_Message.snap @@ -1,22 +1,22 @@ -[ +[ { "Message": "Baz", "Code": "HC0046", "Path": null, "Locations": [ { - "Line": 3, - "Column": 21 + "Line": 2, + "Column": 5 } ], "Extensions": { "field": { "Kind": "Name", "Location": { - "Start": 39, - "End": 65, - "Line": 3, - "Column": 21 + "Start": 6, + "End": 16, + "Line": 2, + "Column": 5 }, "Value": "__schema" }, diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/IntrospectionRuleTests.IntrospectionNotAllowed_Schema_Field_Custom_MessageFactory.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/IntrospectionRuleTests.IntrospectionNotAllowed_Schema_Field_Custom_MessageFactory.snap index 1216159f4aa..9037a66d858 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/IntrospectionRuleTests.IntrospectionNotAllowed_Schema_Field_Custom_MessageFactory.snap +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/IntrospectionRuleTests.IntrospectionNotAllowed_Schema_Field_Custom_MessageFactory.snap @@ -1,22 +1,22 @@ -[ +[ { "Message": "Bar", "Code": "HC0046", "Path": null, "Locations": [ { - "Line": 3, - "Column": 21 + "Line": 2, + "Column": 5 } ], "Extensions": { "field": { "Kind": "Name", "Location": { - "Start": 39, - "End": 65, - "Line": 3, - "Column": 21 + "Start": 6, + "End": 16, + "Line": 2, + "Column": 5 }, "Value": "__schema" }, diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/IntrospectionRuleTests.IntrospectionNotAllowed_Type_Field.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/IntrospectionRuleTests.IntrospectionNotAllowed_Type_Field.snap index 5b9ef2cafe5..9d7d723bbfa 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/IntrospectionRuleTests.IntrospectionNotAllowed_Type_Field.snap +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/IntrospectionRuleTests.IntrospectionNotAllowed_Type_Field.snap @@ -1,22 +1,22 @@ -[ +[ { "Message": "Introspection is not allowed for the current request.", "Code": "HC0046", "Path": null, "Locations": [ { - "Line": 3, - "Column": 21 + "Line": 2, + "Column": 5 } ], "Extensions": { "field": { "Kind": "Name", "Location": { - "Start": 39, - "End": 46, - "Line": 3, - "Column": 21 + "Start": 6, + "End": 13, + "Line": 2, + "Column": 5 }, "Value": "__type" }, diff --git a/src/StrawberryShake/Client/test/Transport.WebSocket.Tests/TestHelper/TestServerHelper.cs b/src/StrawberryShake/Client/test/Transport.WebSocket.Tests/TestHelper/TestServerHelper.cs index d37834cfaa3..dcec8476f3c 100644 --- a/src/StrawberryShake/Client/test/Transport.WebSocket.Tests/TestHelper/TestServerHelper.cs +++ b/src/StrawberryShake/Client/test/Transport.WebSocket.Tests/TestHelper/TestServerHelper.cs @@ -35,7 +35,7 @@ public static IWebHost CreateServer(Action configure, o builder .AddStarWarsTypes() - .RemoveIntrospectionAllowedRule() + .DisableIntrospection(disable: false) .AddStarWarsRepositories() .AddInMemorySubscriptions() .ModifyOptions( From c9b50e43b39e2b20636a5558d11ded1223f42487 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Mon, 30 Sep 2024 15:23:52 +0200 Subject: [PATCH 026/154] Fixed issue when required data was present in query. (#7536) (cherry picked from commit 256dd0701018457bb6b06dff34520e07397c2d71) --- .../Projections/IPropertyNodeProvider.cs | 14 ++++ .../src/Execution/Projections/PropertyNode.cs | 76 ----------------- .../Projections/PropertyNodeContainer.cs | 81 +++++++++++++++++++ .../Projections/SelectionExpressionBuilder.cs | 2 +- .../Projections/ProjectableDataLoaderTests.cs | 37 +++++++++ ...sts.Brand_Details_Requires_Brand_Name_2.md | 24 ++++++ ...nd_Details_Requires_Brand_Name_2_NET7_0.md | 23 ++++++ 7 files changed, 180 insertions(+), 77 deletions(-) create mode 100644 src/HotChocolate/Core/src/Execution/Projections/IPropertyNodeProvider.cs create mode 100644 src/HotChocolate/Core/src/Execution/Projections/PropertyNodeContainer.cs create mode 100644 src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Requires_Brand_Name_2.md create mode 100644 src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Requires_Brand_Name_2_NET7_0.md diff --git a/src/HotChocolate/Core/src/Execution/Projections/IPropertyNodeProvider.cs b/src/HotChocolate/Core/src/Execution/Projections/IPropertyNodeProvider.cs new file mode 100644 index 00000000000..d9e8dec81b5 --- /dev/null +++ b/src/HotChocolate/Core/src/Execution/Projections/IPropertyNodeProvider.cs @@ -0,0 +1,14 @@ +#if NET6_0_OR_GREATER +using System.Reflection; + +namespace HotChocolate.Execution.Projections; + +internal interface IPropertyNodeProvider +{ + IReadOnlyList Nodes { get; } + + PropertyNode AddOrGetNode(PropertyInfo property); + + void TryAddNode(PropertyNode newNode); +} +#endif diff --git a/src/HotChocolate/Core/src/Execution/Projections/PropertyNode.cs b/src/HotChocolate/Core/src/Execution/Projections/PropertyNode.cs index 56cd7e092ef..9685a013b1e 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/PropertyNode.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/PropertyNode.cs @@ -91,80 +91,4 @@ public PropertyNode Clone() return null; } } - -internal class PropertyNodeContainer( - List? nodes = null) - : IPropertyNodeProvider -{ - private static readonly IReadOnlyList _emptyNodes = Array.Empty(); - private List? _nodes = nodes; - private bool _sealed; - - public IReadOnlyList Nodes - => _nodes ?? _emptyNodes; - - public PropertyNode AddOrGetNode(PropertyInfo property) - { - if (_sealed) - { - throw new InvalidOperationException("The property node container is sealed."); - } - - _nodes ??= new(); - - foreach (var node in Nodes) - { - if (node.Property.Name.Equals(property.Name)) - { - return node; - } - } - - var newNode = new PropertyNode(property); - _nodes.Add(newNode); - return newNode; - } - - public void AddNode(PropertyNode newNode) - { - if (_sealed) - { - throw new InvalidOperationException("The property node container is sealed."); - } - - _nodes ??= new(); - - foreach (var node in Nodes) - { - if (node.Property.Name.Equals(node.Property.Name)) - { - throw new InvalidOperationException("Duplicate property."); - } - } - - _nodes.Add(newNode); - } - - public void Seal() - { - if (!_sealed) - { - foreach (var node in Nodes) - { - node.Seal(); - } - - _sealed = true; - } - } -} - -internal interface IPropertyNodeProvider -{ - IReadOnlyList Nodes { get; } - - PropertyNode AddOrGetNode(PropertyInfo property); - - void AddNode(PropertyNode newNode); -} #endif diff --git a/src/HotChocolate/Core/src/Execution/Projections/PropertyNodeContainer.cs b/src/HotChocolate/Core/src/Execution/Projections/PropertyNodeContainer.cs new file mode 100644 index 00000000000..71e91160188 --- /dev/null +++ b/src/HotChocolate/Core/src/Execution/Projections/PropertyNodeContainer.cs @@ -0,0 +1,81 @@ +#if NET6_0_OR_GREATER +using System.Reflection; + +namespace HotChocolate.Execution.Projections; + +internal class PropertyNodeContainer( + List? nodes = null) + : IPropertyNodeProvider +{ + private static readonly IReadOnlyList _emptyNodes = Array.Empty(); + private List? _nodes = nodes; + private bool _sealed; + + public IReadOnlyList Nodes + => _nodes ?? _emptyNodes; + + public PropertyNode AddOrGetNode(PropertyInfo property) + { + if (_sealed) + { + throw new InvalidOperationException("The property node container is sealed."); + } + + _nodes ??= new(); + + foreach (var node in Nodes) + { + if (node.Property.Name.Equals(property.Name)) + { + return node; + } + } + + var newNode = new PropertyNode(property); + _nodes.Add(newNode); + return newNode; + } + + public void TryAddNode(PropertyNode newNode) + { + if (_sealed) + { + throw new InvalidOperationException("The property node container is sealed."); + } + + _nodes ??= new(); + + foreach (var node in _nodes) + { + if (node.Property.Name.Equals(newNode.Property.Name)) + { + if (!node.Property.MetadataToken.Equals(newNode.Property.MetadataToken)) + { + throw new InvalidOperationException("Duplicate property name."); + } + + // we add the child nodes that are not already present + foreach (var newChild in newNode.Nodes) + { + node.TryAddNode(newChild); + } + } + } + + _nodes.Add(newNode); + } + + public void Seal() + { + if (!_sealed) + { + foreach (var node in Nodes) + { + node.Seal(); + } + + _sealed = true; + } + } +} +#endif diff --git a/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs b/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs index 69e6d7b3c65..06b246a918b 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs @@ -143,7 +143,7 @@ private void CollectSelections( { foreach (var requirement in requirements) { - parent.AddNode(requirement.Clone()); + parent.TryAddNode(requirement.Clone()); } } diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs index 517f66350a0..f647740dd99 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs @@ -434,6 +434,43 @@ public async Task Brand_Details_Requires_Brand_Name() } """); +#if NET8_0_OR_GREATER + Snapshot.Create() +#else + Snapshot.Create(postFix: "NET7_0") +#endif + .AddSql(queries) + .AddResult(result) + .MatchMarkdownSnapshot(); + } + + [Fact] + public async Task Brand_Details_Requires_Brand_Name_2() + { + // Arrange + var queries = new List(); + var connectionString = CreateConnectionString(); + await CatalogContext.SeedAsync(connectionString); + + // Act + var result = await new ServiceCollection() + .AddScoped(_ => queries) + .AddTransient(_ => new CatalogContext(connectionString)) + .AddGraphQL() + .AddQueryType() + .AddTypeExtension() + .AddPagingArguments() + .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) + .ExecuteRequestAsync( + """ + { + brandById(id: 1) { + name + details + } + } + """); + #if NET8_0_OR_GREATER Snapshot.Create() #else diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Requires_Brand_Name_2.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Requires_Brand_Name_2.md new file mode 100644 index 00000000000..238f99d2aee --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Requires_Brand_Name_2.md @@ -0,0 +1,24 @@ +# Brand_Details_Requires_Brand_Name_2 + +## SQL + +```text +-- @__keys_0={ '1' } (DbType = Object) +SELECT b."Name", b."Id" +FROM "Brands" AS b +WHERE b."Id" = ANY (@__keys_0) +``` + +## Result + +```json +{ + "data": { + "brandById": { + "name": "Brand0", + "details": "Brand Name:Brand0" + } + } +} +``` + diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Requires_Brand_Name_2_NET7_0.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Requires_Brand_Name_2_NET7_0.md new file mode 100644 index 00000000000..b3610c20d05 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Requires_Brand_Name_2_NET7_0.md @@ -0,0 +1,23 @@ +# Brand_Details_Requires_Brand_Name_2 + +## SQL + +```text +SELECT b."Name", b."Id" +FROM "Brands" AS b +WHERE b."Id" = 1 +``` + +## Result + +```json +{ + "data": { + "brandById": { + "name": "Brand0", + "details": "Brand Name:Brand0" + } + } +} +``` + From f37d6fabfecb29d85f1ef9610c9ae23886200148 Mon Sep 17 00:00:00 2001 From: Glen Date: Mon, 30 Sep 2024 22:31:15 +0200 Subject: [PATCH 027/154] Included tag directive in Apollo Federation import list (#7421) (cherry picked from commit d8b055a2453f4fd0e1ddfc585af3cee28df4b8fa) --- .../FederationTypeInterceptor.cs | 28 +++++++++++++++---- .../CertificationTests.Schema_Snapshot.snap | 2 +- .../CertificationTests.Subgraph_SDL.snap | 2 +- .../CertificationTests.Schema_Snapshot.snap | 2 +- .../CertificationTests.Subgraph_SDL.snap | 2 +- ...iceTypeEmptyQueryTypePureCodeFirst.graphql | 2 +- ...ExternalToTypeFieldAnnotationBased.graphql | 2 +- ...notateExternalToTypeFieldCodeFirst.graphql | 2 +- ...KeyToClassAttributeAnnotationBased.graphql | 2 +- ...eyToClassAttributesAnnotationBased.graphql | 2 +- ...InterfaceAttributesAnnotationBased.graphql | 2 +- ...nnotateKeyToInterfaceTypeCodeFirst.graphql | 2 +- ...tateKeyToObjectTypeAnnotationBased.graphql | 2 +- ...s.AnnotateKeyToObjectTypeCodeFirst.graphql | 2 +- ...KeyToClassAttributeAnnotationBased.graphql | 2 +- ...eyToClassAttributesAnnotationBased.graphql | 2 +- ...InterfaceAttributesAnnotationBased.graphql | 2 +- ...nnotateKeyToInterfaceTypeCodeFirst.graphql | 2 +- ...tateKeyToObjectTypeAnnotationBased.graphql | 2 +- ...s.AnnotateKeyToObjectTypeCodeFirst.graphql | 2 +- ...iveTests.OverrideDirective_Annotation.snap | 2 +- ...rrideDirective_Progressive_Annotation.snap | 2 +- ...ive_GetsAddedCorrectly_Annotations.graphql | 2 +- ...ives_GetAddedCorrectly_Annotations.graphql | 2 +- ...ctives_GetAddedCorrectly_CodeFirst.graphql | 2 +- ...videsToClassAttributePureCodeFirst.graphql | 2 +- ...s.AnnotateProvidesToFieldCodeFirst.graphql | 2 +- ...videsToClassAttributePureCodeFirst.graphql | 2 +- ...s.AnnotateProvidesToFieldCodeFirst.graphql | 2 +- ...ive_GetsAddedCorrectly_Annotations.graphql | 2 +- ...ives_GetAddedCorrectly_Annotations.graphql | 2 +- ...ctives_GetAddedCorrectly_CodeFirst.graphql | 2 +- .../ServiceTypeTests.cs | 4 +-- ...ests.Ensure_PagingInfo_Is_Sharable.graphql | 2 +- ...e_When_Sharable_Already_Registered.graphql | 2 +- 35 files changed, 57 insertions(+), 41 deletions(-) diff --git a/src/HotChocolate/ApolloFederation/src/ApolloFederation/FederationTypeInterceptor.cs b/src/HotChocolate/ApolloFederation/src/ApolloFederation/FederationTypeInterceptor.cs index 26c3a5d6f96..45fb663ae10 100644 --- a/src/HotChocolate/ApolloFederation/src/ApolloFederation/FederationTypeInterceptor.cs +++ b/src/HotChocolate/ApolloFederation/src/ApolloFederation/FederationTypeInterceptor.cs @@ -127,7 +127,8 @@ public override void OnAfterCompleteName( ITypeCompletionContext completionContext, DefinitionBase definition) { - if (definition is not ITypeDefinition and not DirectiveTypeDefinition) + if (_context.GetFederationVersion() == FederationVersion.Federation10 + || definition is not ITypeDefinition and not DirectiveTypeDefinition) { return; } @@ -161,16 +162,18 @@ public override void OnAfterCompleteName( if (type.IsDefined(typeof(PackageAttribute))) { RegisterImport(type); + return; } + + if (type == typeof(DirectiveType)) + { + RegisterTagImport(); + } + return; void RegisterImport(MemberInfo element) { - if (_context.GetFederationVersion() == FederationVersion.Federation10) - { - return; - } - var package = element.GetCustomAttribute(); if (package is null) @@ -193,6 +196,19 @@ void RegisterImport(MemberInfo element) types.Add(completionContext.Type.Name); } } + + void RegisterTagImport() + { + var packageUrl = FederationVersion.Federation20.ToUrl(); + + if (!_imports.TryGetValue(packageUrl, out var types)) + { + types = []; + _imports[packageUrl] = types; + } + + types.Add($"@{completionContext.Type.Name}"); + } } public override void OnTypesCompletedName() diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/CertificationSchema/AnnotationBased/__snapshots__/CertificationTests.Schema_Snapshot.snap b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/CertificationSchema/AnnotationBased/__snapshots__/CertificationTests.Schema_Snapshot.snap index f4513dc7681..29383f074f9 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/CertificationSchema/AnnotationBased/__snapshots__/CertificationTests.Schema_Snapshot.snap +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/CertificationSchema/AnnotationBased/__snapshots__/CertificationTests.Schema_Snapshot.snap @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@external", "@provides", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@tag", "@key", "@external", "@provides", "FieldSet" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/CertificationSchema/AnnotationBased/__snapshots__/CertificationTests.Subgraph_SDL.snap b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/CertificationSchema/AnnotationBased/__snapshots__/CertificationTests.Subgraph_SDL.snap index f4513dc7681..29383f074f9 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/CertificationSchema/AnnotationBased/__snapshots__/CertificationTests.Subgraph_SDL.snap +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/CertificationSchema/AnnotationBased/__snapshots__/CertificationTests.Subgraph_SDL.snap @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@external", "@provides", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@tag", "@key", "@external", "@provides", "FieldSet" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/CertificationSchema/CodeFirst/__snapshots__/CertificationTests.Schema_Snapshot.snap b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/CertificationSchema/CodeFirst/__snapshots__/CertificationTests.Schema_Snapshot.snap index 46dd915c0bc..6d0d1dd4b1c 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/CertificationSchema/CodeFirst/__snapshots__/CertificationTests.Schema_Snapshot.snap +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/CertificationSchema/CodeFirst/__snapshots__/CertificationTests.Schema_Snapshot.snap @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@provides", "@external", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@tag", "@key", "@provides", "@external", "FieldSet" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/CertificationSchema/CodeFirst/__snapshots__/CertificationTests.Subgraph_SDL.snap b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/CertificationSchema/CodeFirst/__snapshots__/CertificationTests.Subgraph_SDL.snap index 46dd915c0bc..6d0d1dd4b1c 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/CertificationSchema/CodeFirst/__snapshots__/CertificationTests.Subgraph_SDL.snap +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/CertificationSchema/CodeFirst/__snapshots__/CertificationTests.Subgraph_SDL.snap @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@provides", "@external", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@tag", "@key", "@provides", "@external", "FieldSet" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ComposeDirectiveTests.TestServiceTypeEmptyQueryTypePureCodeFirst.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ComposeDirectiveTests.TestServiceTypeEmptyQueryTypePureCodeFirst.graphql index 657c3955c20..da0786b488a 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ComposeDirectiveTests.TestServiceTypeEmptyQueryTypePureCodeFirst.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ComposeDirectiveTests.TestServiceTypeEmptyQueryTypePureCodeFirst.graphql @@ -1,4 +1,4 @@ -schema @composeDirective(name: "custom") @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "FieldSet", "@composeDirective" ]) @link(url: "https:\/\/specs.custom.dev\/custom\/v1.0", import: [ "@custom" ]) { +schema @composeDirective(name: "custom") @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@tag", "FieldSet", "@composeDirective" ]) @link(url: "https:\/\/specs.custom.dev\/custom\/v1.0", import: [ "@custom" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ExternalDirectiveTests.AnnotateExternalToTypeFieldAnnotationBased.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ExternalDirectiveTests.AnnotateExternalToTypeFieldAnnotationBased.graphql index 1d8c2ae8af9..1c198381b69 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ExternalDirectiveTests.AnnotateExternalToTypeFieldAnnotationBased.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ExternalDirectiveTests.AnnotateExternalToTypeFieldAnnotationBased.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@external", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@external", "@tag", "FieldSet" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ExternalDirectiveTests.AnnotateExternalToTypeFieldCodeFirst.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ExternalDirectiveTests.AnnotateExternalToTypeFieldCodeFirst.graphql index a346436b10b..ba4d5cb570d 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ExternalDirectiveTests.AnnotateExternalToTypeFieldCodeFirst.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ExternalDirectiveTests.AnnotateExternalToTypeFieldCodeFirst.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@external", "@key", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@external", "@key", "@tag", "FieldSet" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/KeyDirectiveTests.AnnotateKeyToClassAttributeAnnotationBased.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/KeyDirectiveTests.AnnotateKeyToClassAttributeAnnotationBased.graphql index 92904f7d7a5..a46ca1b71e3 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/KeyDirectiveTests.AnnotateKeyToClassAttributeAnnotationBased.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/KeyDirectiveTests.AnnotateKeyToClassAttributeAnnotationBased.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@tag", "FieldSet" ]) { query: QueryOfTestTypePropertyDirective } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/KeyDirectiveTests.AnnotateKeyToClassAttributesAnnotationBased.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/KeyDirectiveTests.AnnotateKeyToClassAttributesAnnotationBased.graphql index 4ca01a79826..1839bd893fd 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/KeyDirectiveTests.AnnotateKeyToClassAttributesAnnotationBased.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/KeyDirectiveTests.AnnotateKeyToClassAttributesAnnotationBased.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@tag", "FieldSet" ]) { query: QueryOfTestTypePropertyDirectives } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/KeyDirectiveTests.AnnotateKeyToInterfaceAttributesAnnotationBased.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/KeyDirectiveTests.AnnotateKeyToInterfaceAttributesAnnotationBased.graphql index b6cdb413d2c..e448bba674a 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/KeyDirectiveTests.AnnotateKeyToInterfaceAttributesAnnotationBased.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/KeyDirectiveTests.AnnotateKeyToInterfaceAttributesAnnotationBased.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@tag", "FieldSet" ]) { query: QueryOfTestTypeClassDirective } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/KeyDirectiveTests.AnnotateKeyToInterfaceTypeCodeFirst.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/KeyDirectiveTests.AnnotateKeyToInterfaceTypeCodeFirst.graphql index 9b0d07316ba..b1e69891b4b 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/KeyDirectiveTests.AnnotateKeyToInterfaceTypeCodeFirst.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/KeyDirectiveTests.AnnotateKeyToInterfaceTypeCodeFirst.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@tag", "FieldSet" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/KeyDirectiveTests.AnnotateKeyToObjectTypeAnnotationBased.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/KeyDirectiveTests.AnnotateKeyToObjectTypeAnnotationBased.graphql index d617f06fae5..a81e78cb445 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/KeyDirectiveTests.AnnotateKeyToObjectTypeAnnotationBased.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/KeyDirectiveTests.AnnotateKeyToObjectTypeAnnotationBased.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@tag", "FieldSet" ]) { query: QueryOfTestTypeClassDirective } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/KeyDirectiveTests.AnnotateKeyToObjectTypeCodeFirst.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/KeyDirectiveTests.AnnotateKeyToObjectTypeCodeFirst.graphql index 0bf1e9a3d0b..c6e684063e1 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/KeyDirectiveTests.AnnotateKeyToObjectTypeCodeFirst.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/KeyDirectiveTests.AnnotateKeyToObjectTypeCodeFirst.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@tag", "FieldSet" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/NonResolvableKeyDirectiveTests.AnnotateKeyToClassAttributeAnnotationBased.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/NonResolvableKeyDirectiveTests.AnnotateKeyToClassAttributeAnnotationBased.graphql index 001c1ed1083..04ac822c43d 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/NonResolvableKeyDirectiveTests.AnnotateKeyToClassAttributeAnnotationBased.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/NonResolvableKeyDirectiveTests.AnnotateKeyToClassAttributeAnnotationBased.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@tag", "FieldSet" ]) { query: QueryOfTestTypePropertyDirective } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/NonResolvableKeyDirectiveTests.AnnotateKeyToClassAttributesAnnotationBased.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/NonResolvableKeyDirectiveTests.AnnotateKeyToClassAttributesAnnotationBased.graphql index 59cee9c7e73..61d772118cd 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/NonResolvableKeyDirectiveTests.AnnotateKeyToClassAttributesAnnotationBased.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/NonResolvableKeyDirectiveTests.AnnotateKeyToClassAttributesAnnotationBased.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@tag", "FieldSet" ]) { query: QueryOfTestTypePropertyDirectives } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/NonResolvableKeyDirectiveTests.AnnotateKeyToInterfaceAttributesAnnotationBased.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/NonResolvableKeyDirectiveTests.AnnotateKeyToInterfaceAttributesAnnotationBased.graphql index 05c596f5ed0..2b616b6e08b 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/NonResolvableKeyDirectiveTests.AnnotateKeyToInterfaceAttributesAnnotationBased.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/NonResolvableKeyDirectiveTests.AnnotateKeyToInterfaceAttributesAnnotationBased.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@tag", "FieldSet" ]) { query: QueryOfTestTypeClassDirective } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/NonResolvableKeyDirectiveTests.AnnotateKeyToInterfaceTypeCodeFirst.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/NonResolvableKeyDirectiveTests.AnnotateKeyToInterfaceTypeCodeFirst.graphql index e4efebb68f2..d30f9f7675e 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/NonResolvableKeyDirectiveTests.AnnotateKeyToInterfaceTypeCodeFirst.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/NonResolvableKeyDirectiveTests.AnnotateKeyToInterfaceTypeCodeFirst.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@tag", "FieldSet" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/NonResolvableKeyDirectiveTests.AnnotateKeyToObjectTypeAnnotationBased.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/NonResolvableKeyDirectiveTests.AnnotateKeyToObjectTypeAnnotationBased.graphql index 646a86b6ac9..125a39f476b 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/NonResolvableKeyDirectiveTests.AnnotateKeyToObjectTypeAnnotationBased.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/NonResolvableKeyDirectiveTests.AnnotateKeyToObjectTypeAnnotationBased.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@tag", "FieldSet" ]) { query: QueryOfTestTypeClassDirective } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/NonResolvableKeyDirectiveTests.AnnotateKeyToObjectTypeCodeFirst.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/NonResolvableKeyDirectiveTests.AnnotateKeyToObjectTypeCodeFirst.graphql index eaadcf45c20..dc014fb1937 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/NonResolvableKeyDirectiveTests.AnnotateKeyToObjectTypeCodeFirst.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/NonResolvableKeyDirectiveTests.AnnotateKeyToObjectTypeCodeFirst.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@tag", "FieldSet" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/OverrideDirectiveTests.OverrideDirective_Annotation.snap b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/OverrideDirectiveTests.OverrideDirective_Annotation.snap index 4b493b6537a..90bb3e041cc 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/OverrideDirectiveTests.OverrideDirective_Annotation.snap +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/OverrideDirectiveTests.OverrideDirective_Annotation.snap @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.0", import: [ "@key", "@override", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.0", import: [ "@key", "@override", "@tag", "FieldSet" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/OverrideDirectiveTests.OverrideDirective_Progressive_Annotation.snap b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/OverrideDirectiveTests.OverrideDirective_Progressive_Annotation.snap index 6811fa4c91d..8f5a8cb9aa2 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/OverrideDirectiveTests.OverrideDirective_Progressive_Annotation.snap +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/OverrideDirectiveTests.OverrideDirective_Progressive_Annotation.snap @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.7", import: [ "@key", "@override", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.7", import: [ "@key", "@override", "@tag", "FieldSet" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/PolicyDirectiveTests.PolicyDirective_GetsAddedCorrectly_Annotations.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/PolicyDirectiveTests.PolicyDirective_GetsAddedCorrectly_Annotations.graphql index 4ac3ad28424..2f166fc7998 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/PolicyDirectiveTests.PolicyDirective_GetsAddedCorrectly_Annotations.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/PolicyDirectiveTests.PolicyDirective_GetsAddedCorrectly_Annotations.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "FieldSet", "@policy" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@tag", "FieldSet", "@policy" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/PolicyDirectiveTests.PolicyDirectives_GetAddedCorrectly_Annotations.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/PolicyDirectiveTests.PolicyDirectives_GetAddedCorrectly_Annotations.graphql index 4ac3ad28424..2f166fc7998 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/PolicyDirectiveTests.PolicyDirectives_GetAddedCorrectly_Annotations.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/PolicyDirectiveTests.PolicyDirectives_GetAddedCorrectly_Annotations.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "FieldSet", "@policy" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@tag", "FieldSet", "@policy" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/PolicyDirectiveTests.PolicyDirectives_GetAddedCorrectly_CodeFirst.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/PolicyDirectiveTests.PolicyDirectives_GetAddedCorrectly_CodeFirst.graphql index 6098ba8ce07..00aa253f46f 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/PolicyDirectiveTests.PolicyDirectives_GetAddedCorrectly_CodeFirst.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/PolicyDirectiveTests.PolicyDirectives_GetAddedCorrectly_CodeFirst.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@policy", "@key", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@policy", "@key", "@tag", "FieldSet" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ProvidesDirectiveTests.AnnotateProvidesToClassAttributePureCodeFirst.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ProvidesDirectiveTests.AnnotateProvidesToClassAttributePureCodeFirst.graphql index 46b087a2eed..ca96d395894 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ProvidesDirectiveTests.AnnotateProvidesToClassAttributePureCodeFirst.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ProvidesDirectiveTests.AnnotateProvidesToClassAttributePureCodeFirst.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@provides", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@provides", "@tag", "FieldSet" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ProvidesDirectiveTests.AnnotateProvidesToFieldCodeFirst.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ProvidesDirectiveTests.AnnotateProvidesToFieldCodeFirst.graphql index c8bcdf300f0..76d4a70aed8 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ProvidesDirectiveTests.AnnotateProvidesToFieldCodeFirst.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ProvidesDirectiveTests.AnnotateProvidesToFieldCodeFirst.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@provides", "@key", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@provides", "@key", "@tag", "FieldSet" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/RequiresDirectiveTests.AnnotateProvidesToClassAttributePureCodeFirst.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/RequiresDirectiveTests.AnnotateProvidesToClassAttributePureCodeFirst.graphql index 42a686b7cee..6c888376617 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/RequiresDirectiveTests.AnnotateProvidesToClassAttributePureCodeFirst.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/RequiresDirectiveTests.AnnotateProvidesToClassAttributePureCodeFirst.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@requires", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@requires", "@tag", "FieldSet" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/RequiresDirectiveTests.AnnotateProvidesToFieldCodeFirst.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/RequiresDirectiveTests.AnnotateProvidesToFieldCodeFirst.graphql index c61ba77ee5c..e585c700436 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/RequiresDirectiveTests.AnnotateProvidesToFieldCodeFirst.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/RequiresDirectiveTests.AnnotateProvidesToFieldCodeFirst.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@requires", "@key", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@requires", "@key", "@tag", "FieldSet" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/RequiresScopesDirectiveTests.RequiresScopesDirective_GetsAddedCorrectly_Annotations.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/RequiresScopesDirectiveTests.RequiresScopesDirective_GetsAddedCorrectly_Annotations.graphql index a7b5c72ed87..7540567cd2c 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/RequiresScopesDirectiveTests.RequiresScopesDirective_GetsAddedCorrectly_Annotations.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/RequiresScopesDirectiveTests.RequiresScopesDirective_GetsAddedCorrectly_Annotations.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "FieldSet", "@requiresScopes" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@tag", "FieldSet", "@requiresScopes" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/RequiresScopesDirectiveTests.RequiresScopesDirectives_GetAddedCorrectly_Annotations.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/RequiresScopesDirectiveTests.RequiresScopesDirectives_GetAddedCorrectly_Annotations.graphql index a7b5c72ed87..7540567cd2c 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/RequiresScopesDirectiveTests.RequiresScopesDirectives_GetAddedCorrectly_Annotations.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/RequiresScopesDirectiveTests.RequiresScopesDirectives_GetAddedCorrectly_Annotations.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "FieldSet", "@requiresScopes" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@tag", "FieldSet", "@requiresScopes" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/RequiresScopesDirectiveTests.RequiresScopesDirectives_GetAddedCorrectly_CodeFirst.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/RequiresScopesDirectiveTests.RequiresScopesDirectives_GetAddedCorrectly_CodeFirst.graphql index 26c0e130ddb..f4ee4435310 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/RequiresScopesDirectiveTests.RequiresScopesDirectives_GetAddedCorrectly_CodeFirst.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/RequiresScopesDirectiveTests.RequiresScopesDirectives_GetAddedCorrectly_CodeFirst.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@requiresScopes", "@key", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@requiresScopes", "@key", "@tag", "FieldSet" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/ServiceTypeTests.cs b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/ServiceTypeTests.cs index fe51807dc7f..6d7b7dbbe0c 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/ServiceTypeTests.cs +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/ServiceTypeTests.cs @@ -33,7 +33,7 @@ public async Task TestServiceTypeEmptyQueryTypePureCodeFirst() .Parse((string)value!) .MatchInlineSnapshot( """ - schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "FieldSet" ]) { + schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@tag", "FieldSet" ]) { query: Query } @@ -89,7 +89,7 @@ public async Task TestServiceTypeTypePureCodeFirst() .Parse((string)value!) .MatchInlineSnapshot( """ - schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.2", import: [ "@key", "FieldSet" ]) { + schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.2", import: [ "@key", "@tag", "FieldSet" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/__snapshots__/BuiltInTypesSharableTests.Ensure_PagingInfo_Is_Sharable.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/__snapshots__/BuiltInTypesSharableTests.Ensure_PagingInfo_Is_Sharable.graphql index ac877e90af5..2e92bc7ce0c 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/__snapshots__/BuiltInTypesSharableTests.Ensure_PagingInfo_Is_Sharable.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/__snapshots__/BuiltInTypesSharableTests.Ensure_PagingInfo_Is_Sharable.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@shareable", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@shareable", "@tag", "FieldSet" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/__snapshots__/BuiltInTypesSharableTests.Ensure_PagingInfo_Is_Sharable_When_Sharable_Already_Registered.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/__snapshots__/BuiltInTypesSharableTests.Ensure_PagingInfo_Is_Sharable_When_Sharable_Already_Registered.graphql index ac877e90af5..2e92bc7ce0c 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/__snapshots__/BuiltInTypesSharableTests.Ensure_PagingInfo_Is_Sharable_When_Sharable_Already_Registered.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/__snapshots__/BuiltInTypesSharableTests.Ensure_PagingInfo_Is_Sharable_When_Sharable_Already_Registered.graphql @@ -1,4 +1,4 @@ -schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@shareable", "FieldSet" ]) { +schema @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@shareable", "@tag", "FieldSet" ]) { query: Query } From 2719fd98b44abf1ed121bb66de13db377ccaf57b Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Mon, 30 Sep 2024 23:11:38 +0200 Subject: [PATCH 028/154] Added more node ID value serializers. (#7538) (cherry picked from commit 9a8ee1cc0fa50476bfba828c8f04344fb5fa7972) --- ...tExecutorBuilderExtensions.IdSerializer.cs | 3 + .../DecimalNodeIdValueSerializer.cs | 35 ++++++++++ .../DoubleNodeIdValueSerializer.cs | 35 ++++++++++ .../SingleNodeIdValueSerializer.cs | 35 ++++++++++ .../Relay/OptimizedNodeIdSerializerTests.cs | 65 ++++++++++++++++++- 5 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 src/HotChocolate/Core/src/Types/Types/Relay/Serialization/DecimalNodeIdValueSerializer.cs create mode 100644 src/HotChocolate/Core/src/Types/Types/Relay/Serialization/DoubleNodeIdValueSerializer.cs create mode 100644 src/HotChocolate/Core/src/Types/Types/Relay/Serialization/SingleNodeIdValueSerializer.cs diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.IdSerializer.cs b/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.IdSerializer.cs index eedbb0a0ae0..3c0c9f47e4d 100644 --- a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.IdSerializer.cs +++ b/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.IdSerializer.cs @@ -50,6 +50,9 @@ public static IRequestExecutorBuilder AddDefaultNodeIdSerializer( builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(new GuidNodeIdValueSerializer(compress: outputNewIdFormat)); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(); } builder.Services.TryAddSingleton(sp => diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/Serialization/DecimalNodeIdValueSerializer.cs b/src/HotChocolate/Core/src/Types/Types/Relay/Serialization/DecimalNodeIdValueSerializer.cs new file mode 100644 index 00000000000..746f12fd7a3 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Types/Relay/Serialization/DecimalNodeIdValueSerializer.cs @@ -0,0 +1,35 @@ +#nullable enable +using System.Buffers.Text; +using System.Diagnostics.CodeAnalysis; + +namespace HotChocolate.Types.Relay; + +internal sealed class DecimalNodeIdValueSerializer : INodeIdValueSerializer +{ + public bool IsSupported(Type type) => type == typeof(decimal) || type == typeof(decimal?); + + public NodeIdFormatterResult Format(Span buffer, object value, out int written) + { + if (value is decimal i) + { + return Utf8Formatter.TryFormat(i, buffer, out written) + ? NodeIdFormatterResult.Success + : NodeIdFormatterResult.BufferTooSmall; + } + + written = 0; + return NodeIdFormatterResult.InvalidValue; + } + + public bool TryParse(ReadOnlySpan buffer, [NotNullWhen(true)] out object? value) + { + if (Utf8Parser.TryParse(buffer, out decimal parsedValue, out _)) + { + value = parsedValue; + return true; + } + + value = null; + return false; + } +} diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/Serialization/DoubleNodeIdValueSerializer.cs b/src/HotChocolate/Core/src/Types/Types/Relay/Serialization/DoubleNodeIdValueSerializer.cs new file mode 100644 index 00000000000..a6bd49c5fc8 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Types/Relay/Serialization/DoubleNodeIdValueSerializer.cs @@ -0,0 +1,35 @@ +#nullable enable +using System.Buffers.Text; +using System.Diagnostics.CodeAnalysis; + +namespace HotChocolate.Types.Relay; + +internal sealed class DoubleNodeIdValueSerializer : INodeIdValueSerializer +{ + public bool IsSupported(Type type) => type == typeof(double) || type == typeof(double?); + + public NodeIdFormatterResult Format(Span buffer, object value, out int written) + { + if (value is double i) + { + return Utf8Formatter.TryFormat(i, buffer, out written) + ? NodeIdFormatterResult.Success + : NodeIdFormatterResult.BufferTooSmall; + } + + written = 0; + return NodeIdFormatterResult.InvalidValue; + } + + public bool TryParse(ReadOnlySpan buffer, [NotNullWhen(true)] out object? value) + { + if (Utf8Parser.TryParse(buffer, out double parsedValue, out _)) + { + value = parsedValue; + return true; + } + + value = null; + return false; + } +} diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/Serialization/SingleNodeIdValueSerializer.cs b/src/HotChocolate/Core/src/Types/Types/Relay/Serialization/SingleNodeIdValueSerializer.cs new file mode 100644 index 00000000000..5b96cbc50fa --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Types/Relay/Serialization/SingleNodeIdValueSerializer.cs @@ -0,0 +1,35 @@ +#nullable enable +using System.Buffers.Text; +using System.Diagnostics.CodeAnalysis; + +namespace HotChocolate.Types.Relay; + +internal sealed class SingleNodeIdValueSerializer : INodeIdValueSerializer +{ + public bool IsSupported(Type type) => type == typeof(float) || type == typeof(float?); + + public NodeIdFormatterResult Format(Span buffer, object value, out int written) + { + if (value is float i) + { + return Utf8Formatter.TryFormat(i, buffer, out written) + ? NodeIdFormatterResult.Success + : NodeIdFormatterResult.BufferTooSmall; + } + + written = 0; + return NodeIdFormatterResult.InvalidValue; + } + + public bool TryParse(ReadOnlySpan buffer, [NotNullWhen(true)] out object? value) + { + if (Utf8Parser.TryParse(buffer, out float parsedValue, out _)) + { + value = parsedValue; + return true; + } + + value = null; + return false; + } +} diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/OptimizedNodeIdSerializerTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/OptimizedNodeIdSerializerTests.cs index 787ebeeb0ad..adf73503872 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/OptimizedNodeIdSerializerTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/OptimizedNodeIdSerializerTests.cs @@ -154,6 +154,36 @@ public void Format_Int64Id_Legacy_Format() Assert.Equal("Rm9vCmw2NA==", id); } + [Fact] + public void Format_DecimalId() + { + var serializer = CreateSerializer("Foo", new DecimalNodeIdValueSerializer()); + + var id = serializer.Format("Foo", (decimal)6); + + Assert.Equal("Rm9vOjY=", id); + } + + [Fact] + public void Format_FloatId() + { + var serializer = CreateSerializer("Foo", new SingleNodeIdValueSerializer()); + + var id = serializer.Format("Foo", (float)6); + + Assert.Equal("Rm9vOjY=", id); + } + + [Fact] + public void Format_DoubleId() + { + var serializer = CreateSerializer("Foo", new DoubleNodeIdValueSerializer()); + + var id = serializer.Format("Foo", (double)6); + + Assert.Equal("Rm9vOjY=", id); + } + [Fact] public void Format_Empty_Guid() { @@ -363,6 +393,39 @@ public void Parse_Legacy_Int64Id() Assert.Equal((long)123, id.InternalId); } + [Fact] + public void Parse_DecimalId() + { + var serializer = CreateSerializer("Foo", new DecimalNodeIdValueSerializer()); + + var id = serializer.Parse("Rm9vOjEyMw==", typeof(decimal)); + + Assert.Equal("Foo", id.TypeName); + Assert.Equal((decimal)123, id.InternalId); + } + + [Fact] + public void Parse_SingleId() + { + var serializer = CreateSerializer("Foo", new SingleNodeIdValueSerializer()); + + var id = serializer.Parse("Rm9vOjEyMw==", typeof(float)); + + Assert.Equal("Foo", id.TypeName); + Assert.Equal((float)123, id.InternalId); + } + + [Fact] + public void Parse_DoublelId() + { + var serializer = CreateSerializer("Foo", new DoubleNodeIdValueSerializer()); + + var id = serializer.Parse("Rm9vOjEyMw==", typeof(double)); + + Assert.Equal("Foo", id.TypeName); + Assert.Equal((double)123, id.InternalId); + } + [Fact] public void Parse_CompositeId() { @@ -497,7 +560,7 @@ protected override NodeIdFormatterResult Format(Span buffer, CompositeId v protected override bool TryParse(ReadOnlySpan buffer, out CompositeId value) { - if(TryParseIdPart(buffer, out string a, out var ac) && + if (TryParseIdPart(buffer, out string a, out var ac) && TryParseIdPart(buffer.Slice(ac), out int b, out var bc) && TryParseIdPart(buffer.Slice(ac + bc), out Guid c, out var cc) && TryParseIdPart(buffer.Slice(ac + bc + cc), out bool d, out _)) From 31cc5e978d6c6d472e5fbe56c3b38bf8e9d98c3c Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Mon, 30 Sep 2024 23:58:45 +0200 Subject: [PATCH 029/154] Renamed ToSelectorExpression() to AsSelector() (#7539) (cherry picked from commit 5fc5a24bc7ebf5b8b882d85de78ff19016fd02ed) --- .../HotChocolateExecutionSelectionExtensions.cs | 2 +- .../HotChocolateExecutionDataLoaderExtensions.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs b/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs index 2c7c85881b2..6e2d5d817ee 100644 --- a/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs +++ b/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs @@ -27,7 +27,7 @@ public static class HotChocolateExecutionSelectionExtensions /// /// Returns a selector expression that can be used for data projections. /// - public static Expression> ToSelectorExpression( + public static Expression> AsSelector( this ISelection selection) => GetOrCreateExpression(selection); diff --git a/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs b/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs index 297b9ebe770..7506a06ea29 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs @@ -44,7 +44,7 @@ public static ISelectionDataLoader Select( ISelection selection) where TKey : notnull { - var expression = selection.ToSelectorExpression(); + var expression = selection.AsSelector(); return dataLoader.Select(expression); } @@ -79,7 +79,7 @@ public static IPagingDataLoader> Select( var count = GetConnectionSelections(selection, buffer); for (var i = 0; i < count; i++) { - var expression = buffer[i].ToSelectorExpression(); + var expression = buffer[i].AsSelector(); dataLoader.Select(expression); } ArrayPool.Shared.Return(buffer); @@ -90,14 +90,14 @@ public static IPagingDataLoader> Select( var count = GetCollectionSelections(selection, buffer); for (var i = 0; i < count; i++) { - var expression = buffer[i].ToSelectorExpression(); + var expression = buffer[i].AsSelector(); dataLoader.Select(expression); } ArrayPool.Shared.Return(buffer); } else { - var expression = selection.ToSelectorExpression(); + var expression = selection.AsSelector(); dataLoader.Select(expression); } From 470e4ddcdf7a719096f5c9243c0d3704f49b9234 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Mon, 30 Sep 2024 23:59:16 +0200 Subject: [PATCH 030/154] Added ToConnection mapping overloads (#7540) (cherry picked from commit b5e9821245e4f637403e18e10d04ed10f898f2e0) --- .../Core/src/Types.CursorPagination/Edge.cs | 35 +++- .../Extensions/PagingResultExtensions.cs | 158 ++++++++++++++++ .../PagingHelperIntegrationTests.cs | 176 +++++++++++++++++- ...erTests.Map_Page_To_Connection_With_Dto.md | 28 +++ ...Tests.Map_Page_To_Connection_With_Dto_2.md | 28 +++ 5 files changed, 412 insertions(+), 13 deletions(-) create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_2.md diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/Edge.cs b/src/HotChocolate/Core/src/Types.CursorPagination/Edge.cs index 0336903f75e..84bffea1b51 100644 --- a/src/HotChocolate/Core/src/Types.CursorPagination/Edge.cs +++ b/src/HotChocolate/Core/src/Types.CursorPagination/Edge.cs @@ -9,7 +9,7 @@ namespace HotChocolate.Types.Pagination; /// public class Edge : IEdge { - private readonly Func? _resolveCursor; + private readonly Func, string>? _resolveCursor; private string? _cursor; /// @@ -42,16 +42,43 @@ public Edge(T node, string cursor) /// The node that the edge will wrap. /// /// - /// A delegate that resolves the cursor which identifies the in the + /// A delegate that resolves the cursor which identifies the in the data set. /// /// /// is . /// public Edge(T node, Func resolveCursor) { + if (resolveCursor is null) + { + throw new ArgumentNullException(nameof(resolveCursor)); + } + Node = node; - _resolveCursor = resolveCursor ?? + _resolveCursor = edge => resolveCursor(edge.Node); + } + + /// + /// Initializes a new instance of . + /// + /// + /// The node that the edge will wrap. + /// + /// + /// A delegate that resolves the cursor which identifies the in the data set. + /// + /// + /// is . + /// + public Edge(T node, Func, string> resolveCursor) + { + if (resolveCursor is null) + { throw new ArgumentNullException(nameof(resolveCursor)); + } + + Node = node; + _resolveCursor = resolveCursor; } /// @@ -78,7 +105,7 @@ public string Cursor throw new InvalidOperationException(Edge_Cursor_CursorAndResolverNull); } - _cursor = _resolveCursor(Node); + _cursor = _resolveCursor(this); Debug.Assert(_cursor is not null, "The edge's cursor resolver returned null."); } diff --git a/src/HotChocolate/Pagination/src/Pagination.Mappings/Extensions/PagingResultExtensions.cs b/src/HotChocolate/Pagination/src/Pagination.Mappings/Extensions/PagingResultExtensions.cs index 9c70360d861..b0fbdcb5eb6 100644 --- a/src/HotChocolate/Pagination/src/Pagination.Mappings/Extensions/PagingResultExtensions.cs +++ b/src/HotChocolate/Pagination/src/Pagination.Mappings/Extensions/PagingResultExtensions.cs @@ -29,6 +29,66 @@ public static async Task> ToConnectionAsync( } #nullable restore + /// + /// Converts a to a . + /// + /// + /// The source entity type. + /// + /// + /// The target entity type. + /// + /// + /// The page result. + /// + /// + /// A factory to create an edge from a source entity. + /// + /// + /// Returns a relay connection. + /// +#nullable disable + public static async Task> ToConnectionAsync( + this Task> resultPromise, + Func> createEdge) + where TTarget : class + where TSource : class + { + var result = await resultPromise; + return CreateConnection(result, createEdge); + } +#nullable restore + + /// + /// Converts a to a . + /// + /// + /// The source entity type. + /// + /// + /// The target entity type. + /// + /// + /// The page result. + /// + /// + /// A factory to create an edge from a source entity. + /// + /// + /// Returns a relay connection. + /// +#nullable disable + public static async Task> ToConnectionAsync( + this Task> resultPromise, + Func, Edge> createEdge) + where TTarget : class + where TSource : class + { + var result = await resultPromise; + return CreateConnection(result, createEdge); + } +#nullable restore + /// /// Converts a to a . /// @@ -41,6 +101,7 @@ public static async Task> ToConnectionAsync( /// /// Returns a relay connection. /// +#nullable disable public static async ValueTask> ToConnectionAsync( this ValueTask> resultPromise) where T : class @@ -48,6 +109,67 @@ public static async ValueTask> ToConnectionAsync( var result = await resultPromise; return CreateConnection(result); } +#nullable restore + + /// + /// Converts a to a . + /// + /// + /// The source entity type. + /// + /// + /// The target entity type. + /// + /// + /// The page result. + /// + /// + /// A factory to create an edge from a source entity. + /// + /// + /// Returns a relay connection. + /// +#nullable disable + public static async ValueTask> ToConnectionAsync( + this ValueTask> resultPromise, + Func> createEdge) + where TTarget : class + where TSource : class + { + var result = await resultPromise; + return CreateConnection(result, createEdge); + } +#nullable restore + + /// + /// Converts a to a . + /// + /// + /// The source entity type. + /// + /// + /// The target entity type. + /// + /// + /// The page result. + /// + /// + /// A factory to create an edge from a source entity. + /// + /// + /// Returns a relay connection. + /// +#nullable disable + public static async ValueTask> ToConnectionAsync( + this ValueTask> resultPromise, + Func, Edge> createEdge) + where TTarget : class + where TSource : class + { + var result = await resultPromise; + return CreateConnection(result, createEdge); + } +#nullable restore /// /// Converts a to a . @@ -80,6 +202,42 @@ private static Connection CreateConnection(Page? page) where T : class page.TotalCount ?? 0); } + private static Connection CreateConnection( + Page? page, + Func> createEdge) + where TTarget : class + where TSource : class + { + page ??= Page.Empty; + + return new Connection( + page.Items.Select(t => createEdge(t, page.CreateCursor(t))).ToArray(), + new ConnectionPageInfo( + page.HasNextPage, + page.HasPreviousPage, + CreateCursor(page.First, page.CreateCursor), + CreateCursor(page.Last, page.CreateCursor)), + page.TotalCount ?? 0); + } + + private static Connection CreateConnection( + Page? page, + Func, Edge> createEdge) + where TTarget : class + where TSource : class + { + page ??= Page.Empty; + + return new Connection( + page.Items.Select(t => createEdge(t, page)).ToArray(), + new ConnectionPageInfo( + page.HasNextPage, + page.HasPreviousPage, + CreateCursor(page.First, page.CreateCursor), + CreateCursor(page.Last, page.CreateCursor)), + page.TotalCount ?? 0); + } + private static string? CreateCursor(T? item, Func createCursor) where T : class => item is null ? null : createCursor(item); } diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs index dc39160eb6e..74a50af3bed 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs @@ -518,14 +518,14 @@ public async Task Paging_Empty_PagingArgs() // Assert await Snapshot.Create() .Add(new - { - result.HasNextPage, - result.HasPreviousPage, - First = result.First?.Id, - FirstCursor = result.First is not null ? result.CreateCursor(result.First) : null, - Last = result.Last?.Id, - LastCursor = result.Last is not null ? result.CreateCursor(result.Last) : null - }) + { + result.HasNextPage, + result.HasPreviousPage, + First = result.First?.Id, + FirstCursor = result.First is not null ? result.CreateCursor(result.First) : null, + Last = result.Last?.Id, + LastCursor = result.Last is not null ? result.CreateCursor(result.Last) : null + }) .Add(result.Items) .MatchMarkdownAsync(); } @@ -709,6 +709,84 @@ public async Task BatchPaging_Last_5() snapshot.MatchMarkdownSnapshot(); } + [Fact] + public async Task Map_Page_To_Connection_With_Dto() + { + // Arrange + var connectionString = CreateConnectionString(); + await SeedAsync(connectionString); + + // Act + var result = await new ServiceCollection() + .AddScoped(_ => new CatalogContext(connectionString)) + .AddGraphQL() + .AddQueryType() + .AddTypeExtension(typeof(BrandConnectionEdgeExtensions)) + .AddPagingArguments() + .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) + .ExecuteRequestAsync( + """ + { + brands(first: 2) { + edges { + cursor + displayName + node { + id + name + } + } + } + } + """); + + // Assert + var operationResult = result.ExpectOperationResult(); + + await Snapshot.Create() + .Add(operationResult.WithExtensions(ImmutableDictionary.Empty)) + .MatchMarkdownAsync(); + } + + [Fact] + public async Task Map_Page_To_Connection_With_Dto_2() + { + // Arrange + var connectionString = CreateConnectionString(); + await SeedAsync(connectionString); + + // Act + var result = await new ServiceCollection() + .AddScoped(_ => new CatalogContext(connectionString)) + .AddGraphQL() + .AddQueryType() + .AddTypeExtension(typeof(BrandConnectionEdgeExtensions2)) + .AddPagingArguments() + .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) + .ExecuteRequestAsync( + """ + { + brands(first: 2) { + edges { + cursor + displayName + node { + id + name + } + } + } + } + """); + + // Assert + var operationResult = result.ExpectOperationResult(); + + await Snapshot.Create() + .Add(operationResult.WithExtensions(ImmutableDictionary.Empty)) + .MatchMarkdownAsync(); + } + private static async Task SeedAsync(string connectionString) { await using var context = new CatalogContext(connectionString); @@ -731,7 +809,9 @@ private static async Task SeedAsync(string connectionString) { var product = new Product { - Name = $"Product {i}-{j}", Type = type, Brand = brand, + Name = $"Product {i}-{j}", + Type = type, + Brand = brand, }; context.Products.Add(product); } @@ -802,6 +882,84 @@ public async Task> GetBrandsDeep( .ToConnectionAsync(); } + public class QueryConnection + { + [UsePaging(ConnectionName = "BrandConnection")] + public async Task> GetBrandsAsync( + CatalogContext context, + PagingArguments arguments, + CancellationToken ct) + => await context.Brands + .OrderBy(t => t.Name) + .ThenBy(t => t.Id) + .ToPageAsync(arguments, cancellationToken: ct) + .ToConnectionAsync((brand, page) => new BrandEdge(brand, edge => page.CreateCursor(edge.Brand))); + } + + public class QueryConnection2 + { + [UsePaging(ConnectionName = "BrandConnection")] + public async Task> GetBrandsAsync( + CatalogContext context, + PagingArguments arguments, + CancellationToken ct) + => await context.Brands + .OrderBy(t => t.Name) + .ThenBy(t => t.Id) + .ToPageAsync(arguments, cancellationToken: ct) + .ToConnectionAsync((brand, cursor) => new BrandEdge2(brand, cursor)); + } + + [ExtendObjectType("BrandConnectionEdge")] + public class BrandConnectionEdgeExtensions + { + public string? GetDisplayName([Parent] BrandEdge edge) + => edge.Brand.DisplayName; + } + + [ExtendObjectType("BrandConnectionEdge")] + public class BrandConnectionEdgeExtensions2 + { + public string? GetDisplayName([Parent] BrandEdge2 edge) + => edge.Brand.DisplayName; + } + + public class BrandEdge : Edge + { + public BrandEdge(Brand brand, Func cursor) + : base(new BrandDto(brand.Id, brand.Name), edge => cursor((BrandEdge)edge)) + { + Brand = brand; + } + + public Brand Brand { get; } + } + + public class BrandEdge2 : Edge + { + public BrandEdge2(Brand brand, string cursor) + : base(new BrandDto(brand.Id, brand.Name), cursor) + { + Brand = brand; + } + + public Brand Brand { get; } + } + + + public class BrandDto + { + public BrandDto(int id, string name) + { + Id = id; + Name = name; + } + + public int Id { get; } + + public string Name { get; } + } + [ExtendObjectType] public static class BrandExtensions { diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto.md new file mode 100644 index 00000000000..5073d8c02fd --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto.md @@ -0,0 +1,28 @@ +# Map_Page_To_Connection_With_Dto + +```json +{ + "data": { + "brands": { + "edges": [ + { + "cursor": "QnJhbmQwOjE=", + "displayName": "BrandDisplay0", + "node": { + "id": 1, + "name": "Brand0" + } + }, + { + "cursor": "QnJhbmQxOjI=", + "displayName": null, + "node": { + "id": 2, + "name": "Brand1" + } + } + ] + } + } +} +``` diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_2.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_2.md new file mode 100644 index 00000000000..6a4cd2863d0 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_2.md @@ -0,0 +1,28 @@ +# Map_Page_To_Connection_With_Dto_2 + +```json +{ + "data": { + "brands": { + "edges": [ + { + "cursor": "QnJhbmQwOjE=", + "displayName": "BrandDisplay0", + "node": { + "id": 1, + "name": "Brand0" + } + }, + { + "cursor": "QnJhbmQxOjI=", + "displayName": null, + "node": { + "id": 2, + "name": "Brand1" + } + } + ] + } + } +} +``` From 2a2943d74dcdd38940fbaddb1a40c8ba6ccbd335 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Tue, 1 Oct 2024 13:54:36 +0200 Subject: [PATCH 031/154] Added nullability awareness to projections. (#7541) (cherry picked from commit 97ebf4b6f3829f62820f346996c332656e89204b) --- src/GreenDonut/src/Core/GreenDonut.csproj | 1 + ...otChocolateExecutionSelectionExtensions.cs | 133 ++++++++++- .../src/Execution/Processing/Operation.cs | 19 ++ ...tChocolateExecutionDataLoaderExtensions.cs | 108 ++------- .../Projections/SelectionExpressionBuilder.cs | 53 ++++- .../Types/Execution/Processing/IOperation.cs | 6 +- ...aLoaderTests.Branches_Are_Merged_NET7_0.md | 2 +- ...Tests.Brand_Details_Country_Name_NET7_0.md | 2 +- ...leDataLoaderTests.Force_A_Branch_NET7_0.md | 4 +- ...ct_With_Name_And_Brand_With_Name_NET7_0.md | 2 +- .../PagingHelperIntegrationTests.cs | 219 +++++++++++++++++- .../TestContext/FooBarContext.cs | 51 ++++ ....Ensure_Nullable_Connections_Dont_Throw.md | 57 +++++ ...nsure_Nullable_Connections_Dont_Throw_2.md | 59 +++++ ...ullable_Connections_Dont_Throw_2_NET6_0.md | 59 +++++ ...ullable_Connections_Dont_Throw_2_NET7_0.md | 59 +++++ ..._Nullable_Connections_Dont_Throw_NET6_0.md | 57 +++++ ..._Nullable_Connections_Dont_Throw_NET7_0.md | 57 +++++ 18 files changed, 838 insertions(+), 110 deletions(-) create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/TestContext/FooBarContext.cs create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET6_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET7_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET6_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET7_0.md diff --git a/src/GreenDonut/src/Core/GreenDonut.csproj b/src/GreenDonut/src/Core/GreenDonut.csproj index e3b8389ee65..a638c155bbf 100644 --- a/src/GreenDonut/src/Core/GreenDonut.csproj +++ b/src/GreenDonut/src/Core/GreenDonut.csproj @@ -8,6 +8,7 @@ + diff --git a/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs b/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs index 6e2d5d817ee..fb61b730ef6 100644 --- a/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs +++ b/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs @@ -1,9 +1,15 @@ #if NET6_0_OR_GREATER +using System.Buffers; using System.Buffers.Text; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Text; using System.Runtime.CompilerServices; +using GreenDonut.Projections; using HotChocolate.Execution.Projections; +using HotChocolate.Types; +using HotChocolate.Types.Descriptors.Definitions; +using HotChocolate.Utilities; // ReSharper disable once CheckNamespace namespace HotChocolate.Execution.Processing; @@ -27,17 +33,136 @@ public static class HotChocolateExecutionSelectionExtensions /// /// Returns a selector expression that can be used for data projections. /// +#if NET8_0_OR_GREATER + [Experimental(Experiments.Projections)] +#endif public static Expression> AsSelector( this ISelection selection) - => GetOrCreateExpression(selection); + { + // we first check if we already have an expression for this selection, + // this would be the cheapest way to get the expression. + if(TryGetExpression(selection, out var expression)) + { + return expression; + } + + // if we do not have an expression we need to create one. + // we first check what kind of field selection we have, + // connection, collection or single field. + var flags = ((ObjectField)selection.Field).Flags; + + if ((flags & FieldFlags.Connection) == FieldFlags.Connection) + { + var builder = new DefaultSelectorBuilder(); + var buffer = ArrayPool.Shared.Rent(16); + var count = GetConnectionSelections(selection, buffer); + for (var i = 0; i < count; i++) + { + builder.Add(GetOrCreateExpression(buffer[i])); + } + ArrayPool.Shared.Return(buffer); + return GetOrCreateExpression(selection, builder); + } + + if ((flags & FieldFlags.CollectionSegment) == FieldFlags.CollectionSegment) + { + var builder = new DefaultSelectorBuilder(); + var buffer = ArrayPool.Shared.Rent(16); + var count = GetCollectionSelections(selection, buffer); + for (var i = 0; i < count; i++) + { + builder.Add(GetOrCreateExpression(buffer[i])); + } + ArrayPool.Shared.Return(buffer); + return GetOrCreateExpression(selection, builder); + } + + return GetOrCreateExpression(selection); + } private static Expression> GetOrCreateExpression( ISelection selection) - { - return selection.DeclaringOperation.GetOrAddState( + => selection.DeclaringOperation.GetOrAddState( CreateExpressionKey(selection.Id), static (_, ctx) => ctx._builder.BuildExpression(ctx.selection), (_builder, selection)); + +#if NET8_0_OR_GREATER + [Experimental(Experiments.Projections)] +#endif + private static Expression> GetOrCreateExpression( + ISelection selection, + ISelectorBuilder builder) + => selection.DeclaringOperation.GetOrAddState( + CreateExpressionKey(selection.Id), + static (_, ctx) => ctx.builder.TryCompile()!, + (builder, selection)); + + private static bool TryGetExpression( + ISelection selection, + [NotNullWhen(true)] out Expression>? expression) + => selection.DeclaringOperation.TryGetState(CreateExpressionKey(selection.Id), out expression); + + private static int GetConnectionSelections(ISelection selection, Span buffer) + { + var pageType = (ObjectType)selection.Field.Type.NamedType(); + var connectionSelections = selection.DeclaringOperation.GetSelectionSet(selection, pageType); + var count = 0; + + foreach (var connectionChild in connectionSelections.Selections) + { + if (connectionChild.Field.Name.EqualsOrdinal("nodes")) + { + if (buffer.Length == count) + { + throw new InvalidOperationException("To many alias selections of nodes and edges."); + } + + buffer[count++] = connectionChild; + } + else if (connectionChild.Field.Name.EqualsOrdinal("edges")) + { + var edgeType = (ObjectType)connectionChild.Field.Type.NamedType(); + var edgeSelections = connectionChild.DeclaringOperation.GetSelectionSet(connectionChild, edgeType); + + foreach (var edgeChild in edgeSelections.Selections) + { + if (edgeChild.Field.Name.EqualsOrdinal("node")) + { + if (buffer.Length == count) + { + throw new InvalidOperationException("To many alias selections of nodes and edges."); + } + + buffer[count++] = edgeChild; + } + } + } + } + + return count; + } + + private static int GetCollectionSelections(ISelection selection, Span buffer) + { + var pageType = (ObjectType)selection.Field.Type.NamedType(); + var connectionSelections = selection.DeclaringOperation.GetSelectionSet(selection, pageType); + var count = 0; + + foreach (var connectionChild in connectionSelections.Selections) + { + if (connectionChild.Field.Name.EqualsOrdinal("items")) + { + if (buffer.Length == count) + { + throw new InvalidOperationException("To many alias selections of items."); + } + + buffer[count++] = connectionChild; + } + } + + return count; } private static string CreateExpressionKey(int key) @@ -63,7 +188,7 @@ private static int EstimateIntLength(int value) } // if the number is negative we need one more digit for the sign - var length = (value < 0) ? 1 : 0; + var length = value < 0 ? 1 : 0; // we add the number of digits the number has to the length of the number. length += (int)Math.Floor(Math.Log10(Math.Abs(value)) + 1); diff --git a/src/HotChocolate/Core/src/Execution/Processing/Operation.cs b/src/HotChocolate/Core/src/Execution/Processing/Operation.cs index 87ea5873070..6aa317beb2b 100644 --- a/src/HotChocolate/Core/src/Execution/Processing/Operation.cs +++ b/src/HotChocolate/Core/src/Execution/Processing/Operation.cs @@ -117,6 +117,25 @@ public long CreateIncludeFlags(IVariableValueCollection variables) return context; } + public bool TryGetState(out TState? state) + { + var key = typeof(TState).FullName ?? throw new InvalidOperationException(); + return TryGetState(key, out state); + } + + public bool TryGetState(string key, out TState? state) + { + if(_contextData.TryGetValue(key, out var value) + && value is TState casted) + { + state = casted; + return true; + } + + state = default; + return false; + } + public TState GetOrAddState(Func createState) => GetOrAddState(_ => createState(), null); diff --git a/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs b/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs index 7506a06ea29..9bac2da93ff 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs @@ -3,12 +3,8 @@ using System.Buffers; using System.Diagnostics.CodeAnalysis; -using HotChocolate.Execution; using HotChocolate.Execution.Processing; using HotChocolate.Pagination; -using HotChocolate.Types; -using HotChocolate.Types.Descriptors.Definitions; -using HotChocolate.Utilities; // ReSharper disable once CheckNamespace namespace GreenDonut.Projections; @@ -44,6 +40,16 @@ public static ISelectionDataLoader Select( ISelection selection) where TKey : notnull { + if (dataLoader == null) + { + throw new ArgumentNullException(nameof(dataLoader)); + } + + if (selection == null) + { + throw new ArgumentNullException(nameof(selection)); + } + var expression = selection.AsSelector(); return dataLoader.Select(expression); } @@ -71,99 +77,19 @@ public static IPagingDataLoader> Select( ISelection selection) where TKey : notnull { - var flags = ((ObjectField)selection.Field).Flags; - - if ((flags & FieldFlags.Connection) == FieldFlags.Connection) + if (dataLoader == null) { - var buffer = ArrayPool.Shared.Rent(16); - var count = GetConnectionSelections(selection, buffer); - for (var i = 0; i < count; i++) - { - var expression = buffer[i].AsSelector(); - dataLoader.Select(expression); - } - ArrayPool.Shared.Return(buffer); - } - else if ((flags & FieldFlags.CollectionSegment) == FieldFlags.CollectionSegment) - { - var buffer = ArrayPool.Shared.Rent(16); - var count = GetCollectionSelections(selection, buffer); - for (var i = 0; i < count; i++) - { - var expression = buffer[i].AsSelector(); - dataLoader.Select(expression); - } - ArrayPool.Shared.Return(buffer); - } - else - { - var expression = selection.AsSelector(); - dataLoader.Select(expression); + throw new ArgumentNullException(nameof(dataLoader)); } - return dataLoader; - } - - private static int GetConnectionSelections(ISelection selection, Span buffer) - { - var pageType = (ObjectType)selection.Field.Type.NamedType(); - var connectionSelections = selection.DeclaringOperation.GetSelectionSet(selection, pageType); - var count = 0; - - foreach (var connectionChild in connectionSelections.Selections) - { - if (connectionChild.Field.Name.EqualsOrdinal("nodes")) - { - if (buffer.Length == count) - { - throw new InvalidOperationException("To many alias selections of nodes and edges."); - } - - buffer[count++] = connectionChild; - } - else if (connectionChild.Field.Name.EqualsOrdinal("edges")) - { - var edgeType = (ObjectType)selection.Field.Type.NamedType(); - var edgeSelections = selection.DeclaringOperation.GetSelectionSet(connectionChild, edgeType); - - foreach (var edgeChild in edgeSelections.Selections) - { - if (edgeChild.Field.Name.EqualsOrdinal("node")) - { - if (buffer.Length == count) - { - throw new InvalidOperationException("To many alias selections of nodes and edges."); - } - - buffer[count++] = edgeChild; - } - } - } - } - - return count; - } - - private static int GetCollectionSelections(ISelection selection, Span buffer) - { - var pageType = (ObjectType)selection.Field.Type.NamedType(); - var connectionSelections = selection.DeclaringOperation.GetSelectionSet(selection, pageType); - var count = 0; - - foreach (var connectionChild in connectionSelections.Selections) + if (selection == null) { - if (connectionChild.Field.Name.EqualsOrdinal("items")) - { - if (buffer.Length == count) - { - throw new InvalidOperationException("To many alias selections of items."); - } - - buffer[count++] = connectionChild; - } + throw new ArgumentNullException(nameof(selection)); } - return count; + var expression = selection.AsSelector(); + dataLoader.Select(expression); + return dataLoader; } } #endif diff --git a/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs b/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs index 06b246a918b..c7fe259fed3 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs @@ -2,6 +2,7 @@ using System.Collections.Immutable; using System.Linq.Expressions; using System.Reflection; +using System.Runtime.CompilerServices; using HotChocolate.Execution.Processing; using HotChocolate.Features; using HotChocolate.Types; @@ -159,7 +160,19 @@ private void CollectSelections( if (node.Nodes.Count == 0) { - return Expression.Bind(node.Property, propertyAccessor); + if (IsNullableType(node.Property)) + { + var nullCheck = Expression.Condition( + Expression.Equal(propertyAccessor, Expression.Constant(null)), + Expression.Constant(null, node.Property.PropertyType), + propertyAccessor); + + return Expression.Bind(node.Property, nullCheck); + } + else + { + return Expression.Bind(node.Property, propertyAccessor); + } } if(node.IsArrayOrCollection) @@ -168,9 +181,43 @@ private void CollectSelections( } var newContext = context with { Parent = propertyAccessor, ParentType = node.Property.PropertyType }; - var requirementsExpression = BuildExpression(node.Nodes, newContext); - return requirementsExpression is null ? null : Expression.Bind(node.Property, requirementsExpression); + var nestedExpression = BuildExpression(node.Nodes, newContext); + + if (IsNullableType(node.Property)) + { + var nullCheck = Expression.Condition( + Expression.Equal(propertyAccessor, Expression.Constant(null)), + Expression.Constant(null, node.Property.PropertyType), + nestedExpression ?? (Expression)Expression.Constant(null, node.Property.PropertyType)); + + return Expression.Bind(node.Property, nullCheck); + } + + return nestedExpression is null ? null : Expression.Bind(node.Property, nestedExpression); + } + + #if NET8_0_OR_GREATER + private static bool IsNullableType(PropertyInfo propertyInfo) + { + if (propertyInfo.PropertyType.IsValueType) + { + return Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null; + } + + var nullableAttribute = propertyInfo.GetCustomAttribute(); + + if (nullableAttribute != null) + { + return nullableAttribute.NullableFlags[0] == 2; + } + + return false; } + #else + private static bool IsNullableType(PropertyInfo propertyInfo) + => !propertyInfo.PropertyType.IsValueType + || Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null; + #endif private MemberInitExpression? BuildExpression( IReadOnlyList properties, diff --git a/src/HotChocolate/Core/src/Types/Execution/Processing/IOperation.cs b/src/HotChocolate/Core/src/Types/Execution/Processing/IOperation.cs index 2e9d3f2c34c..0d4dfd76ddd 100644 --- a/src/HotChocolate/Core/src/Types/Execution/Processing/IOperation.cs +++ b/src/HotChocolate/Core/src/Types/Execution/Processing/IOperation.cs @@ -108,6 +108,10 @@ public interface IOperation : IHasReadOnlyContextData, IEnumerable long CreateIncludeFlags(IVariableValueCollection variables); + bool TryGetState(out TState? state); + + bool TryGetState(string key, out TState? state); + /// /// Gets or adds state to this operation. /// @@ -118,7 +122,7 @@ public interface IOperation : IHasReadOnlyContextData, IEnumerable /// - /// Returns the state. + /// Returns the state. /// TState GetOrAddState( Func createState); diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Branches_Are_Merged_NET7_0.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Branches_Are_Merged_NET7_0.md index 185b19dd69f..3499d4a5fe9 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Branches_Are_Merged_NET7_0.md +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Branches_Are_Merged_NET7_0.md @@ -3,7 +3,7 @@ ## SQL ```text -SELECT p."Name", b."Name", p."Id" +SELECT p."Name", FALSE, b."Name", p."Id" FROM "Products" AS p INNER JOIN "Brands" AS b ON p."BrandId" = b."Id" WHERE p."Id" IN (1, 2) diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Country_Name_NET7_0.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Country_Name_NET7_0.md index a5a7f99d878..0c5cbe5f31d 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Country_Name_NET7_0.md +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Country_Name_NET7_0.md @@ -3,7 +3,7 @@ ## SQL ```text -SELECT b."Name", b."Details_Country_Name" AS "Name", b."Id" +SELECT b."Name", FALSE, b."Details_Country_Name", b."Id" FROM "Brands" AS b WHERE b."Id" = 1 ``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Force_A_Branch_NET7_0.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Force_A_Branch_NET7_0.md index 293c0b0bf66..0e861a7e423 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Force_A_Branch_NET7_0.md +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Force_A_Branch_NET7_0.md @@ -3,11 +3,11 @@ ## SQL ```text -SELECT p."Name", b."Name", p."Id" +SELECT p."Name", FALSE, b."Name", p."Id" FROM "Products" AS p INNER JOIN "Brands" AS b ON p."BrandId" = b."Id" WHERE p."Id" = 1 -SELECT p."Id", b."Id" +SELECT p."Id", FALSE, b."Id" FROM "Products" AS p INNER JOIN "Brands" AS b ON p."BrandId" = b."Id" WHERE p."Id" = 1 diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Product_With_Name_And_Brand_With_Name_NET7_0.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Product_With_Name_And_Brand_With_Name_NET7_0.md index b4823d43c47..38e82a2302c 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Product_With_Name_And_Brand_With_Name_NET7_0.md +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Product_With_Name_And_Brand_With_Name_NET7_0.md @@ -3,7 +3,7 @@ ## SQL ```text -SELECT p."Name", b."Name", p."Id" +SELECT p."Name", FALSE, b."Name", p."Id" FROM "Products" AS p INNER JOIN "Brands" AS b ON p."BrandId" = b."Id" WHERE p."Id" = 1 diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs index 74a50af3bed..5712d7ace85 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs @@ -673,6 +673,7 @@ public async Task BatchPaging_First_5() }, name: page.Key.ToString()); } + snapshot.MatchMarkdownSnapshot(); } @@ -706,6 +707,7 @@ public async Task BatchPaging_Last_5() }, name: page.Key.ToString()); } + snapshot.MatchMarkdownSnapshot(); } @@ -787,6 +789,140 @@ await Snapshot.Create() .MatchMarkdownAsync(); } + [Fact] + public async Task Ensure_Nullable_Connections_Dont_Throw() + { + // Arrange + var connectionString = CreateConnectionString(); + await SeedFooAsync(connectionString); + + // Act + var result = await new ServiceCollection() + .AddScoped(_ => new FooBarContext(connectionString)) + .AddGraphQL() + .AddQueryType() + .AddPagingArguments() + .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) + .ExecuteRequestAsync( + """ + { + foos(first: 10) { + edges { + cursor + } + nodes { + id + name + bar { + id + description + } + } + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + } + } + """); + + // Assert + string? sql = null; + string? expression = null; + var operationResult = result.ExpectOperationResult(); + if (operationResult.Extensions?.TryGetValue("sql", out var value) ?? false) + { + sql = value!.ToString(); + } + + if (operationResult.Extensions?.TryGetValue("expression", out value) ?? false) + { + expression = value!.ToString(); + } + +#if NET8_0_OR_GREATER + await Snapshot.Create() +#elif NET7_0 + await Snapshot.Create(postFix: "NET7_0") +#elif NET6_0 + await Snapshot.Create(postFix: "NET6_0") +#endif + .Add(sql, "SQL") + .Add(expression, "Expression") + .Add(operationResult.WithExtensions(ImmutableDictionary.Empty), "Result") + .MatchMarkdownAsync(); + } + + [Fact] + public async Task Ensure_Nullable_Connections_Dont_Throw_2() + { + // Arrange + var connectionString = CreateConnectionString(); + await SeedFooAsync(connectionString); + + // Act + var result = await new ServiceCollection() + .AddScoped(_ => new FooBarContext(connectionString)) + .AddGraphQL() + .AddQueryType() + .AddPagingArguments() + .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) + .ExecuteRequestAsync( + """ + { + foos(first: 10) { + edges { + cursor + } + nodes { + id + name + bar { + id + description + someField1 + someField2 + } + } + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + } + } + """); + + // Assert + string? sql = null; + string? expression = null; + var operationResult = result.ExpectOperationResult(); + if (operationResult.Extensions?.TryGetValue("sql", out var value) ?? false) + { + sql = value!.ToString(); + } + + if (operationResult.Extensions?.TryGetValue("expression", out value) ?? false) + { + expression = value!.ToString(); + } + +#if NET8_0_OR_GREATER + await Snapshot.Create() +#elif NET7_0 + await Snapshot.Create(postFix: "NET7_0") +#elif NET6_0 + await Snapshot.Create(postFix: "NET6_0") +#endif + .Add(sql, "SQL") + .Add(expression, "Expression") + .Add(operationResult.WithExtensions(ImmutableDictionary.Empty), "Result") + .MatchMarkdownAsync(); + } + private static async Task SeedAsync(string connectionString) { await using var context = new CatalogContext(connectionString); @@ -807,12 +943,7 @@ private static async Task SeedAsync(string connectionString) for (var j = 0; j < 100; j++) { - var product = new Product - { - Name = $"Product {i}-{j}", - Type = type, - Brand = brand, - }; + var product = new Product { Name = $"Product {i}-{j}", Type = type, Brand = brand, }; context.Products.Add(product); } } @@ -820,6 +951,48 @@ private static async Task SeedAsync(string connectionString) await context.SaveChangesAsync(); } + private static async Task SeedFooAsync(string connectionString) + { + await using var context = new FooBarContext(connectionString); + await context.Database.EnsureCreatedAsync(); + + context.Bars.Add( + new Bar + { + Id = 1, + Description = "Bar 1", + SomeField1 = "abc", + SomeField2 = null + }); + + context.Bars.Add( + new Bar + { + Id = 2, + Description = "Bar 2", + SomeField1 = "def", + SomeField2 = "ghi" + }); + + context.Foos.Add( + new Foo + { + Id = 1, + Name = "Foo 1", + BarId = null + }); + + context.Foos.Add( + new Foo + { + Id = 2, + Name = "Foo 2", + BarId = 1 + }); + + await context.SaveChangesAsync(); + } + public class Query { [UsePaging] @@ -910,6 +1083,40 @@ public async Task> GetBrandsAsync( .ToConnectionAsync((brand, cursor) => new BrandEdge2(brand, cursor)); } + public class QueryNullable + { + [UsePaging] + public async Task> GetFoosAsync( + FooBarContext context, + PagingArguments arguments, + ISelection selection, + IResolverContext rc, + CancellationToken ct) + { + var sql = context.Foos + .OrderBy(t => t.Name) + .ThenBy(t => t.Id) + .Select(selection.AsSelector()) + .ToQueryString(); + + var expression = context.Foos + .OrderBy(t => t.Name) + .ThenBy(t => t.Id) + .Select(selection.AsSelector()) + .Expression.ToString(); + + ((IMiddlewareContext)rc).OperationResult.SetExtension("sql", sql); + ((IMiddlewareContext)rc).OperationResult.SetExtension("expression", expression); + + return await context.Foos + .OrderBy(t => t.Name) + .ThenBy(t => t.Id) + .Select(selection.AsSelector()) + .ToPageAsync(arguments, cancellationToken: ct) + .ToConnectionAsync(); + } + } + [ExtendObjectType("BrandConnectionEdge")] public class BrandConnectionEdgeExtensions { diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/TestContext/FooBarContext.cs b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/TestContext/FooBarContext.cs new file mode 100644 index 00000000000..381a2316b80 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/TestContext/FooBarContext.cs @@ -0,0 +1,51 @@ +using System.ComponentModel.DataAnnotations; +using Microsoft.EntityFrameworkCore; + +namespace HotChocolate.Data.TestContext; + +public class FooBarContext(string connectionString) : DbContext +{ + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseNpgsql(connectionString); + } + + public DbSet Foos { get; set; } = default!; + + public DbSet Bars { get; set; } = default!; + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id); + entity.HasOne(e => e.Bar) + .WithMany() + .HasForeignKey("BarId"); + }); + + modelBuilder.Entity(e => e.HasKey(e => e.Id)); + } +} + +public class Foo +{ + public int Id { get; set; } + + [MaxLength(100)] public string Name { get; set; } = default!; + + public int? BarId { get; set; } + + public Bar? Bar { get; set; } +} + +public class Bar +{ + public int Id { get; set; } + + [MaxLength(100)] public string? Description { get; set; } + + [MaxLength(100)] public string SomeField1 { get; set; } = default!; + + [MaxLength(100)] public string? SomeField2 { get; set; } +} diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw.md new file mode 100644 index 00000000000..47144e676f5 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw.md @@ -0,0 +1,57 @@ +# Ensure_Nullable_Connections_Dont_Throw + +## SQL + +```text +SELECT f."Id", f."Name", b."Id" IS NULL, b."Id", b."Description" +FROM "Foos" AS f +LEFT JOIN "Bars" AS b ON f."BarId" = b."Id" +ORDER BY f."Name", f."Id" +``` + +## Expression + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = root.Name, Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = root.Bar.Description})}) +``` + +## Result + +```json +{ + "data": { + "foos": { + "edges": [ + { + "cursor": "Rm9vIDE6MQ==" + }, + { + "cursor": "Rm9vIDI6Mg==" + } + ], + "nodes": [ + { + "id": 1, + "name": "Foo 1", + "bar": null + }, + { + "id": 2, + "name": "Foo 2", + "bar": { + "id": 1, + "description": "Bar 1" + } + } + ], + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "startCursor": "Rm9vIDE6MQ==", + "endCursor": "Rm9vIDI6Mg==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2.md new file mode 100644 index 00000000000..9d5412fef69 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2.md @@ -0,0 +1,59 @@ +# Ensure_Nullable_Connections_Dont_Throw_2 + +## SQL + +```text +SELECT f."Id", f."Name", b."Id" IS NULL, b."Id", b."Description", b."SomeField1", b."SomeField2" +FROM "Foos" AS f +LEFT JOIN "Bars" AS b ON f."BarId" = b."Id" +ORDER BY f."Name", f."Id" +``` + +## Expression + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = root.Name, Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = root.Bar.Description, SomeField1 = root.Bar.SomeField1, SomeField2 = root.Bar.SomeField2})}) +``` + +## Result + +```json +{ + "data": { + "foos": { + "edges": [ + { + "cursor": "Rm9vIDE6MQ==" + }, + { + "cursor": "Rm9vIDI6Mg==" + } + ], + "nodes": [ + { + "id": 1, + "name": "Foo 1", + "bar": null + }, + { + "id": 2, + "name": "Foo 2", + "bar": { + "id": 1, + "description": "Bar 1", + "someField1": "abc", + "someField2": null + } + } + ], + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "startCursor": "Rm9vIDE6MQ==", + "endCursor": "Rm9vIDI6Mg==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET6_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET6_0.md new file mode 100644 index 00000000000..60c823bf34f --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET6_0.md @@ -0,0 +1,59 @@ +# Ensure_Nullable_Connections_Dont_Throw_2 + +## SQL + +```text +SELECT f."Id", f."Name", b."Id" IS NULL, b."Id", b."Description", b."SomeField1", b."SomeField2" +FROM "Foos" AS f +LEFT JOIN "Bars" AS b ON f."BarId" = b."Id" +ORDER BY f."Name", f."Id" +``` + +## Expression + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name), Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description), SomeField1 = IIF((root.Bar.SomeField1 == null), null, root.Bar.SomeField1), SomeField2 = IIF((root.Bar.SomeField2 == null), null, root.Bar.SomeField2)})}) +``` + +## Result + +```json +{ + "data": { + "foos": { + "edges": [ + { + "cursor": "Rm9vIDE6MQ==" + }, + { + "cursor": "Rm9vIDI6Mg==" + } + ], + "nodes": [ + { + "id": 1, + "name": "Foo 1", + "bar": null + }, + { + "id": 2, + "name": "Foo 2", + "bar": { + "id": 1, + "description": "Bar 1", + "someField1": "abc", + "someField2": null + } + } + ], + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "startCursor": "Rm9vIDE6MQ==", + "endCursor": "Rm9vIDI6Mg==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET7_0.md new file mode 100644 index 00000000000..25de998bc69 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET7_0.md @@ -0,0 +1,59 @@ +# Ensure_Nullable_Connections_Dont_Throw_2 + +## SQL + +```text +SELECT f."Id", f."Name", b."Id" IS NULL, b."Id", b."Description", b."SomeField1", b."SomeField2" +FROM "Foos" AS f +LEFT JOIN "Bars" AS b ON f."BarId" = b."Id" +ORDER BY f."Name", f."Id" +``` + +## Expression + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name), Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description), SomeField1 = IIF((root.Bar.SomeField1 == null), null, root.Bar.SomeField1), SomeField2 = IIF((root.Bar.SomeField2 == null), null, root.Bar.SomeField2)})}) +``` + +## Result + +```json +{ + "data": { + "foos": { + "edges": [ + { + "cursor": "Rm9vIDE6MQ==" + }, + { + "cursor": "Rm9vIDI6Mg==" + } + ], + "nodes": [ + { + "id": 1, + "name": "Foo 1", + "bar": null + }, + { + "id": 2, + "name": "Foo 2", + "bar": { + "id": 1, + "description": "Bar 1", + "someField1": "abc", + "someField2": null + } + } + ], + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "startCursor": "Rm9vIDE6MQ==", + "endCursor": "Rm9vIDI6Mg==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET6_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET6_0.md new file mode 100644 index 00000000000..d51b3de6448 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET6_0.md @@ -0,0 +1,57 @@ +# Ensure_Nullable_Connections_Dont_Throw + +## SQL + +```text +SELECT f."Id", f."Name", b."Id" IS NULL, b."Id", b."Description" +FROM "Foos" AS f +LEFT JOIN "Bars" AS b ON f."BarId" = b."Id" +ORDER BY f."Name", f."Id" +``` + +## Expression + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name), Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description)})}) +``` + +## Result + +```json +{ + "data": { + "foos": { + "edges": [ + { + "cursor": "Rm9vIDE6MQ==" + }, + { + "cursor": "Rm9vIDI6Mg==" + } + ], + "nodes": [ + { + "id": 1, + "name": "Foo 1", + "bar": null + }, + { + "id": 2, + "name": "Foo 2", + "bar": { + "id": 1, + "description": "Bar 1" + } + } + ], + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "startCursor": "Rm9vIDE6MQ==", + "endCursor": "Rm9vIDI6Mg==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET7_0.md new file mode 100644 index 00000000000..f67d40e5ee1 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET7_0.md @@ -0,0 +1,57 @@ +# Ensure_Nullable_Connections_Dont_Throw + +## SQL + +```text +SELECT f."Id", f."Name", b."Id" IS NULL, b."Id", b."Description" +FROM "Foos" AS f +LEFT JOIN "Bars" AS b ON f."BarId" = b."Id" +ORDER BY f."Name", f."Id" +``` + +## Expression + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name), Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description)})}) +``` + +## Result + +```json +{ + "data": { + "foos": { + "edges": [ + { + "cursor": "Rm9vIDE6MQ==" + }, + { + "cursor": "Rm9vIDI6Mg==" + } + ], + "nodes": [ + { + "id": 1, + "name": "Foo 1", + "bar": null + }, + { + "id": 2, + "name": "Foo 2", + "bar": { + "id": 1, + "description": "Bar 1" + } + } + ], + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "startCursor": "Rm9vIDE6MQ==", + "endCursor": "Rm9vIDI6Mg==" + } + } + } +} +``` + From 4270746f7b944bbbbf168b111fb6a13204e3fe24 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Tue, 1 Oct 2024 14:01:40 +0200 Subject: [PATCH 032/154] Removed old code fragments. (cherry picked from commit 0ebec4a3465af8ee469a6535b6d729fb5a18d1ff) --- .../Data/src/Data/QueryableExecutable.txt | 174 ------------------ 1 file changed, 174 deletions(-) delete mode 100644 src/HotChocolate/Data/src/Data/QueryableExecutable.txt diff --git a/src/HotChocolate/Data/src/Data/QueryableExecutable.txt b/src/HotChocolate/Data/src/Data/QueryableExecutable.txt deleted file mode 100644 index a970c6de23c..00000000000 --- a/src/HotChocolate/Data/src/Data/QueryableExecutable.txt +++ /dev/null @@ -1,174 +0,0 @@ -using System.Collections; -using System.Runtime.CompilerServices; -using static HotChocolate.Data.ErrorHelper; - -namespace HotChocolate.Data; - -public class QueryableExecutable : IQueryableExecutable -{ - public QueryableExecutable(IQueryable queryable) - { - Source = queryable; - InMemory = Source is EnumerableQuery; - } - - /// - /// The current state of the executable - /// - public IQueryable Source { get; } - - object IExecutable.Source => Source; - - /// - /// Is true if source is a in memory query and - /// false if it is a database query - /// - public bool InMemory { get; } - - /// - /// Returns a new enumerable executable with the provided source - /// - /// The source that should be set - /// The new instance of an enumerable executable - public virtual QueryableExecutable WithSource(IQueryable source) - => new(source); - - /// - public virtual async ValueTask> ToListAsync(CancellationToken cancellationToken) - { - if (Source is not IAsyncEnumerable ae) - { - return Source.ToList(); - } - - var result = new List(); - - await foreach (var element in ae.WithCancellation(cancellationToken).ConfigureAwait(false)) - { - result.Add(element); - } - - return result; - } - - async ValueTask IExecutable.ToListAsync(CancellationToken cancellationToken) - => await ToListAsync(cancellationToken); - - public async IAsyncEnumerable ToAsyncEnumerable([EnumeratorCancellation] CancellationToken cancellationToken) - { - if (Source is IAsyncEnumerable asyncEnumerable) - { - await foreach (var element in asyncEnumerable.WithCancellation(cancellationToken).ConfigureAwait(false)) - { - yield return element; - } - } - else if (Source is EnumerableQuery) - { - foreach (var element in Source) - { - yield return element; - } - } - else - { - var list = await Task.Run(() => Source.ToList(), cancellationToken).WaitAsync(cancellationToken); - - foreach (var element in list) - { - yield return element; - } - } - } - - async IAsyncEnumerable IExecutable.ToAsyncEnumerable( - [EnumeratorCancellation] CancellationToken cancellationToken) - { - if (Source is IAsyncEnumerable asyncEnumerable) - { - await foreach (var element in asyncEnumerable.WithCancellation(cancellationToken).ConfigureAwait(false)) - { - yield return element; - } - } - else if (Source is EnumerableQuery) - { - foreach (var element in Source) - { - yield return element; - } - } - else - { - var list = await Task.Run(() => Source.ToList(), cancellationToken).WaitAsync(cancellationToken); - - foreach (var element in list) - { - yield return element; - } - } - } - - /// - public virtual async ValueTask FirstOrDefaultAsync(CancellationToken cancellationToken) - { - if (Source is IAsyncEnumerable ae) - { - await using var enumerator = - ae.GetAsyncEnumerator(cancellationToken); - - if (await enumerator.MoveNextAsync().ConfigureAwait(false)) - { - return enumerator.Current; - } - - return default!; - } - - return Source.FirstOrDefault(); - } - - async ValueTask IExecutable.FirstOrDefaultAsync(CancellationToken cancellationToken) - => await FirstOrDefaultAsync(cancellationToken); - - /// - public virtual async ValueTask SingleOrDefaultAsync( - CancellationToken cancellationToken) - { - if (Source is IAsyncEnumerable ae) - { - await using var enumerator = ae.GetAsyncEnumerator(cancellationToken); - - T? result; - if (await enumerator.MoveNextAsync().ConfigureAwait(false)) - { - result = enumerator.Current; - } - else - { - result = default; - } - - if (await enumerator.MoveNextAsync().ConfigureAwait(false)) - { - throw new GraphQLException(ProjectionProvider_CreateMoreThanOneError()); - } - - return result; - } - - try - { - return Source.SingleOrDefault(); - } - catch (InvalidOperationException) - { - throw new GraphQLException(ProjectionProvider_CreateMoreThanOneError()); - } - } - - async ValueTask IExecutable.SingleOrDefaultAsync(CancellationToken cancellationToken) - => await SingleOrDefaultAsync(cancellationToken); - - public virtual string Print() => Source.ToString() ?? ""; -} From 1496620d5b8adcf178834354dcb7083e6355ae87 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Tue, 1 Oct 2024 22:06:02 +0200 Subject: [PATCH 033/154] Improved selector when using batching. (#7542) (cherry picked from commit 314c9d880fe323dcea1fe8ee8a0c63c843f8727f) --- .../ExtractOrderPropertiesVisitor.cs | 53 +++++++++++++ .../ExtractSelectExpressionVisitor.cs | 41 ++++++++++ .../Expressions/QueryHelpers.cs | 75 +++++++++++++++++++ .../Expressions/ReplaceSelectorVisitor.cs | 25 +++++++ .../Expressions/ReplacerParameterVisitor.cs | 14 ++++ .../Extensions/PagingQueryableExtensions.cs | 4 + 6 files changed, 212 insertions(+) create mode 100644 src/HotChocolate/Pagination/src/Pagination.EntityFramework/Expressions/ExtractOrderPropertiesVisitor.cs create mode 100644 src/HotChocolate/Pagination/src/Pagination.EntityFramework/Expressions/ExtractSelectExpressionVisitor.cs create mode 100644 src/HotChocolate/Pagination/src/Pagination.EntityFramework/Expressions/QueryHelpers.cs create mode 100644 src/HotChocolate/Pagination/src/Pagination.EntityFramework/Expressions/ReplaceSelectorVisitor.cs create mode 100644 src/HotChocolate/Pagination/src/Pagination.EntityFramework/Expressions/ReplacerParameterVisitor.cs diff --git a/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Expressions/ExtractOrderPropertiesVisitor.cs b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Expressions/ExtractOrderPropertiesVisitor.cs new file mode 100644 index 00000000000..72393194fd1 --- /dev/null +++ b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Expressions/ExtractOrderPropertiesVisitor.cs @@ -0,0 +1,53 @@ +using System.Linq.Expressions; + +namespace HotChocolate.Pagination.Expressions; + +internal sealed class ExtractOrderPropertiesVisitor : ExpressionVisitor +{ + private const string _orderByMethod = "OrderBy"; + private const string _thenByMethod = "ThenBy"; + private const string _orderByDescendingMethod = "OrderByDescending"; + private const string _thenByDescendingMethod = "ThenByDescending"; + private bool _isOrderScope; + + public List OrderProperties { get; } = []; + + protected override Expression VisitMethodCall(MethodCallExpression node) + { + if (node.Method.Name == _orderByMethod || + node.Method.Name == _thenByMethod || + node.Method.Name == _orderByDescendingMethod || + node.Method.Name == _thenByDescendingMethod) + { + _isOrderScope = true; + + var lambda = StripQuotes(node.Arguments[1]); + Visit(lambda.Body); + + _isOrderScope = false; + } + + return base.VisitMethodCall(node); + } + + protected override Expression VisitMember(MemberExpression node) + { + if (_isOrderScope) + { + // we only collect members that are within an order method. + OrderProperties.Add(node); + } + + return base.VisitMember(node); + } + + private static LambdaExpression StripQuotes(Expression expression) + { + while (expression.NodeType == ExpressionType.Quote) + { + expression = ((UnaryExpression)expression).Operand; + } + + return (LambdaExpression)expression; + } +} diff --git a/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Expressions/ExtractSelectExpressionVisitor.cs b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Expressions/ExtractSelectExpressionVisitor.cs new file mode 100644 index 00000000000..e1f6950918d --- /dev/null +++ b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Expressions/ExtractSelectExpressionVisitor.cs @@ -0,0 +1,41 @@ +using System.Linq.Expressions; + +namespace HotChocolate.Pagination.Expressions; + +internal sealed class ExtractSelectExpressionVisitor : ExpressionVisitor +{ + private const string _selectMethod = "Select"; + + public LambdaExpression? Selector { get; private set; } + + protected override Expression VisitMethodCall(MethodCallExpression node) + { + if (node.Method.Name == _selectMethod && node.Arguments.Count == 2) + { + var lambda = StripQuotes(node.Arguments[1]); + if (lambda.Type.IsGenericType + && lambda.Type.GetGenericTypeDefinition() == typeof(Func<,>)) + { + // we make sure that the selector is of type Expression> + // otherwise we are not interested in it. + var genericArgs = lambda.Type.GetGenericArguments(); + if (genericArgs[0] == genericArgs[1]) + { + Selector = lambda; + } + } + } + + return base.VisitMethodCall(node); + } + + private static LambdaExpression StripQuotes(Expression e) + { + while (e.NodeType == ExpressionType.Quote) + { + e = ((UnaryExpression)e).Operand; + } + + return (LambdaExpression)e; + } +} diff --git a/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Expressions/QueryHelpers.cs b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Expressions/QueryHelpers.cs new file mode 100644 index 00000000000..ae6680628f4 --- /dev/null +++ b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Expressions/QueryHelpers.cs @@ -0,0 +1,75 @@ +using System.Linq.Expressions; + +namespace HotChocolate.Pagination.Expressions; + +internal static class QueryHelpers +{ + public static IQueryable EnsureOrderPropsAreSelected( + IQueryable query) + { + var selector = ExtractCurrentSelector(query); + if (selector is null) + { + return query; + } + + var orderByProperties = ExtractOrderProperties(query); + if(orderByProperties.Count == 0) + { + return query; + } + + var updatedSelector = AddPropertiesInSelector(selector, orderByProperties); + return ReplaceSelector(query, updatedSelector); + } + + private static Expression>? ExtractCurrentSelector( + IQueryable query) + { + var visitor = new ExtractSelectExpressionVisitor(); + visitor.Visit(query.Expression); + return visitor.Selector as Expression>; + } + + private static Expression> AddPropertiesInSelector( + Expression> selector, + List properties) + { + var parameter = selector.Parameters[0]; + var bindings = ((MemberInitExpression)selector.Body).Bindings.Cast().ToList(); + + foreach (var property in properties) + { + var propertyName = property.Member.Name; + if(property.Expression is not ParameterExpression parameterExpression + || bindings.Any(b => b.Member.Name == propertyName)) + { + continue; + } + + var replacer = new ReplacerParameterVisitor(parameterExpression, parameter); + var rewrittenProperty = (MemberExpression)replacer.Visit(property); + bindings.Add(Expression.Bind(rewrittenProperty.Member, rewrittenProperty)); + } + + var newBody = Expression.MemberInit(Expression.New(typeof(T)), bindings); + return Expression.Lambda>(newBody, parameter); + } + + private static List ExtractOrderProperties( + IQueryable query) + { + var visitor = new ExtractOrderPropertiesVisitor(); + visitor.Visit(query.Expression); + return visitor.OrderProperties; + } + + private static IQueryable ReplaceSelector( + IQueryable query, + Expression> newSelector) + { + var visitor = new ReplaceSelectorVisitor(newSelector); + var newExpression = visitor.Visit(query.Expression); + return query.Provider.CreateQuery(newExpression); + } +} diff --git a/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Expressions/ReplaceSelectorVisitor.cs b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Expressions/ReplaceSelectorVisitor.cs new file mode 100644 index 00000000000..6c3c325a2c4 --- /dev/null +++ b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Expressions/ReplaceSelectorVisitor.cs @@ -0,0 +1,25 @@ +using System.Linq.Expressions; + +namespace HotChocolate.Pagination.Expressions; + +internal sealed class ReplaceSelectorVisitor( + Expression> newSelector) + : ExpressionVisitor +{ + private const string _selectMethod = "Select"; + + protected override Expression VisitMethodCall(MethodCallExpression node) + { + if (node.Method.Name == _selectMethod && node.Arguments.Count == 2) + { + return Expression.Call( + node.Method.DeclaringType!, + node.Method.Name, + [typeof(T), typeof(T)], + node.Arguments[0], + newSelector); + } + + return base.VisitMethodCall(node); + } +} diff --git a/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Expressions/ReplacerParameterVisitor.cs b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Expressions/ReplacerParameterVisitor.cs new file mode 100644 index 00000000000..5007c3a3891 --- /dev/null +++ b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Expressions/ReplacerParameterVisitor.cs @@ -0,0 +1,14 @@ +using System.Linq.Expressions; + +namespace HotChocolate.Pagination.Expressions; + +internal sealed class ReplacerParameterVisitor( + ParameterExpression oldParameter, + ParameterExpression newParameter) + : ExpressionVisitor +{ + protected override Expression VisitParameter(ParameterExpression node) + => node == oldParameter + ? newParameter + : base.VisitParameter(node); +} diff --git a/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs index 572d74580df..9fa20a89a70 100644 --- a/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs +++ b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs @@ -72,6 +72,8 @@ public static async ValueTask> ToPageAsync( throw new ArgumentNullException(nameof(source)); } + source = QueryHelpers.EnsureOrderPropsAreSelected(source); + var keys = ParseDataSetKeys(source); if (keys.Length == 0) @@ -215,6 +217,8 @@ public static async ValueTask>> ToBatchPageAsync Date: Wed, 2 Oct 2024 15:43:59 +0200 Subject: [PATCH 034/154] Added inheritance support to new projections engine. (#7546) --- .../src/CookieCrumble/QueryInfo.cs | 8 + .../src/Core/Projections/ExpressionHelpers.cs | 54 ++- .../Projections/FieldRequirementsMetadata.cs | 8 +- .../src/Execution/Projections/PropertyNode.cs | 10 +- .../Projections/PropertyTreeBuilder.cs | 35 +- .../RequirementsTypeInterceptor.cs | 2 +- .../Projections/SelectionExpressionBuilder.cs | 152 +++++---- .../Execution/Projections/TypeContainer.cs | 55 ++++ .../{PropertyNodeContainer.cs => TypeNode.cs} | 26 +- .../InterfaceIntegrationTests.cs | 308 ++++++++++++++++++ .../PagingHelperIntegrationTests.cs | 4 +- .../TestContext/AnimalContext.cs | 64 ++++ ...aceIntegrationTests.Query_Owner_Animals.md | 148 +++++++++ ...ests.Query_Owner_Animals_With_Fragments.md | 159 +++++++++ 14 files changed, 934 insertions(+), 99 deletions(-) create mode 100644 src/CookieCrumble/src/CookieCrumble/QueryInfo.cs create mode 100644 src/HotChocolate/Core/src/Execution/Projections/TypeContainer.cs rename src/HotChocolate/Core/src/Execution/Projections/{PropertyNodeContainer.cs => TypeNode.cs} (78%) create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/InterfaceIntegrationTests.cs create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/TestContext/AnimalContext.cs create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments.md diff --git a/src/CookieCrumble/src/CookieCrumble/QueryInfo.cs b/src/CookieCrumble/src/CookieCrumble/QueryInfo.cs new file mode 100644 index 00000000000..13990acd4ff --- /dev/null +++ b/src/CookieCrumble/src/CookieCrumble/QueryInfo.cs @@ -0,0 +1,8 @@ +namespace CookieCrumble; + +public sealed class QueryInfo +{ + public required string QueryText { get; init; } + + public required string ExpressionText { get; init; } +} diff --git a/src/GreenDonut/src/Core/Projections/ExpressionHelpers.cs b/src/GreenDonut/src/Core/Projections/ExpressionHelpers.cs index 5b9640ee071..61695a27fdd 100644 --- a/src/GreenDonut/src/Core/Projections/ExpressionHelpers.cs +++ b/src/GreenDonut/src/Core/Projections/ExpressionHelpers.cs @@ -10,10 +10,10 @@ public static Expression> Combine( Expression> first, Expression> second) { - var parameter = Expression.Parameter(typeof(T), "entity"); - var firstBody = (MemberInitExpression)ReplaceParameter(first.Body, first.Parameters[0], parameter); - var secondBody = (MemberInitExpression)ReplaceParameter(second.Body, second.Parameters[0], parameter); - var combinedBody = CombineMemberInitExpressions(firstBody, secondBody); + var parameter = Expression.Parameter(typeof(T), "root"); + var firstBody = ReplaceParameter(first.Body, first.Parameters[0], parameter); + var secondBody = ReplaceParameter(second.Body, second.Parameters[0], parameter); + var combinedBody = CombineExpressions(firstBody, secondBody); return Expression.Lambda>(combinedBody, parameter); } @@ -23,6 +23,32 @@ private static Expression ReplaceParameter( Expression replacement) => new ParameterReplacer(toReplace, replacement).Visit(body); + private static Expression CombineExpressions(Expression first, Expression second) + { + if (first is MemberInitExpression firstInit && second is MemberInitExpression secondInit) + { + return CombineMemberInitExpressions(firstInit, secondInit); + } + + if (first is ConditionalExpression firstCond && second is ConditionalExpression secondCond) + { + return CombineConditionalExpressions(firstCond, secondCond); + } + + if (first is ConditionalExpression firstConditional && second is MemberInitExpression secondMemberInit) + { + return CombineConditionalAndMemberInit(firstConditional, secondMemberInit); + } + + if (first is MemberInitExpression firstMemberInit && second is ConditionalExpression secondConditional) + { + return CombineConditionalAndMemberInit(secondConditional, firstMemberInit); + } + + // as a fallback we return the second body, assuming it overwrites the first. + return second; + } + private static MemberInitExpression CombineMemberInitExpressions( MemberInitExpression first, MemberInitExpression second) @@ -42,6 +68,26 @@ private static MemberInitExpression CombineMemberInitExpressions( return Expression.MemberInit(first.NewExpression, bindings.Values); } + private static ConditionalExpression CombineConditionalExpressions( + ConditionalExpression first, + ConditionalExpression second) + { + var test = first.Test; + var ifTrue = CombineExpressions(first.IfTrue, second.IfTrue); + var ifFalse = CombineExpressions(first.IfFalse, second.IfFalse); + return Expression.Condition(test, ifTrue, ifFalse); + } + + private static Expression CombineConditionalAndMemberInit( + ConditionalExpression condition, + MemberInitExpression memberInit) + { + var ifTrue = CombineExpressions(condition.IfTrue, memberInit); + var ifFalse = condition.IfFalse; + + return Expression.Condition(condition.Test, ifTrue, ifFalse); + } + public static Expression> Rewrite( Expression> selector) { diff --git a/src/HotChocolate/Core/src/Execution/Projections/FieldRequirementsMetadata.cs b/src/HotChocolate/Core/src/Execution/Projections/FieldRequirementsMetadata.cs index 1d657bcc887..e0af8f99f06 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/FieldRequirementsMetadata.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/FieldRequirementsMetadata.cs @@ -6,13 +6,13 @@ namespace HotChocolate.Execution.Projections; internal sealed class FieldRequirementsMetadata { - private readonly Dictionary> _allRequirements = new(); + private readonly Dictionary _allRequirements = new(); private bool _sealed; - public ImmutableArray? GetRequirements(IObjectField field) - => _allRequirements.TryGetValue(field.Coordinate, out var requirements) ? requirements : null; + public TypeNode? GetRequirements(IObjectField field) + => _allRequirements.GetValueOrDefault(field.Coordinate); - public void TryAddRequirements(SchemaCoordinate fieldCoordinate, ImmutableArray requirements) + public void TryAddRequirements(SchemaCoordinate fieldCoordinate, TypeNode requirements) { if(_sealed) { diff --git a/src/HotChocolate/Core/src/Execution/Projections/PropertyNode.cs b/src/HotChocolate/Core/src/Execution/Projections/PropertyNode.cs index 9685a013b1e..a4fe261d807 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/PropertyNode.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/PropertyNode.cs @@ -3,10 +3,9 @@ namespace HotChocolate.Execution.Projections; -internal sealed class PropertyNode : PropertyNodeContainer +internal sealed class PropertyNode : TypeContainer { - public PropertyNode(PropertyInfo property, List? nodes = null) - : base(nodes) + public PropertyNode(PropertyInfo property, List? nodes = null) : base(nodes) { Property = property; IsArray = property.PropertyType.IsArray; @@ -33,7 +32,7 @@ public PropertyNode(PropertyInfo property, List? nodes = null) private PropertyNode( PropertyInfo property, - List? nodes, + List? nodes, bool isArray, bool isCollection, Type? elementType) @@ -57,7 +56,7 @@ private PropertyNode( public PropertyNode Clone() { - List? nodes = null; + List? nodes = null; if (Nodes.Count > 0) { @@ -71,6 +70,7 @@ public PropertyNode Clone() return new PropertyNode(Property, nodes, IsArray, IsCollection, ElementType); } + private static Type? GetCollectionType(Type type) { if (type.IsGenericType diff --git a/src/HotChocolate/Core/src/Execution/Projections/PropertyTreeBuilder.cs b/src/HotChocolate/Core/src/Execution/Projections/PropertyTreeBuilder.cs index 9448fd7e38d..65e2a7f0b90 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/PropertyTreeBuilder.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/PropertyTreeBuilder.cs @@ -6,7 +6,7 @@ namespace HotChocolate.Execution.Projections; internal static class PropertyTreeBuilder { - public static ImmutableArray Build( + public static TypeNode Build( SchemaCoordinate fieldCoordinate, Type type, string requirements) @@ -16,18 +16,19 @@ public static ImmutableArray Build( requirements = "{" + requirements + "}"; } + var typeNode = new TypeNode(type); var selectionSet = Utf8GraphQLParser.Syntax.ParseSelectionSet(requirements); - return Build(fieldCoordinate, type, selectionSet, Path.Root).ToImmutableArray(); + CollectProperties(typeNode, fieldCoordinate, type, selectionSet, Path.Root); + return typeNode; } - private static List Build( + private static void CollectProperties( + TypeNode parent, SchemaCoordinate fieldCoordinate, Type type, SelectionSetNode selectionSet, Path path) { - var nodes = new List(); - foreach (var selection in selectionSet.Selections) { if (selection is FieldNode field) @@ -71,14 +72,22 @@ private static List Build( .Build()); } - var children = - field.SelectionSet is not null - ? Build(fieldCoordinate, property.PropertyType, field.SelectionSet, fieldPath) - : null; + var propertyNode = new PropertyNode(property); - var node = new PropertyNode(property, children); - nodes.Add(node); - node.Seal(); + if (field.SelectionSet is not null) + { + var typeNode = new TypeNode(property.PropertyType); + CollectProperties( + typeNode, + fieldCoordinate, + property.PropertyType, + field.SelectionSet, + fieldPath); + propertyNode.TryAddNode(typeNode); + } + + propertyNode.Seal(); + parent.TryAddNode(propertyNode); } else { @@ -88,8 +97,6 @@ field.SelectionSet is not null .Build()); } } - - return nodes; } } #endif diff --git a/src/HotChocolate/Core/src/Execution/Projections/RequirementsTypeInterceptor.cs b/src/HotChocolate/Core/src/Execution/Projections/RequirementsTypeInterceptor.cs index ec5b92ad144..aa8cb9c3890 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/RequirementsTypeInterceptor.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/RequirementsTypeInterceptor.cs @@ -44,7 +44,7 @@ public override void OnBeforeCompleteType( // requirements we will take it and skip compilation. if (fieldDef.ContextData.TryGetValue(FieldRequirements, out var value)) { - _metadata.TryAddRequirements(fieldCoordinate, (ImmutableArray)value!); + _metadata.TryAddRequirements(fieldCoordinate, (TypeNode)value!); continue; } diff --git a/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs b/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs index c7fe259fed3..ec42e730d23 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs @@ -17,18 +17,12 @@ public Expression> BuildExpression(ISelection selectio var rootType = typeof(TRoot); var parameter = Expression.Parameter(rootType, "root"); var requirements = selection.DeclaringOperation.Schema.Features.GetRequired(); - var context = new Context(selection.DeclaringOperation, parameter, rootType, requirements); - var root = new PropertyNodeContainer(); - var selectionSet = context.GetSelectionSet(selection); + var context = new Context(parameter, rootType, requirements); + var root = new TypeContainer(); - CollectSelections(context, selectionSet, root); + CollectTypes(context, selection, root); - if (root.Nodes.Count == 0) - { - TryAddAnyLeafField(selection, root); - } - - var selectionSetExpression = BuildSelectionSetExpression(context, root); + var selectionSetExpression = BuildTypeSwitchExpression(context, root); if (selectionSetExpression is null) { @@ -38,15 +32,83 @@ public Expression> BuildExpression(ISelection selectio return Expression.Lambda>(selectionSetExpression, parameter); } - private MemberInitExpression? BuildSelectionSetExpression( + private void CollectTypes(Context context, ISelection selection, TypeContainer parent) + { + var namedType = selection.Type.NamedType(); + + if (namedType.IsLeafType()) + { + return; + } + + if (namedType.IsAbstractType()) + { + foreach (var possibleType in selection.DeclaringOperation.GetPossibleTypes(selection)) + { + var possibleTypeNode = new TypeNode(possibleType.RuntimeType); + var possibleSelectionSet = selection.DeclaringOperation.GetSelectionSet(selection, possibleType); + CollectSelections(context, possibleSelectionSet, possibleTypeNode); + parent.TryAddNode(possibleTypeNode); + + if(possibleTypeNode.Nodes.Count == 0) + { + TryAddAnyLeafField(selection, possibleTypeNode); + } + } + return; + } + + var objectType = (ObjectType)namedType; + var typeNode = new TypeNode(objectType.RuntimeType); + var selectionSet = selection.DeclaringOperation.GetSelectionSet(selection, (ObjectType)namedType); + CollectSelections(context, selectionSet, typeNode); + parent.TryAddNode(typeNode); + + if(typeNode.Nodes.Count == 0) + { + TryAddAnyLeafField(selection, typeNode); + } + } + + private static Expression? BuildTypeSwitchExpression( Context context, - PropertyNodeContainer parent) + TypeContainer parent) + { + if (parent.Nodes.Count > 1) + { + Expression switchExpression = Expression.Constant(null, context.ParentType); + + foreach (var typeNode in parent.Nodes) + { + var newParent = Expression.Convert(context.Parent, typeNode.Type); + var newContext = context with { Parent = newParent, ParentType = typeNode.Type }; + var typeCondition = Expression.TypeIs(context.Parent, typeNode.Type); + var selectionSet = BuildSelectionSetExpression(newContext, typeNode); + + if (selectionSet is null) + { + throw new InvalidOperationException(); + } + + var castedSelectionSet = Expression.Convert(selectionSet, context.ParentType); + switchExpression = Expression.Condition(typeCondition, castedSelectionSet, switchExpression); + } + + return switchExpression; + } + + return BuildSelectionSetExpression(context, parent.Nodes[0]); + } + + private static MemberInitExpression? BuildSelectionSetExpression( + Context context, + TypeNode parent) { var assignments = ImmutableArray.CreateBuilder(); foreach (var property in parent.Nodes) { - var assignment = BuildExpression(property, context); + var assignment = BuildAssignmentExpression(property, context); if (assignment is not null) { assignments.Add(assignment); @@ -66,12 +128,11 @@ public Expression> BuildExpression(ISelection selectio private void CollectSelection( Context context, ISelection selection, - PropertyNodeContainer parent) + TypeNode parent) { var namedType = selection.Field.Type.NamedType(); - if (namedType.IsAbstractType() - || selection.Field.Type.IsListType() && !namedType.IsLeafType() + if (selection.Field.Type.IsListType() && !namedType.IsLeafType() || selection.Field.PureResolver is null || selection.Field.ResolverMember?.ReflectedType != selection.Field.DeclaringType.RuntimeType) { @@ -97,20 +158,12 @@ private void CollectSelection( return; } - var selectionSet = context.GetSelectionSet(selection); - CollectSelections(context, selectionSet, propertyNode); - - if (propertyNode.Nodes.Count > 0) - { - return; - } - - TryAddAnyLeafField(selection, propertyNode); + CollectTypes(context, selection, propertyNode); } private static void TryAddAnyLeafField( ISelection selection, - PropertyNodeContainer parent) + TypeNode parent) { // if we could not collect anything it means that either all fields // are skipped or that __typename is the only field that is selected. @@ -135,14 +188,14 @@ private static void TryAddAnyLeafField( private void CollectSelections( Context context, ISelectionSet selectionSet, - PropertyNodeContainer parent) + TypeNode parent) { foreach (var selection in selectionSet.Selections) { var requirements = context.GetRequirements(selection); if (requirements is not null) { - foreach (var requirement in requirements) + foreach (var requirement in requirements.Nodes) { parent.TryAddNode(requirement.Clone()); } @@ -152,7 +205,7 @@ private void CollectSelections( } } - private MemberAssignment? BuildExpression( + private static MemberAssignment? BuildAssignmentExpression( PropertyNode node, Context context) { @@ -169,10 +222,8 @@ private void CollectSelections( return Expression.Bind(node.Property, nullCheck); } - else - { - return Expression.Bind(node.Property, propertyAccessor); - } + + return Expression.Bind(node.Property, propertyAccessor); } if(node.IsArrayOrCollection) @@ -181,14 +232,14 @@ private void CollectSelections( } var newContext = context with { Parent = propertyAccessor, ParentType = node.Property.PropertyType }; - var nestedExpression = BuildExpression(node.Nodes, newContext); + var nestedExpression = BuildTypeSwitchExpression(newContext, node); if (IsNullableType(node.Property)) { var nullCheck = Expression.Condition( Expression.Equal(propertyAccessor, Expression.Constant(null)), Expression.Constant(null, node.Property.PropertyType), - nestedExpression ?? (Expression)Expression.Constant(null, node.Property.PropertyType)); + nestedExpression ?? Expression.Constant(null, node.Property.PropertyType)); return Expression.Bind(node.Property, nullCheck); } @@ -219,47 +270,18 @@ private static bool IsNullableType(PropertyInfo propertyInfo) || Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null; #endif - private MemberInitExpression? BuildExpression( - IReadOnlyList properties, - Context context) - { - var allAssignments = ImmutableArray.CreateBuilder(); - - foreach (var property in properties) - { - var assignment = BuildExpression(property, context); - if (assignment is not null) - { - allAssignments.Add(assignment); - } - } - - if (allAssignments.Count == 0) - { - return null; - } - - return Expression.MemberInit( - Expression.New(context.ParentType), - allAssignments.ToImmutable()); - } - private readonly record struct Context( - IOperation Operation, Expression Parent, Type ParentType, FieldRequirementsMetadata Requirements) { - public ImmutableArray? GetRequirements(ISelection selection) + public TypeNode? GetRequirements(ISelection selection) { var flags = ((ObjectField)selection.Field).Flags; return (flags & FieldFlags.WithRequirements) == FieldFlags.WithRequirements ? Requirements.GetRequirements(selection.Field) : null; } - - public ISelectionSet GetSelectionSet(ISelection selection) - => Operation.GetSelectionSet(selection, (ObjectType)selection.Type.NamedType()); } } #endif diff --git a/src/HotChocolate/Core/src/Execution/Projections/TypeContainer.cs b/src/HotChocolate/Core/src/Execution/Projections/TypeContainer.cs new file mode 100644 index 00000000000..b78eb912928 --- /dev/null +++ b/src/HotChocolate/Core/src/Execution/Projections/TypeContainer.cs @@ -0,0 +1,55 @@ +namespace HotChocolate.Execution.Projections; + +internal class TypeContainer(List? nodes = null) +{ + private static readonly IReadOnlyList _emptyNodes = Array.Empty(); + private List? _nodes = nodes; + private bool _sealed; + + public IReadOnlyList Nodes + => _nodes ?? _emptyNodes; + + public void TryAddNode(TypeNode newNode) + { + if (_sealed) + { + throw new InvalidOperationException("The property node container is sealed."); + } + + _nodes ??= new(); + + foreach (var node in _nodes) + { + if (node.Type.FullName!.Equals(newNode.Type.FullName!)) + { + if (!node.Type.MetadataToken.Equals(newNode.Type.MetadataToken)) + { + throw new InvalidOperationException("Duplicate type name."); + } + + // we add the child nodes that are not already present + foreach (var newChild in newNode.Nodes) + { + node.TryAddNode(newChild); + } + + return; + } + } + + _nodes.Add(newNode); + } + + public void Seal() + { + if (!_sealed) + { + foreach (var node in Nodes) + { + node.Seal(); + } + + _sealed = true; + } + } +} diff --git a/src/HotChocolate/Core/src/Execution/Projections/PropertyNodeContainer.cs b/src/HotChocolate/Core/src/Execution/Projections/TypeNode.cs similarity index 78% rename from src/HotChocolate/Core/src/Execution/Projections/PropertyNodeContainer.cs rename to src/HotChocolate/Core/src/Execution/Projections/TypeNode.cs index 71e91160188..735ac8214a5 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/PropertyNodeContainer.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/TypeNode.cs @@ -3,14 +3,14 @@ namespace HotChocolate.Execution.Projections; -internal class PropertyNodeContainer( - List? nodes = null) - : IPropertyNodeProvider +internal sealed class TypeNode(Type type, List? nodes = null) { private static readonly IReadOnlyList _emptyNodes = Array.Empty(); - private List? _nodes = nodes; + private List? _nodes = nodes ?? []; private bool _sealed; + public Type Type => type; + public IReadOnlyList Nodes => _nodes ?? _emptyNodes; @@ -59,12 +59,30 @@ public void TryAddNode(PropertyNode newNode) { node.TryAddNode(newChild); } + + return; } } _nodes.Add(newNode); } + public TypeNode Clone() + { + List? nodes = null; + + if (Nodes.Count > 0) + { + nodes = new(Nodes.Count); + foreach (var node in Nodes) + { + nodes.Add(node.Clone()); + } + } + + return new TypeNode(Type, nodes); + } + public void Seal() { if (!_sealed) diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/InterfaceIntegrationTests.cs b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/InterfaceIntegrationTests.cs new file mode 100644 index 00000000000..eec148a6b0b --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/InterfaceIntegrationTests.cs @@ -0,0 +1,308 @@ +using System.Collections.Immutable; +using CookieCrumble; +using GreenDonut; +using GreenDonut.Projections; +using HotChocolate.Data.TestContext; +using HotChocolate.Execution; +using HotChocolate.Execution.Processing; +using HotChocolate.Pagination; +using HotChocolate.Resolvers; +using HotChocolate.Types; +using HotChocolate.Types.Pagination; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Squadron; + +namespace HotChocolate.Data; + +[Collection(PostgresCacheCollectionFixture.DefinitionName)] +public class InterfaceIntegrationTests(PostgreSqlResource resource) +{ + public PostgreSqlResource Resource { get; } = resource; + + private string CreateConnectionString() + => Resource.GetConnectionString($"db_{Guid.NewGuid():N}"); + + [Fact] + public async Task Query_Owner_Animals() + { + var connectionString = CreateConnectionString(); + await SeedAsync(connectionString); + + var queries = new List(); + + var result = await new ServiceCollection() + .AddScoped(_ => new AnimalContext(connectionString)) + .AddGraphQL() + .AddQueryType() + .AddTypeExtension(typeof(OwnerExtensions)) + .AddDataLoader() + .AddObjectType() + .AddObjectType() + .AddPagingArguments() + .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) + .ExecuteRequestAsync( + OperationRequestBuilder.New() + .SetDocument( + """ + { + owners(first: 10) { + nodes { + id + name + pets(first: 10) { + nodes { + __typename + id + name + } + } + } + } + } + """) + .AddQueries(queries) + .Build()); + + var operationResult = result.ExpectOperationResult(); + + await Snapshot.Create() + .AddQueries(queries) + .Add(operationResult.WithExtensions(ImmutableDictionary.Empty)) + .MatchMarkdownAsync(); + } + + [Fact] + public async Task Query_Owner_Animals_With_Fragments() + { + var connectionString = CreateConnectionString(); + await SeedAsync(connectionString); + + var queries = new List(); + + var result = await new ServiceCollection() + .AddScoped(_ => new AnimalContext(connectionString)) + .AddGraphQL() + .AddQueryType() + .AddTypeExtension(typeof(OwnerExtensions)) + .AddDataLoader() + .AddObjectType() + .AddObjectType() + .AddPagingArguments() + .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) + .ExecuteRequestAsync( + OperationRequestBuilder.New() + .SetDocument( + """ + { + owners(first: 10) { + nodes { + id + name + pets(first: 10) { + nodes { + __typename + id + name + ... on Dog { + isBarking + } + ... on Cat { + isPurring + } + } + } + } + } + } + """) + .AddQueries(queries) + .Build()); + + var operationResult = result.ExpectOperationResult(); + + await Snapshot.Create() + .AddQueries(queries) + .Add(operationResult.WithExtensions(ImmutableDictionary.Empty)) + .MatchMarkdownAsync(); + } + + + private static async Task SeedAsync(string connectionString) + { + await using var context = new AnimalContext(connectionString); + await context.Database.EnsureCreatedAsync(); + + var owners = new List + { + new Owner + { + Name = "Owner 1", + Pets = + [ + new Cat { Name = "Cat 1" }, + new Dog { Name = "Dog 1", IsBarking = true }, + new Dog { Name = "Dog 2", IsBarking = false } + ] + }, + new Owner + { + Name = "Owner 2", + Pets = + [ + new Cat { Name = "Cat 2" }, + new Dog { Name = "Dog 3", IsBarking = true }, + new Dog { Name = "Dog 4", IsBarking = false } + ] + }, + new Owner + { + Name = "Owner 3", + Pets = + [ + new Cat { Name = "Cat 3 (Not Pure)", IsPurring = true }, + new Dog { Name = "Dog 5", IsBarking = true }, + new Dog { Name = "Dog 6", IsBarking = false } + ] + }, + new Owner + { + Name = "Owner 4 - No Pets" + }, + new Owner + { + Name = "Owner 5 - Only Cat", + Pets = [new Cat { Name = "Only Cat" }] + }, + new Owner + { + Name = "Owner 6 - Only Dog", + Pets = [new Dog { Name = "Only Dog", IsBarking = true }] + } + }; + + context.Owners.AddRange(owners); + await context.SaveChangesAsync(); + } + + public class Query + { + [UsePaging] + public async Task> GetOwnersAsync( + PagingArguments pagingArgs, + AnimalContext context, + ISelection selection, + IResolverContext resolverContext, + [GlobalState] List queries, + CancellationToken cancellationToken) + { + return await context.Owners + .Select(selection.AsSelector()) + .OrderBy(t => t.Name) + .ThenBy(t => t.Id) + .Capture(queries) + .ToPageAsync(pagingArgs, cancellationToken) + .ToConnectionAsync(); + } + } + + [ExtendObjectType] + public static class OwnerExtensions + { + [BindMember(nameof(Owner.Pets))] + [UsePaging] + public static async Task> GetPetsAsync( + [Parent("Id")] Owner owner, + PagingArguments pagingArgs, + AnimalsByOwnerDataLoader animalsByOwner, + ISelection selection, + [GlobalState] List queries, + CancellationToken cancellationToken) + { + return await animalsByOwner + .WithPagingArguments(pagingArgs) + .Select(selection) + .SetState(queries) + .LoadAsync(owner.Id, cancellationToken) + .ToConnectionAsync(); + } + } + + public sealed class AnimalsByOwnerDataLoader + : StatefulBatchDataLoader> + { + private readonly IServiceProvider _services; + + public AnimalsByOwnerDataLoader( + IServiceProvider services, + IBatchScheduler batchScheduler, + DataLoaderOptions options) + : base(batchScheduler, options) + { + _services = services; + } + + protected override async Task>> LoadBatchAsync( + IReadOnlyList keys, + DataLoaderFetchContext> context, + CancellationToken cancellationToken) + { + var pagingArgs = context.GetPagingArguments(); + var selector = context.GetSelector(); + + await using var scope = _services.CreateAsyncScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + + return await dbContext.Owners + .Where(t => keys.Contains(t.Id)) + .SelectMany(t => t.Pets) + .OrderBy(t => t.Name) + .ThenBy(t => t.Id) + .Select(selector, t => t.OwnerId) + .Capture(context.GetQueries()) + .ToBatchPageAsync( + t => t.OwnerId, + pagingArgs, + cancellationToken); + } + } +} + +file static class Extensions +{ + public static IQueryable Capture( + this IQueryable query, + List queryInfos) + { + queryInfos.Add( + new QueryInfo + { + QueryText = query.ToQueryString(), + ExpressionText = query.Expression.ToString() + }); + return query; + } + + public static List GetQueries( + this DataLoaderFetchContext> context) + => context.GetRequiredState>(); + + public static OperationRequestBuilder AddQueries( + this OperationRequestBuilder builder, + List queries) + => builder.SetGlobalState("queries", queries); + + public static Snapshot AddQueries( + this Snapshot snapshot, + List queries) + { + for (var i = 0; i < queries.Count; i++) + { + snapshot + .Add(queries[i].QueryText, $"SQL {i}", "sql") + .Add(queries[i].ExpressionText, $"Expression {i}"); + } + + return snapshot; + } +} diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs index 5712d7ace85..9976533eb8c 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs @@ -1248,12 +1248,12 @@ protected override async Task>> LoadBatch CancellationToken cancellationToken) { var pagingArgs = context.GetPagingArguments(); - var sql = context.GetState(); + var sql = context.GetRequiredState(); await using var scope = _services.CreateAsyncScope(); await using var catalogContext = scope.ServiceProvider.GetRequiredService(); - sql!.Text = catalogContext.Products + sql.Text = catalogContext.Products .Where(t => keys.Contains(t.BrandId)) .Select(context.GetSelector(), b => b.Id) .OrderBy(t => t.Name).ThenBy(t => t.Id) diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/TestContext/AnimalContext.cs b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/TestContext/AnimalContext.cs new file mode 100644 index 00000000000..7bd06628848 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/TestContext/AnimalContext.cs @@ -0,0 +1,64 @@ +using System.ComponentModel.DataAnnotations; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; + +namespace HotChocolate.Data.TestContext; + +public class AnimalContext(string connectionString) : DbContext +{ + public DbSet Owners { get; set; } + public DbSet Dogs { get; set; } + public DbSet Cats { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => optionsBuilder.UseNpgsql(connectionString); + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .HasMany(o => o.Pets) + .WithOne(t => t.Owner) + .HasForeignKey(t => t.OwnerId) + .HasPrincipalKey(o => o.Id); + + modelBuilder.Entity() + .HasDiscriminator("AnimalType") + .HasValue("Dog") + .HasValue("Cat"); + + base.OnModelCreating(modelBuilder); + } +} + +public class Owner +{ + public int Id { get; set; } + + [MaxLength(100)] + public required string Name { get; set; } + + public List Pets { get; set; } = new(); +} + +[InterfaceType] +public abstract class Animal +{ + public int Id { get; set; } + + [MaxLength(100)] + public required string Name { get; set; } + + public int OwnerId { get; set; } + + public Owner? Owner { get; set; } +} + +public class Dog : Animal +{ + public bool IsBarking { get; set; } +} + +public class Cat : Animal +{ + public bool IsPurring { get; set; } +} diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals.md new file mode 100644 index 00000000000..bb2bba71c47 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals.md @@ -0,0 +1,148 @@ +# Query_Owner_Animals + +## SQL 0 + +```sql +SELECT o."Id", o."Name" +FROM "Owners" AS o +ORDER BY o."Name", o."Id" +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Select(root => new Owner() {Id = root.Id, Name = root.Name}).OrderBy(t => t.Name).ThenBy(t => t.Id) +``` + +## SQL 1 + +```sql +-- @__keys_0={ '6', '5', '4', '3', '2', ... } (DbType = Object) +SELECT a."Id", a."AnimalType", a."Name", a."OwnerId", a."IsPurring", a."IsBarking" +FROM "Owners" AS o +INNER JOIN "Animal" AS a ON o."Id" = a."OwnerId" +WHERE o."Id" = ANY (@__keys_0) +ORDER BY a."Name", a."Id" +``` + +## Expression 1 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Where(t => value(HotChocolate.Data.InterfaceIntegrationTests+AnimalsByOwnerDataLoader+<>c__DisplayClass2_0).keys.Contains(t.Id)).SelectMany(t => t.Pets).OrderBy(t => t.Name).ThenBy(t => t.Id) +``` + +## Result 5 + +```json +{ + "data": { + "owners": { + "nodes": [ + { + "id": 1, + "name": "Owner 1", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 1, + "name": "Cat 1" + }, + { + "__typename": "Dog", + "id": 5, + "name": "Dog 1" + }, + { + "__typename": "Dog", + "id": 6, + "name": "Dog 2" + } + ] + } + }, + { + "id": 2, + "name": "Owner 2", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 2, + "name": "Cat 2" + }, + { + "__typename": "Dog", + "id": 7, + "name": "Dog 3" + }, + { + "__typename": "Dog", + "id": 8, + "name": "Dog 4" + } + ] + } + }, + { + "id": 3, + "name": "Owner 3", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 3, + "name": "Cat 3 (Not Pure)" + }, + { + "__typename": "Dog", + "id": 9, + "name": "Dog 5" + }, + { + "__typename": "Dog", + "id": 10, + "name": "Dog 6" + } + ] + } + }, + { + "id": 4, + "name": "Owner 4 - No Pets", + "pets": { + "nodes": [] + } + }, + { + "id": 5, + "name": "Owner 5 - Only Cat", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 4, + "name": "Only Cat" + } + ] + } + }, + { + "id": 6, + "name": "Owner 6 - Only Dog", + "pets": { + "nodes": [ + { + "__typename": "Dog", + "id": 11, + "name": "Only Dog" + } + ] + } + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments.md new file mode 100644 index 00000000000..f08d748670a --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments.md @@ -0,0 +1,159 @@ +# Query_Owner_Animals_With_Fragments + +## SQL 0 + +```sql +SELECT o."Id", o."Name" +FROM "Owners" AS o +ORDER BY o."Name", o."Id" +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Select(root => new Owner() {Id = root.Id, Name = root.Name}).OrderBy(t => t.Name).ThenBy(t => t.Id) +``` + +## SQL 1 + +```sql +-- @__keys_0={ '6', '5', '4', '3', '2', ... } (DbType = Object) +SELECT a."Id", a."AnimalType", a."Name", a."OwnerId", a."IsPurring", a."IsBarking" +FROM "Owners" AS o +INNER JOIN "Animal" AS a ON o."Id" = a."OwnerId" +WHERE o."Id" = ANY (@__keys_0) +ORDER BY a."Name", a."Id" +``` + +## Expression 1 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Where(t => value(HotChocolate.Data.InterfaceIntegrationTests+AnimalsByOwnerDataLoader+<>c__DisplayClass2_0).keys.Contains(t.Id)).SelectMany(t => t.Pets).OrderBy(t => t.Name).ThenBy(t => t.Id) +``` + +## Result 5 + +```json +{ + "data": { + "owners": { + "nodes": [ + { + "id": 1, + "name": "Owner 1", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 1, + "name": "Cat 1", + "isPurring": false + }, + { + "__typename": "Dog", + "id": 5, + "name": "Dog 1", + "isBarking": true + }, + { + "__typename": "Dog", + "id": 6, + "name": "Dog 2", + "isBarking": false + } + ] + } + }, + { + "id": 2, + "name": "Owner 2", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 2, + "name": "Cat 2", + "isPurring": false + }, + { + "__typename": "Dog", + "id": 7, + "name": "Dog 3", + "isBarking": true + }, + { + "__typename": "Dog", + "id": 8, + "name": "Dog 4", + "isBarking": false + } + ] + } + }, + { + "id": 3, + "name": "Owner 3", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 3, + "name": "Cat 3 (Not Pure)", + "isPurring": true + }, + { + "__typename": "Dog", + "id": 9, + "name": "Dog 5", + "isBarking": true + }, + { + "__typename": "Dog", + "id": 10, + "name": "Dog 6", + "isBarking": false + } + ] + } + }, + { + "id": 4, + "name": "Owner 4 - No Pets", + "pets": { + "nodes": [] + } + }, + { + "id": 5, + "name": "Owner 5 - Only Cat", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 4, + "name": "Only Cat", + "isPurring": false + } + ] + } + }, + { + "id": 6, + "name": "Owner 6 - Only Dog", + "pets": { + "nodes": [ + { + "__typename": "Dog", + "id": 11, + "name": "Only Dog", + "isBarking": true + } + ] + } + } + ] + } + } +} +``` + From 9cc769e9fe4542327841e89e760045106dbb8d8e Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 2 Oct 2024 18:03:34 +0200 Subject: [PATCH 035/154] Allow selector to be null when using Select. (#7547) --- .../SelectionDataLoaderExtensions.cs | 106 ++------------- ...tChocolateExecutionDataLoaderExtensions.cs | 2 +- .../Contracts/IObjectFieldDescriptor.cs | 22 +++ .../Descriptors/ObjectFieldDescriptor.cs | 33 +++++ .../Projections/ProjectableDataLoaderTests.cs | 126 +++++++++++++++++- ...ls_Requires_Brand_Name_With_Proper_Type.md | 23 ++++ ..._With_Proper_Type_With_Explicit_Generic.md | 23 ++++ ...rTests.Brand_With_Name_Selector_is_Null.md | 23 ++++ ...ePaginationBatchingDataLoaderExtensions.cs | 6 +- 9 files changed, 265 insertions(+), 99 deletions(-) create mode 100644 src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Requires_Brand_Name_With_Proper_Type.md create mode 100644 src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Requires_Brand_Name_With_Proper_Type_With_Explicit_Generic.md create mode 100644 src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Selector_is_Null.md diff --git a/src/GreenDonut/src/Core/Projections/SelectionDataLoaderExtensions.cs b/src/GreenDonut/src/Core/Projections/SelectionDataLoaderExtensions.cs index fbea2470039..95298fe4265 100644 --- a/src/GreenDonut/src/Core/Projections/SelectionDataLoaderExtensions.cs +++ b/src/GreenDonut/src/Core/Projections/SelectionDataLoaderExtensions.cs @@ -35,9 +35,9 @@ public static class SelectionDataLoaderExtensions /// /// Throws if is null. /// - public static ISelectionDataLoader Select( + public static IDataLoader Select( this IDataLoader dataLoader, - Expression> selector) + Expression>? selector) where TKey : notnull { if (dataLoader is null) @@ -47,7 +47,14 @@ public static ISelectionDataLoader Select( if (selector is null) { - throw new ArgumentNullException(nameof(selector)); + return dataLoader; + } + + if (dataLoader is ISelectionDataLoader) + { + var context = (DefaultSelectorBuilder)dataLoader.ContextData[typeof(ISelectorBuilder).FullName!]!; + context.Add(selector); + return dataLoader; } var branchKey = selector.ToString(); @@ -68,48 +75,6 @@ static IDataLoader CreateBranch( } } - /// - /// Adds another selector to the branched DataLoader. - /// - /// - /// The branched DataLoader. - /// - /// - /// The data selector. - /// - /// - /// The key type. - /// - /// - /// The value type. - /// - /// - /// Returns the branched DataLoader with the selector applied. - /// - /// - /// Throws if is null. - /// - public static ISelectionDataLoader Select( - this ISelectionDataLoader dataLoader, - Expression> selector) - where TKey : notnull - { - if (dataLoader is null) - { - throw new ArgumentNullException(nameof(dataLoader)); - } - - if (selector is null) - { - throw new ArgumentNullException(nameof(selector)); - } - - var context = (DefaultSelectorBuilder)dataLoader.ContextData[typeof(ISelectorBuilder).FullName!]!; - context.Add(selector); - return dataLoader; - } - - /* /// /// Includes a property in the query. /// @@ -164,54 +129,9 @@ public static IDataLoader Include( nameof(includeSelector)); } - DefaultSelectorBuilder context; - if (dataLoader.ContextData.TryGetValue(typeof(ISelectorBuilder).FullName!, out var value) - && value is DefaultSelectorBuilder casted) - { - context = casted; - } - else - { - context = new DefaultSelectorBuilder(); - } - - context.Add(ExpressionHelpers.Rewrite(includeSelector)); - dataLoader.ContextData = dataLoader.ContextData.SetItem(typeof(ISelectorBuilder).FullName!, context); - return dataLoader; - } - */ - - public static ISelectionDataLoader Include( - this ISelectionDataLoader dataLoader, - Expression> includeSelector) - where TKey : notnull - { - if (dataLoader is null) - { - throw new ArgumentNullException(nameof(dataLoader)); - } - - if (includeSelector is null) - { - throw new ArgumentNullException(nameof(includeSelector)); - } - - if (includeSelector is not LambdaExpression lambda) - { - throw new ArgumentException( - "The include selector must be a lambda expression.", - nameof(includeSelector)); - } - - if (lambda.Body is not MemberExpression member - || member.Member.MemberType != MemberTypes.Property) - { - throw new ArgumentException( - "The include selector must be a property selector.", - nameof(includeSelector)); - } - - var context = (DefaultSelectorBuilder)dataLoader.ContextData[typeof(ISelectorBuilder).FullName!]!; + var context = dataLoader.GetOrSetState( + typeof(ISelectorBuilder).FullName!, + _ => new DefaultSelectorBuilder()); context.Add(Rewrite(includeSelector)); return dataLoader; } diff --git a/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs b/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs index 9bac2da93ff..0ba273ab3b4 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs @@ -35,7 +35,7 @@ public static class HotChocolateExecutionDataLoaderExtensions /// /// Returns a new data loader that applies the selection. /// - public static ISelectionDataLoader Select( + public static IDataLoader Select( this IDataLoader dataLoader, ISelection selection) where TKey : notnull diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Contracts/IObjectFieldDescriptor.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Contracts/IObjectFieldDescriptor.cs index fe66947b2b7..8bb19a465df 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Contracts/IObjectFieldDescriptor.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Contracts/IObjectFieldDescriptor.cs @@ -319,4 +319,26 @@ IObjectFieldDescriptor Directive() /// The arguments of the directive /// The descriptor IObjectFieldDescriptor Directive(string name, params ArgumentNode[] arguments); + + /// + /// Specifies the requirements for the parent object. + /// + /// + /// The requirements for the parent object. + /// + /// + /// Returns the descriptor to chain further configuration. + /// + IObjectFieldDescriptor ParentRequires(string? requires); + + /// + /// Specifies the requirements for the parent object. + /// + /// + /// The requirements for the parent object. + /// + /// + /// Returns the descriptor to chain further configuration. + /// + IObjectFieldDescriptor ParentRequires(string? requires); } diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/ObjectFieldDescriptor.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/ObjectFieldDescriptor.cs index 8a977fce3c3..b54679db33a 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/ObjectFieldDescriptor.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/ObjectFieldDescriptor.cs @@ -472,6 +472,39 @@ public IObjectFieldDescriptor Use(FieldMiddleware middleware) return this; } + /// + public IObjectFieldDescriptor ParentRequires(string? requires) + { + if (!(requires?.Length > 0)) + { + Definition.Flags &= ~FieldFlags.WithRequirements; + Definition.ContextData.Remove(FieldRequirementsSyntax); + Definition.ContextData.Remove(FieldRequirementsEntity); + return this; + } + + Definition.Flags |= FieldFlags.WithRequirements; + Definition.ContextData[FieldRequirementsSyntax] = requires; + Definition.ContextData[FieldRequirementsEntity] = typeof(TParent); + return this; + } + + public IObjectFieldDescriptor ParentRequires(string? requires) + { + if (!(requires?.Length > 0)) + { + Definition.Flags &= ~FieldFlags.WithRequirements; + Definition.ContextData.Remove(FieldRequirementsSyntax); + Definition.ContextData.Remove(FieldRequirementsEntity); + return this; + } + + Definition.Flags |= FieldFlags.WithRequirements; + Definition.ContextData[FieldRequirementsSyntax] = requires; + Definition.ContextData[FieldRequirementsEntity] = Definition.SourceType; + return this; + } + /// /// Creates a new instance of /// diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs index f647740dd99..1a2a3727c73 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs @@ -1,4 +1,5 @@ #if NET7_0_OR_GREATER +using System.Linq.Expressions; using CookieCrumble; using GreenDonut; using GreenDonut.Projections; @@ -54,6 +55,37 @@ public async Task Brand_With_Name() .MatchMarkdownSnapshot(); } + [Fact] + public async Task Brand_With_Name_Selector_is_Null() + { + // Arrange + var queries = new List(); + var connectionString = CreateConnectionString(); + await CatalogContext.SeedAsync(connectionString); + + // Act + var result = await new ServiceCollection() + .AddScoped(_ => queries) + .AddTransient(_ => new CatalogContext(connectionString)) + .AddGraphQL() + .AddQueryType() + .AddPagingArguments() + .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) + .ExecuteRequestAsync( + """ + { + brandByIdSelectorNull(id: 1) { + name + } + } + """); + + Snapshot.Create() + .AddSql(queries) + .AddResult(result) + .MatchMarkdownSnapshot(); + } + [Fact] public async Task Manual_Include_Of_Brand() { @@ -444,6 +476,70 @@ public async Task Brand_Details_Requires_Brand_Name() .MatchMarkdownSnapshot(); } + [Fact] + public async Task Brand_Details_Requires_Brand_Name_With_Proper_Type() + { + // Arrange + var queries = new List(); + var connectionString = CreateConnectionString(); + await CatalogContext.SeedAsync(connectionString); + + // Act + var result = await new ServiceCollection() + .AddScoped(_ => queries) + .AddTransient(_ => new CatalogContext(connectionString)) + .AddGraphQL() + .AddQueryType() + .AddType() + .AddPagingArguments() + .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) + .ExecuteRequestAsync( + """ + { + brandById(id: 1) { + details + } + } + """); + + Snapshot.Create() + .AddSql(queries) + .AddResult(result) + .MatchMarkdownSnapshot(); + } + + [Fact] + public async Task Brand_Details_Requires_Brand_Name_With_Proper_Type_With_Explicit_Generic() + { + // Arrange + var queries = new List(); + var connectionString = CreateConnectionString(); + await CatalogContext.SeedAsync(connectionString); + + // Act + var result = await new ServiceCollection() + .AddScoped(_ => queries) + .AddTransient(_ => new CatalogContext(connectionString)) + .AddGraphQL() + .AddQueryType() + .AddType() + .AddPagingArguments() + .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) + .ExecuteRequestAsync( + """ + { + brandById(id: 1) { + details + } + } + """); + + Snapshot.Create() + .AddSql(queries) + .AddResult(result) + .MatchMarkdownSnapshot(); + } + [Fact] public async Task Brand_Details_Requires_Brand_Name_2() { @@ -586,14 +682,18 @@ public class Query CancellationToken cancellationToken) => await productById.Select(selection).Include(c => c.Brand).LoadAsync(id, cancellationToken); - /* public async Task GetProductByIdWithBrandNoSelectionAsync( int id, ISelection selection, ProductByIdDataLoader productById, CancellationToken cancellationToken) => await productById.Include(c => c.Brand).LoadAsync(id, cancellationToken); - */ + + public async Task GetBrandByIdSelectorNullAsync( + int id, + BrandByIdDataLoader brandById, + CancellationToken cancellationToken) + => await brandById.Select(default(Expression>)).LoadAsync(id, cancellationToken); } [ExtendObjectType] @@ -614,6 +714,28 @@ public string GetDetails( => "Brand Name:" + brand.Name; } + public class BrandWithRequirementType : ObjectType + { + protected override void Configure(IObjectTypeDescriptor descriptor) + { + descriptor + .Field(t => t.Details) + .ParentRequires(nameof(Brand.Name)) + .Resolve(ctx => "Brand Name:" + ctx.Parent().Name); + } + } + + public class BrandWithRequirementTypeWithGeneric : ObjectType + { + protected override void Configure(IObjectTypeDescriptor descriptor) + { + descriptor + .Field(t => t.Details) + .ParentRequires(nameof(Brand.Name)) + .Resolve(ctx => "Brand Name:" + ctx.Parent().Name); + } + } + [ExtendObjectType] public class ProductExtensions { diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Requires_Brand_Name_With_Proper_Type.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Requires_Brand_Name_With_Proper_Type.md new file mode 100644 index 00000000000..7e43b8fba6b --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Requires_Brand_Name_With_Proper_Type.md @@ -0,0 +1,23 @@ +# Brand_Details_Requires_Brand_Name_With_Proper_Type + +## SQL + +```text +-- @__keys_0={ '1' } (DbType = Object) +SELECT b."Name", b."Id" +FROM "Brands" AS b +WHERE b."Id" = ANY (@__keys_0) +``` + +## Result + +```json +{ + "data": { + "brandById": { + "details": "Brand Name:Brand0" + } + } +} +``` + diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Requires_Brand_Name_With_Proper_Type_With_Explicit_Generic.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Requires_Brand_Name_With_Proper_Type_With_Explicit_Generic.md new file mode 100644 index 00000000000..c4ab2771644 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Requires_Brand_Name_With_Proper_Type_With_Explicit_Generic.md @@ -0,0 +1,23 @@ +# Brand_Details_Requires_Brand_Name_With_Proper_Type_With_Explicit_Generic + +## SQL + +```text +-- @__keys_0={ '1' } (DbType = Object) +SELECT b."Name", b."Id" +FROM "Brands" AS b +WHERE b."Id" = ANY (@__keys_0) +``` + +## Result + +```json +{ + "data": { + "brandById": { + "details": "Brand Name:Brand0" + } + } +} +``` + diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Selector_is_Null.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Selector_is_Null.md new file mode 100644 index 00000000000..d984244c65f --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Selector_is_Null.md @@ -0,0 +1,23 @@ +# Brand_With_Name_Selector_is_Null + +## SQL + +```text +-- @__keys_0={ '1' } (DbType = Object) +SELECT b."Id", b."DisplayName", b."Name", b."Details_Country_Name" +FROM "Brands" AS b +WHERE b."Id" = ANY (@__keys_0) +``` + +## Result + +```json +{ + "data": { + "brandByIdSelectorNull": { + "name": "Brand0" + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs b/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs index 68bd083f92f..5664ede5891 100644 --- a/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs +++ b/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs @@ -19,7 +19,7 @@ public static class HotChocolatePaginationBatchingDataLoaderExtensions /// The DataLoader that shall be branched. /// /// - /// The paging arguments that shall be exist as state in the branched DataLoader. + /// The paging arguments that shall exist as state in the branched DataLoader. /// /// /// The key type of the DataLoader. @@ -88,7 +88,7 @@ static IDataLoader CreatePagingDataLoader( #endif public static IPagingDataLoader> Select( this IPagingDataLoader> dataLoader, - Expression> selector) + Expression>? selector) where TKey : notnull { if (dataLoader is null) @@ -98,7 +98,7 @@ public static IPagingDataLoader> Select( if (selector is null) { - throw new ArgumentNullException(nameof(selector)); + return dataLoader; } var builder = dataLoader.GetOrSetState(_ => new DefaultSelectorBuilder()); From 8631e30660479fa9421bc01134af901411c3b9a8 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 2 Oct 2024 18:08:10 +0200 Subject: [PATCH 036/154] =?UTF-8?q?Fixed=20issue=20with=20errors=20in=20mu?= =?UTF-8?q?tation=20convention=20when=20explicitly=20bindun=E2=80=A6=20(#7?= =?UTF-8?q?548)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Core/src/Types.Errors/ErrorObjectType.cs | 1 + .../src/Types.Errors/ExceptionObjectType.cs | 1 + .../AnnotationBasedMutations.cs | 25 +++++++++++++++ ...ErrorEvenIfExplicitFieldBindingIsUsed.snap | 31 +++++++++++++++++++ 4 files changed, 58 insertions(+) create mode 100644 src/HotChocolate/Core/test/Types.Mutations.Tests/__snapshots__/AnnotationBasedMutations.InferErrorEvenIfExplicitFieldBindingIsUsed.snap diff --git a/src/HotChocolate/Core/src/Types.Errors/ErrorObjectType.cs b/src/HotChocolate/Core/src/Types.Errors/ErrorObjectType.cs index 179e25ef42b..d87ca32898f 100644 --- a/src/HotChocolate/Core/src/Types.Errors/ErrorObjectType.cs +++ b/src/HotChocolate/Core/src/Types.Errors/ErrorObjectType.cs @@ -6,6 +6,7 @@ protected override void Configure(IObjectTypeDescriptor descriptor) { descriptor.Extend().OnBeforeCreate(RewriteMessageFieldToNonNullableStringType); descriptor.Extend().Definition.ContextData.MarkAsError(); + descriptor.BindFieldsImplicitly(); } private void RewriteMessageFieldToNonNullableStringType( diff --git a/src/HotChocolate/Core/src/Types.Errors/ExceptionObjectType.cs b/src/HotChocolate/Core/src/Types.Errors/ExceptionObjectType.cs index 4985c9c5c3b..682e43d4d32 100644 --- a/src/HotChocolate/Core/src/Types.Errors/ExceptionObjectType.cs +++ b/src/HotChocolate/Core/src/Types.Errors/ExceptionObjectType.cs @@ -15,6 +15,7 @@ protected override void Configure(IObjectTypeDescriptor descriptor) descriptor.Ignore(x => x.GetBaseException()); descriptor.Field(x => x.Message).Type>(); descriptor.Extend().Definition.ContextData.MarkAsError(); + descriptor.BindFieldsImplicitly(); } private static string GetNameFromException() diff --git a/src/HotChocolate/Core/test/Types.Mutations.Tests/AnnotationBasedMutations.cs b/src/HotChocolate/Core/test/Types.Mutations.Tests/AnnotationBasedMutations.cs index 85197cc663b..42c0b5743d4 100644 --- a/src/HotChocolate/Core/test/Types.Mutations.Tests/AnnotationBasedMutations.cs +++ b/src/HotChocolate/Core/test/Types.Mutations.Tests/AnnotationBasedMutations.cs @@ -1275,6 +1275,31 @@ public async Task Mutation_With_ErrorAnnotatedAndCustomInterface_LateAndEarlyReg result.Print().MatchSnapshot(); } + [Fact] + public async Task InferErrorEvenIfExplicitFieldBindingIsUsed() + { + var schema = + await new ServiceCollection() + .AddGraphQL() + .AddQueryType(d => d.Field("abc").Resolve("def")) + .AddMutationType(c => c.Field(t => t.DoSomething(default))) + .AddMutationConventions() + .BuildSchemaAsync(); + + schema.Print().MatchSnapshot(); + } + + public class ExplicitMutation + { + public FieldResult DoSomething(int status) + => new ExplicitCustomError { Message = "Error" }; + } + + public sealed class ExplicitCustomError + { + public required string Message { get; set; } + } + public class SimpleMutation { public string DoSomething(string something) diff --git a/src/HotChocolate/Core/test/Types.Mutations.Tests/__snapshots__/AnnotationBasedMutations.InferErrorEvenIfExplicitFieldBindingIsUsed.snap b/src/HotChocolate/Core/test/Types.Mutations.Tests/__snapshots__/AnnotationBasedMutations.InferErrorEvenIfExplicitFieldBindingIsUsed.snap new file mode 100644 index 00000000000..bb6f894e883 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Mutations.Tests/__snapshots__/AnnotationBasedMutations.InferErrorEvenIfExplicitFieldBindingIsUsed.snap @@ -0,0 +1,31 @@ +schema { + query: Query + mutation: ExplicitMutation +} + +interface Error { + message: String! +} + +type DoSomethingPayload { + int: Int + errors: [DoSomethingError!] +} + +type ExplicitCustomError implements Error { + message: String! +} + +type ExplicitMutation { + doSomething(input: DoSomethingInput!): DoSomethingPayload! +} + +type Query { + abc: String +} + +union DoSomethingError = ExplicitCustomError + +input DoSomethingInput { + status: Int! +} From 41f2efb6d2be762a9fc30baaa1c4db7b2ed33987 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 2 Oct 2024 18:46:35 +0200 Subject: [PATCH 037/154] Fixed compile and test issues --- .../src/CookieCrumble/QueryInfo.cs | 4 +- .../Execution/Projections/TypeContainer.cs | 2 + .../Projections/ProjectableDataLoaderTests.cs | 12 ++ ...ires_Brand_Name_With_Proper_Type_NET7_0.md | 22 +++ ...roper_Type_With_Explicit_Generic_NET7_0.md | 22 +++ ...Brand_With_Name_Selector_is_Null_NET7_0.md | 22 +++ .../AnnotationBasedMutations.cs | 2 +- .../InterfaceIntegrationTests.cs | 13 +- .../TestContext/AnimalContext.cs | 10 +- ...rationTests.Query_Owner_Animals_NET_6_0.md | 147 ++++++++++++++++ ...rationTests.Query_Owner_Animals_NET_7_0.md | 147 ++++++++++++++++ ...ry_Owner_Animals_With_Fragments_NET_6_0.md | 158 ++++++++++++++++++ ...ry_Owner_Animals_With_Fragments_NET_7_0.md | 158 ++++++++++++++++++ 13 files changed, 710 insertions(+), 9 deletions(-) create mode 100644 src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Requires_Brand_Name_With_Proper_Type_NET7_0.md create mode 100644 src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Requires_Brand_Name_With_Proper_Type_With_Explicit_Generic_NET7_0.md create mode 100644 src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Selector_is_Null_NET7_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_NET_6_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_NET_7_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments_NET_6_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments_NET_7_0.md diff --git a/src/CookieCrumble/src/CookieCrumble/QueryInfo.cs b/src/CookieCrumble/src/CookieCrumble/QueryInfo.cs index 13990acd4ff..1fe077e76bd 100644 --- a/src/CookieCrumble/src/CookieCrumble/QueryInfo.cs +++ b/src/CookieCrumble/src/CookieCrumble/QueryInfo.cs @@ -2,7 +2,7 @@ namespace CookieCrumble; public sealed class QueryInfo { - public required string QueryText { get; init; } + public string QueryText { get; init; } = default!; - public required string ExpressionText { get; init; } + public string ExpressionText { get; init; } = default!; } diff --git a/src/HotChocolate/Core/src/Execution/Projections/TypeContainer.cs b/src/HotChocolate/Core/src/Execution/Projections/TypeContainer.cs index b78eb912928..a7567ab703a 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/TypeContainer.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/TypeContainer.cs @@ -1,3 +1,4 @@ +#if NET6_0_OR_GREATER namespace HotChocolate.Execution.Projections; internal class TypeContainer(List? nodes = null) @@ -53,3 +54,4 @@ public void Seal() } } } +#endif diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs index 1a2a3727c73..188625d68c7 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs @@ -80,7 +80,11 @@ public async Task Brand_With_Name_Selector_is_Null() } """); +#if NET8_0_OR_GREATER Snapshot.Create() +#else + Snapshot.Create(postFix: "NET7_0") +#endif .AddSql(queries) .AddResult(result) .MatchMarkdownSnapshot(); @@ -502,7 +506,11 @@ public async Task Brand_Details_Requires_Brand_Name_With_Proper_Type() } """); +#if NET8_0_OR_GREATER Snapshot.Create() +#else + Snapshot.Create(postFix: "NET7_0") +#endif .AddSql(queries) .AddResult(result) .MatchMarkdownSnapshot(); @@ -534,7 +542,11 @@ public async Task Brand_Details_Requires_Brand_Name_With_Proper_Type_With_Explic } """); +#if NET8_0_OR_GREATER Snapshot.Create() +#else + Snapshot.Create(postFix: "NET7_0") +#endif .AddSql(queries) .AddResult(result) .MatchMarkdownSnapshot(); diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Requires_Brand_Name_With_Proper_Type_NET7_0.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Requires_Brand_Name_With_Proper_Type_NET7_0.md new file mode 100644 index 00000000000..ea0e12c81d5 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Requires_Brand_Name_With_Proper_Type_NET7_0.md @@ -0,0 +1,22 @@ +# Brand_Details_Requires_Brand_Name_With_Proper_Type + +## SQL + +```text +SELECT b."Name", b."Id" +FROM "Brands" AS b +WHERE b."Id" = 1 +``` + +## Result + +```json +{ + "data": { + "brandById": { + "details": "Brand Name:Brand0" + } + } +} +``` + diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Requires_Brand_Name_With_Proper_Type_With_Explicit_Generic_NET7_0.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Requires_Brand_Name_With_Proper_Type_With_Explicit_Generic_NET7_0.md new file mode 100644 index 00000000000..befdf354273 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_Details_Requires_Brand_Name_With_Proper_Type_With_Explicit_Generic_NET7_0.md @@ -0,0 +1,22 @@ +# Brand_Details_Requires_Brand_Name_With_Proper_Type_With_Explicit_Generic + +## SQL + +```text +SELECT b."Name", b."Id" +FROM "Brands" AS b +WHERE b."Id" = 1 +``` + +## Result + +```json +{ + "data": { + "brandById": { + "details": "Brand Name:Brand0" + } + } +} +``` + diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Selector_is_Null_NET7_0.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Selector_is_Null_NET7_0.md new file mode 100644 index 00000000000..100073f03f2 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Selector_is_Null_NET7_0.md @@ -0,0 +1,22 @@ +# Brand_With_Name_Selector_is_Null + +## SQL + +```text +SELECT b."Id", b."DisplayName", b."Name", b."Details_Country_Name" +FROM "Brands" AS b +WHERE b."Id" = 1 +``` + +## Result + +```json +{ + "data": { + "brandByIdSelectorNull": { + "name": "Brand0" + } + } +} +``` + diff --git a/src/HotChocolate/Core/test/Types.Mutations.Tests/AnnotationBasedMutations.cs b/src/HotChocolate/Core/test/Types.Mutations.Tests/AnnotationBasedMutations.cs index 42c0b5743d4..b0c457a595f 100644 --- a/src/HotChocolate/Core/test/Types.Mutations.Tests/AnnotationBasedMutations.cs +++ b/src/HotChocolate/Core/test/Types.Mutations.Tests/AnnotationBasedMutations.cs @@ -1297,7 +1297,7 @@ public FieldResult DoSomething(int status) public sealed class ExplicitCustomError { - public required string Message { get; set; } + public string Message { get; set; } = default!; } public class SimpleMutation diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/InterfaceIntegrationTests.cs b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/InterfaceIntegrationTests.cs index eec148a6b0b..7d21165d9a4 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/InterfaceIntegrationTests.cs +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/InterfaceIntegrationTests.cs @@ -66,7 +66,13 @@ public async Task Query_Owner_Animals() var operationResult = result.ExpectOperationResult(); +#if NET6_0 + await Snapshot.Create("NET_6_0") +#elif NET7_0 + await Snapshot.Create("NET_7_0") +#else await Snapshot.Create() +#endif .AddQueries(queries) .Add(operationResult.WithExtensions(ImmutableDictionary.Empty)) .MatchMarkdownAsync(); @@ -121,13 +127,18 @@ ... on Cat { var operationResult = result.ExpectOperationResult(); +#if NET6_0 + await Snapshot.Create("NET_6_0") +#elif NET7_0 + await Snapshot.Create("NET_7_0") +#else await Snapshot.Create() +#endif .AddQueries(queries) .Add(operationResult.WithExtensions(ImmutableDictionary.Empty)) .MatchMarkdownAsync(); } - private static async Task SeedAsync(string connectionString) { await using var context = new AnimalContext(connectionString); diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/TestContext/AnimalContext.cs b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/TestContext/AnimalContext.cs index 7bd06628848..a303b6f1958 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/TestContext/AnimalContext.cs +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/TestContext/AnimalContext.cs @@ -6,9 +6,9 @@ namespace HotChocolate.Data.TestContext; public class AnimalContext(string connectionString) : DbContext { - public DbSet Owners { get; set; } - public DbSet Dogs { get; set; } - public DbSet Cats { get; set; } + public DbSet Owners { get; set; } = default!; + public DbSet Dogs { get; set; } = default!; + public DbSet Cats { get; set; } = default!; protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseNpgsql(connectionString); @@ -35,7 +35,7 @@ public class Owner public int Id { get; set; } [MaxLength(100)] - public required string Name { get; set; } + public string Name { get; set; } = default!; public List Pets { get; set; } = new(); } @@ -46,7 +46,7 @@ public abstract class Animal public int Id { get; set; } [MaxLength(100)] - public required string Name { get; set; } + public string Name { get; set; } = default!; public int OwnerId { get; set; } diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_NET_6_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_NET_6_0.md new file mode 100644 index 00000000000..9451199d619 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_NET_6_0.md @@ -0,0 +1,147 @@ +# Query_Owner_Animals + +## SQL 0 + +```sql +SELECT o."Id", o."Name" +FROM "Owners" AS o +ORDER BY o."Name", o."Id" +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].Select(root => new Owner() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name)}).OrderBy(t => t.Name).ThenBy(t => t.Id) +``` + +## SQL 1 + +```sql +SELECT a."Id", a."AnimalType", a."Name", a."OwnerId", a."IsPurring", a."IsBarking" +FROM "Owners" AS o +INNER JOIN "Animal" AS a ON o."Id" = a."OwnerId" +WHERE o."Id" IN (6, 5, 4, 3, 2, 1) +ORDER BY a."Name", a."Id" +``` + +## Expression 1 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].Where(t => value(HotChocolate.Data.InterfaceIntegrationTests+AnimalsByOwnerDataLoader+<>c__DisplayClass2_0).keys.Contains(t.Id)).SelectMany(t => t.Pets).OrderBy(t => t.Name).ThenBy(t => t.Id) +``` + +## Result 5 + +```json +{ + "data": { + "owners": { + "nodes": [ + { + "id": 1, + "name": "Owner 1", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 1, + "name": "Cat 1" + }, + { + "__typename": "Dog", + "id": 5, + "name": "Dog 1" + }, + { + "__typename": "Dog", + "id": 6, + "name": "Dog 2" + } + ] + } + }, + { + "id": 2, + "name": "Owner 2", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 2, + "name": "Cat 2" + }, + { + "__typename": "Dog", + "id": 7, + "name": "Dog 3" + }, + { + "__typename": "Dog", + "id": 8, + "name": "Dog 4" + } + ] + } + }, + { + "id": 3, + "name": "Owner 3", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 3, + "name": "Cat 3 (Not Pure)" + }, + { + "__typename": "Dog", + "id": 9, + "name": "Dog 5" + }, + { + "__typename": "Dog", + "id": 10, + "name": "Dog 6" + } + ] + } + }, + { + "id": 4, + "name": "Owner 4 - No Pets", + "pets": { + "nodes": [] + } + }, + { + "id": 5, + "name": "Owner 5 - Only Cat", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 4, + "name": "Only Cat" + } + ] + } + }, + { + "id": 6, + "name": "Owner 6 - Only Dog", + "pets": { + "nodes": [ + { + "__typename": "Dog", + "id": 11, + "name": "Only Dog" + } + ] + } + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_NET_7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_NET_7_0.md new file mode 100644 index 00000000000..0aae6260674 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_NET_7_0.md @@ -0,0 +1,147 @@ +# Query_Owner_Animals + +## SQL 0 + +```sql +SELECT o."Id", o."Name" +FROM "Owners" AS o +ORDER BY o."Name", o."Id" +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Select(root => new Owner() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name)}).OrderBy(t => t.Name).ThenBy(t => t.Id) +``` + +## SQL 1 + +```sql +SELECT a."Id", a."AnimalType", a."Name", a."OwnerId", a."IsPurring", a."IsBarking" +FROM "Owners" AS o +INNER JOIN "Animal" AS a ON o."Id" = a."OwnerId" +WHERE o."Id" IN (6, 5, 4, 3, 2, 1) +ORDER BY a."Name", a."Id" +``` + +## Expression 1 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Where(t => value(HotChocolate.Data.InterfaceIntegrationTests+AnimalsByOwnerDataLoader+<>c__DisplayClass2_0).keys.Contains(t.Id)).SelectMany(t => t.Pets).OrderBy(t => t.Name).ThenBy(t => t.Id) +``` + +## Result 5 + +```json +{ + "data": { + "owners": { + "nodes": [ + { + "id": 1, + "name": "Owner 1", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 1, + "name": "Cat 1" + }, + { + "__typename": "Dog", + "id": 5, + "name": "Dog 1" + }, + { + "__typename": "Dog", + "id": 6, + "name": "Dog 2" + } + ] + } + }, + { + "id": 2, + "name": "Owner 2", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 2, + "name": "Cat 2" + }, + { + "__typename": "Dog", + "id": 7, + "name": "Dog 3" + }, + { + "__typename": "Dog", + "id": 8, + "name": "Dog 4" + } + ] + } + }, + { + "id": 3, + "name": "Owner 3", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 3, + "name": "Cat 3 (Not Pure)" + }, + { + "__typename": "Dog", + "id": 9, + "name": "Dog 5" + }, + { + "__typename": "Dog", + "id": 10, + "name": "Dog 6" + } + ] + } + }, + { + "id": 4, + "name": "Owner 4 - No Pets", + "pets": { + "nodes": [] + } + }, + { + "id": 5, + "name": "Owner 5 - Only Cat", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 4, + "name": "Only Cat" + } + ] + } + }, + { + "id": 6, + "name": "Owner 6 - Only Dog", + "pets": { + "nodes": [ + { + "__typename": "Dog", + "id": 11, + "name": "Only Dog" + } + ] + } + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments_NET_6_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments_NET_6_0.md new file mode 100644 index 00000000000..6c8d064a4ae --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments_NET_6_0.md @@ -0,0 +1,158 @@ +# Query_Owner_Animals_With_Fragments + +## SQL 0 + +```sql +SELECT o."Id", o."Name" +FROM "Owners" AS o +ORDER BY o."Name", o."Id" +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].Select(root => new Owner() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name)}).OrderBy(t => t.Name).ThenBy(t => t.Id) +``` + +## SQL 1 + +```sql +SELECT a."Id", a."AnimalType", a."Name", a."OwnerId", a."IsPurring", a."IsBarking" +FROM "Owners" AS o +INNER JOIN "Animal" AS a ON o."Id" = a."OwnerId" +WHERE o."Id" IN (6, 5, 4, 3, 2, 1) +ORDER BY a."Name", a."Id" +``` + +## Expression 1 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].Where(t => value(HotChocolate.Data.InterfaceIntegrationTests+AnimalsByOwnerDataLoader+<>c__DisplayClass2_0).keys.Contains(t.Id)).SelectMany(t => t.Pets).OrderBy(t => t.Name).ThenBy(t => t.Id) +``` + +## Result 5 + +```json +{ + "data": { + "owners": { + "nodes": [ + { + "id": 1, + "name": "Owner 1", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 1, + "name": "Cat 1", + "isPurring": false + }, + { + "__typename": "Dog", + "id": 5, + "name": "Dog 1", + "isBarking": true + }, + { + "__typename": "Dog", + "id": 6, + "name": "Dog 2", + "isBarking": false + } + ] + } + }, + { + "id": 2, + "name": "Owner 2", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 2, + "name": "Cat 2", + "isPurring": false + }, + { + "__typename": "Dog", + "id": 7, + "name": "Dog 3", + "isBarking": true + }, + { + "__typename": "Dog", + "id": 8, + "name": "Dog 4", + "isBarking": false + } + ] + } + }, + { + "id": 3, + "name": "Owner 3", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 3, + "name": "Cat 3 (Not Pure)", + "isPurring": true + }, + { + "__typename": "Dog", + "id": 9, + "name": "Dog 5", + "isBarking": true + }, + { + "__typename": "Dog", + "id": 10, + "name": "Dog 6", + "isBarking": false + } + ] + } + }, + { + "id": 4, + "name": "Owner 4 - No Pets", + "pets": { + "nodes": [] + } + }, + { + "id": 5, + "name": "Owner 5 - Only Cat", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 4, + "name": "Only Cat", + "isPurring": false + } + ] + } + }, + { + "id": 6, + "name": "Owner 6 - Only Dog", + "pets": { + "nodes": [ + { + "__typename": "Dog", + "id": 11, + "name": "Only Dog", + "isBarking": true + } + ] + } + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments_NET_7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments_NET_7_0.md new file mode 100644 index 00000000000..70045af4643 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments_NET_7_0.md @@ -0,0 +1,158 @@ +# Query_Owner_Animals_With_Fragments + +## SQL 0 + +```sql +SELECT o."Id", o."Name" +FROM "Owners" AS o +ORDER BY o."Name", o."Id" +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Select(root => new Owner() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name)}).OrderBy(t => t.Name).ThenBy(t => t.Id) +``` + +## SQL 1 + +```sql +SELECT a."Id", a."AnimalType", a."Name", a."OwnerId", a."IsPurring", a."IsBarking" +FROM "Owners" AS o +INNER JOIN "Animal" AS a ON o."Id" = a."OwnerId" +WHERE o."Id" IN (6, 5, 4, 3, 2, 1) +ORDER BY a."Name", a."Id" +``` + +## Expression 1 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Where(t => value(HotChocolate.Data.InterfaceIntegrationTests+AnimalsByOwnerDataLoader+<>c__DisplayClass2_0).keys.Contains(t.Id)).SelectMany(t => t.Pets).OrderBy(t => t.Name).ThenBy(t => t.Id) +``` + +## Result 5 + +```json +{ + "data": { + "owners": { + "nodes": [ + { + "id": 1, + "name": "Owner 1", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 1, + "name": "Cat 1", + "isPurring": false + }, + { + "__typename": "Dog", + "id": 5, + "name": "Dog 1", + "isBarking": true + }, + { + "__typename": "Dog", + "id": 6, + "name": "Dog 2", + "isBarking": false + } + ] + } + }, + { + "id": 2, + "name": "Owner 2", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 2, + "name": "Cat 2", + "isPurring": false + }, + { + "__typename": "Dog", + "id": 7, + "name": "Dog 3", + "isBarking": true + }, + { + "__typename": "Dog", + "id": 8, + "name": "Dog 4", + "isBarking": false + } + ] + } + }, + { + "id": 3, + "name": "Owner 3", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 3, + "name": "Cat 3 (Not Pure)", + "isPurring": true + }, + { + "__typename": "Dog", + "id": 9, + "name": "Dog 5", + "isBarking": true + }, + { + "__typename": "Dog", + "id": 10, + "name": "Dog 6", + "isBarking": false + } + ] + } + }, + { + "id": 4, + "name": "Owner 4 - No Pets", + "pets": { + "nodes": [] + } + }, + { + "id": 5, + "name": "Owner 5 - Only Cat", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 4, + "name": "Only Cat", + "isPurring": false + } + ] + } + }, + { + "id": 6, + "name": "Owner 6 - Only Dog", + "pets": { + "nodes": [ + { + "__typename": "Dog", + "id": 11, + "name": "Only Dog", + "isBarking": true + } + ] + } + } + ] + } + } +} +``` + From cba3f14f22a7b7e2af701f645189bbabb82d98a6 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 2 Oct 2024 21:14:29 +0200 Subject: [PATCH 038/154] Fixed issue with DataLoader groups and selector injection. (#7549) --- .../Core/Projections/DefaultSelectorBuilder.cs | 8 +++++++- .../FileBuilders/DataLoaderFileBuilder.cs | 16 ++++++++++++++-- .../Generators/DataLoaderGenerator.cs | 4 ++-- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/GreenDonut/src/Core/Projections/DefaultSelectorBuilder.cs b/src/GreenDonut/src/Core/Projections/DefaultSelectorBuilder.cs index ca30af4313c..c6a217f6dda 100644 --- a/src/GreenDonut/src/Core/Projections/DefaultSelectorBuilder.cs +++ b/src/GreenDonut/src/Core/Projections/DefaultSelectorBuilder.cs @@ -4,13 +4,18 @@ namespace GreenDonut.Projections; +/// +/// A default implementation of the . +/// +/// #if NET8_0_OR_GREATER [Experimental(Experiments.Projections)] #endif -internal sealed class DefaultSelectorBuilder : ISelectorBuilder +public sealed class DefaultSelectorBuilder : ISelectorBuilder { private LambdaExpression? _expression; + /// public void Add(Expression> selector) { if (typeof(T) != typeof(TValue)) @@ -32,6 +37,7 @@ public void Add(Expression> selector) } } + /// public Expression>? TryCompile() { if (_expression is null) diff --git a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs index cbbbb4a5aea..16e2679bda7 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs @@ -255,8 +255,20 @@ public void WriteDataLoaderLoadMethod( isScoped ? "scope.ServiceProvider" : "_services", parameter.Type.ToFullyQualified()); } - else if (parameter.Kind is DataLoaderParameterKind.SelectorBuilder - || parameter.Kind is DataLoaderParameterKind.PagingArguments) + else if (parameter.Kind is DataLoaderParameterKind.SelectorBuilder) + { + _writer.WriteIndentedLine( + "var {0} = context.GetState<{1}>(\"{2}\")", + parameter.VariableName, + parameter.Type.ToFullyQualified(), + parameter.StateKey); + _writer.IncreaseIndent(); + _writer.WriteIndentedLine( + "?? new global::GreenDonut.Projections.DefaultSelectorBuilder<{0}>();", + value.ToFullyQualified()); + _writer.DecreaseIndent(); + } + else if (parameter.Kind is DataLoaderParameterKind.PagingArguments) { _writer.WriteIndentedLine( "var {0} = context.GetRequiredState<{1}>(\"{2}\");", diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Generators/DataLoaderGenerator.cs b/src/HotChocolate/Core/src/Types.Analyzers/Generators/DataLoaderGenerator.cs index d9e8be27124..8615167a22a 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Generators/DataLoaderGenerator.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Generators/DataLoaderGenerator.cs @@ -100,7 +100,7 @@ private static void WriteDataLoader( .OrderBy(t => t.Key, StringComparer.Ordinal)) { var isPublic = defaults.IsInterfacePublic ?? true; - var groups = group.Select( + var dataLoaderGroups = dataLoaderGroup.Select( t => new GroupedDataLoaderInfo( t.NameWithoutSuffix, t.InterfaceName, @@ -108,7 +108,7 @@ private static void WriteDataLoader( buffer ??= new(); buffer.Clear(); - buffer.AddRange(groups); + buffer.AddRange(dataLoaderGroups); generator.WriteDataLoaderGroupClass(dataLoaderGroup.Key, buffer); } From c7b5274e0b97c226dd12e12da862cfd57a2ad31b Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 2 Oct 2024 22:25:55 +0200 Subject: [PATCH 039/154] Added more fine grained control over the persisted operation pipeline. (#7550) --- .../TestOperationDocumentStorage.cs | 4 + .../PersistedOperationTests.cs | 73 ++++++++++++++++--- ...Standard_Query_Allowed_When_Persisted.snap | 11 +++ ...Query_Not_Allowed_Even_When_Persisted.snap | 14 ++++ .../Execution/Caching/DefaultDocumentCache.cs | 6 +- .../IPersistedOperationOptionsAccessor.cs | 6 ++ .../Options/PersistedOperationOptions.cs | 30 ++++++++ .../Options/RequestExecutorOptions.cs | 33 +++++++-- .../Pipeline/DocumentCacheMiddleware.cs | 13 +++- ...nlyPersistedOperationsAllowedMiddleware.cs | 42 +++++++++-- .../ReadPersistedOperationMiddleware.cs | 27 +++++-- .../Pipeline/DocumentCacheMiddlewareTests.cs | 6 +- .../src/Language.Web/CachedDocument.cs | 21 ++++++ .../src/Language.Web/IDocumentCache.cs | 4 +- .../Language.Web/Utf8GraphQLRequestParser.cs | 6 +- .../Parser/GraphQLRequestParserTests.cs | 8 +- 16 files changed, 259 insertions(+), 45 deletions(-) create mode 100644 src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/PersistedOperationTests.Standard_Query_Allowed_When_Persisted.snap create mode 100644 src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/PersistedOperationTests.Standard_Query_Not_Allowed_Even_When_Persisted.snap create mode 100644 src/HotChocolate/Core/src/Execution/Options/PersistedOperationOptions.cs create mode 100644 src/HotChocolate/Language/src/Language.Web/CachedDocument.cs diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/TestOperationDocumentStorage.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/TestOperationDocumentStorage.cs index 2f8644bf4b7..2a8cb03ea45 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/TestOperationDocumentStorage.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/TestOperationDocumentStorage.cs @@ -16,6 +16,10 @@ public TestOperationDocumentStorage() _cache.Add( "abc123", Utf8GraphQLParser.Parse(@"query Test($if: Boolean) { hero { name @skip(if: $if) } }")); + + _cache.Add( + "a73defcdf38e5891e91b9ba532cf4c36", + Utf8GraphQLParser.Parse(@"query GetHeroName { hero { name } }")); } public async ValueTask TryReadAsync( diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/PersistedOperationTests.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/PersistedOperationTests.cs index dc7c3c725e7..d36a992e978 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/PersistedOperationTests.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/PersistedOperationTests.cs @@ -5,17 +5,13 @@ using HotChocolate.Execution; using HotChocolate.Language; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Server.HttpSys; +using static HotChocolate.Execution.Options.PersistedOperationOptions; namespace HotChocolate.AspNetCore; -public class PersistedOperationTests : ServerTestBase +public class PersistedOperationTests(TestServerFactory serverFactory) + : ServerTestBase(serverFactory) { - public PersistedOperationTests(TestServerFactory serverFactory) - : base(serverFactory) - { - } - [Fact] public async Task HotChocolateStyle_MD5Hash_Success() { @@ -293,7 +289,7 @@ public async Task Standard_Query_Not_Allowed() var server = CreateStarWarsServer( configureServices: s => s .AddGraphQL("StarWars") - .ModifyRequestOptions(o => o.OnlyAllowPersistedOperations = true) + .ModifyRequestOptions(o => o.PersistedOperationOptions = OnlyPersistedOperations) .ConfigureSchemaServices(c => c.AddSingleton(storage)) .UsePersistedOperationPipeline()); @@ -308,6 +304,63 @@ public async Task Standard_Query_Not_Allowed() result.MatchSnapshot(); } + [Fact] + public async Task Standard_Query_Not_Allowed_Even_When_Persisted() + { + // arrange + var storage = new OperationStorage(); + storage.AddOperation( + "a73defcdf38e5891e91b9ba532cf4c36", + "query GetHeroName { hero { name } }"); + + var server = CreateStarWarsServer( + configureServices: s => s + .AddGraphQL("StarWars") + .ModifyRequestOptions(o => o.PersistedOperationOptions |= OnlyPersistedOperations) + .ConfigureSchemaServices(c => c.AddSingleton(storage)) + .UsePersistedOperationPipeline()); + + var query = "query GetHeroName { hero { name } }"; + + // act + var result = await server.PostAsync( + new ClientQueryRequest { Query = query, }, + path: "/starwars"); + + // assert + result.MatchSnapshot(); + } + + [Fact] + public async Task Standard_Query_Allowed_When_Persisted() + { + // arrange + var storage = new OperationStorage(); + storage.AddOperation( + "a73defcdf38e5891e91b9ba532cf4c36", + "query GetHeroName { hero { name } }"); + + var server = CreateStarWarsServer( + configureServices: s => s + .AddGraphQL("StarWars") + .ModifyRequestOptions(o => + o.PersistedOperationOptions = + OnlyPersistedOperations + | MatchStandardDocument) + .ConfigureSchemaServices(c => c.AddSingleton(storage)) + .UsePersistedOperationPipeline()); + + var query = "query GetHeroName { hero { name } }"; + + // act + var result = await server.PostAsync( + new ClientQueryRequest { Query = query, }, + path: "/starwars"); + + // assert + result.MatchSnapshot(); + } + [Fact] public async Task Standard_Query_Not_Allowed_Custom_Error() { @@ -319,7 +372,7 @@ public async Task Standard_Query_Not_Allowed_Custom_Error() .AddGraphQL("StarWars") .ModifyRequestOptions(o => { - o.OnlyAllowPersistedOperations = true; + o.PersistedOperationOptions = OnlyPersistedOperations; o.OnlyPersistedOperationsAreAllowedError = ErrorBuilder.New() .SetMessage("Not allowed!") @@ -350,7 +403,7 @@ public async Task Standard_Query_Not_Allowed_Override_Per_Request() .AddGraphQL("StarWars") .ModifyRequestOptions(o => { - o.OnlyAllowPersistedOperations = true; + o.PersistedOperationOptions = OnlyPersistedOperations; }) .ConfigureSchemaServices(c => c.AddSingleton(storage)) .UsePersistedOperationPipeline() diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/PersistedOperationTests.Standard_Query_Allowed_When_Persisted.snap b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/PersistedOperationTests.Standard_Query_Allowed_When_Persisted.snap new file mode 100644 index 00000000000..46e78843ecd --- /dev/null +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/PersistedOperationTests.Standard_Query_Allowed_When_Persisted.snap @@ -0,0 +1,11 @@ +{ + "ContentType": "application/graphql-response+json; charset=utf-8", + "StatusCode": "OK", + "Data": { + "hero": { + "name": "R2-D2" + } + }, + "Errors": null, + "Extensions": null +} diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/PersistedOperationTests.Standard_Query_Not_Allowed_Even_When_Persisted.snap b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/PersistedOperationTests.Standard_Query_Not_Allowed_Even_When_Persisted.snap new file mode 100644 index 00000000000..701a27fa185 --- /dev/null +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/PersistedOperationTests.Standard_Query_Not_Allowed_Even_When_Persisted.snap @@ -0,0 +1,14 @@ +{ + "ContentType": "application/graphql-response+json; charset=utf-8", + "StatusCode": "BadRequest", + "Data": null, + "Errors": [ + { + "message": "Only persisted operations are allowed.", + "extensions": { + "code": "HC0067" + } + } + ], + "Extensions": null +} diff --git a/src/HotChocolate/Core/src/Execution/Caching/DefaultDocumentCache.cs b/src/HotChocolate/Core/src/Execution/Caching/DefaultDocumentCache.cs index d55c5433a7c..5da08dfe2d3 100644 --- a/src/HotChocolate/Core/src/Execution/Caching/DefaultDocumentCache.cs +++ b/src/HotChocolate/Core/src/Execution/Caching/DefaultDocumentCache.cs @@ -6,16 +6,16 @@ namespace HotChocolate.Execution.Caching; internal sealed class DefaultDocumentCache(int capacity = 100) : IDocumentCache { - private readonly Cache _cache = new(capacity); + private readonly Cache _cache = new(capacity); public int Capacity => _cache.Capacity; public int Count => _cache.Usage; - public void TryAddDocument(string documentId, DocumentNode document) + public void TryAddDocument(string documentId, CachedDocument document) => _cache.GetOrCreate(documentId, () => document); - public bool TryGetDocument(string documentId, [NotNullWhen(true)] out DocumentNode? document) + public bool TryGetDocument(string documentId, [NotNullWhen(true)] out CachedDocument? document) => _cache.TryGet(documentId, out document!); public void Clear() => _cache.Clear(); diff --git a/src/HotChocolate/Core/src/Execution/Options/IPersistedOperationOptionsAccessor.cs b/src/HotChocolate/Core/src/Execution/Options/IPersistedOperationOptionsAccessor.cs index ab6a6c45121..5bea5202c2d 100644 --- a/src/HotChocolate/Core/src/Execution/Options/IPersistedOperationOptionsAccessor.cs +++ b/src/HotChocolate/Core/src/Execution/Options/IPersistedOperationOptionsAccessor.cs @@ -9,8 +9,14 @@ public interface IPersistedOperationOptionsAccessor /// Specifies if only persisted operations are allowed when using /// the persisted operation pipeline. /// + [Obsolete("Use PersistedOperationOptions instead.")] bool OnlyAllowPersistedOperations { get; } + /// + /// Specifies the behavior of the persisted operation middleware. + /// + PersistedOperationOptions PersistedOperationOptions { get; set; } + /// /// The error that will be thrown when only persisted /// operations are allowed and a normal operation is issued. diff --git a/src/HotChocolate/Core/src/Execution/Options/PersistedOperationOptions.cs b/src/HotChocolate/Core/src/Execution/Options/PersistedOperationOptions.cs new file mode 100644 index 00000000000..4d0ad6f6ebe --- /dev/null +++ b/src/HotChocolate/Core/src/Execution/Options/PersistedOperationOptions.cs @@ -0,0 +1,30 @@ +namespace HotChocolate.Execution.Options; + +/// +/// Represents the options to configure the +/// behavior of the persisted operation middleware. +/// +[Flags] +public enum PersistedOperationOptions +{ + /// + /// Nothing is configured. + /// + None = 0, + + /// + /// Only persisted operations are allowed. + /// + OnlyPersistedOperations = 1, + + /// + /// Allow standard GraphQL requests if the GraphQL document + /// match a persisted operation document. + /// + MatchStandardDocument = 2, + + /// + /// Skip validation for persisted operations documents. + /// + SkipValidationForPersistedDocument = 4 +} diff --git a/src/HotChocolate/Core/src/Execution/Options/RequestExecutorOptions.cs b/src/HotChocolate/Core/src/Execution/Options/RequestExecutorOptions.cs index 7e07703eebb..618be1c011f 100644 --- a/src/HotChocolate/Core/src/Execution/Options/RequestExecutorOptions.cs +++ b/src/HotChocolate/Core/src/Execution/Options/RequestExecutorOptions.cs @@ -1,4 +1,5 @@ using System.Diagnostics; +using static HotChocolate.Execution.Options.PersistedOperationOptions; namespace HotChocolate.Execution.Options; @@ -43,20 +44,42 @@ public TimeSpan ExecutionTimeout } /// + /// /// Gets or sets a value indicating whether the GraphQL errors /// should be extended with exception details. - /// - /// The default value is . + /// + /// The default value is . /// public bool IncludeExceptionDetails { get; set; } = Debugger.IsAttached; /// + /// /// Specifies if only persisted operations are allowed when using /// the persisted operation pipeline. - /// - /// The default is false. + /// + /// The default is false. + /// + [Obsolete("Use PersistedOperationOptions instead.")] + public bool OnlyAllowPersistedOperations + { + get => (PersistedOperationOptions & OnlyPersistedOperations) == OnlyPersistedOperations; + set + { + if (value) + { + PersistedOperationOptions |= OnlyPersistedOperations; + } + else + { + PersistedOperationOptions &= ~OnlyPersistedOperations; + } + } + } + + /// + /// Specifies the behavior of the persisted operation middleware. /// - public bool OnlyAllowPersistedOperations { get; set; } = false; + public PersistedOperationOptions PersistedOperationOptions { get; set; } = None; /// /// The error that will be thrown when only persisted diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/DocumentCacheMiddleware.cs b/src/HotChocolate/Core/src/Execution/Pipeline/DocumentCacheMiddleware.cs index fae3ebf0f18..ba00ae1c331 100644 --- a/src/HotChocolate/Core/src/Execution/Pipeline/DocumentCacheMiddleware.cs +++ b/src/HotChocolate/Core/src/Execution/Pipeline/DocumentCacheMiddleware.cs @@ -38,9 +38,10 @@ public async ValueTask InvokeAsync(IRequestContext context) _documentCache.TryGetDocument(request.DocumentId.Value.Value, out var document)) { context.DocumentId = request.DocumentId; - context.Document = document; + context.Document = document.Body; context.ValidationResult = DocumentValidatorResult.Ok; context.IsCachedDocument = true; + context.IsPersistedDocument = document.IsPersisted; addToCache = false; _diagnosticEvents.RetrievedDocumentFromCache(context); } @@ -48,9 +49,10 @@ public async ValueTask InvokeAsync(IRequestContext context) _documentCache.TryGetDocument(request.DocumentHash, out document)) { context.DocumentId = request.DocumentHash; - context.Document = document; + context.Document = document.Body; context.ValidationResult = DocumentValidatorResult.Ok; context.IsCachedDocument = true; + context.IsPersistedDocument = document.IsPersisted; addToCache = false; _diagnosticEvents.RetrievedDocumentFromCache(context); } @@ -60,9 +62,10 @@ public async ValueTask InvokeAsync(IRequestContext context) if (_documentCache.TryGetDocument(context.DocumentHash, out document)) { context.DocumentId = context.DocumentHash; - context.Document = document; + context.Document = document.Body; context.ValidationResult = DocumentValidatorResult.Ok; context.IsCachedDocument = true; + context.IsPersistedDocument = document.IsPersisted; addToCache = false; _diagnosticEvents.RetrievedDocumentFromCache(context); } @@ -76,7 +79,9 @@ public async ValueTask InvokeAsync(IRequestContext context) context.Document != null && context.IsValidDocument) { - _documentCache.TryAddDocument(context.DocumentId.Value.Value, context.Document); + _documentCache.TryAddDocument( + context.DocumentId.Value.Value, + new CachedDocument(context.Document, context.IsPersistedDocument)); _diagnosticEvents.AddedDocumentToCache(context); } } diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/OnlyPersistedOperationsAllowedMiddleware.cs b/src/HotChocolate/Core/src/Execution/Pipeline/OnlyPersistedOperationsAllowedMiddleware.cs index 4a4eeba0a5d..deaa1acc229 100644 --- a/src/HotChocolate/Core/src/Execution/Pipeline/OnlyPersistedOperationsAllowedMiddleware.cs +++ b/src/HotChocolate/Core/src/Execution/Pipeline/OnlyPersistedOperationsAllowedMiddleware.cs @@ -1,6 +1,7 @@ using HotChocolate.Execution.Instrumentation; using HotChocolate.Execution.Options; using Microsoft.Extensions.DependencyInjection; +using static HotChocolate.Execution.Options.PersistedOperationOptions; namespace HotChocolate.Execution.Pipeline; @@ -8,7 +9,7 @@ internal sealed class OnlyPersistedOperationsAllowedMiddleware { private readonly RequestDelegate _next; private readonly IExecutionDiagnosticEvents _diagnosticEvents; - private readonly bool _allowAllOperations; + private readonly PersistedOperationOptions _options; private readonly IOperationResult _errorResult; private readonly GraphQLException _exception; private readonly Dictionary _statusCode = new() { { WellKnownContextData.HttpStatusCode, 400 }, }; @@ -29,7 +30,7 @@ private OnlyPersistedOperationsAllowedMiddleware( ?? throw new ArgumentNullException(nameof(diagnosticEvents)); // prepare options. - _allowAllOperations = !options.OnlyAllowPersistedOperations; + _options = options.PersistedOperationOptions; var error = options.OnlyPersistedOperationsAreAllowedError; _errorResult = OperationResultBuilder.CreateError(error, _statusCode); _exception = new GraphQLException(error); @@ -37,18 +38,43 @@ private OnlyPersistedOperationsAllowedMiddleware( public ValueTask InvokeAsync(IRequestContext context) { - if (_allowAllOperations || - context.Request.Document is null || - context.ContextData.ContainsKey(WellKnownContextData.NonPersistedOperationAllowed)) + // if all operations are allowed we can skip this middleware. + if((_options & OnlyPersistedOperations) != OnlyPersistedOperations) { return _next(context); } - // we know that the key is not null since otherwise the request would have - // failed already since no operation is specified. + // if the document is a persisted operation document than in general we can + // skip this middleware. + if(context.IsPersistedDocument) + { + // however this could still be a standard GraphQL request that contains a document + // that just matches a persisted operation document. + // either this is allowed by the configuration and we can skip this middleware + if ((_options & MatchStandardDocument) == MatchStandardDocument) + { + return _next(context); + } + + // or we have to make sure that the GraphQL request is a persisted operation request. + // if the operation request has no document we can be sure that it's + // a persisted operation request and we can skip this middleware. + if (context.Request.Document is null) + { + return _next(context); + } + } + + // lastly it might be that the request is allowed for the current session even + // if it's not a persisted operation request. + if (context.ContextData.ContainsKey(WellKnownContextData.NonPersistedOperationAllowed)) + { + return _next(context); + } + + // if we reach this point we have to throw an error since the request is not allowed. _diagnosticEvents.RequestError(context, _exception); context.Result = _errorResult; - return default; } diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/ReadPersistedOperationMiddleware.cs b/src/HotChocolate/Core/src/Execution/Pipeline/ReadPersistedOperationMiddleware.cs index 8f278b9658c..c3459ae1848 100644 --- a/src/HotChocolate/Core/src/Execution/Pipeline/ReadPersistedOperationMiddleware.cs +++ b/src/HotChocolate/Core/src/Execution/Pipeline/ReadPersistedOperationMiddleware.cs @@ -1,7 +1,9 @@ using HotChocolate.Execution.Instrumentation; +using HotChocolate.Execution.Options; using HotChocolate.Language; using HotChocolate.Validation; using Microsoft.Extensions.DependencyInjection; +using static HotChocolate.Execution.Options.PersistedOperationOptions; namespace HotChocolate.Execution.Pipeline; @@ -10,10 +12,13 @@ internal sealed class ReadPersistedOperationMiddleware private readonly RequestDelegate _next; private readonly IExecutionDiagnosticEvents _diagnosticEvents; private readonly IOperationDocumentStorage _operationDocumentStorage; + private readonly PersistedOperationOptions _options; - private ReadPersistedOperationMiddleware(RequestDelegate next, + private ReadPersistedOperationMiddleware( + RequestDelegate next, [SchemaService] IExecutionDiagnosticEvents diagnosticEvents, - [SchemaService] IOperationDocumentStorage operationDocumentStorage) + [SchemaService] IOperationDocumentStorage operationDocumentStorage, + PersistedOperationOptions options) { _next = next ?? throw new ArgumentNullException(nameof(next)); @@ -21,12 +26,12 @@ private ReadPersistedOperationMiddleware(RequestDelegate next, throw new ArgumentNullException(nameof(diagnosticEvents)); _operationDocumentStorage = operationDocumentStorage ?? throw new ArgumentNullException(nameof(operationDocumentStorage)); + _options = options; } public async ValueTask InvokeAsync(IRequestContext context) { - if (context.Document is null && - context.Request.Document is null) + if (context.Document is null) { await TryLoadQueryAsync(context).ConfigureAwait(false); } @@ -56,6 +61,10 @@ await _operationDocumentStorage.TryReadAsync( context.ValidationResult = DocumentValidatorResult.Ok; context.IsCachedDocument = true; context.IsPersistedDocument = true; + if ((_options & SkipValidationForPersistedDocument) == SkipValidationForPersistedDocument) + { + context.ValidationResult = DocumentValidatorResult.Ok; + } _diagnosticEvents.RetrievedDocumentFromStorage(context); } @@ -66,6 +75,10 @@ await _operationDocumentStorage.TryReadAsync( context.ValidationResult = DocumentValidatorResult.Ok; context.IsCachedDocument = true; context.IsPersistedDocument = true; + if ((_options & SkipValidationForPersistedDocument) == SkipValidationForPersistedDocument) + { + context.ValidationResult = DocumentValidatorResult.Ok; + } _diagnosticEvents.RetrievedDocumentFromStorage(context); } } @@ -76,7 +89,11 @@ public static RequestCoreMiddleware Create() { var diagnosticEvents = core.SchemaServices.GetRequiredService(); var persistedOperationStore = core.SchemaServices.GetRequiredService(); - var middleware = new ReadPersistedOperationMiddleware(next, diagnosticEvents, persistedOperationStore); + var middleware = new ReadPersistedOperationMiddleware( + next, + diagnosticEvents, + persistedOperationStore, + core.Options.PersistedOperationOptions); return context => middleware.InvokeAsync(context); }; } diff --git a/src/HotChocolate/Core/test/Execution.Tests/Pipeline/DocumentCacheMiddlewareTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Pipeline/DocumentCacheMiddlewareTests.cs index b49cc95336f..7fc847ed2f8 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Pipeline/DocumentCacheMiddlewareTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Pipeline/DocumentCacheMiddlewareTests.cs @@ -25,7 +25,7 @@ public async Task RetrieveItemFromCache_DocumentFoundOnCache() .Build(); var document = Utf8GraphQLParser.Parse("{ a }"); - cache.TryAddDocument("a", document); + cache.TryAddDocument("a", new CachedDocument(document, false)); var requestContext = new Mock(); var schema = new Mock(); @@ -63,7 +63,7 @@ public async Task RetrieveItemFromCacheByHash_DocumentFoundOnCache() .Build(); var document = Utf8GraphQLParser.Parse("{ a }"); - cache.TryAddDocument("a", document); + cache.TryAddDocument("a", new CachedDocument(document, false)); var requestContext = new Mock(); var schema = new Mock(); @@ -101,7 +101,7 @@ public async Task RetrieveItemFromCache_DocumentNotFoundOnCache() .Build(); var document = Utf8GraphQLParser.Parse("{ a }"); - cache.TryAddDocument("b", document); + cache.TryAddDocument("b", new CachedDocument(document, false)); var requestContext = new Mock(); var schema = new Mock(); diff --git a/src/HotChocolate/Language/src/Language.Web/CachedDocument.cs b/src/HotChocolate/Language/src/Language.Web/CachedDocument.cs new file mode 100644 index 00000000000..7124f1aa8b3 --- /dev/null +++ b/src/HotChocolate/Language/src/Language.Web/CachedDocument.cs @@ -0,0 +1,21 @@ +namespace HotChocolate.Language; + +/// +/// Represents a cached document. +/// +/// +/// +public sealed class CachedDocument( + DocumentNode body, + bool isPersisted) +{ + /// + /// Gets the actual GraphQL syntax tree. + /// + public DocumentNode Body { get; } = body; + + /// + /// Defines if the document is a persisted document. + /// + public bool IsPersisted { get; } = isPersisted; +} diff --git a/src/HotChocolate/Language/src/Language.Web/IDocumentCache.cs b/src/HotChocolate/Language/src/Language.Web/IDocumentCache.cs index 1862796c582..8daf57f45e1 100644 --- a/src/HotChocolate/Language/src/Language.Web/IDocumentCache.cs +++ b/src/HotChocolate/Language/src/Language.Web/IDocumentCache.cs @@ -31,7 +31,7 @@ public interface IDocumentCache /// true if a cached GraphQL syntax tree was found that matches the /// , otherwise false. /// - bool TryGetDocument(string documentId, [NotNullWhen(true)] out DocumentNode? document); + bool TryGetDocument(string documentId, [NotNullWhen(true)] out CachedDocument? document); /// /// Tries to add a parsed GraphQL syntax tree to the cache. @@ -42,7 +42,7 @@ public interface IDocumentCache /// /// The GraphQL syntax tree. /// - void TryAddDocument(string documentId, DocumentNode document); + void TryAddDocument(string documentId, CachedDocument document); /// /// Clears the cache. diff --git a/src/HotChocolate/Language/src/Language.Web/Utf8GraphQLRequestParser.cs b/src/HotChocolate/Language/src/Language.Web/Utf8GraphQLRequestParser.cs index 52f2c8c430b..b792ed36d02 100644 --- a/src/HotChocolate/Language/src/Language.Web/Utf8GraphQLRequestParser.cs +++ b/src/HotChocolate/Language/src/Language.Web/Utf8GraphQLRequestParser.cs @@ -282,7 +282,11 @@ private void ParseQuery(ref Request request) { queryId ??= request.QueryHash = _hashProvider!.ComputeHash(unescapedSpan); - if (!_cache!.TryGetDocument(queryId, out document)) + if (_cache!.TryGetDocument(queryId, out var cachedDocument)) + { + document = cachedDocument.Body; + } + else { document = unescapedSpan.Length == 0 ? null diff --git a/src/HotChocolate/Language/test/Language.Tests/Parser/GraphQLRequestParserTests.cs b/src/HotChocolate/Language/test/Language.Tests/Parser/GraphQLRequestParserTests.cs index fbd7b3151c0..7e56b68475e 100644 --- a/src/HotChocolate/Language/test/Language.Tests/Parser/GraphQLRequestParserTests.cs +++ b/src/HotChocolate/Language/test/Language.Tests/Parser/GraphQLRequestParserTests.cs @@ -214,7 +214,7 @@ public void Parse_Kitchen_Sink_Query_With_Cache() var first = requestParser.Parse(); - cache.TryAddDocument(first[0].QueryId!, first[0].Query!); + cache.TryAddDocument(first[0].QueryId!, new CachedDocument(first[0].Query!, false)); // act requestParser = new Utf8GraphQLRequestParser( @@ -763,13 +763,13 @@ private sealed class RelayGraphQLRequestDto(string id, string query) private sealed class DocumentCache : IDocumentCache { - private readonly Dictionary _cache = new(); + private readonly Dictionary _cache = new(); public int Capacity => int.MaxValue; public int Count => _cache.Count; - public void TryAddDocument(string documentId, DocumentNode document) + public void TryAddDocument(string documentId, CachedDocument document) { if (!_cache.ContainsKey(documentId)) { @@ -779,7 +779,7 @@ public void TryAddDocument(string documentId, DocumentNode document) public bool TryGetDocument( string documentId, - [NotNullWhen(true)] out DocumentNode? document) => + [NotNullWhen(true)] out CachedDocument? document) => _cache.TryGetValue(documentId, out document); public void Clear() From 3cd3d50a2a6bbc926eac86f0f2191cd60dcef24d Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Thu, 3 Oct 2024 11:09:03 +0200 Subject: [PATCH 040/154] Marked internal API experimental. --- .../FederationTypeInterceptor.cs | 2 +- .../CacheControlValidationTypeInterceptor.cs | 2 +- .../src/Caching/HotChocolate.Caching.csproj | 1 + .../AuthorizationTypeInterceptor.cs | 2 +- .../HotChocolate.Authorization.csproj | 1 + .../DataLoaderRootFieldTypeInterceptor.cs | 2 +- .../src/Fetching/HotChocolate.Fetching.csproj | 1 + .../HotChocolate.Types.Queries.csproj | 1 + .../QueryConventionTypeInterceptor.cs | 2 +- .../Configuration/AggregateTypeInterceptor.cs | 2 +- .../Contracts/ITypeCompletionContext.cs | 10 ++++++++++ .../Configuration/OnAfterSchemaCreate.cs | 3 +++ .../Configuration/OnBeforeSchemaCreate.cs | 3 +++ .../src/Types/Configuration/OnCompleteType.cs | 3 +++ .../Types/Configuration/OnCompleteType~1.cs | 4 ++++ .../Types/Configuration/OnInitializeType.cs | 3 +++ .../Types/Configuration/OnInitializeType~1.cs | 4 ++++ .../src/Types/Configuration/OnSchemaError.cs | 3 +++ .../Types/Configuration/TypeInterceptor.cs | 20 ++++++++++++++++++- .../Core/src/Types/Experiments.cs | 6 ++++++ .../Core/src/Types/HotChocolate.Types.csproj | 1 + .../IntrospectionTypeInterceptor.cs | 2 +- .../Types/Relay/NodeFieldTypeInterceptor.cs | 2 +- .../Relay/NodeResolverTypeInterceptor.cs | 2 +- .../Types/Relay/QueryFieldTypeInterceptor.cs | 2 +- .../Projections/ProjectableDataLoaderTests.cs | 4 ++++ .../Data/src/Data/HotChocolate.Data.csproj | 1 + .../Projections/ProjectionTypeInterceptor.cs | 2 +- 28 files changed, 79 insertions(+), 12 deletions(-) create mode 100644 src/HotChocolate/Core/src/Types/Experiments.cs diff --git a/src/HotChocolate/ApolloFederation/src/ApolloFederation/FederationTypeInterceptor.cs b/src/HotChocolate/ApolloFederation/src/ApolloFederation/FederationTypeInterceptor.cs index 45fb663ae10..b737233188b 100644 --- a/src/HotChocolate/ApolloFederation/src/ApolloFederation/FederationTypeInterceptor.cs +++ b/src/HotChocolate/ApolloFederation/src/ApolloFederation/FederationTypeInterceptor.cs @@ -314,7 +314,7 @@ private void RegisterExportedDirectives() } } - internal override void OnAfterResolveRootType( + public override void OnAfterResolveRootType( ITypeCompletionContext completionContext, ObjectTypeDefinition definition, OperationType operationType) diff --git a/src/HotChocolate/Caching/src/Caching/CacheControlValidationTypeInterceptor.cs b/src/HotChocolate/Caching/src/Caching/CacheControlValidationTypeInterceptor.cs index 6330ca96756..de0d963358c 100644 --- a/src/HotChocolate/Caching/src/Caching/CacheControlValidationTypeInterceptor.cs +++ b/src/HotChocolate/Caching/src/Caching/CacheControlValidationTypeInterceptor.cs @@ -10,7 +10,7 @@ internal sealed class CacheControlValidationTypeInterceptor : TypeInterceptor { private ITypeCompletionContext _queryContext = default!; - internal override void OnAfterResolveRootType( + public override void OnAfterResolveRootType( ITypeCompletionContext completionContext, ObjectTypeDefinition definition, OperationType operationType) diff --git a/src/HotChocolate/Caching/src/Caching/HotChocolate.Caching.csproj b/src/HotChocolate/Caching/src/Caching/HotChocolate.Caching.csproj index f7382bc14db..8f587f3859a 100644 --- a/src/HotChocolate/Caching/src/Caching/HotChocolate.Caching.csproj +++ b/src/HotChocolate/Caching/src/Caching/HotChocolate.Caching.csproj @@ -13,6 +13,7 @@ HotChocolate.Caching HotChocolate.Caching HotChocolate.Caching + HC8001;$(NoWarn) diff --git a/src/HotChocolate/Core/src/Authorization/AuthorizationTypeInterceptor.cs b/src/HotChocolate/Core/src/Authorization/AuthorizationTypeInterceptor.cs index be25e8e0065..dd048ef1bc0 100644 --- a/src/HotChocolate/Core/src/Authorization/AuthorizationTypeInterceptor.cs +++ b/src/HotChocolate/Core/src/Authorization/AuthorizationTypeInterceptor.cs @@ -101,7 +101,7 @@ public override void OnBeforeCompleteTypes() FindFieldsAndApplyAuthMiddleware(state); } - internal override void OnAfterResolveRootType( + public override void OnAfterResolveRootType( ITypeCompletionContext completionContext, ObjectTypeDefinition definition, OperationType operationType) diff --git a/src/HotChocolate/Core/src/Authorization/HotChocolate.Authorization.csproj b/src/HotChocolate/Core/src/Authorization/HotChocolate.Authorization.csproj index 57f5161ff23..eddfb2c0500 100644 --- a/src/HotChocolate/Core/src/Authorization/HotChocolate.Authorization.csproj +++ b/src/HotChocolate/Core/src/Authorization/HotChocolate.Authorization.csproj @@ -9,6 +9,7 @@ HotChocolate.Authorization HotChocolate.Authorization This package contains the authorization abstractions for Hot Chocolate. + HC8001;$(NoWarn) diff --git a/src/HotChocolate/Core/src/Fetching/DataLoaderRootFieldTypeInterceptor.cs b/src/HotChocolate/Core/src/Fetching/DataLoaderRootFieldTypeInterceptor.cs index e4dd88ea760..15775ab300c 100644 --- a/src/HotChocolate/Core/src/Fetching/DataLoaderRootFieldTypeInterceptor.cs +++ b/src/HotChocolate/Core/src/Fetching/DataLoaderRootFieldTypeInterceptor.cs @@ -29,7 +29,7 @@ internal override void InitializeContext( internal override bool IsEnabled(IDescriptorContext context) => context.Options.PublishRootFieldPagesToPromiseCache; - internal override void OnAfterResolveRootType( + public override void OnAfterResolveRootType( ITypeCompletionContext completionContext, ObjectTypeDefinition definition, OperationType operationType) diff --git a/src/HotChocolate/Core/src/Fetching/HotChocolate.Fetching.csproj b/src/HotChocolate/Core/src/Fetching/HotChocolate.Fetching.csproj index 1c7d187f26e..2bb53fa3578 100644 --- a/src/HotChocolate/Core/src/Fetching/HotChocolate.Fetching.csproj +++ b/src/HotChocolate/Core/src/Fetching/HotChocolate.Fetching.csproj @@ -4,6 +4,7 @@ HotChocolate.Fetching HotChocolate.Fetching HotChocolate.Fetching + HC8001;$(NoWarn) diff --git a/src/HotChocolate/Core/src/Types.Queries/HotChocolate.Types.Queries.csproj b/src/HotChocolate/Core/src/Types.Queries/HotChocolate.Types.Queries.csproj index 5581afb25f4..285ea2b380e 100644 --- a/src/HotChocolate/Core/src/Types.Queries/HotChocolate.Types.Queries.csproj +++ b/src/HotChocolate/Core/src/Types.Queries/HotChocolate.Types.Queries.csproj @@ -5,6 +5,7 @@ HotChocolate.Types.Queries HotChocolate.Types This package provides helpers for creating GraphQL queries. + HC8001;$(NoWarn) diff --git a/src/HotChocolate/Core/src/Types.Queries/QueryConventionTypeInterceptor.cs b/src/HotChocolate/Core/src/Types.Queries/QueryConventionTypeInterceptor.cs index 9ba892175f4..562724ce5d0 100644 --- a/src/HotChocolate/Core/src/Types.Queries/QueryConventionTypeInterceptor.cs +++ b/src/HotChocolate/Core/src/Types.Queries/QueryConventionTypeInterceptor.cs @@ -26,7 +26,7 @@ internal override void InitializeContext( _errorTypeHelper.InitializerErrorTypeInterface(_context); } - internal override void OnAfterResolveRootType( + public override void OnAfterResolveRootType( ITypeCompletionContext completionContext, ObjectTypeDefinition definition, OperationType operationType) diff --git a/src/HotChocolate/Core/src/Types/Configuration/AggregateTypeInterceptor.cs b/src/HotChocolate/Core/src/Types/Configuration/AggregateTypeInterceptor.cs index e87a50d12a7..91943e1ec38 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/AggregateTypeInterceptor.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/AggregateTypeInterceptor.cs @@ -270,7 +270,7 @@ public override void OnAfterCompleteName( } } - internal override void OnAfterResolveRootType( + public override void OnAfterResolveRootType( ITypeCompletionContext completionContext, ObjectTypeDefinition definition, OperationType operationType) diff --git a/src/HotChocolate/Core/src/Types/Configuration/Contracts/ITypeCompletionContext.cs b/src/HotChocolate/Core/src/Types/Configuration/Contracts/ITypeCompletionContext.cs index ba5382f63e9..fada86be637 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/Contracts/ITypeCompletionContext.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/Contracts/ITypeCompletionContext.cs @@ -73,6 +73,16 @@ public interface ITypeCompletionContext : ITypeSystemObjectContext /// IEnumerable GetTypes() where T : IType; + /// + /// Tries to resolve a directive type by its . + /// + /// + /// The directive reference representing the directive. + /// + /// + /// The resolved directive type. + /// + /// bool TryGetDirectiveType( TypeReference directiveRef, [NotNullWhen(true)] out DirectiveType? directiveType); diff --git a/src/HotChocolate/Core/src/Types/Configuration/OnAfterSchemaCreate.cs b/src/HotChocolate/Core/src/Types/Configuration/OnAfterSchemaCreate.cs index 6bed68ffe9d..4d9eed6f202 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/OnAfterSchemaCreate.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/OnAfterSchemaCreate.cs @@ -2,6 +2,9 @@ namespace HotChocolate.Configuration; +/// +/// Represents a callback that is invoked after a schema has been created. +/// public delegate void OnAfterSchemaCreate( IDescriptorContext descriptorContext, ISchema schema); diff --git a/src/HotChocolate/Core/src/Types/Configuration/OnBeforeSchemaCreate.cs b/src/HotChocolate/Core/src/Types/Configuration/OnBeforeSchemaCreate.cs index 1b9e4922357..217ec588350 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/OnBeforeSchemaCreate.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/OnBeforeSchemaCreate.cs @@ -2,6 +2,9 @@ namespace HotChocolate.Configuration; +/// +/// Represents a callback that is invoked before a schema is created. +/// public delegate void OnBeforeSchemaCreate( IDescriptorContext descriptorContext, ISchemaBuilder schemaBuilder); diff --git a/src/HotChocolate/Core/src/Types/Configuration/OnCompleteType.cs b/src/HotChocolate/Core/src/Types/Configuration/OnCompleteType.cs index 6d48686e604..68a98666139 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/OnCompleteType.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/OnCompleteType.cs @@ -4,6 +4,9 @@ namespace HotChocolate.Configuration; +/// +/// Represents a callback that is invoked when a type has been completed. +/// public delegate void OnCompleteType( ITypeCompletionContext context, DefinitionBase? definition, diff --git a/src/HotChocolate/Core/src/Types/Configuration/OnCompleteType~1.cs b/src/HotChocolate/Core/src/Types/Configuration/OnCompleteType~1.cs index 1e0c459b9ed..dd27700b82a 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/OnCompleteType~1.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/OnCompleteType~1.cs @@ -4,6 +4,10 @@ namespace HotChocolate.Configuration; +/// +/// Represents a callback that is invoked when a type has been completed. +/// +/// public delegate void OnCompleteType( ITypeCompletionContext context, T? definition, diff --git a/src/HotChocolate/Core/src/Types/Configuration/OnInitializeType.cs b/src/HotChocolate/Core/src/Types/Configuration/OnInitializeType.cs index 0a2ad8ef53b..78f01be0e1e 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/OnInitializeType.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/OnInitializeType.cs @@ -4,6 +4,9 @@ namespace HotChocolate.Configuration; +/// +/// Represents a callback that is invoked when a type has been completed. +/// public delegate void OnInitializeType( ITypeDiscoveryContext context, DefinitionBase? definition, diff --git a/src/HotChocolate/Core/src/Types/Configuration/OnInitializeType~1.cs b/src/HotChocolate/Core/src/Types/Configuration/OnInitializeType~1.cs index 9dc14b12132..565a10b914d 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/OnInitializeType~1.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/OnInitializeType~1.cs @@ -4,6 +4,10 @@ namespace HotChocolate.Configuration; +/// +/// Represents a callback that is invoked when a type has been completed. +/// +/// public delegate void OnInitializeType( ITypeDiscoveryContext context, T? definition, diff --git a/src/HotChocolate/Core/src/Types/Configuration/OnSchemaError.cs b/src/HotChocolate/Core/src/Types/Configuration/OnSchemaError.cs index f46a12add36..557e7406754 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/OnSchemaError.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/OnSchemaError.cs @@ -2,6 +2,9 @@ namespace HotChocolate.Configuration; +/// +/// Represents a callback that is invoked when an error occurs during schema creation. +/// public delegate void OnSchemaError( IDescriptorContext descriptorContext, Exception exception); diff --git a/src/HotChocolate/Core/src/Types/Configuration/TypeInterceptor.cs b/src/HotChocolate/Core/src/Types/Configuration/TypeInterceptor.cs index 73dd17003aa..5b2d812414a 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/TypeInterceptor.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/TypeInterceptor.cs @@ -124,6 +124,9 @@ public virtual void OnTypeRegistered( { } + /// + /// This event is called after all types are initialized. + /// public virtual void OnTypesInitialized() { } /// @@ -198,7 +201,22 @@ public virtual void OnAfterCompleteName( { } - internal virtual void OnAfterResolveRootType( + /// + /// This event is called after the root type is resolved. + /// + /// + /// The type completion context. + /// + /// + /// The type definition of the type system member. + /// + /// + /// Specifies what kind of operation type is resolved. + /// + #if NET8_0_OR_GREATER + [Experimental(Experiments.RootTypeResolved)] + #endif + public virtual void OnAfterResolveRootType( ITypeCompletionContext completionContext, ObjectTypeDefinition definition, OperationType operationType) diff --git a/src/HotChocolate/Core/src/Types/Experiments.cs b/src/HotChocolate/Core/src/Types/Experiments.cs new file mode 100644 index 00000000000..058cf440968 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Experiments.cs @@ -0,0 +1,6 @@ +namespace HotChocolate; + +internal static class Experiments +{ + public const string RootTypeResolved = "HC8001"; +} diff --git a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj index 891ab9a7e22..590548d5b75 100644 --- a/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj +++ b/src/HotChocolate/Core/src/Types/HotChocolate.Types.csproj @@ -3,6 +3,7 @@ true disable + HC8001;$(NoWarn) diff --git a/src/HotChocolate/Core/src/Types/Types/Introspection/IntrospectionTypeInterceptor.cs b/src/HotChocolate/Core/src/Types/Types/Introspection/IntrospectionTypeInterceptor.cs index 53551d866d6..581bef7ca49 100644 --- a/src/HotChocolate/Core/src/Types/Types/Introspection/IntrospectionTypeInterceptor.cs +++ b/src/HotChocolate/Core/src/Types/Types/Introspection/IntrospectionTypeInterceptor.cs @@ -36,7 +36,7 @@ public override void OnAfterCompleteName( } } - internal override void OnAfterResolveRootType( + public override void OnAfterResolveRootType( ITypeCompletionContext completionContext, ObjectTypeDefinition definition, OperationType operationType) diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/NodeFieldTypeInterceptor.cs b/src/HotChocolate/Core/src/Types/Types/Relay/NodeFieldTypeInterceptor.cs index a0089813286..cf76c1e4b1a 100644 --- a/src/HotChocolate/Core/src/Types/Types/Relay/NodeFieldTypeInterceptor.cs +++ b/src/HotChocolate/Core/src/Types/Types/Relay/NodeFieldTypeInterceptor.cs @@ -22,7 +22,7 @@ internal sealed class NodeFieldTypeInterceptor : TypeInterceptor internal override uint Position => uint.MaxValue - 100; - internal override void OnAfterResolveRootType( + public override void OnAfterResolveRootType( ITypeCompletionContext completionContext, ObjectTypeDefinition definition, OperationType operationType) diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/NodeResolverTypeInterceptor.cs b/src/HotChocolate/Core/src/Types/Types/Relay/NodeResolverTypeInterceptor.cs index d91e6144ac1..b6866d7af4a 100644 --- a/src/HotChocolate/Core/src/Types/Types/Relay/NodeResolverTypeInterceptor.cs +++ b/src/HotChocolate/Core/src/Types/Types/Relay/NodeResolverTypeInterceptor.cs @@ -38,7 +38,7 @@ private bool IsInitialized TypeDef is not null && CompletionContext is not null; - internal override void OnAfterResolveRootType( + public override void OnAfterResolveRootType( ITypeCompletionContext completionContext, ObjectTypeDefinition definition, OperationType operationType) diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/QueryFieldTypeInterceptor.cs b/src/HotChocolate/Core/src/Types/Types/Relay/QueryFieldTypeInterceptor.cs index 2e3c1a3cc34..06ed1bf89a4 100644 --- a/src/HotChocolate/Core/src/Types/Types/Relay/QueryFieldTypeInterceptor.cs +++ b/src/HotChocolate/Core/src/Types/Types/Relay/QueryFieldTypeInterceptor.cs @@ -19,7 +19,7 @@ internal sealed class QueryFieldTypeInterceptor : TypeInterceptor private ObjectFieldDefinition _queryField = default!; private ObjectTypeDefinition? _mutationDefinition; - internal override void OnAfterResolveRootType( + public override void OnAfterResolveRootType( ITypeCompletionContext completionContext, ObjectTypeDefinition definition, OperationType operationType) diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs index 188625d68c7..5ee139ce35e 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs @@ -6,6 +6,10 @@ using HotChocolate.Execution.Processing; using HotChocolate.Execution.TestContext; using HotChocolate.Types; +<<<<<<< Updated upstream +======= +using HotChocolate.Types.Relay; +>>>>>>> Stashed changes using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Squadron; diff --git a/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj b/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj index c02c63a5dfb..d185e44b091 100644 --- a/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj +++ b/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj @@ -5,6 +5,7 @@ HotChocolate.Data HotChocolate.Data Contains ready to use extensions for data management in HotChocolate. This includes filtering, projections and sorting + HC8001;$(NoWarn) diff --git a/src/HotChocolate/Data/src/Data/Projections/ProjectionTypeInterceptor.cs b/src/HotChocolate/Data/src/Data/Projections/ProjectionTypeInterceptor.cs index df62805084e..fb0303515a3 100644 --- a/src/HotChocolate/Data/src/Data/Projections/ProjectionTypeInterceptor.cs +++ b/src/HotChocolate/Data/src/Data/Projections/ProjectionTypeInterceptor.cs @@ -11,7 +11,7 @@ internal sealed class ProjectionTypeInterceptor : TypeInterceptor { private ITypeCompletionContext? _queryContext; - internal override void OnAfterResolveRootType( + public override void OnAfterResolveRootType( ITypeCompletionContext completionContext, ObjectTypeDefinition definition, OperationType operationType) From f72f11755d6dce03bb060badf6fa7a4fda1acada Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Thu, 3 Oct 2024 11:28:11 +0200 Subject: [PATCH 041/154] Fixed issue when projection on node fields. (#7551) --- ...otChocolateExecutionSelectionExtensions.cs | 36 ++++++++++++++ .../Projections/SelectionExpressionBuilder.cs | 47 +++++++++++++++++-- .../Projections/ProjectableDataLoaderTests.cs | 4 -- ...taLoaderTests.Brand_With_Name_Over_Node.md | 24 ++++++++++ 4 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Over_Node.md diff --git a/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs b/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs index fb61b730ef6..dddb8f06d9b 100644 --- a/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs +++ b/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs @@ -77,6 +77,11 @@ public static Expression> AsSelector( return GetOrCreateExpression(selection, builder); } + if ((flags & FieldFlags.NodesField) == FieldFlags.NodesField) + { + return GetOrCreateNodeExpression(selection); + } + return GetOrCreateExpression(selection); } @@ -98,6 +103,13 @@ private static Expression> GetOrCreateExpression( static (_, ctx) => ctx.builder.TryCompile()!, (builder, selection)); + private static Expression> GetOrCreateNodeExpression( + ISelection selection) + => selection.DeclaringOperation.GetOrAddState( + CreateNodeExpressionKey(selection.Id), + static (_, ctx) => ctx._builder.BuildNodeExpression(ctx.selection), + (_builder, selection)); + private static bool TryGetExpression( ISelection selection, [NotNullWhen(true)] out Expression>? expression) @@ -175,6 +187,30 @@ private static string CreateExpressionKey(int key) return Encoding.UTF8.GetString(span.Slice(0, written + keyPrefix.Length)); } + private static string CreateNodeExpressionKey(int key) + { + var typeName = typeof(TValue).FullName!; + var typeNameLength = Encoding.UTF8.GetMaxByteCount(typeName.Length); + var keyPrefix = GetKeyPrefix(); + var requiredBufferSize = EstimateIntLength(key) + keyPrefix.Length + typeNameLength; + byte[]? rented = null; + var span = requiredBufferSize <= 256 + ? stackalloc byte[requiredBufferSize] + : (rented = ArrayPool.Shared.Rent(requiredBufferSize)); + + keyPrefix.CopyTo(span); + Utf8Formatter.TryFormat(key, span.Slice(keyPrefix.Length), out var written, 'D'); + var typeNameWritten = Encoding.UTF8.GetBytes(typeName, span.Slice(written + keyPrefix.Length)); + var keyString = Encoding.UTF8.GetString(span.Slice(0, written + keyPrefix.Length + typeNameWritten)); + + if (rented is not null) + { + ArrayPool.Shared.Return(rented); + } + + return keyString; + } + private static ReadOnlySpan GetKeyPrefix() => "hc-dataloader-expr-"u8; diff --git a/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs b/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs index ec42e730d23..fa1e95ef012 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs @@ -32,6 +32,44 @@ public Expression> BuildExpression(ISelection selectio return Expression.Lambda>(selectionSetExpression, parameter); } + public Expression> BuildNodeExpression(ISelection selection) + { + var rootType = typeof(TRoot); + var parameter = Expression.Parameter(rootType, "root"); + var requirements = selection.DeclaringOperation.Schema.Features.GetRequired(); + var context = new Context(parameter, rootType, requirements); + var root = new TypeContainer(); + + var entityType = selection.DeclaringOperation.GetPossibleTypes(selection).FirstOrDefault(t => t.RuntimeType == typeof(TRoot)); + + if (entityType is null) + { + throw new InvalidOperationException( + $"Unable to resolve the entity type from `{typeof(TRoot).FullName}`."); + } + + var typeNode = new TypeNode(entityType.RuntimeType); + var selectionSet = selection.DeclaringOperation.GetSelectionSet(selection, entityType); + CollectSelections(context, selectionSet, typeNode); + root.TryAddNode(typeNode); + + if (typeNode.Nodes.Count == 0) + { + TryAddAnyLeafField(selection, typeNode); + } + + CollectTypes(context, selection, root); + + var selectionSetExpression = BuildTypeSwitchExpression(context, root); + + if (selectionSetExpression is null) + { + throw new InvalidOperationException("The selection set is empty."); + } + + return Expression.Lambda>(selectionSetExpression, parameter); + } + private void CollectTypes(Context context, ISelection selection, TypeContainer parent) { var namedType = selection.Type.NamedType(); @@ -50,11 +88,12 @@ private void CollectTypes(Context context, ISelection selection, TypeContainer p CollectSelections(context, possibleSelectionSet, possibleTypeNode); parent.TryAddNode(possibleTypeNode); - if(possibleTypeNode.Nodes.Count == 0) + if (possibleTypeNode.Nodes.Count == 0) { TryAddAnyLeafField(selection, possibleTypeNode); } } + return; } @@ -64,7 +103,7 @@ private void CollectTypes(Context context, ISelection selection, TypeContainer p CollectSelections(context, selectionSet, typeNode); parent.TryAddNode(typeNode); - if(typeNode.Nodes.Count == 0) + if (typeNode.Nodes.Count == 0) { TryAddAnyLeafField(selection, typeNode); } @@ -81,7 +120,7 @@ private void CollectTypes(Context context, ISelection selection, TypeContainer p foreach (var typeNode in parent.Nodes) { var newParent = Expression.Convert(context.Parent, typeNode.Type); - var newContext = context with { Parent = newParent, ParentType = typeNode.Type }; + var newContext = context with { Parent = newParent, ParentType = typeNode.Type }; var typeCondition = Expression.TypeIs(context.Parent, typeNode.Type); var selectionSet = BuildSelectionSetExpression(newContext, typeNode); @@ -226,7 +265,7 @@ private void CollectSelections( return Expression.Bind(node.Property, propertyAccessor); } - if(node.IsArrayOrCollection) + if (node.IsArrayOrCollection) { throw new NotSupportedException("List projections are not supported."); } diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs index 5ee139ce35e..188625d68c7 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs @@ -6,10 +6,6 @@ using HotChocolate.Execution.Processing; using HotChocolate.Execution.TestContext; using HotChocolate.Types; -<<<<<<< Updated upstream -======= -using HotChocolate.Types.Relay; ->>>>>>> Stashed changes using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Squadron; diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Over_Node.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Over_Node.md new file mode 100644 index 00000000000..79426c6ad06 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Over_Node.md @@ -0,0 +1,24 @@ +# Brand_With_Name_Over_Node + +## SQL + +```text +-- @__keys_0={ '1' } (DbType = Object) +SELECT b."Id", b."Name" +FROM "Brands" AS b +WHERE b."Id" = ANY (@__keys_0) +``` + +## Result + +```json +{ + "data": { + "node": { + "id": "QnJhbmQ6MQ==", + "name": "Brand0" + } + } +} +``` + From f2a93cb79b692dc4a5b735c1d2695560ae6efc14 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Thu, 3 Oct 2024 15:45:02 +0200 Subject: [PATCH 042/154] Fixed infer arguments when using lookup. (#7552) --- .../src/Composition/FusionGraphComposer.cs | 1 + .../Enrichers/LookupEntityEnricher.cs | 62 ++++++++++++ .../Composition.Tests/DemoIntegrationTests.cs | 94 +++++++++++++++++-- ...er_Field_Is_Fully_Specified_Lookup.graphql | 24 +++++ ..._Field__Lookup_Infers_Is_Directive.graphql | 24 +++++ .../test/Core.Tests/RequestPlannerTests.cs | 16 ++-- 6 files changed, 206 insertions(+), 15 deletions(-) create mode 100644 src/HotChocolate/Fusion/src/Composition/Pipeline/Enrichers/LookupEntityEnricher.cs create mode 100644 src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/DemoIntegrationTests.User_Field_Is_Fully_Specified_Lookup.graphql create mode 100644 src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/DemoIntegrationTests.User_Field__Lookup_Infers_Is_Directive.graphql diff --git a/src/HotChocolate/Fusion/src/Composition/FusionGraphComposer.cs b/src/HotChocolate/Fusion/src/Composition/FusionGraphComposer.cs index b7afe7a5833..535b6e3e0c9 100644 --- a/src/HotChocolate/Fusion/src/Composition/FusionGraphComposer.cs +++ b/src/HotChocolate/Fusion/src/Composition/FusionGraphComposer.cs @@ -33,6 +33,7 @@ public FusionGraphComposer( Func? logFactory = null) : this( [ + new LookupEntityEnricher(), new RefResolverEntityEnricher(), new PatternEntityEnricher(), new RequireEnricher(), diff --git a/src/HotChocolate/Fusion/src/Composition/Pipeline/Enrichers/LookupEntityEnricher.cs b/src/HotChocolate/Fusion/src/Composition/Pipeline/Enrichers/LookupEntityEnricher.cs new file mode 100644 index 00000000000..8f2346667e9 --- /dev/null +++ b/src/HotChocolate/Fusion/src/Composition/Pipeline/Enrichers/LookupEntityEnricher.cs @@ -0,0 +1,62 @@ +using System.Runtime.CompilerServices; +using HotChocolate.Language; +using HotChocolate.Skimmed; +using HotChocolate.Types; + +namespace HotChocolate.Fusion.Composition.Pipeline; + +/// +/// A pipeline enricher that processes entity groups and adds entity resolvers to +/// metadata for all arguments that contain the @ref directive. +/// +internal sealed class LookupEntityEnricher : IEntityEnricher +{ + /// + public ValueTask EnrichAsync( + CompositionContext context, + EntityGroup entity, + CancellationToken cancellationToken = default) + { + foreach (var (type, schema) in entity.Parts) + { + // Check if the schema has a query type + if (schema.QueryType is null) + { + continue; + } + + if (!schema.DirectiveDefinitions.TryGetDirective("is", out var isDirective)) + { + if(!schema.Types.TryGetType(BuiltIns.String.Name, out var stringType)) + { + stringType = BuiltIns.String.Create(); + schema.Types.Add(stringType); + } + + isDirective = new DirectiveDefinition("is"); + isDirective.Arguments.Add(new InputFieldDefinition("field", stringType)); + schema.DirectiveDefinitions.Add(isDirective); + } + + // Loop through each query field + foreach (var entityResolverField in schema.QueryType.Fields) + { + if (entityResolverField.Directives.ContainsName("lookup")) + { + foreach (var argument in entityResolverField.Arguments) + { + if (!argument.ContainsIsDirective()) + { + argument.Directives.Add( + new Directive( + isDirective, + new ArgumentAssignment("field", argument.Name))); + } + } + } + } + } + + return default; + } +} diff --git a/src/HotChocolate/Fusion/test/Composition.Tests/DemoIntegrationTests.cs b/src/HotChocolate/Fusion/test/Composition.Tests/DemoIntegrationTests.cs index 42183f7f5f9..dd15057c9d6 100644 --- a/src/HotChocolate/Fusion/test/Composition.Tests/DemoIntegrationTests.cs +++ b/src/HotChocolate/Fusion/test/Composition.Tests/DemoIntegrationTests.cs @@ -20,7 +20,7 @@ public async Task Accounts_And_Reviews() var fusionConfig = await composer.ComposeAsync( [ demoProject.Accounts.ToConfiguration(AccountsExtensionSdl), - demoProject.Reviews.ToConfiguration(ReviewsExtensionSdl) + demoProject.Reviews.ToConfiguration(ReviewsExtensionSdl) ]); fusionConfig.MatchSnapshot(extension: ".graphql"); @@ -37,7 +37,7 @@ public async Task Accounts_And_Reviews_Infer_Patterns() var fusionConfig = await composer.ComposeAsync( [ demoProject.Accounts.ToConfiguration(), - demoProject.Reviews.ToConfiguration(ReviewsExtensionSdl) + demoProject.Reviews.ToConfiguration(ReviewsExtensionSdl) ]); fusionConfig.MatchSnapshot(extension: ".graphql"); @@ -54,8 +54,8 @@ public async Task Accounts_And_Reviews_Products() var fusionConfig = await composer.ComposeAsync( [ demoProject.Accounts.ToConfiguration(AccountsExtensionSdl), - demoProject.Reviews.ToConfiguration(ReviewsExtensionSdl), - demoProject.Products.ToConfiguration(ProductsExtensionSdl) + demoProject.Reviews.ToConfiguration(ReviewsExtensionSdl), + demoProject.Products.ToConfiguration(ProductsExtensionSdl) ]); fusionConfig.MatchSnapshot(extension: ".graphql"); @@ -110,8 +110,8 @@ public async Task Accounts_And_Reviews_Products_AutoCompose_With_Node() var fusionConfig = await composer.ComposeAsync( [ demoProject.Accounts.ToConfiguration(), - demoProject.Reviews.ToConfiguration(), - demoProject.Products.ToConfiguration() + demoProject.Reviews.ToConfiguration(), + demoProject.Products.ToConfiguration() ]); fusionConfig.MatchSnapshot(extension: ".graphql"); @@ -135,4 +135,86 @@ public async Task Compose_With_SourceSchema_Lib() fusionConfig.MatchSnapshot(extension: ".graphql"); } + + [Fact] + public async Task User_Field_Is_Fully_Specified_Lookup() + { + // arrange + using var demoProject = await DemoProject.CreateAsync(); + + var composer = new FusionGraphComposer(logFactory: _logFactory); + + var fusionConfig = await composer.ComposeAsync( + [ + new SubgraphConfiguration( + "Schema1", + """ + schema { + query: Query + } + + type Query { + user(id: Int! @is(field: "id")): User @lookup + } + + type User { + id: Int! + name: String! + email: String! + password: String! + } + + """, + Array.Empty(), + [ + new HttpClientConfiguration( + new Uri("http://localhost:5000/graphql"), + "Schema1") + ], + default) + ]); + + fusionConfig.MatchSnapshot(extension: ".graphql"); + } + + [Fact] + public async Task User_Field__Lookup_Infers_Is_Directive() + { + // arrange + using var demoProject = await DemoProject.CreateAsync(); + + var composer = new FusionGraphComposer(logFactory: _logFactory); + + var fusionConfig = await composer.ComposeAsync( + [ + new SubgraphConfiguration( + "Schema1", + """ + schema { + query: Query + } + + type Query { + user(id: Int!): User @lookup + } + + type User { + id: Int! + name: String! + email: String! + password: String! + } + + """, + Array.Empty(), + [ + new HttpClientConfiguration( + new Uri("http://localhost:5000/graphql"), + "Schema1") + ], + default) + ]); + + fusionConfig.MatchSnapshot(extension: ".graphql"); + } } diff --git a/src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/DemoIntegrationTests.User_Field_Is_Fully_Specified_Lookup.graphql b/src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/DemoIntegrationTests.User_Field_Is_Fully_Specified_Lookup.graphql new file mode 100644 index 00000000000..f8ec92cbb59 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/DemoIntegrationTests.User_Field_Is_Fully_Specified_Lookup.graphql @@ -0,0 +1,24 @@ +schema + @fusion(version: 1) + @transport(subgraph: "Schema1", group: "Schema1", location: "http:\/\/localhost:5000\/graphql", kind: "HTTP") { + query: Query +} + +type Query { + user(id: Int!): User + @variable(subgraph: "Schema1", name: "id", argument: "id") + @resolver(subgraph: "Schema1", select: "{ user(id: $id) }", arguments: [ { name: "id", type: "Int!" } ]) +} + +type User + @variable(subgraph: "Schema1", name: "User_id", select: "id") + @resolver(subgraph: "Schema1", select: "{ user(id: $User_id) }", arguments: [ { name: "User_id", type: "Int!" } ]) { + email: String! + @source(subgraph: "Schema1") + id: Int! + @source(subgraph: "Schema1") + name: String! + @source(subgraph: "Schema1") + password: String! + @source(subgraph: "Schema1") +} diff --git a/src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/DemoIntegrationTests.User_Field__Lookup_Infers_Is_Directive.graphql b/src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/DemoIntegrationTests.User_Field__Lookup_Infers_Is_Directive.graphql new file mode 100644 index 00000000000..f8ec92cbb59 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/DemoIntegrationTests.User_Field__Lookup_Infers_Is_Directive.graphql @@ -0,0 +1,24 @@ +schema + @fusion(version: 1) + @transport(subgraph: "Schema1", group: "Schema1", location: "http:\/\/localhost:5000\/graphql", kind: "HTTP") { + query: Query +} + +type Query { + user(id: Int!): User + @variable(subgraph: "Schema1", name: "id", argument: "id") + @resolver(subgraph: "Schema1", select: "{ user(id: $id) }", arguments: [ { name: "id", type: "Int!" } ]) +} + +type User + @variable(subgraph: "Schema1", name: "User_id", select: "id") + @resolver(subgraph: "Schema1", select: "{ user(id: $User_id) }", arguments: [ { name: "User_id", type: "Int!" } ]) { + email: String! + @source(subgraph: "Schema1") + id: Int! + @source(subgraph: "Schema1") + name: String! + @source(subgraph: "Schema1") + password: String! + @source(subgraph: "Schema1") +} diff --git a/src/HotChocolate/Fusion/test/Core.Tests/RequestPlannerTests.cs b/src/HotChocolate/Fusion/test/Core.Tests/RequestPlannerTests.cs index c06423c27f9..eb894f47e35 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/RequestPlannerTests.cs +++ b/src/HotChocolate/Fusion/test/Core.Tests/RequestPlannerTests.cs @@ -1450,9 +1450,8 @@ public async Task Query_Plan_31_Argument_No_Value_Specified_With_Selection_Set() { // arrange var fusionGraph = await FusionGraphComposer.ComposeAsync( - new[] - { - new SubgraphConfiguration( + [ + new SubgraphConfiguration( "Test", """ type Query { @@ -1469,12 +1468,11 @@ enum TestEnum { } """, "", - new [] - { - new HttpClientConfiguration(new Uri("http://client"), "Test"), - }, - null), - }); + [ + new HttpClientConfiguration(new Uri("http://client"), "Test") + ], + null) + ]); // act var result = await CreateQueryPlanAsync( From b7b3910f9b7697dc7b68561305a7f446c2bc6011 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Thu, 3 Oct 2024 16:56:22 +0200 Subject: [PATCH 043/154] Fixed node field projections when nothing is selected. (#7554) --- .../Projections/SelectionExpressionBuilder.cs | 15 ++- .../Projections/ProjectableDataLoaderTests.cs | 114 ++++++++++++++++++ ...ests.Brand_With_Default_Field_Over_Node.md | 23 ++++ ...rTests.Brand_With_Id_And_Name_Over_Node.md | 24 ++++ ...taLoaderTests.Brand_With_Name_Over_Node.md | 3 +- 5 files changed, 169 insertions(+), 10 deletions(-) create mode 100644 src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Default_Field_Over_Node.md create mode 100644 src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Id_And_Name_Over_Node.md diff --git a/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs b/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs index fa1e95ef012..2b3f072f8cb 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs @@ -55,7 +55,7 @@ public Expression> BuildNodeExpression(ISelection sele if (typeNode.Nodes.Count == 0) { - TryAddAnyLeafField(selection, typeNode); + TryAddAnyLeafField(typeNode, entityType); } CollectTypes(context, selection, root); @@ -90,7 +90,7 @@ private void CollectTypes(Context context, ISelection selection, TypeContainer p if (possibleTypeNode.Nodes.Count == 0) { - TryAddAnyLeafField(selection, possibleTypeNode); + TryAddAnyLeafField(possibleTypeNode, possibleType); } } @@ -105,7 +105,7 @@ private void CollectTypes(Context context, ISelection selection, TypeContainer p if (typeNode.Nodes.Count == 0) { - TryAddAnyLeafField(selection, typeNode); + TryAddAnyLeafField(typeNode, objectType); } } @@ -201,22 +201,21 @@ private void CollectSelection( } private static void TryAddAnyLeafField( - ISelection selection, - TypeNode parent) + TypeNode parent, + IObjectType selectionType) { // if we could not collect anything it means that either all fields // are skipped or that __typename is the only field that is selected. // in this case we will try to select the id field or if that does // not exist we will look for a leaf field that we can select. - var type = (ObjectType)selection.Type.NamedType(); - if (type.Fields.TryGetField("id", out var idField) + if (selectionType.Fields.TryGetField("id", out var idField) && idField.Member is PropertyInfo idProperty) { parent.AddOrGetNode(idProperty); } else { - var anyProperty = type.Fields.FirstOrDefault(t => t.Type.IsLeafType() && t.Member is PropertyInfo); + var anyProperty = selectionType.Fields.FirstOrDefault(t => t.Type.IsLeafType() && t.Member is PropertyInfo); if (anyProperty?.Member is PropertyInfo anyPropertyInfo) { parent.AddOrGetNode(anyPropertyInfo); diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs index 188625d68c7..2b0e14a763b 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs @@ -6,6 +6,7 @@ using HotChocolate.Execution.Processing; using HotChocolate.Execution.TestContext; using HotChocolate.Types; +using HotChocolate.Types.Relay; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Squadron; @@ -664,6 +665,108 @@ public async Task Brand_Only_TypeName() .MatchMarkdownSnapshot(); } + + [Fact] + public async Task Brand_With_Id_And_Name_Over_Node() + { + // Arrange + var queries = new List(); + var connectionString = CreateConnectionString(); + await CatalogContext.SeedAsync(connectionString); + + // Act + var result = await new ServiceCollection() + .AddScoped(_ => queries) + .AddTransient(_ => new CatalogContext(connectionString)) + .AddGraphQL() + .AddQueryType() + .AddGlobalObjectIdentification() + .AddPagingArguments() + .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) + .ExecuteRequestAsync( + """ + { + node(id: "QnJhbmQ6MQ==") { + id + ... on Brand { + name + } + } + } + """); + + Snapshot.Create() + .AddSql(queries) + .AddResult(result) + .MatchMarkdownSnapshot(); + } + + [Fact] + public async Task Brand_With_Name_Over_Node() + { + // Arrange + var queries = new List(); + var connectionString = CreateConnectionString(); + await CatalogContext.SeedAsync(connectionString); + + // Act + var result = await new ServiceCollection() + .AddScoped(_ => queries) + .AddTransient(_ => new CatalogContext(connectionString)) + .AddGraphQL() + .AddQueryType() + .AddGlobalObjectIdentification() + .AddPagingArguments() + .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) + .ExecuteRequestAsync( + """ + { + node(id: "QnJhbmQ6MQ==") { + ... on Brand { + name + } + } + } + """); + + Snapshot.Create() + .AddSql(queries) + .AddResult(result) + .MatchMarkdownSnapshot(); + } + + [Fact] + public async Task Brand_With_Default_Field_Over_Node() + { + // Arrange + var queries = new List(); + var connectionString = CreateConnectionString(); + await CatalogContext.SeedAsync(connectionString); + + // Act + var result = await new ServiceCollection() + .AddScoped(_ => queries) + .AddTransient(_ => new CatalogContext(connectionString)) + .AddGraphQL() + .AddQueryType() + .AddGlobalObjectIdentification() + .AddPagingArguments() + .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) + .ExecuteRequestAsync( + """ + { + node(id: "QnJhbmQ6MQ==") { + __typename + } + } + """); + + Snapshot.Create() + .AddSql(queries) + .AddResult(result) + .MatchMarkdownSnapshot(); + } + public class Query { public async Task GetBrandByIdAsync( @@ -708,6 +811,17 @@ public class Query => await brandById.Select(default(Expression>)).LoadAsync(id, cancellationToken); } + public class NodeQuery + { + [NodeResolver] + public async Task GetBrandByIdAsync( + int id, + ISelection selection, + BrandByIdDataLoader brandById, + CancellationToken cancellationToken) + => await brandById.Select(selection).LoadAsync(id, cancellationToken); + } + [ExtendObjectType] public class BrandExtensions { diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Default_Field_Over_Node.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Default_Field_Over_Node.md new file mode 100644 index 00000000000..a533e21fac4 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Default_Field_Over_Node.md @@ -0,0 +1,23 @@ +# Brand_With_Default_Field_Over_Node + +## SQL + +```text +-- @__keys_0={ '1' } (DbType = Object) +SELECT b."Id" +FROM "Brands" AS b +WHERE b."Id" = ANY (@__keys_0) +``` + +## Result + +```json +{ + "data": { + "node": { + "__typename": "Brand" + } + } +} +``` + diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Id_And_Name_Over_Node.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Id_And_Name_Over_Node.md new file mode 100644 index 00000000000..aba267d352a --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Id_And_Name_Over_Node.md @@ -0,0 +1,24 @@ +# Brand_With_Id_And_Name_Over_Node + +## SQL + +```text +-- @__keys_0={ '1' } (DbType = Object) +SELECT b."Id", b."Name" +FROM "Brands" AS b +WHERE b."Id" = ANY (@__keys_0) +``` + +## Result + +```json +{ + "data": { + "node": { + "id": "QnJhbmQ6MQ==", + "name": "Brand0" + } + } +} +``` + diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Over_Node.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Over_Node.md index 79426c6ad06..b0130913f49 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Over_Node.md +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Over_Node.md @@ -4,7 +4,7 @@ ```text -- @__keys_0={ '1' } (DbType = Object) -SELECT b."Id", b."Name" +SELECT b."Name", b."Id" FROM "Brands" AS b WHERE b."Id" = ANY (@__keys_0) ``` @@ -15,7 +15,6 @@ WHERE b."Id" = ANY (@__keys_0) { "data": { "node": { - "id": "QnJhbmQ6MQ==", "name": "Brand0" } } From 7263689bad300c2b6649db703d129f212b9e3317 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Thu, 3 Oct 2024 17:52:52 +0200 Subject: [PATCH 044/154] Added flags for node and nodes field. (#7556) --- .../HotChocolateExecutionSelectionExtensions.cs | 3 ++- .../Execution/Projections/SelectionExpressionBuilder.cs | 2 -- .../Core/src/Types.CursorPagination/ConnectionType.cs | 8 ++++---- .../Types/Types/Descriptors/Definitions/FieldFlags.cs | 6 ++++-- .../src/Types/Types/Relay/NodeFieldTypeInterceptor.cs | 2 ++ .../Projections/ProjectableDataLoaderTests.cs | 9 +++++++++ 6 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs b/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs index dddb8f06d9b..841c448a24a 100644 --- a/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs +++ b/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs @@ -77,7 +77,8 @@ public static Expression> AsSelector( return GetOrCreateExpression(selection, builder); } - if ((flags & FieldFlags.NodesField) == FieldFlags.NodesField) + if ((flags & FieldFlags.GlobalIdNodeField) == FieldFlags.GlobalIdNodeField + || (flags & FieldFlags.GlobalIdNodeField) == FieldFlags.GlobalIdNodeField) { return GetOrCreateNodeExpression(selection); } diff --git a/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs b/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs index 2b3f072f8cb..8d85f8d996b 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs @@ -58,8 +58,6 @@ public Expression> BuildNodeExpression(ISelection sele TryAddAnyLeafField(typeNode, entityType); } - CollectTypes(context, selection, root); - var selectionSetExpression = BuildTypeSwitchExpression(context, root); if (selectionSetExpression is null) diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/ConnectionType.cs b/src/HotChocolate/Core/src/Types.CursorPagination/ConnectionType.cs index 625297ca8ae..42bdaa9c16a 100644 --- a/src/HotChocolate/Core/src/Types.CursorPagination/ConnectionType.cs +++ b/src/HotChocolate/Core/src/Types.CursorPagination/ConnectionType.cs @@ -180,14 +180,14 @@ private static ObjectTypeDefinition CreateTypeDefinition( ConnectionType_Edges_Description, edgesType, pureResolver: GetEdges) - { Flags = FieldFlags.EdgesField }); + { Flags = FieldFlags.ConnectionEdgesField }); if (includeNodesField) { definition.Fields.Add(new( Names.Nodes, ConnectionType_Nodes_Description, pureResolver: GetNodes) - { Flags = FieldFlags.NodesField }); + { Flags = FieldFlags.ConnectionNodesField }); } if (includeTotalCount) @@ -206,10 +206,10 @@ private static ObjectTypeDefinition CreateTypeDefinition( } private static bool IsEdgesField(ObjectFieldDefinition field) - => (field.Flags & FieldFlags.EdgesField) == FieldFlags.EdgesField; + => (field.Flags & FieldFlags.ConnectionEdgesField) == FieldFlags.ConnectionEdgesField; private static bool IsNodesField(ObjectFieldDefinition field) - => (field.Flags & FieldFlags.NodesField) == FieldFlags.NodesField; + => (field.Flags & FieldFlags.ConnectionNodesField) == FieldFlags.ConnectionNodesField; private static IPageInfo GetPagingInfo(IResolverContext context) => context.Parent().Info; diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/FieldFlags.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/FieldFlags.cs index 0235e46b860..2ba03aac4e7 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/FieldFlags.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/FieldFlags.cs @@ -23,9 +23,11 @@ internal enum FieldFlags TotalCount = 65536, SourceGenerator = 131072, MutationQueryField = 262144, - EdgesField = 524288, - NodesField = 1048576, + ConnectionEdgesField = 524288, + ConnectionNodesField = 1048576, ItemsField = 2097152, WithRequirements = 4194304, UsesProjections = 8388608, + GlobalIdNodeField = 16777216, + GlobalIdNodesField = 33554432, } diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/NodeFieldTypeInterceptor.cs b/src/HotChocolate/Core/src/Types/Types/Relay/NodeFieldTypeInterceptor.cs index cf76c1e4b1a..96f6b95e39d 100644 --- a/src/HotChocolate/Core/src/Types/Types/Relay/NodeFieldTypeInterceptor.cs +++ b/src/HotChocolate/Core/src/Types/Types/Relay/NodeFieldTypeInterceptor.cs @@ -94,6 +94,7 @@ private static void CreateNodeField( }; }), }, + Flags = FieldFlags.GlobalIdNodeField }; // In the projection interceptor we want to change the context data that is on this field @@ -133,6 +134,7 @@ private static void CreateNodesField( }; }), }, + Flags = FieldFlags.GlobalIdNodesField }; // In the projection interceptor we want to change the context data that is on this field diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs index 2b0e14a763b..cfd077dd99e 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs @@ -820,6 +820,15 @@ public class NodeQuery BrandByIdDataLoader brandById, CancellationToken cancellationToken) => await brandById.Select(selection).LoadAsync(id, cancellationToken); + + [NodeResolver] + public async Task GetProductByIdAsync( + int id, + CancellationToken cancellationToken) + { + await Task.Run(() => new InvalidOperationException(), cancellationToken); + return default!; + } } [ExtendObjectType] From e3ddba903459345b8b2f4eb0e5b1309fe210abcb Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Thu, 3 Oct 2024 22:35:59 +0200 Subject: [PATCH 045/154] Update Npgsql Packages --- src/Directory.Packages.props | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 57ed7ee2f19..bd5c3fc3255 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -40,7 +40,7 @@ - + @@ -83,8 +83,8 @@ - - + + @@ -107,8 +107,8 @@ - - + + @@ -130,8 +130,8 @@ - - + + From b5f9d95cfdef5ad0f3d2e5729c85b612b9de1753 Mon Sep 17 00:00:00 2001 From: PascalSenn Date: Thu, 3 Oct 2024 20:35:57 +0200 Subject: [PATCH 046/154] Fixed request context cleanup (#7558) --- src/HotChocolate/Core/src/Execution/RequestContext.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/HotChocolate/Core/src/Execution/RequestContext.cs b/src/HotChocolate/Core/src/Execution/RequestContext.cs index 41e3cec80f3..0b0c68b5823 100644 --- a/src/HotChocolate/Core/src/Execution/RequestContext.cs +++ b/src/HotChocolate/Core/src/Execution/RequestContext.cs @@ -127,6 +127,7 @@ public void Reset() DocumentId = default; DocumentHash = default; IsCachedDocument = false; + IsPersistedDocument = false; Document = default; ValidationResult = default; IsValidDocument = false; @@ -138,3 +139,4 @@ public void Reset() RequestIndex = default; } } + From 0fc48cae0e63d224b3fd1f86f529f55bd412ab35 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Thu, 3 Oct 2024 22:32:22 +0200 Subject: [PATCH 047/154] Reworked Persisted Operation Options (#7560) --- .../PersistedOperationTests.cs | 61 ++++++++++++++++--- ...Tests.Ensure_Pooled_Objects_Are_Cleared.md | 53 ++++++++++++++++ .../IPersistedOperationOptionsAccessor.cs | 17 +----- .../Options/PersistedOperationOptions.cs | 37 +++++++---- .../Options/RequestExecutorOptions.cs | 47 +++----------- ...nlyPersistedOperationsAllowedMiddleware.cs | 8 +-- .../ReadPersistedOperationMiddleware.cs | 7 +-- 7 files changed, 147 insertions(+), 83 deletions(-) create mode 100644 src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/PersistedOperationTests.Ensure_Pooled_Objects_Are_Cleared.md diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/PersistedOperationTests.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/PersistedOperationTests.cs index d36a992e978..28270b736f8 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/PersistedOperationTests.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/PersistedOperationTests.cs @@ -289,7 +289,7 @@ public async Task Standard_Query_Not_Allowed() var server = CreateStarWarsServer( configureServices: s => s .AddGraphQL("StarWars") - .ModifyRequestOptions(o => o.PersistedOperationOptions = OnlyPersistedOperations) + .ModifyRequestOptions(o => o.PersistedOperations.OnlyAllowPersistedDocuments = true) .ConfigureSchemaServices(c => c.AddSingleton(storage)) .UsePersistedOperationPipeline()); @@ -316,7 +316,7 @@ public async Task Standard_Query_Not_Allowed_Even_When_Persisted() var server = CreateStarWarsServer( configureServices: s => s .AddGraphQL("StarWars") - .ModifyRequestOptions(o => o.PersistedOperationOptions |= OnlyPersistedOperations) + .ModifyRequestOptions(o => o.PersistedOperations.OnlyAllowPersistedDocuments = true) .ConfigureSchemaServices(c => c.AddSingleton(storage)) .UsePersistedOperationPipeline()); @@ -344,9 +344,10 @@ public async Task Standard_Query_Allowed_When_Persisted() configureServices: s => s .AddGraphQL("StarWars") .ModifyRequestOptions(o => - o.PersistedOperationOptions = - OnlyPersistedOperations - | MatchStandardDocument) + { + o.PersistedOperations.OnlyAllowPersistedDocuments = true; + o.PersistedOperations.AllowDocumentBody = true; + }) .ConfigureSchemaServices(c => c.AddSingleton(storage)) .UsePersistedOperationPipeline()); @@ -372,8 +373,8 @@ public async Task Standard_Query_Not_Allowed_Custom_Error() .AddGraphQL("StarWars") .ModifyRequestOptions(o => { - o.PersistedOperationOptions = OnlyPersistedOperations; - o.OnlyPersistedOperationsAreAllowedError = + o.PersistedOperations.OnlyAllowPersistedDocuments = true; + o.PersistedOperations.OperationNotAllowedError = ErrorBuilder.New() .SetMessage("Not allowed!") .Build(); @@ -403,7 +404,7 @@ public async Task Standard_Query_Not_Allowed_Override_Per_Request() .AddGraphQL("StarWars") .ModifyRequestOptions(o => { - o.PersistedOperationOptions = OnlyPersistedOperations; + o.PersistedOperations.OnlyAllowPersistedDocuments = true; }) .ConfigureSchemaServices(c => c.AddSingleton(storage)) .UsePersistedOperationPipeline() @@ -420,6 +421,50 @@ public async Task Standard_Query_Not_Allowed_Override_Per_Request() result.MatchSnapshot(); } + [Fact] + public async Task Ensure_Pooled_Objects_Are_Cleared() + { + // arrange + // we have one operation in our storage that is allowed. + var storage = new OperationStorage(); + storage.AddOperation( + "a73defcdf38e5891e91b9ba532cf4c36", + "query GetHeroName { hero { name } }"); + + var server = CreateStarWarsServer( + configureServices: s => s + .AddGraphQL("StarWars") + .ModifyRequestOptions(o => + { + // we only allow persisted operations but we also allow standard requests + // as long as they match a persisted operation. + o.PersistedOperations.OnlyAllowPersistedDocuments = true; + o.PersistedOperations.AllowDocumentBody = true; + }) + .ConfigureSchemaServices(c => c.AddSingleton(storage)) + .UsePersistedOperationPipeline()); + + // act + var result1ShouldBeOk = await server.PostAsync( + new ClientQueryRequest { Id = "a73defcdf38e5891e91b9ba532cf4c36" }, + path: "/starwars"); + + var result2ShouldBeOk = await server.PostAsync( + new ClientQueryRequest { Query = "query GetHeroName { hero { name } }"}, + path: "/starwars"); + + var result3ShouldFail = await server.PostAsync( + new ClientQueryRequest { Query = "{ __typename }" }, + path: "/starwars"); + + // assert + await Snapshot.Create() + .Add(result1ShouldBeOk, "Result 1 - Should be OK") + .Add(result2ShouldBeOk, "Result 2 - Should be OK") + .Add(result3ShouldFail, "Result 3 - Should fail") + .MatchMarkdownAsync(); + } + private ClientQueryRequest CreateApolloStyleRequest(string hashName, string key) => new() { diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/PersistedOperationTests.Ensure_Pooled_Objects_Are_Cleared.md b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/PersistedOperationTests.Ensure_Pooled_Objects_Are_Cleared.md new file mode 100644 index 00000000000..a5484f775f7 --- /dev/null +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/PersistedOperationTests.Ensure_Pooled_Objects_Are_Cleared.md @@ -0,0 +1,53 @@ +# Ensure_Pooled_Objects_Are_Cleared + +## Result 1 - Should be OK + +```json +{ + "ContentType": "application/graphql-response+json; charset=utf-8", + "StatusCode": "OK", + "Data": { + "hero": { + "name": "R2-D2" + } + }, + "Errors": null, + "Extensions": null +} +``` + +## Result 2 - Should be OK + +```json +{ + "ContentType": "application/graphql-response+json; charset=utf-8", + "StatusCode": "OK", + "Data": { + "hero": { + "name": "R2-D2" + } + }, + "Errors": null, + "Extensions": null +} +``` + +## Result 3 - Should fail + +```json +{ + "ContentType": "application/graphql-response+json; charset=utf-8", + "StatusCode": "BadRequest", + "Data": null, + "Errors": [ + { + "message": "Only persisted operations are allowed.", + "extensions": { + "code": "HC0067" + } + } + ], + "Extensions": null +} +``` + diff --git a/src/HotChocolate/Core/src/Execution/Options/IPersistedOperationOptionsAccessor.cs b/src/HotChocolate/Core/src/Execution/Options/IPersistedOperationOptionsAccessor.cs index 5bea5202c2d..60cf1f55d81 100644 --- a/src/HotChocolate/Core/src/Execution/Options/IPersistedOperationOptionsAccessor.cs +++ b/src/HotChocolate/Core/src/Execution/Options/IPersistedOperationOptionsAccessor.cs @@ -6,20 +6,7 @@ namespace HotChocolate.Execution.Options; public interface IPersistedOperationOptionsAccessor { /// - /// Specifies if only persisted operations are allowed when using - /// the persisted operation pipeline. + /// Specifies the behavior of the persisted operation pipeline. /// - [Obsolete("Use PersistedOperationOptions instead.")] - bool OnlyAllowPersistedOperations { get; } - - /// - /// Specifies the behavior of the persisted operation middleware. - /// - PersistedOperationOptions PersistedOperationOptions { get; set; } - - /// - /// The error that will be thrown when only persisted - /// operations are allowed and a normal operation is issued. - /// - IError OnlyPersistedOperationsAreAllowedError { get; } + PersistedOperationOptions PersistedOperations { get; } } diff --git a/src/HotChocolate/Core/src/Execution/Options/PersistedOperationOptions.cs b/src/HotChocolate/Core/src/Execution/Options/PersistedOperationOptions.cs index 4d0ad6f6ebe..5698044195f 100644 --- a/src/HotChocolate/Core/src/Execution/Options/PersistedOperationOptions.cs +++ b/src/HotChocolate/Core/src/Execution/Options/PersistedOperationOptions.cs @@ -1,30 +1,41 @@ namespace HotChocolate.Execution.Options; /// -/// Represents the options to configure the -/// behavior of the persisted operation middleware. +/// Represents the persisted operation options. /// -[Flags] -public enum PersistedOperationOptions +public sealed class PersistedOperationOptions { + private IError _operationNotAllowedError = ErrorHelper.OnlyPersistedOperationsAreAllowed(); + /// - /// Nothing is configured. + /// Specifies if only persisted operation documents are allowed. /// - None = 0, + public bool OnlyAllowPersistedDocuments { get; set; } /// - /// Only persisted operations are allowed. + /// Specifies that if is switched on + /// whether a standard GraphQL request with document body is allowed as long as + /// it matches a persisted document. /// - OnlyPersistedOperations = 1, + public bool AllowDocumentBody { get; set; } /// - /// Allow standard GraphQL requests if the GraphQL document - /// match a persisted operation document. + /// Specifies if persisted operation documents + /// need to be validated. /// - MatchStandardDocument = 2, + public bool SkipPersistedDocumentValidation { get; set; } /// - /// Skip validation for persisted operations documents. + /// The error that will be thrown when only persisted + /// operations are allowed and a normal operation is issued. /// - SkipValidationForPersistedDocument = 4 + public IError OperationNotAllowedError + { + get => _operationNotAllowedError; + set + { + _operationNotAllowedError = value + ?? throw new ArgumentNullException(nameof(OperationNotAllowedError)); + } + } } diff --git a/src/HotChocolate/Core/src/Execution/Options/RequestExecutorOptions.cs b/src/HotChocolate/Core/src/Execution/Options/RequestExecutorOptions.cs index 618be1c011f..eb3ffc30c74 100644 --- a/src/HotChocolate/Core/src/Execution/Options/RequestExecutorOptions.cs +++ b/src/HotChocolate/Core/src/Execution/Options/RequestExecutorOptions.cs @@ -11,7 +11,7 @@ public class RequestExecutorOptions : IRequestExecutorOptionsAccessor { private static readonly TimeSpan _minExecutionTimeout = TimeSpan.FromMilliseconds(100); private TimeSpan _executionTimeout; - private IError _onlyPersistedOperationsAreAllowedError = ErrorHelper.OnlyPersistedOperationsAreAllowed(); + private PersistedOperationOptions _persistedOperations = new(); /// /// Initializes a new instance of . @@ -53,51 +53,20 @@ public TimeSpan ExecutionTimeout public bool IncludeExceptionDetails { get; set; } = Debugger.IsAttached; /// - /// - /// Specifies if only persisted operations are allowed when using - /// the persisted operation pipeline. - /// - /// The default is false. - /// - [Obsolete("Use PersistedOperationOptions instead.")] - public bool OnlyAllowPersistedOperations - { - get => (PersistedOperationOptions & OnlyPersistedOperations) == OnlyPersistedOperations; - set - { - if (value) - { - PersistedOperationOptions |= OnlyPersistedOperations; - } - else - { - PersistedOperationOptions &= ~OnlyPersistedOperations; - } - } - } - - /// - /// Specifies the behavior of the persisted operation middleware. + /// Specifies that the transport is allowed to provide the schema SDL document as a file. /// - public PersistedOperationOptions PersistedOperationOptions { get; set; } = None; + public bool EnableSchemaFileSupport { get; set; } = true; /// - /// The error that will be thrown when only persisted - /// operations are allowed and a normal operation is issued. + /// Specifies the behavior of the persisted operation pipeline. /// - public IError OnlyPersistedOperationsAreAllowedError + public PersistedOperationOptions PersistedOperations { - get => _onlyPersistedOperationsAreAllowedError; + get => _persistedOperations; set { - _onlyPersistedOperationsAreAllowedError = value - ?? throw new ArgumentNullException( - nameof(OnlyPersistedOperationsAreAllowedError)); + _persistedOperations = value + ?? throw new ArgumentNullException(nameof(PersistedOperations)); } } - - /// - /// Specifies that the transport is allowed to provide the schema SDL document as a file. - /// - public bool EnableSchemaFileSupport { get; set; } = true; } diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/OnlyPersistedOperationsAllowedMiddleware.cs b/src/HotChocolate/Core/src/Execution/Pipeline/OnlyPersistedOperationsAllowedMiddleware.cs index deaa1acc229..64508627949 100644 --- a/src/HotChocolate/Core/src/Execution/Pipeline/OnlyPersistedOperationsAllowedMiddleware.cs +++ b/src/HotChocolate/Core/src/Execution/Pipeline/OnlyPersistedOperationsAllowedMiddleware.cs @@ -30,8 +30,8 @@ private OnlyPersistedOperationsAllowedMiddleware( ?? throw new ArgumentNullException(nameof(diagnosticEvents)); // prepare options. - _options = options.PersistedOperationOptions; - var error = options.OnlyPersistedOperationsAreAllowedError; + _options = options.PersistedOperations; + var error = options.PersistedOperations.OperationNotAllowedError; _errorResult = OperationResultBuilder.CreateError(error, _statusCode); _exception = new GraphQLException(error); } @@ -39,7 +39,7 @@ private OnlyPersistedOperationsAllowedMiddleware( public ValueTask InvokeAsync(IRequestContext context) { // if all operations are allowed we can skip this middleware. - if((_options & OnlyPersistedOperations) != OnlyPersistedOperations) + if(!_options.OnlyAllowPersistedDocuments) { return _next(context); } @@ -51,7 +51,7 @@ public ValueTask InvokeAsync(IRequestContext context) // however this could still be a standard GraphQL request that contains a document // that just matches a persisted operation document. // either this is allowed by the configuration and we can skip this middleware - if ((_options & MatchStandardDocument) == MatchStandardDocument) + if (_options.AllowDocumentBody) { return _next(context); } diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/ReadPersistedOperationMiddleware.cs b/src/HotChocolate/Core/src/Execution/Pipeline/ReadPersistedOperationMiddleware.cs index c3459ae1848..03f55bf7769 100644 --- a/src/HotChocolate/Core/src/Execution/Pipeline/ReadPersistedOperationMiddleware.cs +++ b/src/HotChocolate/Core/src/Execution/Pipeline/ReadPersistedOperationMiddleware.cs @@ -3,7 +3,6 @@ using HotChocolate.Language; using HotChocolate.Validation; using Microsoft.Extensions.DependencyInjection; -using static HotChocolate.Execution.Options.PersistedOperationOptions; namespace HotChocolate.Execution.Pipeline; @@ -61,7 +60,7 @@ await _operationDocumentStorage.TryReadAsync( context.ValidationResult = DocumentValidatorResult.Ok; context.IsCachedDocument = true; context.IsPersistedDocument = true; - if ((_options & SkipValidationForPersistedDocument) == SkipValidationForPersistedDocument) + if (_options.SkipPersistedDocumentValidation) { context.ValidationResult = DocumentValidatorResult.Ok; } @@ -75,7 +74,7 @@ await _operationDocumentStorage.TryReadAsync( context.ValidationResult = DocumentValidatorResult.Ok; context.IsCachedDocument = true; context.IsPersistedDocument = true; - if ((_options & SkipValidationForPersistedDocument) == SkipValidationForPersistedDocument) + if (_options.SkipPersistedDocumentValidation) { context.ValidationResult = DocumentValidatorResult.Ok; } @@ -93,7 +92,7 @@ public static RequestCoreMiddleware Create() next, diagnosticEvents, persistedOperationStore, - core.Options.PersistedOperationOptions); + core.Options.PersistedOperations); return context => middleware.InvokeAsync(context); }; } From b7451ae8dfff6253b314206748e98936f4e3d1e3 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Sun, 6 Oct 2024 21:04:30 -0400 Subject: [PATCH 048/154] Fixed issue where the selector is not correctly set. (#7562) --- .editorconfig | 4 + .../src/Core/DataLoaderFetchContext.cs | 13 +- .../Projections/DefaultSelectorBuilder.cs | 37 +- .../src/Core/Projections/ExpressionHelpers.cs | 87 +- .../Projections/ProjectableDataLoaderTests.cs | 12 + ...ests.Brand_With_Default_Field_Over_Node.md | 3 +- ...and_With_Default_Field_Over_Node_NET7_0.md | 22 + ...rTests.Brand_With_Id_And_Name_Over_Node.md | 3 +- ...Brand_With_Id_And_Name_Over_Node_NET7_0.md | 23 + ...taLoaderTests.Brand_With_Name_Over_Node.md | 3 +- ...rTests.Brand_With_Name_Over_Node_NET7_0.md | 22 + ...ePaginationBatchingDataLoaderExtensions.cs | 4 +- .../Expressions/QueryHelpers.cs | 57 +- .../Extensions/PagingQueryableExtensions.cs | 41 +- .../PagingQueryInterceptor.cs | 40 + .../InterfaceIntegrationTests.cs | 195 ++- .../PagingHelperIntegrationTests.cs | 1181 ++++++++++------ .../TestContext/AnimalContext.cs | 1 + ...onPagingHelperTests.BatchPaging_First_5.md | 28 + ...gHelperTests.BatchPaging_First_5_NET7_0.md | 5 + ...gHelperTests.BatchPaging_First_5_NET9_0.md | 153 ++ ...ionPagingHelperTests.BatchPaging_Last_5.md | 28 + ...ngHelperTests.BatchPaging_Last_5_NET7_0.md | 5 + ...ngHelperTests.BatchPaging_Last_5_NET8_0.md | 5 + ...ngHelperTests.BatchPaging_Last_5_NET9_0.md | 153 ++ ....Ensure_Nullable_Connections_Dont_Throw.md | 22 +- ...nsure_Nullable_Connections_Dont_Throw_2.md | 22 +- ...ullable_Connections_Dont_Throw_2_NET7_0.md | 22 +- ...ullable_Connections_Dont_Throw_2_NET8_0.md | 65 + ...ullable_Connections_Dont_Throw_2_NET9_0.md | 65 + ..._Nullable_Connections_Dont_Throw_NET7_0.md | 22 +- ..._Nullable_Connections_Dont_Throw_NET8_0.md | 63 + ..._Nullable_Connections_Dont_Throw_NET9_0.md | 63 + ...grationPagingHelperTests.GetDefaultPage.md | 19 + ...rationPagingHelperTests.GetDefaultPage2.md | 19 + ...agingHelperTests.GetDefaultPage2_NET7_0.md | 77 + ...agingHelperTests.GetDefaultPage2_NET8_0.md | 77 + ...PagingHelperTests.GetDefaultPage_NET7_0.md | 77 + ...PagingHelperTests.GetDefaultPage_NET8_0.md | 77 + ...ingHelperTests.GetDefaultPage_With_Deep.md | 19 + ...erTests.GetDefaultPage_With_Deep_NET7_0.md | 169 +++ ...erTests.GetDefaultPage_With_Deep_NET8_0.md | 169 +++ ...sts.GetDefaultPage_With_Deep_SecondPage.md | 22 + ...DefaultPage_With_Deep_SecondPage_NET7_0.md | 68 + ...DefaultPage_With_Deep_SecondPage_NET8_0.md | 68 + ...elperTests.GetDefaultPage_With_Nullable.md | 19 + ...s.GetDefaultPage_With_Nullable_Fallback.md | 19 + ...faultPage_With_Nullable_Fallback_NET7_0.md | 169 +++ ...faultPage_With_Nullable_Fallback_NET8_0.md | 169 +++ ...tPage_With_Nullable_Fallback_SecondPage.md | 22 + ...ith_Nullable_Fallback_SecondPage_NET7_0.md | 68 + ...ith_Nullable_Fallback_SecondPage_NET8_0.md | 68 + ...sts.GetDefaultPage_With_Nullable_NET7_0.md | 169 +++ ...sts.GetDefaultPage_With_Nullable_NET8_0.md | 169 +++ ...GetDefaultPage_With_Nullable_SecondPage.md | 22 + ...ultPage_With_Nullable_SecondPage_NET7_0.md | 68 + ...ultPage_With_Nullable_SecondPage_NET8_0.md | 68 + ...gHelperTests.GetSecondPage_With_2_Items.md | 22 + ...Tests.GetSecondPage_With_2_Items_NET7_0.md | 48 + ...Tests.GetSecondPage_With_2_Items_NET8_0.md | 48 + ...erTests.Map_Page_To_Connection_With_Dto.md | 19 + ...Tests.Map_Page_To_Connection_With_Dto_2.md | 19 + ...ap_Page_To_Connection_With_Dto_2_NET7_0.md | 47 + ...ap_Page_To_Connection_With_Dto_2_NET8_0.md | 47 + ....Map_Page_To_Connection_With_Dto_NET7_0.md | 47 + ....Map_Page_To_Connection_With_Dto_NET8_0.md | 47 + ...PagingHelperTests.Nested_Paging_First_2.md | 48 +- ...elperTests.Nested_Paging_First_2_NET7_0.md | 103 ++ ...elperTests.Nested_Paging_First_2_NET8_0.md | 104 ++ ...elperTests.Nested_Paging_First_2_NET9_0.md | 103 ++ ....Nested_Paging_First_2_With_Projections.md | 48 +- ..._Paging_First_2_With_Projections_NET7_0.md | 103 ++ ..._Paging_First_2_With_Projections_NET8_0.md | 104 ++ ..._Paging_First_2_With_Projections_NET9_0.md | 103 ++ ...gingHelperTests.Paging_Empty_PagingArgs.md | 18 +- ...perTests.Paging_Empty_PagingArgs_NET7_0.md | 1236 +++++++++++++++++ ...perTests.Paging_Empty_PagingArgs_NET8_0.md | 1236 +++++++++++++++++ ...grationPagingHelperTests.Paging_First_5.md | 20 +- ...gHelperTests.Paging_First_5_After_Id_13.md | 23 +- ...Tests.Paging_First_5_After_Id_13_NET7_0.md | 101 ++ ...Tests.Paging_First_5_After_Id_13_NET8_0.md | 101 ++ ...HelperTests.Paging_First_5_Before_Id_96.md | 23 +- ...ests.Paging_First_5_Before_Id_96_NET7_0.md | 101 ++ ...ests.Paging_First_5_Before_Id_96_NET8_0.md | 101 ++ ...PagingHelperTests.Paging_First_5_NET7_0.md | 98 ++ ...PagingHelperTests.Paging_First_5_NET8_0.md | 98 ++ ...egrationPagingHelperTests.Paging_Last_5.md | 20 +- ...nPagingHelperTests.Paging_Last_5_NET7_0.md | 98 ++ ...nPagingHelperTests.Paging_Last_5_NET8_0.md | 98 ++ ...aceIntegrationTests.Query_Owner_Animals.md | 31 +- ...grationTests.Query_Owner_Animals_NET7_0.md | 163 +++ ...grationTests.Query_Owner_Animals_NET8_0.md | 164 +++ ...rationTests.Query_Owner_Animals_NET_9_0.md | 164 +++ ...ests.Query_Owner_Animals_With_Fragments.md | 31 +- ...ery_Owner_Animals_With_Fragments_NET7_0.md | 174 +++ ...ery_Owner_Animals_With_Fragments_NET8_0.md | 175 +++ ...ry_Owner_Animals_With_Fragments_NET_9_0.md | 175 +++ .../InterfaceIntegrationTests.Query_Pets.md | 71 + ...faceIntegrationTests.Query_Pets_NET_6_0.md | 71 + ...faceIntegrationTests.Query_Pets_NET_7_0.md | 71 + 100 files changed, 9457 insertions(+), 625 deletions(-) create mode 100644 src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Default_Field_Over_Node_NET7_0.md create mode 100644 src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Id_And_Name_Over_Node_NET7_0.md create mode 100644 src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Over_Node_NET7_0.md create mode 100644 src/HotChocolate/Pagination/src/Pagination.EntityFramework/PagingQueryInterceptor.cs create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_First_5_NET7_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_First_5_NET9_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5_NET7_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5_NET8_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5_NET9_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET8_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET9_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET8_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET9_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage2_NET7_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage2_NET8_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_NET7_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_NET8_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_NET7_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_NET8_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_SecondPage_NET7_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_SecondPage_NET8_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_NET7_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_NET8_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_SecondPage_NET7_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_SecondPage_NET8_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_NET7_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_NET8_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_SecondPage_NET7_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_SecondPage_NET8_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetSecondPage_With_2_Items_NET7_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetSecondPage_With_2_Items_NET8_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_2_NET7_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_2_NET8_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_NET7_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_NET8_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_NET7_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_NET8_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_NET9_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections_NET7_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections_NET8_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections_NET9_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Empty_PagingArgs_NET7_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Empty_PagingArgs_NET8_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_After_Id_13_NET7_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_After_Id_13_NET8_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_Before_Id_96_NET7_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_Before_Id_96_NET8_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_NET7_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_NET8_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Last_5_NET7_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Last_5_NET8_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_NET7_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_NET8_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_NET_9_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments_NET7_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments_NET8_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments_NET_9_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Pets.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Pets_NET_6_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Pets_NET_7_0.md diff --git a/.editorconfig b/.editorconfig index 2c1db1d27ec..4e504303688 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,6 +12,10 @@ rulers = 120 # ReSharper properties resharper_align_multiline_statement_conditions = false resharper_braces_for_ifelse = not_required +resharper_wrap_object_and_collection_initializer_style = chop_always + +# Microsoft .NET properties +csharp_new_line_before_members_in_object_initializers = true [*.md] trim_trailing_whitespace = false diff --git a/src/GreenDonut/src/Core/DataLoaderFetchContext.cs b/src/GreenDonut/src/Core/DataLoaderFetchContext.cs index 4a4535ecaae..9629960df32 100644 --- a/src/GreenDonut/src/Core/DataLoaderFetchContext.cs +++ b/src/GreenDonut/src/Core/DataLoaderFetchContext.cs @@ -151,18 +151,15 @@ public TState GetStateOrDefault(string key, TState defaultValue) #endif public ISelectorBuilder GetSelector() { - DefaultSelectorBuilder context; if (ContextData.TryGetValue(typeof(ISelectorBuilder).FullName!, out var value) - && value is DefaultSelectorBuilder casted) + && value is ISelectorBuilder casted) { - context = casted; - } - else - { - context = new DefaultSelectorBuilder(); + return casted; } - return context; + // if no selector was found we will just return + // a new default selector builder. + return new DefaultSelectorBuilder(); } #endif } diff --git a/src/GreenDonut/src/Core/Projections/DefaultSelectorBuilder.cs b/src/GreenDonut/src/Core/Projections/DefaultSelectorBuilder.cs index c6a217f6dda..b1989282a33 100644 --- a/src/GreenDonut/src/Core/Projections/DefaultSelectorBuilder.cs +++ b/src/GreenDonut/src/Core/Projections/DefaultSelectorBuilder.cs @@ -13,7 +13,7 @@ namespace GreenDonut.Projections; #endif public sealed class DefaultSelectorBuilder : ISelectorBuilder { - private LambdaExpression? _expression; + private List? _selectors; /// public void Add(Expression> selector) @@ -25,22 +25,17 @@ public void Add(Expression> selector) nameof(selector)); } - if (_expression is null) + _selectors ??= new List(); + if (!_selectors.Contains(selector)) { - _expression = selector; - } - else - { - _expression = ExpressionHelpers.Combine( - (Expression>)_expression, - selector); + _selectors.Add(selector); } } /// public Expression>? TryCompile() { - if (_expression is null) + if (_selectors is null) { return null; } @@ -50,7 +45,27 @@ public void Add(Expression> selector) return null; } - return (Expression>)_expression; + if (_selectors.Count == 1) + { + return (Expression>)_selectors[0]; + } + + if (_selectors.Count == 2) + { + return ExpressionHelpers.Combine( + (Expression>)_selectors[0], + (Expression>)_selectors[1]); + } + + var expression = (Expression>)_selectors[0]; + for (var i = 1; i < _selectors.Count; i++) + { + expression = ExpressionHelpers.Combine( + expression, + (Expression>)_selectors[i]); + } + + return expression; } } #endif diff --git a/src/GreenDonut/src/Core/Projections/ExpressionHelpers.cs b/src/GreenDonut/src/Core/Projections/ExpressionHelpers.cs index 61695a27fdd..23e9598c438 100644 --- a/src/GreenDonut/src/Core/Projections/ExpressionHelpers.cs +++ b/src/GreenDonut/src/Core/Projections/ExpressionHelpers.cs @@ -25,6 +25,11 @@ private static Expression ReplaceParameter( private static Expression CombineExpressions(Expression first, Expression second) { + if (first is UnaryExpression { NodeType: ExpressionType.Convert } firstUnary) + { + return CombineWithConvertExpression(firstUnary, second); + } + if (first is MemberInitExpression firstInit && second is MemberInitExpression secondInit) { return CombineMemberInitExpressions(firstInit, secondInit); @@ -49,6 +54,24 @@ private static Expression CombineExpressions(Expression first, Expression second return second; } + + private static Expression CombineWithConvertExpression(UnaryExpression first, Expression second) + { + if (second is MemberInitExpression otherMemberInit) + { + var combinedInit = CombineMemberInitExpressions((MemberInitExpression)first.Operand, otherMemberInit); + return Expression.Convert(combinedInit, first.Type); + } + + if (second is ConditionalExpression otherConditional) + { + var combinedCond = CombineConditionalExpressions((ConditionalExpression)first.Operand, otherConditional); + return Expression.Convert(combinedCond, first.Type); + } + + return Expression.Convert(second, first.Type); + } + private static MemberInitExpression CombineMemberInitExpressions( MemberInitExpression first, MemberInitExpression second) @@ -60,14 +83,69 @@ private static MemberInitExpression CombineMemberInitExpressions( bindings[binding.Member.Name] = binding; } - foreach (var binding in second.Bindings.Cast()) + var firstRootExpression = ExtractRootExpressionFromBindings(first.Bindings.Cast()); + var parameterToReplace = ExtractParameterExpression(firstRootExpression); + + if (firstRootExpression != null && parameterToReplace != null) { - bindings[binding.Member.Name] = binding; + var replacer = new RootExpressionReplacerVisitor(parameterToReplace, firstRootExpression); + + foreach (var binding in second.Bindings.Cast()) + { + var newBindingExpression = replacer.Visit(binding.Expression); + bindings[binding.Member.Name] = Expression.Bind(binding.Member, newBindingExpression); + } + } + else + { + foreach (var binding in second.Bindings.Cast()) + { + bindings[binding.Member.Name] = binding; + } } return Expression.MemberInit(first.NewExpression, bindings.Values); } + private static Expression? ExtractRootExpressionFromBindings( + IEnumerable bindings) + => bindings.FirstOrDefault()?.Expression is MemberExpression memberExpr + ? memberExpr.Expression + : null; + + private static ParameterExpression? ExtractParameterExpression(Expression? expression) + => expression switch + { + UnaryExpression { NodeType: ExpressionType.Convert } expr => ExtractParameterExpression(expr.Operand), + ParameterExpression paramExpr => paramExpr, + _ => null + }; + + private sealed class RootExpressionReplacerVisitor( + ParameterExpression parameterToReplace, + Expression replacementExpression) + : ExpressionVisitor + { + protected override Expression VisitParameter(ParameterExpression node) + { + if (node == parameterToReplace) + { + return replacementExpression; + } + return base.VisitParameter(node); + } + + protected override Expression VisitMember(MemberExpression node) + { + var expr = Visit(node.Expression); + if (expr != node.Expression) + { + return Expression.MakeMemberAccess(expr, node.Member); + } + return base.VisitMember(node); + } + } + private static ConditionalExpression CombineConditionalExpressions( ConditionalExpression first, ConditionalExpression second) @@ -83,7 +161,10 @@ private static Expression CombineConditionalAndMemberInit( MemberInitExpression memberInit) { var ifTrue = CombineExpressions(condition.IfTrue, memberInit); - var ifFalse = condition.IfFalse; + + var ifFalse = condition.IfFalse is ConstantExpression + ? condition.IfFalse + : CombineExpressions(condition.IfFalse, memberInit); return Expression.Condition(condition.Test, ifTrue, ifFalse); } diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs index cfd077dd99e..5c785890636 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs @@ -695,7 +695,11 @@ ... on Brand { } """); +#if NET7_0 + Snapshot.Create("NET7_0") +#else Snapshot.Create() +#endif .AddSql(queries) .AddResult(result) .MatchMarkdownSnapshot(); @@ -729,7 +733,11 @@ ... on Brand { } """); +#if NET7_0 + Snapshot.Create("NET7_0") +#else Snapshot.Create() +#endif .AddSql(queries) .AddResult(result) .MatchMarkdownSnapshot(); @@ -761,7 +769,11 @@ public async Task Brand_With_Default_Field_Over_Node() } """); +#if NET7_0 + Snapshot.Create("NET7_0") +#else Snapshot.Create() +#endif .AddSql(queries) .AddResult(result) .MatchMarkdownSnapshot(); diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Default_Field_Over_Node.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Default_Field_Over_Node.md index a533e21fac4..358196bf1aa 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Default_Field_Over_Node.md +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Default_Field_Over_Node.md @@ -3,10 +3,9 @@ ## SQL ```text --- @__keys_0={ '1' } (DbType = Object) SELECT b."Id" FROM "Brands" AS b -WHERE b."Id" = ANY (@__keys_0) +WHERE b."Id" = 1 ``` ## Result diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Default_Field_Over_Node_NET7_0.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Default_Field_Over_Node_NET7_0.md new file mode 100644 index 00000000000..358196bf1aa --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Default_Field_Over_Node_NET7_0.md @@ -0,0 +1,22 @@ +# Brand_With_Default_Field_Over_Node + +## SQL + +```text +SELECT b."Id" +FROM "Brands" AS b +WHERE b."Id" = 1 +``` + +## Result + +```json +{ + "data": { + "node": { + "__typename": "Brand" + } + } +} +``` + diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Id_And_Name_Over_Node.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Id_And_Name_Over_Node.md index aba267d352a..e501abd2fba 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Id_And_Name_Over_Node.md +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Id_And_Name_Over_Node.md @@ -3,10 +3,9 @@ ## SQL ```text --- @__keys_0={ '1' } (DbType = Object) SELECT b."Id", b."Name" FROM "Brands" AS b -WHERE b."Id" = ANY (@__keys_0) +WHERE b."Id" = 1 ``` ## Result diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Id_And_Name_Over_Node_NET7_0.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Id_And_Name_Over_Node_NET7_0.md new file mode 100644 index 00000000000..e501abd2fba --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Id_And_Name_Over_Node_NET7_0.md @@ -0,0 +1,23 @@ +# Brand_With_Id_And_Name_Over_Node + +## SQL + +```text +SELECT b."Id", b."Name" +FROM "Brands" AS b +WHERE b."Id" = 1 +``` + +## Result + +```json +{ + "data": { + "node": { + "id": "QnJhbmQ6MQ==", + "name": "Brand0" + } + } +} +``` + diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Over_Node.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Over_Node.md index b0130913f49..c9d15e2d389 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Over_Node.md +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Over_Node.md @@ -3,10 +3,9 @@ ## SQL ```text --- @__keys_0={ '1' } (DbType = Object) SELECT b."Name", b."Id" FROM "Brands" AS b -WHERE b."Id" = ANY (@__keys_0) +WHERE b."Id" = 1 ``` ## Result diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Over_Node_NET7_0.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Over_Node_NET7_0.md new file mode 100644 index 00000000000..c9d15e2d389 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Over_Node_NET7_0.md @@ -0,0 +1,22 @@ +# Brand_With_Name_Over_Node + +## SQL + +```text +SELECT b."Name", b."Id" +FROM "Brands" AS b +WHERE b."Id" = 1 +``` + +## Result + +```json +{ + "data": { + "node": { + "name": "Brand0" + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs b/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs index 5664ede5891..0c94ed32f08 100644 --- a/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs +++ b/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs @@ -101,7 +101,9 @@ public static IPagingDataLoader> Select( return dataLoader; } - var builder = dataLoader.GetOrSetState(_ => new DefaultSelectorBuilder()); + var builder = dataLoader.GetOrSetState( + typeof(ISelectorBuilder).FullName!, + _ => new DefaultSelectorBuilder()); builder.Add(selector); return dataLoader; } diff --git a/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Expressions/QueryHelpers.cs b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Expressions/QueryHelpers.cs index ae6680628f4..34df17f5505 100644 --- a/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Expressions/QueryHelpers.cs +++ b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Expressions/QueryHelpers.cs @@ -36,24 +36,9 @@ private static Expression> AddPropertiesInSelector( List properties) { var parameter = selector.Parameters[0]; - var bindings = ((MemberInitExpression)selector.Body).Bindings.Cast().ToList(); - - foreach (var property in properties) - { - var propertyName = property.Member.Name; - if(property.Expression is not ParameterExpression parameterExpression - || bindings.Any(b => b.Member.Name == propertyName)) - { - continue; - } - - var replacer = new ReplacerParameterVisitor(parameterExpression, parameter); - var rewrittenProperty = (MemberExpression)replacer.Visit(property); - bindings.Add(Expression.Bind(rewrittenProperty.Member, rewrittenProperty)); - } - - var newBody = Expression.MemberInit(Expression.New(typeof(T)), bindings); - return Expression.Lambda>(newBody, parameter); + var visitor = new AddPropertiesVisitorRewriter(properties, parameter); + var updatedBody = visitor.Visit(selector.Body); + return Expression.Lambda>(updatedBody, parameter); } private static List ExtractOrderProperties( @@ -72,4 +57,40 @@ private static IQueryable ReplaceSelector( var newExpression = visitor.Visit(query.Expression); return query.Provider.CreateQuery(newExpression); } + + public class AddPropertiesVisitorRewriter : ExpressionVisitor + { + private readonly List _propertiesToAdd; + private readonly ParameterExpression _parameter; + + public AddPropertiesVisitorRewriter( + List propertiesToAdd, + ParameterExpression parameter) + { + _propertiesToAdd = propertiesToAdd; + _parameter = parameter; + } + + protected override Expression VisitMemberInit(MemberInitExpression node) + { + // Get existing bindings (properties in the current selector) + var existingBindings = node.Bindings.Cast().ToList(); + + // Add the properties that are not already present in the bindings + foreach (var property in _propertiesToAdd) + { + var propertyName = property.Member.Name; + if (property.Expression is ParameterExpression parameterExpression + && existingBindings.All(b => b.Member.Name != propertyName)) + { + var replacer = new ReplacerParameterVisitor(parameterExpression, _parameter); + var rewrittenProperty = (MemberExpression)replacer.Visit(property); + existingBindings.Add(Expression.Bind(rewrittenProperty.Member, rewrittenProperty)); + } + } + + // Create new MemberInitExpression with updated bindings + return Expression.MemberInit(node.NewExpression, existingBindings); + } + } } diff --git a/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs index 9fa20a89a70..4622b1ce8b2 100644 --- a/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs +++ b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs @@ -10,6 +10,8 @@ namespace HotChocolate.Pagination; /// public static class PagingQueryableExtensions { + private static readonly AsyncLocal _interceptor = new(); + /// /// Executes a query with paging and returns the selected page. /// @@ -126,6 +128,8 @@ public static async ValueTask> ToPageAsync( { var combinedQuery = source.Select(t => new { TotalCount = originalQuery.Count(), Item = t }); + TryGetQueryInterceptor()?.OnBeforeExecute(combinedQuery); + await foreach (var item in combinedQuery.AsAsyncEnumerable() .WithCancellation(cancellationToken).ConfigureAwait(false)) { @@ -142,6 +146,8 @@ public static async ValueTask> ToPageAsync( } else { + TryGetQueryInterceptor()?.OnBeforeExecute(source); + await foreach (var item in source.AsAsyncEnumerable() .WithCancellation(cancellationToken).ConfigureAwait(false)) { @@ -239,6 +245,8 @@ public static async ValueTask>> ToBatchPageAsync(ordering.Expression); + TryGetQueryInterceptor()?.OnBeforeExecute(source.GroupBy(keySelector).Select(selectExpression)); + await foreach (var item in source .GroupBy(keySelector) .Select(selectExpression) @@ -305,7 +313,12 @@ private static Page CreatePage( hasNext = true; } - return new Page(items, hasNext, hasPrevious, item => CursorFormatter.Format(item, keys), totalCount); + return new Page( + items, + hasNext, + hasPrevious, + item => CursorFormatter.Format(item, keys), + totalCount); } private static CursorKey[] ParseDataSetKeys(IQueryable source) @@ -314,4 +327,30 @@ private static CursorKey[] ParseDataSetKeys(IQueryable source) parser.Visit(source.Expression); return parser.Keys.ToArray(); } + + private sealed class InterceptorHolder + { + public PagingQueryInterceptor? Interceptor { get; set; } + } + + private static PagingQueryInterceptor? TryGetQueryInterceptor() + => _interceptor.Value?.Interceptor; + + internal static void SetQueryInterceptor(PagingQueryInterceptor pagingQueryInterceptor) + { + if (_interceptor.Value is null) + { + _interceptor.Value = new InterceptorHolder(); + } + + _interceptor.Value.Interceptor = pagingQueryInterceptor; + } + + internal static void ClearQueryInterceptor(PagingQueryInterceptor pagingQueryInterceptor) + { + if (_interceptor.Value is not null) + { + _interceptor.Value.Interceptor = null; + } + } } diff --git a/src/HotChocolate/Pagination/src/Pagination.EntityFramework/PagingQueryInterceptor.cs b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/PagingQueryInterceptor.cs new file mode 100644 index 00000000000..266a798f569 --- /dev/null +++ b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/PagingQueryInterceptor.cs @@ -0,0 +1,40 @@ +namespace HotChocolate.Pagination; + +/// +/// This interceptor allows to capture paging queries for analysis. +/// +public abstract class PagingQueryInterceptor : IDisposable +{ + private bool _disposed; + + /// + /// Initializes a new instance of the class. + /// + protected PagingQueryInterceptor() + { + PagingQueryableExtensions.SetQueryInterceptor(this); + } + + /// + /// This method is called before the query is executed and allows to intercept it. + /// + /// + /// The query that is about to be executed. + /// + /// + /// The type of the items in the query. + /// + public abstract void OnBeforeExecute(IQueryable query); + + /// + /// The dispose call will remove the interceptor from the current scope. + /// + public void Dispose() + { + if (!_disposed) + { + PagingQueryableExtensions.ClearQueryInterceptor(this); + _disposed = true; + } + } +} diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/InterfaceIntegrationTests.cs b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/InterfaceIntegrationTests.cs index 7d21165d9a4..4c5e69206e2 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/InterfaceIntegrationTests.cs +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/InterfaceIntegrationTests.cs @@ -30,6 +30,7 @@ public async Task Query_Owner_Animals() await SeedAsync(connectionString); var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); var result = await new ServiceCollection() .AddScoped(_ => new AnimalContext(connectionString)) @@ -61,15 +62,16 @@ public async Task Query_Owner_Animals() } } """) - .AddQueries(queries) .Build()); var operationResult = result.ExpectOperationResult(); -#if NET6_0 - await Snapshot.Create("NET_6_0") +#if NET9_0_OR_GREATER + await Snapshot.Create("NET9_0") +#elif NET8_0 + await Snapshot.Create("NET8_0") #elif NET7_0 - await Snapshot.Create("NET_7_0") + await Snapshot.Create("NET7_0") #else await Snapshot.Create() #endif @@ -85,6 +87,7 @@ public async Task Query_Owner_Animals_With_Fragments() await SeedAsync(connectionString); var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); var result = await new ServiceCollection() .AddScoped(_ => new AnimalContext(connectionString)) @@ -122,7 +125,56 @@ ... on Cat { } } """) - .AddQueries(queries) + .Build()); + + var operationResult = result.ExpectOperationResult(); + +#if NET9_0_OR_GREATER + await Snapshot.Create("NET9_0") +#elif NET8_0 + await Snapshot.Create("NET8_0") +#elif NET7_0 + await Snapshot.Create("NET7_0") +#else + await Snapshot.Create() +#endif + .AddQueries(queries) + .Add(operationResult.WithExtensions(ImmutableDictionary.Empty)) + .MatchMarkdownAsync(); + } + + [Fact] + public async Task Query_Pets() + { + var connectionString = CreateConnectionString(); + await SeedAsync(connectionString); + + var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); + + var result = await new ServiceCollection() + .AddScoped(_ => new AnimalContext(connectionString)) + .AddGraphQL() + .AddQueryType() + .AddTypeExtension(typeof(OwnerExtensions)) + .AddDataLoader() + .AddObjectType() + .AddObjectType() + .AddPagingArguments() + .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) + .ExecuteRequestAsync( + OperationRequestBuilder.New() + .SetDocument( + """ + { + pets(first: 10) { + nodes { + id + name + } + } + } + """) .Build()); var operationResult = result.ExpectOperationResult(); @@ -152,8 +204,16 @@ private static async Task SeedAsync(string connectionString) Pets = [ new Cat { Name = "Cat 1" }, - new Dog { Name = "Dog 1", IsBarking = true }, - new Dog { Name = "Dog 2", IsBarking = false } + new Dog + { + Name = "Dog 1", + IsBarking = true + }, + new Dog + { + Name = "Dog 2", + IsBarking = false + } ] }, new Owner @@ -162,8 +222,16 @@ private static async Task SeedAsync(string connectionString) Pets = [ new Cat { Name = "Cat 2" }, - new Dog { Name = "Dog 3", IsBarking = true }, - new Dog { Name = "Dog 4", IsBarking = false } + new Dog + { + Name = "Dog 3", + IsBarking = true + }, + new Dog + { + Name = "Dog 4", + IsBarking = false + } ] }, new Owner @@ -171,15 +239,24 @@ private static async Task SeedAsync(string connectionString) Name = "Owner 3", Pets = [ - new Cat { Name = "Cat 3 (Not Pure)", IsPurring = true }, - new Dog { Name = "Dog 5", IsBarking = true }, - new Dog { Name = "Dog 6", IsBarking = false } + new Cat + { + Name = "Cat 3 (Not Pure)", + IsPurring = true + }, + new Dog + { + Name = "Dog 5", + IsBarking = true + }, + new Dog + { + Name = "Dog 6", + IsBarking = false + } ] }, - new Owner - { - Name = "Owner 4 - No Pets" - }, + new Owner { Name = "Owner 4 - No Pets" }, new Owner { Name = "Owner 5 - Only Cat", @@ -188,7 +265,14 @@ private static async Task SeedAsync(string connectionString) new Owner { Name = "Owner 6 - Only Dog", - Pets = [new Dog { Name = "Only Dog", IsBarking = true }] + Pets = + [ + new Dog + { + Name = "Only Dog", + IsBarking = true + } + ] } }; @@ -204,17 +288,27 @@ public async Task> GetOwnersAsync( AnimalContext context, ISelection selection, IResolverContext resolverContext, - [GlobalState] List queries, CancellationToken cancellationToken) - { - return await context.Owners + => await context.Owners + .OrderBy(t => t.Name) + .ThenBy(t => t.Id) .Select(selection.AsSelector()) + .ToPageAsync(pagingArgs, cancellationToken) + .ToConnectionAsync(); + + [UsePaging] + public async Task> GetPetsAsync( + PagingArguments pagingArgs, + AnimalContext context, + ISelection selection, + IResolverContext resolverContext, + CancellationToken cancellationToken) + => await context.Pets .OrderBy(t => t.Name) .ThenBy(t => t.Id) - .Capture(queries) + .Select(selection.AsSelector()) .ToPageAsync(pagingArgs, cancellationToken) .ToConnectionAsync(); - } } [ExtendObjectType] @@ -227,16 +321,12 @@ public static async Task> GetPetsAsync( PagingArguments pagingArgs, AnimalsByOwnerDataLoader animalsByOwner, ISelection selection, - [GlobalState] List queries, CancellationToken cancellationToken) - { - return await animalsByOwner + => await animalsByOwner .WithPagingArguments(pagingArgs) .Select(selection) - .SetState(queries) .LoadAsync(owner.Id, cancellationToken) .ToConnectionAsync(); - } } public sealed class AnimalsByOwnerDataLoader @@ -259,7 +349,7 @@ protected override async Task>> LoadBatchA CancellationToken cancellationToken) { var pagingArgs = context.GetPagingArguments(); - var selector = context.GetSelector(); + // var selector = context.GetSelector(); await using var scope = _services.CreateAsyncScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); @@ -269,8 +359,8 @@ protected override async Task>> LoadBatchA .SelectMany(t => t.Pets) .OrderBy(t => t.Name) .ThenBy(t => t.Id) - .Select(selector, t => t.OwnerId) - .Capture(context.GetQueries()) + // selections do not work when inheritance is used for nested batching. + // .Select(selector, t => t.OwnerId) .ToBatchPageAsync( t => t.OwnerId, pagingArgs, @@ -281,28 +371,6 @@ protected override async Task>> LoadBatchA file static class Extensions { - public static IQueryable Capture( - this IQueryable query, - List queryInfos) - { - queryInfos.Add( - new QueryInfo - { - QueryText = query.ToQueryString(), - ExpressionText = query.Expression.ToString() - }); - return query; - } - - public static List GetQueries( - this DataLoaderFetchContext> context) - => context.GetRequiredState>(); - - public static OperationRequestBuilder AddQueries( - this OperationRequestBuilder builder, - List queries) - => builder.SetGlobalState("queries", queries); - public static Snapshot AddQueries( this Snapshot snapshot, List queries) @@ -317,3 +385,26 @@ public static Snapshot AddQueries( return snapshot; } } + +file sealed class CapturePagingQueryInterceptor(List queries) : PagingQueryInterceptor +{ + public override void OnBeforeExecute(IQueryable query) + { + string queryText; + try + { + queryText = query.ToQueryString(); + } + catch (Exception ex) + { + queryText = ex.Message; + } + + queries.Add( + new QueryInfo + { + ExpressionText = query.Expression.ToString(), + QueryText = queryText + }); + } +} diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs index 9976533eb8c..3d2f3a32f0d 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs @@ -29,6 +29,8 @@ public async Task GetDefaultPage() // Arrange var connectionString = CreateConnectionString(); await SeedAsync(connectionString); + var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); // Act var result = await new ServiceCollection() @@ -37,25 +39,39 @@ public async Task GetDefaultPage() .AddQueryType() .AddPagingArguments() .ExecuteRequestAsync( - """ - { - brands { - nodes { - id - name - } - pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor + OperationRequestBuilder.New() + .SetDocument( + """ + { + brands { + nodes { + id + name + } + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + } } - } - } - """); + """) + .Build()); // Assert - result.MatchMarkdownSnapshot(); +#if NET9_0_OR_GREATER + await Snapshot.Create("NET9_0") +#elif NET8_0 + await Snapshot.Create("NET8_0") +#elif NET7_0 + await Snapshot.Create("NET7_0") +#else + await Snapshot.Create() +#endif + .AddQueries(queries) + .Add(result, "Result") + .MatchMarkdownAsync(); } [Fact] @@ -64,6 +80,8 @@ public async Task GetDefaultPage2() // Arrange var connectionString = CreateConnectionString(); await SeedAsync(connectionString); + var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); // Act var result = await new ServiceCollection() @@ -72,25 +90,39 @@ public async Task GetDefaultPage2() .AddQueryType() .AddPagingArguments() .ExecuteRequestAsync( - """ - { - brands2 { - nodes { - id - name - } - pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor + OperationRequestBuilder.New() + .SetDocument( + """ + { + brands2 { + nodes { + id + name + } + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + } } - } - } - """); + """) + .Build()); // Assert - result.MatchMarkdownSnapshot(); +#if NET9_0_OR_GREATER + await Snapshot.Create("NET9_0") +#elif NET8_0 + await Snapshot.Create("NET8_0") +#elif NET7_0 + await Snapshot.Create("NET7_0") +#else + await Snapshot.Create() +#endif + .AddQueries(queries) + .Add(result, "Result") + .MatchMarkdownAsync(); } [Fact] @@ -99,6 +131,8 @@ public async Task GetSecondPage_With_2_Items() // Arrange var connectionString = CreateConnectionString(); await SeedAsync(connectionString); + var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); // Act var result = await new ServiceCollection() @@ -107,25 +141,39 @@ public async Task GetSecondPage_With_2_Items() .AddQueryType() .AddPagingArguments() .ExecuteRequestAsync( - """ - { - brands(first: 2, after: "QnJhbmQxNzoxOA==") { - nodes { - id - name - } - pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor + OperationRequestBuilder.New() + .SetDocument( + """ + { + brands(first: 2, after: "QnJhbmQxNzoxOA==") { + nodes { + id + name + } + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + } } - } - } - """); + """) + .Build()); // Assert - result.MatchMarkdownSnapshot(); +#if NET9_0_OR_GREATER + await Snapshot.Create("NET9_0") +#elif NET8_0 + await Snapshot.Create("NET8_0") +#elif NET7_0 + await Snapshot.Create("NET7_0") +#else + await Snapshot.Create() +#endif + .AddQueries(queries) + .Add(result, "Result") + .MatchMarkdownAsync(); } [Fact] @@ -134,6 +182,8 @@ public async Task GetDefaultPage_With_Nullable() // Arrange var connectionString = CreateConnectionString(); await SeedAsync(connectionString); + var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); // Act var result = await new ServiceCollection() @@ -142,34 +192,48 @@ public async Task GetDefaultPage_With_Nullable() .AddQueryType() .AddPagingArguments() .ExecuteRequestAsync( - """ - { - brandsNullable { - edges { - cursor - } - nodes { - id - name - displayName - brandDetails { - country { + OperationRequestBuilder.New() + .SetDocument( + """ + { + brandsNullable { + edges { + cursor + } + nodes { + id name + displayName + brandDetails { + country { + name + } + } + } + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor } } } - pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor - } - } - } - """); + """) + .Build()); // Assert - result.MatchMarkdownSnapshot(); +#if NET9_0_OR_GREATER + await Snapshot.Create("NET9_0") +#elif NET8_0 + await Snapshot.Create("NET8_0") +#elif NET7_0 + await Snapshot.Create("NET7_0") +#else + await Snapshot.Create() +#endif + .AddQueries(queries) + .Add(result, "Result") + .MatchMarkdownAsync(); } [Fact] @@ -178,6 +242,8 @@ public async Task GetDefaultPage_With_Nullable_SecondPage() // Arrange var connectionString = CreateConnectionString(); await SeedAsync(connectionString); + var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); // Act var result = await new ServiceCollection() @@ -186,34 +252,48 @@ public async Task GetDefaultPage_With_Nullable_SecondPage() .AddQueryType() .AddPagingArguments() .ExecuteRequestAsync( - """ - { - brandsNullable(first: 2, after: "QnJhbmQxMDpcbnVsbDoxMQ==") { - edges { - cursor - } - nodes { - id - name - displayName - brandDetails { - country { + OperationRequestBuilder.New() + .SetDocument( + """ + { + brandsNullable(first: 2, after: "QnJhbmQxMDpcbnVsbDoxMQ==") { + edges { + cursor + } + nodes { + id name + displayName + brandDetails { + country { + name + } + } + } + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor } } } - pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor - } - } - } - """); + """) + .Build()); // Assert - result.MatchMarkdownSnapshot(); +#if NET9_0_OR_GREATER + await Snapshot.Create("NET9_0") +#elif NET8_0 + await Snapshot.Create("NET8_0") +#elif NET7_0 + await Snapshot.Create("NET7_0") +#else + await Snapshot.Create() +#endif + .AddQueries(queries) + .Add(result, "Result") + .MatchMarkdownAsync(); } [Fact] @@ -222,6 +302,8 @@ public async Task GetDefaultPage_With_Nullable_Fallback() // Arrange var connectionString = CreateConnectionString(); await SeedAsync(connectionString); + var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); // Act var result = await new ServiceCollection() @@ -230,34 +312,48 @@ public async Task GetDefaultPage_With_Nullable_Fallback() .AddQueryType() .AddPagingArguments() .ExecuteRequestAsync( - """ - { - brandsNullableFallback { - edges { - cursor - } - nodes { - id - name - displayName - brandDetails { - country { + OperationRequestBuilder.New() + .SetDocument( + """ + { + brandsNullableFallback { + edges { + cursor + } + nodes { + id name + displayName + brandDetails { + country { + name + } + } + } + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor } } } - pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor - } - } - } - """); + """) + .Build()); // Assert - result.MatchMarkdownSnapshot(); +#if NET9_0_OR_GREATER + await Snapshot.Create("NET9_0") +#elif NET8_0 + await Snapshot.Create("NET8_0") +#elif NET7_0 + await Snapshot.Create("NET7_0") +#else + await Snapshot.Create() +#endif + .AddQueries(queries) + .Add(result, "Result") + .MatchMarkdownAsync(); } [Fact] @@ -266,6 +362,8 @@ public async Task GetDefaultPage_With_Nullable_Fallback_SecondPage() // Arrange var connectionString = CreateConnectionString(); await SeedAsync(connectionString); + var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); // Act var result = await new ServiceCollection() @@ -274,34 +372,48 @@ public async Task GetDefaultPage_With_Nullable_Fallback_SecondPage() .AddQueryType() .AddPagingArguments() .ExecuteRequestAsync( - """ - { - brandsNullableFallback(first: 2, after: "QnJhbmQxMToxMg==") { - edges { - cursor - } - nodes { - id - name - displayName - brandDetails { - country { + OperationRequestBuilder.New() + .SetDocument( + """ + { + brandsNullableFallback(first: 2, after: "QnJhbmQxMToxMg==") { + edges { + cursor + } + nodes { + id name + displayName + brandDetails { + country { + name + } + } + } + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor } } } - pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor - } - } - } - """); + """) + .Build()); // Assert - result.MatchMarkdownSnapshot(); +#if NET9_0_OR_GREATER + await Snapshot.Create("NET9_0") +#elif NET8_0 + await Snapshot.Create("NET8_0") +#elif NET7_0 + await Snapshot.Create("NET7_0") +#else + await Snapshot.Create() +#endif + .AddQueries(queries) + .Add(result, "Result") + .MatchMarkdownAsync(); } [Fact] @@ -310,6 +422,8 @@ public async Task GetDefaultPage_With_Deep() // Arrange var connectionString = CreateConnectionString(); await SeedAsync(connectionString); + var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); // Act var result = await new ServiceCollection() @@ -318,34 +432,48 @@ public async Task GetDefaultPage_With_Deep() .AddQueryType() .AddPagingArguments() .ExecuteRequestAsync( - """ - { - brandsDeep { - edges { - cursor - } - nodes { - id - name - displayName - brandDetails { - country { + OperationRequestBuilder.New() + .SetDocument( + """ + { + brandsDeep { + edges { + cursor + } + nodes { + id name + displayName + brandDetails { + country { + name + } + } + } + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor } } } - pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor - } - } - } - """); + """) + .Build()); // Assert - result.MatchMarkdownSnapshot(); +#if NET9_0_OR_GREATER + await Snapshot.Create("NET9_0") +#elif NET8_0 + await Snapshot.Create("NET8_0") +#elif NET7_0 + await Snapshot.Create("NET7_0") +#else + await Snapshot.Create() +#endif + .AddQueries(queries) + .Add(result, "Result") + .MatchMarkdownAsync(); } [Fact] @@ -354,6 +482,8 @@ public async Task GetDefaultPage_With_Deep_SecondPage() // Arrange var connectionString = CreateConnectionString(); await SeedAsync(connectionString); + var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); // Act var result = await new ServiceCollection() @@ -362,34 +492,48 @@ public async Task GetDefaultPage_With_Deep_SecondPage() .AddQueryType() .AddPagingArguments() .ExecuteRequestAsync( - @" - { - brandsDeep(first: 2, after: ""Q291bnRyeTE6Mg=="") { - edges { - cursor - } - nodes { - id - name - displayName - brandDetails { - country { - name + OperationRequestBuilder.New() + .SetDocument( + """ + { + brandsDeep(first: 2, after: "Q291bnRyeTE6Mg==") { + edges { + cursor + } + nodes { + id + name + displayName + brandDetails { + country { + name + } } } - } - pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } } } - } - "); + """) + .Build()); // Assert - result.MatchMarkdownSnapshot(); +#if NET9_0_OR_GREATER + await Snapshot.Create("NET9_0") +#elif NET8_0 + await Snapshot.Create("NET8_0") +#elif NET7_0 + await Snapshot.Create("NET7_0") +#else + await Snapshot.Create() +#endif + .AddQueries(queries) + .Add(result, "Result") + .MatchMarkdownAsync(); } [Fact] @@ -398,6 +542,8 @@ public async Task Nested_Paging_First_2() // Arrange var connectionString = CreateConnectionString(); await SeedAsync(connectionString); + var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); // Act var result = await new ServiceCollection() @@ -408,41 +554,45 @@ public async Task Nested_Paging_First_2() .AddPagingArguments() .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) .ExecuteRequestAsync( - """ - { - brands(first: 2) { - edges { - cursor - } - nodes { - products(first: 2) { - nodes { - name + OperationRequestBuilder.New() + .SetDocument( + """ + { + brands(first: 2) { + edges { + cursor } - pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor + nodes { + products(first: 2) { + nodes { + name + } + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + } } } } - } - } - """); + """) + .Build()); // Assert var operationResult = result.ExpectOperationResult(); - var sql = operationResult.Extensions!["sql"]!.ToString(); -#if NET8_0_OR_GREATER - await Snapshot.Create() -#elif NET7_0_OR_GREATER - await Snapshot.Create("NET7") +#if NET9_0_OR_GREATER + await Snapshot.Create("NET9_0") +#elif NET8_0 + await Snapshot.Create("NET8_0") +#elif NET7_0 + await Snapshot.Create("NET7_0") #else - await Snapshot.Create("NET6") + await Snapshot.Create() #endif - .Add(sql, "SQL", "sql") + .AddQueries(queries) .Add(operationResult.WithExtensions(ImmutableDictionary.Empty)) .MatchMarkdownAsync(); } @@ -454,6 +604,9 @@ public async Task Nested_Paging_First_2_With_Projections() var connectionString = CreateConnectionString(); await SeedAsync(connectionString); + var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); + // Act var result = await new ServiceCollection() .AddScoped(_ => new CatalogContext(connectionString)) @@ -463,42 +616,46 @@ public async Task Nested_Paging_First_2_With_Projections() .AddPagingArguments() .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) .ExecuteRequestAsync( - """ - { - brands(first: 2) { - edges { - cursor - } - nodes { - products(first: 2) { - nodes { - name + OperationRequestBuilder.New() + .SetDocument( + """ + { + brands(first: 2) { + edges { + cursor } - pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor + nodes { + products(first: 2) { + nodes { + name + } + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + } } } } - } - } - """); + """) + .Build()); // Assert var operationResult = result.ExpectOperationResult(); - var sql = operationResult.Extensions!["sql"]!.ToString(); -#if NET8_0_OR_GREATER - await Snapshot.Create() -#elif NET7_0_OR_GREATER - await Snapshot.Create("NET7") +#if NET9_0_OR_GREATER + await Snapshot.Create("NET9_0") +#elif NET8_0 + await Snapshot.Create("NET8_0") +#elif NET7_0 + await Snapshot.Create("NET7_0") #else - await Snapshot.Create("NET6") + await Snapshot.Create() #endif - .Add(sql, "SQL", "sql") - .Add(operationResult.WithExtensions(ImmutableDictionary.Empty)) + .AddQueries(queries) + .Add(operationResult, "Result") .MatchMarkdownAsync(); } @@ -508,6 +665,8 @@ public async Task Paging_Empty_PagingArgs() // Arrange var connectionString = CreateConnectionString(); await SeedAsync(connectionString); + var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); // Act await using var context = new CatalogContext(connectionString); @@ -516,16 +675,26 @@ public async Task Paging_Empty_PagingArgs() var result = await context.Brands.OrderBy(t => t.Name).ThenBy(t => t.Id).ToPageAsync(pagingArgs); // Assert +#if NET9_0_OR_GREATER + await Snapshot.Create("NET9_0") +#elif NET8_0 + await Snapshot.Create("NET8_0") +#elif NET7_0 + await Snapshot.Create("NET7_0") +#else await Snapshot.Create() - .Add(new - { - result.HasNextPage, - result.HasPreviousPage, - First = result.First?.Id, - FirstCursor = result.First is not null ? result.CreateCursor(result.First) : null, - Last = result.Last?.Id, - LastCursor = result.Last is not null ? result.CreateCursor(result.Last) : null - }) +#endif + .AddQueries(queries) + .Add( + new + { + result.HasNextPage, + result.HasPreviousPage, + First = result.First?.Id, + FirstCursor = result.First is not null ? result.CreateCursor(result.First) : null, + Last = result.Last?.Id, + LastCursor = result.Last is not null ? result.CreateCursor(result.Last) : null + }) .Add(result.Items) .MatchMarkdownAsync(); } @@ -536,6 +705,8 @@ public async Task Paging_First_5() // Arrange var connectionString = CreateConnectionString(); await SeedAsync(connectionString); + var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); // Act await using var context = new CatalogContext(connectionString); @@ -544,16 +715,26 @@ public async Task Paging_First_5() var result = await context.Brands.OrderBy(t => t.Name).ThenBy(t => t.Id).ToPageAsync(pagingArgs); // Assert +#if NET9_0_OR_GREATER + await Snapshot.Create("NET9_0") +#elif NET8_0 + await Snapshot.Create("NET8_0") +#elif NET7_0 + await Snapshot.Create("NET7_0") +#else await Snapshot.Create() - .Add(new - { - result.HasNextPage, - result.HasPreviousPage, - First = result.First?.Id, - FirstCursor = result.First is not null ? result.CreateCursor(result.First) : null, - Last = result.Last?.Id, - LastCursor = result.Last is not null ? result.CreateCursor(result.Last) : null - }) +#endif + .AddQueries(queries) + .Add( + new + { + result.HasNextPage, + result.HasPreviousPage, + First = result.First?.Id, + FirstCursor = result.First is not null ? result.CreateCursor(result.First) : null, + Last = result.Last?.Id, + LastCursor = result.Last is not null ? result.CreateCursor(result.Last) : null + }) .Add(result.Items) .MatchMarkdownAsync(); } @@ -564,24 +745,40 @@ public async Task Paging_First_5_After_Id_13() // Arrange var connectionString = CreateConnectionString(); await SeedAsync(connectionString); + var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); // Act await using var context = new CatalogContext(connectionString); - var pagingArgs = new PagingArguments { First = 5, After = "QnJhbmQxMjoxMw==" }; + var pagingArgs = new PagingArguments + { + First = 5, + After = "QnJhbmQxMjoxMw==" + }; var result = await context.Brands.OrderBy(t => t.Name).ThenBy(t => t.Id).ToPageAsync(pagingArgs); // Assert +#if NET9_0_OR_GREATER + await Snapshot.Create("NET9_0") +#elif NET8_0 + await Snapshot.Create("NET8_0") +#elif NET7_0 + await Snapshot.Create("NET7_0") +#else await Snapshot.Create() - .Add(new - { - result.HasNextPage, - result.HasPreviousPage, - First = result.First?.Id, - FirstCursor = result.First is not null ? result.CreateCursor(result.First) : null, - Last = result.Last?.Id, - LastCursor = result.Last is not null ? result.CreateCursor(result.Last) : null - }) +#endif + .AddQueries(queries) + .Add( + new + { + result.HasNextPage, + result.HasPreviousPage, + First = result.First?.Id, + FirstCursor = result.First is not null ? result.CreateCursor(result.First) : null, + Last = result.Last?.Id, + LastCursor = result.Last is not null ? result.CreateCursor(result.Last) : null + }) .Add(result.Items) .MatchMarkdownAsync(); } @@ -592,6 +789,8 @@ public async Task Paging_Last_5() // Arrange var connectionString = CreateConnectionString(); await SeedAsync(connectionString); + var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); // Act await using var context = new CatalogContext(connectionString); @@ -600,16 +799,26 @@ public async Task Paging_Last_5() var result = await context.Brands.OrderBy(t => t.Name).ThenBy(t => t.Id).ToPageAsync(pagingArgs); // Assert +#if NET9_0_OR_GREATER + await Snapshot.Create("NET9_0") +#elif NET8_0 + await Snapshot.Create("NET8_0") +#elif NET7_0 + await Snapshot.Create("NET7_0") +#else await Snapshot.Create() - .Add(new - { - result.HasNextPage, - result.HasPreviousPage, - First = result.First?.Id, - FirstCursor = result.First is not null ? result.CreateCursor(result.First) : null, - Last = result.Last?.Id, - LastCursor = result.Last is not null ? result.CreateCursor(result.Last) : null - }) +#endif + .AddQueries(queries) + .Add( + new + { + result.HasNextPage, + result.HasPreviousPage, + First = result.First?.Id, + FirstCursor = result.First is not null ? result.CreateCursor(result.First) : null, + Last = result.Last?.Id, + LastCursor = result.Last is not null ? result.CreateCursor(result.Last) : null + }) .Add(result.Items) .MatchMarkdownAsync(); } @@ -620,24 +829,40 @@ public async Task Paging_First_5_Before_Id_96() // Arrange var connectionString = CreateConnectionString(); await SeedAsync(connectionString); + var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); // Act await using var context = new CatalogContext(connectionString); - var pagingArgs = new PagingArguments { Last = 5, Before = "QnJhbmQ5NTo5Ng==" }; + var pagingArgs = new PagingArguments + { + Last = 5, + Before = "QnJhbmQ5NTo5Ng==" + }; var result = await context.Brands.OrderBy(t => t.Name).ThenBy(t => t.Id).ToPageAsync(pagingArgs); // Assert +#if NET9_0_OR_GREATER + await Snapshot.Create("NET9_0") +#elif NET8_0 + await Snapshot.Create("NET8_0") +#elif NET7_0 + await Snapshot.Create("NET7_0") +#else await Snapshot.Create() - .Add(new - { - result.HasNextPage, - result.HasPreviousPage, - First = result.First?.Id, - FirstCursor = result.First is not null ? result.CreateCursor(result.First) : null, - Last = result.Last?.Id, - LastCursor = result.Last is not null ? result.CreateCursor(result.Last) : null - }) +#endif + .AddQueries(queries) + .Add( + new + { + result.HasNextPage, + result.HasPreviousPage, + First = result.First?.Id, + FirstCursor = result.First is not null ? result.CreateCursor(result.First) : null, + Last = result.Last?.Id, + LastCursor = result.Last is not null ? result.CreateCursor(result.Last) : null + }) .Add(result.Items) .MatchMarkdownAsync(); } @@ -646,8 +871,20 @@ await Snapshot.Create() public async Task BatchPaging_First_5() { // Arrange +#if NET9_0_OR_GREATER + var snapshot = Snapshot.Create("NET9_0"); +#elif NET8_0 + var snapshot = Snapshot.Create("NET8_0"); +#elif NET7_0 + var snapshot = Snapshot.Create("NET7_0"); +#else + var snapshot = Snapshot.Create(); +#endif + var connectionString = CreateConnectionString(); await SeedAsync(connectionString); + var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); // Act await using var context = new CatalogContext(connectionString); @@ -661,7 +898,6 @@ public async Task BatchPaging_First_5() .ToBatchPageAsync(k => k.BrandId, pagingArgs); // Assert - var snapshot = Snapshot.Create(); foreach (var page in results) { snapshot.Add( @@ -674,15 +910,32 @@ public async Task BatchPaging_First_5() name: page.Key.ToString()); } + snapshot.AddQueries(queries); +#if NET7_0_OR_GREATER + snapshot.MatchMarkdownSnapshot("NET7_0"); +#else snapshot.MatchMarkdownSnapshot(); +#endif } [Fact] public async Task BatchPaging_Last_5() { // Arrange +#if NET9_0_OR_GREATER + var snapshot = Snapshot.Create("NET9_0"); +#elif NET8_0 + var snapshot = Snapshot.Create("NET8_0"); +#elif NET7_0 + var snapshot = Snapshot.Create("NET7_0"); +#else + var snapshot = Snapshot.Create(); +#endif + var connectionString = CreateConnectionString(); await SeedAsync(connectionString); + var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); // Act await using var context = new CatalogContext(connectionString); @@ -695,7 +948,6 @@ public async Task BatchPaging_Last_5() .ToBatchPageAsync(k => k.BrandId, pagingArgs); // Assert - var snapshot = Snapshot.Create(); foreach (var page in results) { snapshot.Add( @@ -708,7 +960,17 @@ public async Task BatchPaging_Last_5() name: page.Key.ToString()); } + snapshot.AddQueries(queries); + +#if NET9_0_OR_GREATER + snapshot.MatchMarkdownSnapshot("NET9_0"); +#elif NET8_0 + snapshot.MatchMarkdownSnapshot("NET8_0"); +#elif NET7_0 + snapshot.MatchMarkdownSnapshot("NET7_0"); +#else snapshot.MatchMarkdownSnapshot(); +#endif } [Fact] @@ -717,6 +979,8 @@ public async Task Map_Page_To_Connection_With_Dto() // Arrange var connectionString = CreateConnectionString(); await SeedAsync(connectionString); + var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); // Act var result = await new ServiceCollection() @@ -727,26 +991,38 @@ public async Task Map_Page_To_Connection_With_Dto() .AddPagingArguments() .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) .ExecuteRequestAsync( - """ - { - brands(first: 2) { - edges { - cursor - displayName - node { - id - name + OperationRequestBuilder.New() + .SetDocument( + """ + { + brands(first: 2) { + edges { + cursor + displayName + node { + id + name + } + } } } - } - } - """); + """) + .Build()); // Assert var operationResult = result.ExpectOperationResult(); +#if NET9_0_OR_GREATER + await Snapshot.Create("NET9_0") +#elif NET8_0 + await Snapshot.Create("NET8_0") +#elif NET7_0 + await Snapshot.Create("NET7_0") +#else await Snapshot.Create() - .Add(operationResult.WithExtensions(ImmutableDictionary.Empty)) +#endif + .AddQueries(queries) + .Add(operationResult) .MatchMarkdownAsync(); } @@ -756,6 +1032,8 @@ public async Task Map_Page_To_Connection_With_Dto_2() // Arrange var connectionString = CreateConnectionString(); await SeedAsync(connectionString); + var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); // Act var result = await new ServiceCollection() @@ -766,26 +1044,38 @@ public async Task Map_Page_To_Connection_With_Dto_2() .AddPagingArguments() .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) .ExecuteRequestAsync( - """ - { - brands(first: 2) { - edges { - cursor - displayName - node { - id - name + OperationRequestBuilder.New() + .SetDocument( + """ + { + brands(first: 2) { + edges { + cursor + displayName + node { + id + name + } + } } } - } - } - """); + """) + .Build()); // Assert var operationResult = result.ExpectOperationResult(); +#if NET9_0_OR_GREATER + await Snapshot.Create("NET9_0") +#elif NET8_0 + await Snapshot.Create("NET8_0") +#elif NET7_0 + await Snapshot.Create("NET7_0") +#else await Snapshot.Create() - .Add(operationResult.WithExtensions(ImmutableDictionary.Empty)) +#endif + .AddQueries(queries) + .Add(operationResult) .MatchMarkdownAsync(); } @@ -795,6 +1085,8 @@ public async Task Ensure_Nullable_Connections_Dont_Throw() // Arrange var connectionString = CreateConnectionString(); await SeedFooAsync(connectionString); + var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); // Act var result = await new ServiceCollection() @@ -804,54 +1096,47 @@ public async Task Ensure_Nullable_Connections_Dont_Throw() .AddPagingArguments() .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) .ExecuteRequestAsync( - """ - { - foos(first: 10) { - edges { - cursor - } - nodes { - id - name - bar { - id - description + OperationRequestBuilder.New() + .SetDocument( + """ + { + foos(first: 10) { + edges { + cursor + } + nodes { + id + name + bar { + id + description + } + } + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } } } - pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor - } - } - } - """); + """) + .Build()); // Assert - string? sql = null; - string? expression = null; var operationResult = result.ExpectOperationResult(); - if (operationResult.Extensions?.TryGetValue("sql", out var value) ?? false) - { - sql = value!.ToString(); - } - if (operationResult.Extensions?.TryGetValue("expression", out value) ?? false) - { - expression = value!.ToString(); - } - -#if NET8_0_OR_GREATER - await Snapshot.Create() +#if NET9_0_OR_GREATER + await Snapshot.Create("NET9_0") +#elif NET8_0 + await Snapshot.Create("NET8_0") #elif NET7_0 - await Snapshot.Create(postFix: "NET7_0") -#elif NET6_0 - await Snapshot.Create(postFix: "NET6_0") + await Snapshot.Create("NET7_0") +#else + await Snapshot.Create() #endif - .Add(sql, "SQL") - .Add(expression, "Expression") - .Add(operationResult.WithExtensions(ImmutableDictionary.Empty), "Result") + .AddQueries(queries) + .Add(operationResult, "Result") .MatchMarkdownAsync(); } @@ -861,6 +1146,8 @@ public async Task Ensure_Nullable_Connections_Dont_Throw_2() // Arrange var connectionString = CreateConnectionString(); await SeedFooAsync(connectionString); + var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); // Act var result = await new ServiceCollection() @@ -870,56 +1157,49 @@ public async Task Ensure_Nullable_Connections_Dont_Throw_2() .AddPagingArguments() .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) .ExecuteRequestAsync( - """ - { - foos(first: 10) { - edges { - cursor - } - nodes { - id - name - bar { - id - description - someField1 - someField2 + OperationRequestBuilder.New() + .SetDocument( + """ + { + foos(first: 10) { + edges { + cursor + } + nodes { + id + name + bar { + id + description + someField1 + someField2 + } + } + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } } } - pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor - } - } - } - """); + """) + .Build()); // Assert - string? sql = null; - string? expression = null; var operationResult = result.ExpectOperationResult(); - if (operationResult.Extensions?.TryGetValue("sql", out var value) ?? false) - { - sql = value!.ToString(); - } - if (operationResult.Extensions?.TryGetValue("expression", out value) ?? false) - { - expression = value!.ToString(); - } - -#if NET8_0_OR_GREATER - await Snapshot.Create() +#if NET9_0_OR_GREATER + await Snapshot.Create("NET9_0") +#elif NET8_0 + await Snapshot.Create("NET8_0") #elif NET7_0 - await Snapshot.Create(postFix: "NET7_0") -#elif NET6_0 - await Snapshot.Create(postFix: "NET6_0") + await Snapshot.Create("NET7_0") +#else + await Snapshot.Create() #endif - .Add(sql, "SQL") - .Add(expression, "Expression") - .Add(operationResult.WithExtensions(ImmutableDictionary.Empty), "Result") + .AddQueries(queries) + .Add(operationResult, "Result") .MatchMarkdownAsync(); } @@ -928,7 +1208,10 @@ private static async Task SeedAsync(string connectionString) await using var context = new CatalogContext(connectionString); await context.Database.EnsureCreatedAsync(); - var type = new ProductType { Name = "T-Shirt", }; + var type = new ProductType + { + Name = "T-Shirt", + }; context.ProductTypes.Add(type); for (var i = 0; i < 100; i++) @@ -943,7 +1226,12 @@ private static async Task SeedAsync(string connectionString) for (var j = 0; j < 100; j++) { - var product = new Product { Name = $"Product {i}-{j}", Type = type, Brand = brand, }; + var product = new Product + { + Name = $"Product {i}-{j}", + Type = type, + Brand = brand, + }; context.Products.Add(product); } } @@ -1000,11 +1288,13 @@ public async Task> GetBrandsAsync( CatalogContext context, PagingArguments arguments, CancellationToken ct) - => await context.Brands + { + return await context.Brands .OrderBy(t => t.Name) .ThenBy(t => t.Id) .ToPageAsync(arguments, cancellationToken: ct) .ToConnectionAsync(); + } [UsePaging] public async Task> GetBrands2Async( @@ -1025,34 +1315,40 @@ public async Task> GetBrandsNullable( CatalogContext context, PagingArguments arguments, CancellationToken ct) - => await context.Brands + { + return await context.Brands .OrderBy(t => t.Name) .ThenBy(x => x.AlwaysNull) .ThenBy(t => t.Id) .ToPageAsync(arguments, cancellationToken: ct) .ToConnectionAsync(); + } [UsePaging] public async Task> GetBrandsNullableFallback( CatalogContext context, PagingArguments arguments, CancellationToken ct) - => await context.Brands + { + return await context.Brands .OrderBy(t => t.DisplayName ?? t.Name) .ThenBy(t => t.Id) .ToPageAsync(arguments, cancellationToken: ct) .ToConnectionAsync(); + } [UsePaging] public async Task> GetBrandsDeep( CatalogContext context, PagingArguments arguments, CancellationToken ct) - => await context.Brands + { + return await context.Brands .OrderBy(x => x.BrandDetails.Country.Name) .ThenBy(t => t.Id) .ToPageAsync(arguments, cancellationToken: ct) .ToConnectionAsync(); + } } public class QueryConnection @@ -1062,11 +1358,13 @@ public async Task> GetBrandsAsync( CatalogContext context, PagingArguments arguments, CancellationToken ct) - => await context.Brands + { + return await context.Brands .OrderBy(t => t.Name) .ThenBy(t => t.Id) .ToPageAsync(arguments, cancellationToken: ct) .ToConnectionAsync((brand, page) => new BrandEdge(brand, edge => page.CreateCursor(edge.Brand))); + } } public class QueryConnection2 @@ -1076,11 +1374,13 @@ public async Task> GetBrandsAsync( CatalogContext context, PagingArguments arguments, CancellationToken ct) - => await context.Brands + { + return await context.Brands .OrderBy(t => t.Name) .ThenBy(t => t.Id) .ToPageAsync(arguments, cancellationToken: ct) .ToConnectionAsync((brand, cursor) => new BrandEdge2(brand, cursor)); + } } public class QueryNullable @@ -1093,21 +1393,6 @@ public async Task> GetFoosAsync( IResolverContext rc, CancellationToken ct) { - var sql = context.Foos - .OrderBy(t => t.Name) - .ThenBy(t => t.Id) - .Select(selection.AsSelector()) - .ToQueryString(); - - var expression = context.Foos - .OrderBy(t => t.Name) - .ThenBy(t => t.Id) - .Select(selection.AsSelector()) - .Expression.ToString(); - - ((IMiddlewareContext)rc).OperationResult.SetExtension("sql", sql); - ((IMiddlewareContext)rc).OperationResult.SetExtension("expression", expression); - return await context.Foos .OrderBy(t => t.Name) .ThenBy(t => t.Id) @@ -1177,24 +1462,10 @@ public static async Task> GetProducts( PagingArguments arguments, IResolverContext context, CancellationToken cancellationToken) - { - var sql = new SqlQuery(); - - var stateFullDataLoader = dataLoader + => await dataLoader .WithPagingArguments(arguments) - .SetState(sql); - - var result = await stateFullDataLoader .LoadAsync(brand.Id, cancellationToken) .ToConnectionAsync(); - - if (!string.IsNullOrEmpty(sql.Text)) - { - ((IMiddlewareContext)context).OperationResult.SetExtension("sql", sql.Text); - } - - return result; - } } [ExtendObjectType] @@ -1208,25 +1479,11 @@ public static async Task> GetProducts( PagingArguments arguments, IResolverContext context, CancellationToken cancellationToken) - { - var sql = new SqlQuery(); - var stateFullDataLoader = - dataLoader - .WithPagingArguments(arguments) - .Select(selection) - .SetState(sql); - - var result = await stateFullDataLoader + => await dataLoader + .WithPagingArguments(arguments) + .Select(selection) .LoadAsync(brand.Id, cancellationToken) .ToConnectionAsync(); - - if (!string.IsNullOrEmpty(sql.Text)) - { - ((IMiddlewareContext)context).OperationResult.SetExtension("sql", sql.Text); - } - - return result; - } } public class ProductsByBrandDataLoader : StatefulBatchDataLoader> @@ -1248,27 +1505,45 @@ protected override async Task>> LoadBatch CancellationToken cancellationToken) { var pagingArgs = context.GetPagingArguments(); - var sql = context.GetRequiredState(); await using var scope = _services.CreateAsyncScope(); await using var catalogContext = scope.ServiceProvider.GetRequiredService(); - sql.Text = catalogContext.Products - .Where(t => keys.Contains(t.BrandId)) - .Select(context.GetSelector(), b => b.Id) - .OrderBy(t => t.Name).ThenBy(t => t.Id) - .ToQueryString(); - return await catalogContext.Products .Where(t => keys.Contains(t.BrandId)) - .Select(context.GetSelector(), b => b.Id) + .Select(context.GetSelector(), b => b.BrandId) .OrderBy(t => t.Name).ThenBy(t => t.Id) .ToBatchPageAsync(t => t.BrandId, pagingArgs, cancellationToken); } } +} - private class SqlQuery +file static class Extensions +{ + public static Snapshot AddQueries( + this Snapshot snapshot, + List queries) { - public string? Text { get; set; } + for (var i = 0; i < queries.Count; i++) + { + snapshot + .Add(queries[i].QueryText, $"SQL {i}", "sql") + .Add(queries[i].ExpressionText, $"Expression {i}"); + } + + return snapshot; + } +} + +file sealed class CapturePagingQueryInterceptor(List queries) : PagingQueryInterceptor +{ + public override void OnBeforeExecute(IQueryable query) + { + queries.Add( + new QueryInfo + { + ExpressionText = query.Expression.ToString(), + QueryText = query.ToQueryString() + }); } } diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/TestContext/AnimalContext.cs b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/TestContext/AnimalContext.cs index a303b6f1958..62e065d393a 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/TestContext/AnimalContext.cs +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/TestContext/AnimalContext.cs @@ -7,6 +7,7 @@ namespace HotChocolate.Data.TestContext; public class AnimalContext(string connectionString) : DbContext { public DbSet Owners { get; set; } = default!; + public DbSet Pets { get; set; } = default!; public DbSet Dogs { get; set; } = default!; public DbSet Cats { get; set; } = default!; diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_First_5.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_First_5.md index 278a12936ef..0d5f5deea55 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_First_5.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_First_5.md @@ -123,3 +123,31 @@ } ``` +## SQL 0 + +```sql +SELECT t."BrandId", t0."Id", t0."AvailableStock", t0."BrandId", t0."Description", t0."ImageFileName", t0."MaxStockThreshold", t0."Name", t0."OnReorder", t0."Price", t0."RestockThreshold", t0."TypeId" +FROM ( + SELECT p."BrandId" + FROM "Products" AS p + WHERE p."BrandId" IN (1, 2, 3) + GROUP BY p."BrandId" +) AS t +LEFT JOIN ( + SELECT t1."Id", t1."AvailableStock", t1."BrandId", t1."Description", t1."ImageFileName", t1."MaxStockThreshold", t1."Name", t1."OnReorder", t1."Price", t1."RestockThreshold", t1."TypeId" + FROM ( + SELECT p0."Id", p0."AvailableStock", p0."BrandId", p0."Description", p0."ImageFileName", p0."MaxStockThreshold", p0."Name", p0."OnReorder", p0."Price", p0."RestockThreshold", p0."TypeId", ROW_NUMBER() OVER(PARTITION BY p0."BrandId" ORDER BY p0."Name", p0."Id") AS row + FROM "Products" AS p0 + WHERE ((p0."BrandId" = 1) OR (p0."BrandId" = 2)) OR (p0."BrandId" = 3) + ) AS t1 + WHERE t1.row <= 3 +) AS t0 ON t."BrandId" = t0."BrandId" +ORDER BY t."BrandId", t0."BrandId", t0."Name", t0."Id" +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].Where(t => (((t.BrandId == 1) OrElse (t.BrandId == 2)) OrElse (t.BrandId == 3))).GroupBy(k => k.BrandId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderBy(p => p.Name).ThenBy(p => p.Id).Take(3).ToList()}) +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_First_5_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_First_5_NET7_0.md new file mode 100644 index 00000000000..06914fecc42 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_First_5_NET7_0.md @@ -0,0 +1,5 @@ +# BatchPaging_First_5 + +```json +{} +``` diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_First_5_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_First_5_NET9_0.md new file mode 100644 index 00000000000..0d5f5deea55 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_First_5_NET9_0.md @@ -0,0 +1,153 @@ +# BatchPaging_First_5 + +## 1 + +```json +{ + "First": "UHJvZHVjdCAwLTA6MQ==", + "Last": "UHJvZHVjdCAwLTE6Mg==", + "Items": [ + { + "Id": 1, + "Name": "Product 0-0", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 1, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + }, + { + "Id": 2, + "Name": "Product 0-1", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 1, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + } + ] +} +``` + +## 2 + +```json +{ + "First": "UHJvZHVjdCAxLTA6MTAx", + "Last": "UHJvZHVjdCAxLTE6MTAy", + "Items": [ + { + "Id": 101, + "Name": "Product 1-0", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 2, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + }, + { + "Id": 102, + "Name": "Product 1-1", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 2, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + } + ] +} +``` + +## 3 + +```json +{ + "First": "UHJvZHVjdCAyLTA6MjAx", + "Last": "UHJvZHVjdCAyLTE6MjAy", + "Items": [ + { + "Id": 201, + "Name": "Product 2-0", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 3, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + }, + { + "Id": 202, + "Name": "Product 2-1", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 3, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + } + ] +} +``` + +## SQL 0 + +```sql +SELECT t."BrandId", t0."Id", t0."AvailableStock", t0."BrandId", t0."Description", t0."ImageFileName", t0."MaxStockThreshold", t0."Name", t0."OnReorder", t0."Price", t0."RestockThreshold", t0."TypeId" +FROM ( + SELECT p."BrandId" + FROM "Products" AS p + WHERE p."BrandId" IN (1, 2, 3) + GROUP BY p."BrandId" +) AS t +LEFT JOIN ( + SELECT t1."Id", t1."AvailableStock", t1."BrandId", t1."Description", t1."ImageFileName", t1."MaxStockThreshold", t1."Name", t1."OnReorder", t1."Price", t1."RestockThreshold", t1."TypeId" + FROM ( + SELECT p0."Id", p0."AvailableStock", p0."BrandId", p0."Description", p0."ImageFileName", p0."MaxStockThreshold", p0."Name", p0."OnReorder", p0."Price", p0."RestockThreshold", p0."TypeId", ROW_NUMBER() OVER(PARTITION BY p0."BrandId" ORDER BY p0."Name", p0."Id") AS row + FROM "Products" AS p0 + WHERE ((p0."BrandId" = 1) OR (p0."BrandId" = 2)) OR (p0."BrandId" = 3) + ) AS t1 + WHERE t1.row <= 3 +) AS t0 ON t."BrandId" = t0."BrandId" +ORDER BY t."BrandId", t0."BrandId", t0."Name", t0."Id" +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].Where(t => (((t.BrandId == 1) OrElse (t.BrandId == 2)) OrElse (t.BrandId == 3))).GroupBy(k => k.BrandId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderBy(p => p.Name).ThenBy(p => p.Id).Take(3).ToList()}) +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5.md index 7c1eca27dcd..7263c51b5f2 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5.md @@ -123,3 +123,31 @@ } ``` +## SQL 0 + +```sql +SELECT t."BrandId", t0."Id", t0."AvailableStock", t0."BrandId", t0."Description", t0."ImageFileName", t0."MaxStockThreshold", t0."Name", t0."OnReorder", t0."Price", t0."RestockThreshold", t0."TypeId" +FROM ( + SELECT p."BrandId" + FROM "Products" AS p + WHERE p."BrandId" IN (1, 2, 3) + GROUP BY p."BrandId" +) AS t +LEFT JOIN ( + SELECT t1."Id", t1."AvailableStock", t1."BrandId", t1."Description", t1."ImageFileName", t1."MaxStockThreshold", t1."Name", t1."OnReorder", t1."Price", t1."RestockThreshold", t1."TypeId" + FROM ( + SELECT p0."Id", p0."AvailableStock", p0."BrandId", p0."Description", p0."ImageFileName", p0."MaxStockThreshold", p0."Name", p0."OnReorder", p0."Price", p0."RestockThreshold", p0."TypeId", ROW_NUMBER() OVER(PARTITION BY p0."BrandId" ORDER BY p0."Id" DESC) AS row + FROM "Products" AS p0 + WHERE ((p0."BrandId" = 1) OR (p0."BrandId" = 2)) OR (p0."BrandId" = 3) + ) AS t1 + WHERE t1.row <= 3 +) AS t0 ON t."BrandId" = t0."BrandId" +ORDER BY t."BrandId", t0."BrandId", t0."Id" DESC +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].Where(t => (((t.BrandId == 1) OrElse (t.BrandId == 2)) OrElse (t.BrandId == 3))).GroupBy(k => k.BrandId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderByDescending(p => p.Id).Take(3).ToList()}) +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5_NET7_0.md new file mode 100644 index 00000000000..d433943523c --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5_NET7_0.md @@ -0,0 +1,5 @@ +# BatchPaging_Last_5 + +```json +{} +``` diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5_NET8_0.md new file mode 100644 index 00000000000..d433943523c --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5_NET8_0.md @@ -0,0 +1,5 @@ +# BatchPaging_Last_5 + +```json +{} +``` diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5_NET9_0.md new file mode 100644 index 00000000000..7263c51b5f2 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5_NET9_0.md @@ -0,0 +1,153 @@ +# BatchPaging_Last_5 + +## 1 + +```json +{ + "First": "MTAw", + "Last": "OTk=", + "Items": [ + { + "Id": 100, + "Name": "Product 0-99", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 1, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + }, + { + "Id": 99, + "Name": "Product 0-98", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 1, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + } + ] +} +``` + +## 2 + +```json +{ + "First": "MjAw", + "Last": "MTk5", + "Items": [ + { + "Id": 200, + "Name": "Product 1-99", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 2, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + }, + { + "Id": 199, + "Name": "Product 1-98", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 2, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + } + ] +} +``` + +## 3 + +```json +{ + "First": "MzAw", + "Last": "Mjk5", + "Items": [ + { + "Id": 300, + "Name": "Product 2-99", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 3, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + }, + { + "Id": 299, + "Name": "Product 2-98", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 3, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + } + ] +} +``` + +## SQL 0 + +```sql +SELECT t."BrandId", t0."Id", t0."AvailableStock", t0."BrandId", t0."Description", t0."ImageFileName", t0."MaxStockThreshold", t0."Name", t0."OnReorder", t0."Price", t0."RestockThreshold", t0."TypeId" +FROM ( + SELECT p."BrandId" + FROM "Products" AS p + WHERE p."BrandId" IN (1, 2, 3) + GROUP BY p."BrandId" +) AS t +LEFT JOIN ( + SELECT t1."Id", t1."AvailableStock", t1."BrandId", t1."Description", t1."ImageFileName", t1."MaxStockThreshold", t1."Name", t1."OnReorder", t1."Price", t1."RestockThreshold", t1."TypeId" + FROM ( + SELECT p0."Id", p0."AvailableStock", p0."BrandId", p0."Description", p0."ImageFileName", p0."MaxStockThreshold", p0."Name", p0."OnReorder", p0."Price", p0."RestockThreshold", p0."TypeId", ROW_NUMBER() OVER(PARTITION BY p0."BrandId" ORDER BY p0."Id" DESC) AS row + FROM "Products" AS p0 + WHERE ((p0."BrandId" = 1) OR (p0."BrandId" = 2)) OR (p0."BrandId" = 3) + ) AS t1 + WHERE t1.row <= 3 +) AS t0 ON t."BrandId" = t0."BrandId" +ORDER BY t."BrandId", t0."BrandId", t0."Id" DESC +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].Where(t => (((t.BrandId == 1) OrElse (t.BrandId == 2)) OrElse (t.BrandId == 3))).GroupBy(k => k.BrandId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderByDescending(p => p.Id).Take(3).ToList()}) +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw.md index 47144e676f5..0de22cee471 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw.md @@ -1,18 +1,24 @@ # Ensure_Nullable_Connections_Dont_Throw -## SQL +## SQL 0 -```text -SELECT f."Id", f."Name", b."Id" IS NULL, b."Id", b."Description" -FROM "Foos" AS f -LEFT JOIN "Bars" AS b ON f."BarId" = b."Id" -ORDER BY f."Name", f."Id" +```sql +-- @__p_0='11' +SELECT t."Id", t."Name", (b."Id" IS NULL), b."Id", b."Description" +FROM ( + SELECT f."Id", f."BarId", f."Name" + FROM "Foos" AS f + ORDER BY f."Name", f."Id" + LIMIT @__p_0 +) AS t +LEFT JOIN "Bars" AS b ON t."BarId" = b."Id" +ORDER BY t."Name", t."Id" ``` -## Expression +## Expression 0 ```text -[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = root.Name, Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = root.Bar.Description})}) +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name), Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description)})}).Take(11) ``` ## Result diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2.md index 9d5412fef69..4b0b1ca96b4 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2.md @@ -1,18 +1,24 @@ # Ensure_Nullable_Connections_Dont_Throw_2 -## SQL +## SQL 0 -```text -SELECT f."Id", f."Name", b."Id" IS NULL, b."Id", b."Description", b."SomeField1", b."SomeField2" -FROM "Foos" AS f -LEFT JOIN "Bars" AS b ON f."BarId" = b."Id" -ORDER BY f."Name", f."Id" +```sql +-- @__p_0='11' +SELECT t."Id", t."Name", (b."Id" IS NULL), b."Id", b."Description", b."SomeField1", b."SomeField2" +FROM ( + SELECT f."Id", f."BarId", f."Name" + FROM "Foos" AS f + ORDER BY f."Name", f."Id" + LIMIT @__p_0 +) AS t +LEFT JOIN "Bars" AS b ON t."BarId" = b."Id" +ORDER BY t."Name", t."Id" ``` -## Expression +## Expression 0 ```text -[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = root.Name, Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = root.Bar.Description, SomeField1 = root.Bar.SomeField1, SomeField2 = root.Bar.SomeField2})}) +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name), Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description), SomeField1 = IIF((root.Bar.SomeField1 == null), null, root.Bar.SomeField1), SomeField2 = IIF((root.Bar.SomeField2 == null), null, root.Bar.SomeField2)})}).Take(11) ``` ## Result diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET7_0.md index 25de998bc69..9c57b3d2986 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET7_0.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET7_0.md @@ -1,18 +1,24 @@ # Ensure_Nullable_Connections_Dont_Throw_2 -## SQL +## SQL 0 -```text -SELECT f."Id", f."Name", b."Id" IS NULL, b."Id", b."Description", b."SomeField1", b."SomeField2" -FROM "Foos" AS f -LEFT JOIN "Bars" AS b ON f."BarId" = b."Id" -ORDER BY f."Name", f."Id" +```sql +-- @__p_0='11' +SELECT t."Id", t."Name", b."Id" IS NULL, b."Id", b."Description", b."SomeField1", b."SomeField2" +FROM ( + SELECT f."Id", f."BarId", f."Name" + FROM "Foos" AS f + ORDER BY f."Name", f."Id" + LIMIT @__p_0 +) AS t +LEFT JOIN "Bars" AS b ON t."BarId" = b."Id" +ORDER BY t."Name", t."Id" ``` -## Expression +## Expression 0 ```text -[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name), Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description), SomeField1 = IIF((root.Bar.SomeField1 == null), null, root.Bar.SomeField1), SomeField2 = IIF((root.Bar.SomeField2 == null), null, root.Bar.SomeField2)})}) +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name), Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description), SomeField1 = IIF((root.Bar.SomeField1 == null), null, root.Bar.SomeField1), SomeField2 = IIF((root.Bar.SomeField2 == null), null, root.Bar.SomeField2)})}).Take(11) ``` ## Result diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET8_0.md new file mode 100644 index 00000000000..c2a1a8f8595 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET8_0.md @@ -0,0 +1,65 @@ +# Ensure_Nullable_Connections_Dont_Throw_2 + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT t."Id", t."Name", b."Id" IS NULL, b."Id", b."Description", b."SomeField1", b."SomeField2" +FROM ( + SELECT f."Id", f."BarId", f."Name" + FROM "Foos" AS f + ORDER BY f."Name", f."Id" + LIMIT @__p_0 +) AS t +LEFT JOIN "Bars" AS b ON t."BarId" = b."Id" +ORDER BY t."Name", t."Id" +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = root.Name, Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = root.Bar.Description, SomeField1 = root.Bar.SomeField1, SomeField2 = root.Bar.SomeField2})}).Take(11) +``` + +## Result + +```json +{ + "data": { + "foos": { + "edges": [ + { + "cursor": "Rm9vIDE6MQ==" + }, + { + "cursor": "Rm9vIDI6Mg==" + } + ], + "nodes": [ + { + "id": 1, + "name": "Foo 1", + "bar": null + }, + { + "id": 2, + "name": "Foo 2", + "bar": { + "id": 1, + "description": "Bar 1", + "someField1": "abc", + "someField2": null + } + } + ], + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "startCursor": "Rm9vIDE6MQ==", + "endCursor": "Rm9vIDI6Mg==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET9_0.md new file mode 100644 index 00000000000..4b0b1ca96b4 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET9_0.md @@ -0,0 +1,65 @@ +# Ensure_Nullable_Connections_Dont_Throw_2 + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT t."Id", t."Name", (b."Id" IS NULL), b."Id", b."Description", b."SomeField1", b."SomeField2" +FROM ( + SELECT f."Id", f."BarId", f."Name" + FROM "Foos" AS f + ORDER BY f."Name", f."Id" + LIMIT @__p_0 +) AS t +LEFT JOIN "Bars" AS b ON t."BarId" = b."Id" +ORDER BY t."Name", t."Id" +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name), Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description), SomeField1 = IIF((root.Bar.SomeField1 == null), null, root.Bar.SomeField1), SomeField2 = IIF((root.Bar.SomeField2 == null), null, root.Bar.SomeField2)})}).Take(11) +``` + +## Result + +```json +{ + "data": { + "foos": { + "edges": [ + { + "cursor": "Rm9vIDE6MQ==" + }, + { + "cursor": "Rm9vIDI6Mg==" + } + ], + "nodes": [ + { + "id": 1, + "name": "Foo 1", + "bar": null + }, + { + "id": 2, + "name": "Foo 2", + "bar": { + "id": 1, + "description": "Bar 1", + "someField1": "abc", + "someField2": null + } + } + ], + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "startCursor": "Rm9vIDE6MQ==", + "endCursor": "Rm9vIDI6Mg==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET7_0.md index f67d40e5ee1..85a298ee818 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET7_0.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET7_0.md @@ -1,18 +1,24 @@ # Ensure_Nullable_Connections_Dont_Throw -## SQL +## SQL 0 -```text -SELECT f."Id", f."Name", b."Id" IS NULL, b."Id", b."Description" -FROM "Foos" AS f -LEFT JOIN "Bars" AS b ON f."BarId" = b."Id" -ORDER BY f."Name", f."Id" +```sql +-- @__p_0='11' +SELECT t."Id", t."Name", b."Id" IS NULL, b."Id", b."Description" +FROM ( + SELECT f."Id", f."BarId", f."Name" + FROM "Foos" AS f + ORDER BY f."Name", f."Id" + LIMIT @__p_0 +) AS t +LEFT JOIN "Bars" AS b ON t."BarId" = b."Id" +ORDER BY t."Name", t."Id" ``` -## Expression +## Expression 0 ```text -[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name), Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description)})}) +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name), Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description)})}).Take(11) ``` ## Result diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET8_0.md new file mode 100644 index 00000000000..26d995e5bec --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET8_0.md @@ -0,0 +1,63 @@ +# Ensure_Nullable_Connections_Dont_Throw + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT t."Id", t."Name", b."Id" IS NULL, b."Id", b."Description" +FROM ( + SELECT f."Id", f."BarId", f."Name" + FROM "Foos" AS f + ORDER BY f."Name", f."Id" + LIMIT @__p_0 +) AS t +LEFT JOIN "Bars" AS b ON t."BarId" = b."Id" +ORDER BY t."Name", t."Id" +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = root.Name, Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = root.Bar.Description})}).Take(11) +``` + +## Result + +```json +{ + "data": { + "foos": { + "edges": [ + { + "cursor": "Rm9vIDE6MQ==" + }, + { + "cursor": "Rm9vIDI6Mg==" + } + ], + "nodes": [ + { + "id": 1, + "name": "Foo 1", + "bar": null + }, + { + "id": 2, + "name": "Foo 2", + "bar": { + "id": 1, + "description": "Bar 1" + } + } + ], + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "startCursor": "Rm9vIDE6MQ==", + "endCursor": "Rm9vIDI6Mg==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET9_0.md new file mode 100644 index 00000000000..0de22cee471 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET9_0.md @@ -0,0 +1,63 @@ +# Ensure_Nullable_Connections_Dont_Throw + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT t."Id", t."Name", (b."Id" IS NULL), b."Id", b."Description" +FROM ( + SELECT f."Id", f."BarId", f."Name" + FROM "Foos" AS f + ORDER BY f."Name", f."Id" + LIMIT @__p_0 +) AS t +LEFT JOIN "Bars" AS b ON t."BarId" = b."Id" +ORDER BY t."Name", t."Id" +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name), Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description)})}).Take(11) +``` + +## Result + +```json +{ + "data": { + "foos": { + "edges": [ + { + "cursor": "Rm9vIDE6MQ==" + }, + { + "cursor": "Rm9vIDI6Mg==" + } + ], + "nodes": [ + { + "id": 1, + "name": "Foo 1", + "bar": null + }, + { + "id": 2, + "name": "Foo 2", + "bar": { + "id": 1, + "description": "Bar 1" + } + } + ], + "pageInfo": { + "hasNextPage": false, + "hasPreviousPage": false, + "startCursor": "Rm9vIDE6MQ==", + "endCursor": "Rm9vIDI6Mg==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage.md index d11253defc1..1ce01edfd9b 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage.md @@ -1,5 +1,23 @@ # GetDefaultPage +## SQL 0 + +```sql +-- @__p_0='11' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(11) +``` + +## Result + ```json { "data": { @@ -56,3 +74,4 @@ } } ``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage2.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage2.md index 29d72fb59c8..fc776f00bc5 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage2.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage2.md @@ -1,5 +1,23 @@ # GetDefaultPage2 +## SQL 0 + +```sql +-- @__p_0='11' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(11) +``` + +## Result + ```json { "data": { @@ -56,3 +74,4 @@ } } ``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage2_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage2_NET7_0.md new file mode 100644 index 00000000000..ac096892d0b --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage2_NET7_0.md @@ -0,0 +1,77 @@ +# GetDefaultPage2 + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(11) +``` + +## Result + +```json +{ + "data": { + "brands2": { + "nodes": [ + { + "id": 1, + "name": "Brand0" + }, + { + "id": 2, + "name": "Brand1" + }, + { + "id": 11, + "name": "Brand10" + }, + { + "id": 12, + "name": "Brand11" + }, + { + "id": 13, + "name": "Brand12" + }, + { + "id": 14, + "name": "Brand13" + }, + { + "id": 15, + "name": "Brand14" + }, + { + "id": 16, + "name": "Brand15" + }, + { + "id": 17, + "name": "Brand16" + }, + { + "id": 18, + "name": "Brand17" + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "QnJhbmQwOjE=", + "endCursor": "QnJhbmQxNzoxOA==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage2_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage2_NET8_0.md new file mode 100644 index 00000000000..ac096892d0b --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage2_NET8_0.md @@ -0,0 +1,77 @@ +# GetDefaultPage2 + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(11) +``` + +## Result + +```json +{ + "data": { + "brands2": { + "nodes": [ + { + "id": 1, + "name": "Brand0" + }, + { + "id": 2, + "name": "Brand1" + }, + { + "id": 11, + "name": "Brand10" + }, + { + "id": 12, + "name": "Brand11" + }, + { + "id": 13, + "name": "Brand12" + }, + { + "id": 14, + "name": "Brand13" + }, + { + "id": 15, + "name": "Brand14" + }, + { + "id": 16, + "name": "Brand15" + }, + { + "id": 17, + "name": "Brand16" + }, + { + "id": 18, + "name": "Brand17" + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "QnJhbmQwOjE=", + "endCursor": "QnJhbmQxNzoxOA==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_NET7_0.md new file mode 100644 index 00000000000..c794a8571bb --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_NET7_0.md @@ -0,0 +1,77 @@ +# GetDefaultPage + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(11) +``` + +## Result + +```json +{ + "data": { + "brands": { + "nodes": [ + { + "id": 1, + "name": "Brand0" + }, + { + "id": 2, + "name": "Brand1" + }, + { + "id": 11, + "name": "Brand10" + }, + { + "id": 12, + "name": "Brand11" + }, + { + "id": 13, + "name": "Brand12" + }, + { + "id": 14, + "name": "Brand13" + }, + { + "id": 15, + "name": "Brand14" + }, + { + "id": 16, + "name": "Brand15" + }, + { + "id": 17, + "name": "Brand16" + }, + { + "id": 18, + "name": "Brand17" + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "QnJhbmQwOjE=", + "endCursor": "QnJhbmQxNzoxOA==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_NET8_0.md new file mode 100644 index 00000000000..c794a8571bb --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_NET8_0.md @@ -0,0 +1,77 @@ +# GetDefaultPage + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(11) +``` + +## Result + +```json +{ + "data": { + "brands": { + "nodes": [ + { + "id": 1, + "name": "Brand0" + }, + { + "id": 2, + "name": "Brand1" + }, + { + "id": 11, + "name": "Brand10" + }, + { + "id": 12, + "name": "Brand11" + }, + { + "id": 13, + "name": "Brand12" + }, + { + "id": 14, + "name": "Brand13" + }, + { + "id": 15, + "name": "Brand14" + }, + { + "id": 16, + "name": "Brand15" + }, + { + "id": 17, + "name": "Brand16" + }, + { + "id": 18, + "name": "Brand17" + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "QnJhbmQwOjE=", + "endCursor": "QnJhbmQxNzoxOA==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep.md index cb52b6f10ab..28b88dcb52f 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep.md @@ -1,5 +1,23 @@ # GetDefaultPage_With_Deep +## SQL 0 + +```sql +-- @__p_0='11' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."BrandDetails_Country_Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(x => x.BrandDetails.Country.Name).ThenBy(t => t.Id).Take(11) +``` + +## Result + ```json { "data": { @@ -148,3 +166,4 @@ } } ``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_NET7_0.md new file mode 100644 index 00000000000..94e9856164c --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_NET7_0.md @@ -0,0 +1,169 @@ +# GetDefaultPage_With_Deep + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."BrandDetails_Country_Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(x => x.BrandDetails.Country.Name).ThenBy(t => t.Id).Take(11) +``` + +## Result + +```json +{ + "data": { + "brandsDeep": { + "edges": [ + { + "cursor": "Q291bnRyeTA6MQ==" + }, + { + "cursor": "Q291bnRyeTE6Mg==" + }, + { + "cursor": "Q291bnRyeTEwOjEx" + }, + { + "cursor": "Q291bnRyeTExOjEy" + }, + { + "cursor": "Q291bnRyeTEyOjEz" + }, + { + "cursor": "Q291bnRyeTEzOjE0" + }, + { + "cursor": "Q291bnRyeTE0OjE1" + }, + { + "cursor": "Q291bnRyeTE1OjE2" + }, + { + "cursor": "Q291bnRyeTE2OjE3" + }, + { + "cursor": "Q291bnRyeTE3OjE4" + } + ], + "nodes": [ + { + "id": 1, + "name": "Brand0", + "displayName": "BrandDisplay0", + "brandDetails": { + "country": { + "name": "Country0" + } + } + }, + { + "id": 2, + "name": "Brand1", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country1" + } + } + }, + { + "id": 11, + "name": "Brand10", + "displayName": "BrandDisplay10", + "brandDetails": { + "country": { + "name": "Country10" + } + } + }, + { + "id": 12, + "name": "Brand11", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country11" + } + } + }, + { + "id": 13, + "name": "Brand12", + "displayName": "BrandDisplay12", + "brandDetails": { + "country": { + "name": "Country12" + } + } + }, + { + "id": 14, + "name": "Brand13", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country13" + } + } + }, + { + "id": 15, + "name": "Brand14", + "displayName": "BrandDisplay14", + "brandDetails": { + "country": { + "name": "Country14" + } + } + }, + { + "id": 16, + "name": "Brand15", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country15" + } + } + }, + { + "id": 17, + "name": "Brand16", + "displayName": "BrandDisplay16", + "brandDetails": { + "country": { + "name": "Country16" + } + } + }, + { + "id": 18, + "name": "Brand17", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country17" + } + } + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "Q291bnRyeTA6MQ==", + "endCursor": "Q291bnRyeTE3OjE4" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_NET8_0.md new file mode 100644 index 00000000000..94e9856164c --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_NET8_0.md @@ -0,0 +1,169 @@ +# GetDefaultPage_With_Deep + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."BrandDetails_Country_Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(x => x.BrandDetails.Country.Name).ThenBy(t => t.Id).Take(11) +``` + +## Result + +```json +{ + "data": { + "brandsDeep": { + "edges": [ + { + "cursor": "Q291bnRyeTA6MQ==" + }, + { + "cursor": "Q291bnRyeTE6Mg==" + }, + { + "cursor": "Q291bnRyeTEwOjEx" + }, + { + "cursor": "Q291bnRyeTExOjEy" + }, + { + "cursor": "Q291bnRyeTEyOjEz" + }, + { + "cursor": "Q291bnRyeTEzOjE0" + }, + { + "cursor": "Q291bnRyeTE0OjE1" + }, + { + "cursor": "Q291bnRyeTE1OjE2" + }, + { + "cursor": "Q291bnRyeTE2OjE3" + }, + { + "cursor": "Q291bnRyeTE3OjE4" + } + ], + "nodes": [ + { + "id": 1, + "name": "Brand0", + "displayName": "BrandDisplay0", + "brandDetails": { + "country": { + "name": "Country0" + } + } + }, + { + "id": 2, + "name": "Brand1", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country1" + } + } + }, + { + "id": 11, + "name": "Brand10", + "displayName": "BrandDisplay10", + "brandDetails": { + "country": { + "name": "Country10" + } + } + }, + { + "id": 12, + "name": "Brand11", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country11" + } + } + }, + { + "id": 13, + "name": "Brand12", + "displayName": "BrandDisplay12", + "brandDetails": { + "country": { + "name": "Country12" + } + } + }, + { + "id": 14, + "name": "Brand13", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country13" + } + } + }, + { + "id": 15, + "name": "Brand14", + "displayName": "BrandDisplay14", + "brandDetails": { + "country": { + "name": "Country14" + } + } + }, + { + "id": 16, + "name": "Brand15", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country15" + } + } + }, + { + "id": 17, + "name": "Brand16", + "displayName": "BrandDisplay16", + "brandDetails": { + "country": { + "name": "Country16" + } + } + }, + { + "id": 18, + "name": "Brand17", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country17" + } + } + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "Q291bnRyeTA6MQ==", + "endCursor": "Q291bnRyeTE3OjE4" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_SecondPage.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_SecondPage.md index e2f3e6c336a..a0531d99e0e 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_SecondPage.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_SecondPage.md @@ -1,5 +1,26 @@ # GetDefaultPage_With_Deep_SecondPage +## SQL 0 + +```sql +-- @__p_0='Country1' +-- @__p_1='2' +-- @__p_2='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +WHERE (b."BrandDetails_Country_Name" > @__p_0) OR ((b."BrandDetails_Country_Name" = @__p_0) AND (b."Id" > @__p_1)) +ORDER BY b."BrandDetails_Country_Name", b."Id" +LIMIT @__p_2 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(x => x.BrandDetails.Country.Name).ThenBy(t => t.Id).Where(t => ((t.BrandDetails.Country.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) > 0) OrElse ((t.BrandDetails.Country.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.Id.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.Int32]).value, Int32)) > 0)))).Take(3) +``` + +## Result + ```json { "data": { @@ -44,3 +65,4 @@ } } ``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_SecondPage_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_SecondPage_NET7_0.md new file mode 100644 index 00000000000..c18777c97d3 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_SecondPage_NET7_0.md @@ -0,0 +1,68 @@ +# GetDefaultPage_With_Deep_SecondPage + +## SQL 0 + +```sql +-- @__p_0='Country1' +-- @__p_1='2' +-- @__p_2='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +WHERE b."BrandDetails_Country_Name" > @__p_0 OR (b."BrandDetails_Country_Name" = @__p_0 AND b."Id" > @__p_1) +ORDER BY b."BrandDetails_Country_Name", b."Id" +LIMIT @__p_2 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(x => x.BrandDetails.Country.Name).ThenBy(t => t.Id).Where(t => ((t.BrandDetails.Country.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) > 0) OrElse ((t.BrandDetails.Country.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.Id.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.Int32]).value, Int32)) > 0)))).Take(3) +``` + +## Result + +```json +{ + "data": { + "brandsDeep": { + "edges": [ + { + "cursor": "Q291bnRyeTEwOjEx" + }, + { + "cursor": "Q291bnRyeTExOjEy" + } + ], + "nodes": [ + { + "id": 11, + "name": "Brand10", + "displayName": "BrandDisplay10", + "brandDetails": { + "country": { + "name": "Country10" + } + } + }, + { + "id": 12, + "name": "Brand11", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country11" + } + } + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": true, + "startCursor": "Q291bnRyeTEwOjEx", + "endCursor": "Q291bnRyeTExOjEy" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_SecondPage_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_SecondPage_NET8_0.md new file mode 100644 index 00000000000..c18777c97d3 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_SecondPage_NET8_0.md @@ -0,0 +1,68 @@ +# GetDefaultPage_With_Deep_SecondPage + +## SQL 0 + +```sql +-- @__p_0='Country1' +-- @__p_1='2' +-- @__p_2='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +WHERE b."BrandDetails_Country_Name" > @__p_0 OR (b."BrandDetails_Country_Name" = @__p_0 AND b."Id" > @__p_1) +ORDER BY b."BrandDetails_Country_Name", b."Id" +LIMIT @__p_2 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(x => x.BrandDetails.Country.Name).ThenBy(t => t.Id).Where(t => ((t.BrandDetails.Country.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) > 0) OrElse ((t.BrandDetails.Country.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.Id.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.Int32]).value, Int32)) > 0)))).Take(3) +``` + +## Result + +```json +{ + "data": { + "brandsDeep": { + "edges": [ + { + "cursor": "Q291bnRyeTEwOjEx" + }, + { + "cursor": "Q291bnRyeTExOjEy" + } + ], + "nodes": [ + { + "id": 11, + "name": "Brand10", + "displayName": "BrandDisplay10", + "brandDetails": { + "country": { + "name": "Country10" + } + } + }, + { + "id": 12, + "name": "Brand11", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country11" + } + } + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": true, + "startCursor": "Q291bnRyeTEwOjEx", + "endCursor": "Q291bnRyeTExOjEy" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable.md index 8a50b17ffe7..0945c8540c9 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable.md @@ -1,5 +1,23 @@ # GetDefaultPage_With_Nullable +## SQL 0 + +```sql +-- @__p_0='11' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."AlwaysNull", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(x => x.AlwaysNull).ThenBy(t => t.Id).Take(11) +``` + +## Result + ```json { "data": { @@ -148,3 +166,4 @@ } } ``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback.md index efc772eb931..ab5f7965f40 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback.md @@ -1,5 +1,23 @@ # GetDefaultPage_With_Nullable_Fallback +## SQL 0 + +```sql +-- @__p_0='11' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY COALESCE(b."DisplayName", b."Name"), b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => (t.DisplayName ?? t.Name)).ThenBy(t => t.Id).Take(11) +``` + +## Result + ```json { "data": { @@ -148,3 +166,4 @@ } } ``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_NET7_0.md new file mode 100644 index 00000000000..84621cf9bb9 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_NET7_0.md @@ -0,0 +1,169 @@ +# GetDefaultPage_With_Nullable_Fallback + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY COALESCE(b."DisplayName", b."Name"), b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => (t.DisplayName ?? t.Name)).ThenBy(t => t.Id).Take(11) +``` + +## Result + +```json +{ + "data": { + "brandsNullableFallback": { + "edges": [ + { + "cursor": "QnJhbmQxOjI=" + }, + { + "cursor": "QnJhbmQxMToxMg==" + }, + { + "cursor": "QnJhbmQxMzoxNA==" + }, + { + "cursor": "QnJhbmQxNToxNg==" + }, + { + "cursor": "QnJhbmQxNzoxOA==" + }, + { + "cursor": "QnJhbmQxOToyMA==" + }, + { + "cursor": "QnJhbmQyMToyMg==" + }, + { + "cursor": "QnJhbmQyMzoyNA==" + }, + { + "cursor": "QnJhbmQyNToyNg==" + }, + { + "cursor": "QnJhbmQyNzoyOA==" + } + ], + "nodes": [ + { + "id": 2, + "name": "Brand1", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country1" + } + } + }, + { + "id": 12, + "name": "Brand11", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country11" + } + } + }, + { + "id": 14, + "name": "Brand13", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country13" + } + } + }, + { + "id": 16, + "name": "Brand15", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country15" + } + } + }, + { + "id": 18, + "name": "Brand17", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country17" + } + } + }, + { + "id": 20, + "name": "Brand19", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country19" + } + } + }, + { + "id": 22, + "name": "Brand21", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country21" + } + } + }, + { + "id": 24, + "name": "Brand23", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country23" + } + } + }, + { + "id": 26, + "name": "Brand25", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country25" + } + } + }, + { + "id": 28, + "name": "Brand27", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country27" + } + } + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "QnJhbmQxOjI=", + "endCursor": "QnJhbmQyNzoyOA==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_NET8_0.md new file mode 100644 index 00000000000..84621cf9bb9 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_NET8_0.md @@ -0,0 +1,169 @@ +# GetDefaultPage_With_Nullable_Fallback + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY COALESCE(b."DisplayName", b."Name"), b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => (t.DisplayName ?? t.Name)).ThenBy(t => t.Id).Take(11) +``` + +## Result + +```json +{ + "data": { + "brandsNullableFallback": { + "edges": [ + { + "cursor": "QnJhbmQxOjI=" + }, + { + "cursor": "QnJhbmQxMToxMg==" + }, + { + "cursor": "QnJhbmQxMzoxNA==" + }, + { + "cursor": "QnJhbmQxNToxNg==" + }, + { + "cursor": "QnJhbmQxNzoxOA==" + }, + { + "cursor": "QnJhbmQxOToyMA==" + }, + { + "cursor": "QnJhbmQyMToyMg==" + }, + { + "cursor": "QnJhbmQyMzoyNA==" + }, + { + "cursor": "QnJhbmQyNToyNg==" + }, + { + "cursor": "QnJhbmQyNzoyOA==" + } + ], + "nodes": [ + { + "id": 2, + "name": "Brand1", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country1" + } + } + }, + { + "id": 12, + "name": "Brand11", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country11" + } + } + }, + { + "id": 14, + "name": "Brand13", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country13" + } + } + }, + { + "id": 16, + "name": "Brand15", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country15" + } + } + }, + { + "id": 18, + "name": "Brand17", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country17" + } + } + }, + { + "id": 20, + "name": "Brand19", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country19" + } + } + }, + { + "id": 22, + "name": "Brand21", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country21" + } + } + }, + { + "id": 24, + "name": "Brand23", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country23" + } + } + }, + { + "id": 26, + "name": "Brand25", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country25" + } + } + }, + { + "id": 28, + "name": "Brand27", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country27" + } + } + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "QnJhbmQxOjI=", + "endCursor": "QnJhbmQyNzoyOA==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_SecondPage.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_SecondPage.md index 6abc84a8047..482ed6ab460 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_SecondPage.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_SecondPage.md @@ -1,5 +1,26 @@ # GetDefaultPage_With_Nullable_Fallback_SecondPage +## SQL 0 + +```sql +-- @__p_0='Brand11' +-- @__p_1='12' +-- @__p_2='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +WHERE (COALESCE(b."DisplayName", b."Name") > @__p_0) OR ((COALESCE(b."DisplayName", b."Name") = @__p_0) AND (b."Id" > @__p_1)) +ORDER BY COALESCE(b."DisplayName", b."Name"), b."Id" +LIMIT @__p_2 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => (t.DisplayName ?? t.Name)).ThenBy(t => t.Id).Where(t => (((t.DisplayName ?? t.Name).CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) > 0) OrElse (((t.DisplayName ?? t.Name).CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.Id.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.Int32]).value, Int32)) > 0)))).Take(3) +``` + +## Result + ```json { "data": { @@ -44,3 +65,4 @@ } } ``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_SecondPage_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_SecondPage_NET7_0.md new file mode 100644 index 00000000000..6b40ccce2c4 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_SecondPage_NET7_0.md @@ -0,0 +1,68 @@ +# GetDefaultPage_With_Nullable_Fallback_SecondPage + +## SQL 0 + +```sql +-- @__p_0='Brand11' +-- @__p_1='12' +-- @__p_2='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +WHERE COALESCE(b."DisplayName", b."Name") > @__p_0 OR (COALESCE(b."DisplayName", b."Name") = @__p_0 AND b."Id" > @__p_1) +ORDER BY COALESCE(b."DisplayName", b."Name"), b."Id" +LIMIT @__p_2 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => (t.DisplayName ?? t.Name)).ThenBy(t => t.Id).Where(t => (((t.DisplayName ?? t.Name).CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) > 0) OrElse (((t.DisplayName ?? t.Name).CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.Id.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.Int32]).value, Int32)) > 0)))).Take(3) +``` + +## Result + +```json +{ + "data": { + "brandsNullableFallback": { + "edges": [ + { + "cursor": "QnJhbmQxMzoxNA==" + }, + { + "cursor": "QnJhbmQxNToxNg==" + } + ], + "nodes": [ + { + "id": 14, + "name": "Brand13", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country13" + } + } + }, + { + "id": 16, + "name": "Brand15", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country15" + } + } + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": true, + "startCursor": "QnJhbmQxMzoxNA==", + "endCursor": "QnJhbmQxNToxNg==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_SecondPage_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_SecondPage_NET8_0.md new file mode 100644 index 00000000000..6b40ccce2c4 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_SecondPage_NET8_0.md @@ -0,0 +1,68 @@ +# GetDefaultPage_With_Nullable_Fallback_SecondPage + +## SQL 0 + +```sql +-- @__p_0='Brand11' +-- @__p_1='12' +-- @__p_2='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +WHERE COALESCE(b."DisplayName", b."Name") > @__p_0 OR (COALESCE(b."DisplayName", b."Name") = @__p_0 AND b."Id" > @__p_1) +ORDER BY COALESCE(b."DisplayName", b."Name"), b."Id" +LIMIT @__p_2 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => (t.DisplayName ?? t.Name)).ThenBy(t => t.Id).Where(t => (((t.DisplayName ?? t.Name).CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) > 0) OrElse (((t.DisplayName ?? t.Name).CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.Id.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.Int32]).value, Int32)) > 0)))).Take(3) +``` + +## Result + +```json +{ + "data": { + "brandsNullableFallback": { + "edges": [ + { + "cursor": "QnJhbmQxMzoxNA==" + }, + { + "cursor": "QnJhbmQxNToxNg==" + } + ], + "nodes": [ + { + "id": 14, + "name": "Brand13", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country13" + } + } + }, + { + "id": 16, + "name": "Brand15", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country15" + } + } + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": true, + "startCursor": "QnJhbmQxMzoxNA==", + "endCursor": "QnJhbmQxNToxNg==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_NET7_0.md new file mode 100644 index 00000000000..8894d6b129d --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_NET7_0.md @@ -0,0 +1,169 @@ +# GetDefaultPage_With_Nullable + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."AlwaysNull", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(x => x.AlwaysNull).ThenBy(t => t.Id).Take(11) +``` + +## Result + +```json +{ + "data": { + "brandsNullable": { + "edges": [ + { + "cursor": "QnJhbmQwOlxudWxsOjE=" + }, + { + "cursor": "QnJhbmQxOlxudWxsOjI=" + }, + { + "cursor": "QnJhbmQxMDpcbnVsbDoxMQ==" + }, + { + "cursor": "QnJhbmQxMTpcbnVsbDoxMg==" + }, + { + "cursor": "QnJhbmQxMjpcbnVsbDoxMw==" + }, + { + "cursor": "QnJhbmQxMzpcbnVsbDoxNA==" + }, + { + "cursor": "QnJhbmQxNDpcbnVsbDoxNQ==" + }, + { + "cursor": "QnJhbmQxNTpcbnVsbDoxNg==" + }, + { + "cursor": "QnJhbmQxNjpcbnVsbDoxNw==" + }, + { + "cursor": "QnJhbmQxNzpcbnVsbDoxOA==" + } + ], + "nodes": [ + { + "id": 1, + "name": "Brand0", + "displayName": "BrandDisplay0", + "brandDetails": { + "country": { + "name": "Country0" + } + } + }, + { + "id": 2, + "name": "Brand1", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country1" + } + } + }, + { + "id": 11, + "name": "Brand10", + "displayName": "BrandDisplay10", + "brandDetails": { + "country": { + "name": "Country10" + } + } + }, + { + "id": 12, + "name": "Brand11", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country11" + } + } + }, + { + "id": 13, + "name": "Brand12", + "displayName": "BrandDisplay12", + "brandDetails": { + "country": { + "name": "Country12" + } + } + }, + { + "id": 14, + "name": "Brand13", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country13" + } + } + }, + { + "id": 15, + "name": "Brand14", + "displayName": "BrandDisplay14", + "brandDetails": { + "country": { + "name": "Country14" + } + } + }, + { + "id": 16, + "name": "Brand15", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country15" + } + } + }, + { + "id": 17, + "name": "Brand16", + "displayName": "BrandDisplay16", + "brandDetails": { + "country": { + "name": "Country16" + } + } + }, + { + "id": 18, + "name": "Brand17", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country17" + } + } + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "QnJhbmQwOlxudWxsOjE=", + "endCursor": "QnJhbmQxNzpcbnVsbDoxOA==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_NET8_0.md new file mode 100644 index 00000000000..8894d6b129d --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_NET8_0.md @@ -0,0 +1,169 @@ +# GetDefaultPage_With_Nullable + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."AlwaysNull", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(x => x.AlwaysNull).ThenBy(t => t.Id).Take(11) +``` + +## Result + +```json +{ + "data": { + "brandsNullable": { + "edges": [ + { + "cursor": "QnJhbmQwOlxudWxsOjE=" + }, + { + "cursor": "QnJhbmQxOlxudWxsOjI=" + }, + { + "cursor": "QnJhbmQxMDpcbnVsbDoxMQ==" + }, + { + "cursor": "QnJhbmQxMTpcbnVsbDoxMg==" + }, + { + "cursor": "QnJhbmQxMjpcbnVsbDoxMw==" + }, + { + "cursor": "QnJhbmQxMzpcbnVsbDoxNA==" + }, + { + "cursor": "QnJhbmQxNDpcbnVsbDoxNQ==" + }, + { + "cursor": "QnJhbmQxNTpcbnVsbDoxNg==" + }, + { + "cursor": "QnJhbmQxNjpcbnVsbDoxNw==" + }, + { + "cursor": "QnJhbmQxNzpcbnVsbDoxOA==" + } + ], + "nodes": [ + { + "id": 1, + "name": "Brand0", + "displayName": "BrandDisplay0", + "brandDetails": { + "country": { + "name": "Country0" + } + } + }, + { + "id": 2, + "name": "Brand1", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country1" + } + } + }, + { + "id": 11, + "name": "Brand10", + "displayName": "BrandDisplay10", + "brandDetails": { + "country": { + "name": "Country10" + } + } + }, + { + "id": 12, + "name": "Brand11", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country11" + } + } + }, + { + "id": 13, + "name": "Brand12", + "displayName": "BrandDisplay12", + "brandDetails": { + "country": { + "name": "Country12" + } + } + }, + { + "id": 14, + "name": "Brand13", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country13" + } + } + }, + { + "id": 15, + "name": "Brand14", + "displayName": "BrandDisplay14", + "brandDetails": { + "country": { + "name": "Country14" + } + } + }, + { + "id": 16, + "name": "Brand15", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country15" + } + } + }, + { + "id": 17, + "name": "Brand16", + "displayName": "BrandDisplay16", + "brandDetails": { + "country": { + "name": "Country16" + } + } + }, + { + "id": 18, + "name": "Brand17", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country17" + } + } + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "QnJhbmQwOlxudWxsOjE=", + "endCursor": "QnJhbmQxNzpcbnVsbDoxOA==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_SecondPage.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_SecondPage.md index 3f2dd1ad8da..c75b3a87354 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_SecondPage.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_SecondPage.md @@ -1,5 +1,26 @@ # GetDefaultPage_With_Nullable_SecondPage +## SQL 0 + +```sql +-- @__p_0='Brand10' +-- @__p_2='11' +-- @__p_3='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +WHERE ((b."Name" > @__p_0) OR ((b."Name" = @__p_0) AND (b."AlwaysNull" > NULL))) OR (((b."Name" = @__p_0) AND ((b."AlwaysNull" IS NULL))) AND (b."Id" > @__p_2)) +ORDER BY b."Name", b."AlwaysNull", b."Id" +LIMIT @__p_3 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(x => x.AlwaysNull).ThenBy(t => t.Id).Where(t => (((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) > 0) OrElse ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.AlwaysNull.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) > 0))) OrElse (((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.AlwaysNull.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0)) AndAlso (t.Id.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.Int32]).value, Int32)) > 0)))).Take(3) +``` + +## Result + ```json { "data": { @@ -44,3 +65,4 @@ } } ``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_SecondPage_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_SecondPage_NET7_0.md new file mode 100644 index 00000000000..9b2146b8a6a --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_SecondPage_NET7_0.md @@ -0,0 +1,68 @@ +# GetDefaultPage_With_Nullable_SecondPage + +## SQL 0 + +```sql +-- @__p_0='Brand10' +-- @__p_2='11' +-- @__p_3='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +WHERE b."Name" > @__p_0 OR (b."Name" = @__p_0 AND b."AlwaysNull" > NULL) OR (b."Name" = @__p_0 AND (b."AlwaysNull" IS NULL) AND b."Id" > @__p_2) +ORDER BY b."Name", b."AlwaysNull", b."Id" +LIMIT @__p_3 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(x => x.AlwaysNull).ThenBy(t => t.Id).Where(t => (((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) > 0) OrElse ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.AlwaysNull.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) > 0))) OrElse (((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.AlwaysNull.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0)) AndAlso (t.Id.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.Int32]).value, Int32)) > 0)))).Take(3) +``` + +## Result + +```json +{ + "data": { + "brandsNullable": { + "edges": [ + { + "cursor": "QnJhbmQxMTpcbnVsbDoxMg==" + }, + { + "cursor": "QnJhbmQxMjpcbnVsbDoxMw==" + } + ], + "nodes": [ + { + "id": 12, + "name": "Brand11", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country11" + } + } + }, + { + "id": 13, + "name": "Brand12", + "displayName": "BrandDisplay12", + "brandDetails": { + "country": { + "name": "Country12" + } + } + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": true, + "startCursor": "QnJhbmQxMTpcbnVsbDoxMg==", + "endCursor": "QnJhbmQxMjpcbnVsbDoxMw==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_SecondPage_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_SecondPage_NET8_0.md new file mode 100644 index 00000000000..5da7d78b12a --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_SecondPage_NET8_0.md @@ -0,0 +1,68 @@ +# GetDefaultPage_With_Nullable_SecondPage + +## SQL 0 + +```sql +-- @__p_0='Brand10' +-- @__p_2='11' +-- @__p_3='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +WHERE b."Name" > @__p_0 OR (b."Name" = @__p_0 AND b."AlwaysNull" > NULL) OR (b."Name" = @__p_0 AND b."AlwaysNull" IS NULL AND b."Id" > @__p_2) +ORDER BY b."Name", b."AlwaysNull", b."Id" +LIMIT @__p_3 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(x => x.AlwaysNull).ThenBy(t => t.Id).Where(t => (((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) > 0) OrElse ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.AlwaysNull.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) > 0))) OrElse (((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.AlwaysNull.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0)) AndAlso (t.Id.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.Int32]).value, Int32)) > 0)))).Take(3) +``` + +## Result + +```json +{ + "data": { + "brandsNullable": { + "edges": [ + { + "cursor": "QnJhbmQxMTpcbnVsbDoxMg==" + }, + { + "cursor": "QnJhbmQxMjpcbnVsbDoxMw==" + } + ], + "nodes": [ + { + "id": 12, + "name": "Brand11", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country11" + } + } + }, + { + "id": 13, + "name": "Brand12", + "displayName": "BrandDisplay12", + "brandDetails": { + "country": { + "name": "Country12" + } + } + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": true, + "startCursor": "QnJhbmQxMTpcbnVsbDoxMg==", + "endCursor": "QnJhbmQxMjpcbnVsbDoxMw==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetSecondPage_With_2_Items.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetSecondPage_With_2_Items.md index 2b23d4ec62c..59c8beda14f 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetSecondPage_With_2_Items.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetSecondPage_With_2_Items.md @@ -1,5 +1,26 @@ # GetSecondPage_With_2_Items +## SQL 0 + +```sql +-- @__p_0='Brand17' +-- @__p_1='18' +-- @__p_2='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +WHERE (b."Name" > @__p_0) OR ((b."Name" = @__p_0) AND (b."Id" > @__p_1)) +ORDER BY b."Name", b."Id" +LIMIT @__p_2 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Where(t => ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) > 0) OrElse ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.Id.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.Int32]).value, Int32)) > 0)))).Take(3) +``` + +## Result + ```json { "data": { @@ -24,3 +45,4 @@ } } ``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetSecondPage_With_2_Items_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetSecondPage_With_2_Items_NET7_0.md new file mode 100644 index 00000000000..94747f17c6a --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetSecondPage_With_2_Items_NET7_0.md @@ -0,0 +1,48 @@ +# GetSecondPage_With_2_Items + +## SQL 0 + +```sql +-- @__p_0='Brand17' +-- @__p_1='18' +-- @__p_2='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +WHERE b."Name" > @__p_0 OR (b."Name" = @__p_0 AND b."Id" > @__p_1) +ORDER BY b."Name", b."Id" +LIMIT @__p_2 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Where(t => ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) > 0) OrElse ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.Id.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.Int32]).value, Int32)) > 0)))).Take(3) +``` + +## Result + +```json +{ + "data": { + "brands": { + "nodes": [ + { + "id": 19, + "name": "Brand18" + }, + { + "id": 20, + "name": "Brand19" + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": true, + "startCursor": "QnJhbmQxODoxOQ==", + "endCursor": "QnJhbmQxOToyMA==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetSecondPage_With_2_Items_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetSecondPage_With_2_Items_NET8_0.md new file mode 100644 index 00000000000..94747f17c6a --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetSecondPage_With_2_Items_NET8_0.md @@ -0,0 +1,48 @@ +# GetSecondPage_With_2_Items + +## SQL 0 + +```sql +-- @__p_0='Brand17' +-- @__p_1='18' +-- @__p_2='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +WHERE b."Name" > @__p_0 OR (b."Name" = @__p_0 AND b."Id" > @__p_1) +ORDER BY b."Name", b."Id" +LIMIT @__p_2 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Where(t => ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) > 0) OrElse ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.Id.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.Int32]).value, Int32)) > 0)))).Take(3) +``` + +## Result + +```json +{ + "data": { + "brands": { + "nodes": [ + { + "id": 19, + "name": "Brand18" + }, + { + "id": 20, + "name": "Brand19" + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": true, + "startCursor": "QnJhbmQxODoxOQ==", + "endCursor": "QnJhbmQxOToyMA==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto.md index 5073d8c02fd..739a6c46e34 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto.md @@ -1,5 +1,23 @@ # Map_Page_To_Connection_With_Dto +## SQL 0 + +```sql +-- @__p_0='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3) +``` + +## Result 3 + ```json { "data": { @@ -26,3 +44,4 @@ } } ``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_2.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_2.md index 6a4cd2863d0..90c0eb3f43f 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_2.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_2.md @@ -1,5 +1,23 @@ # Map_Page_To_Connection_With_Dto_2 +## SQL 0 + +```sql +-- @__p_0='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3) +``` + +## Result 3 + ```json { "data": { @@ -26,3 +44,4 @@ } } ``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_2_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_2_NET7_0.md new file mode 100644 index 00000000000..0c6e949bacf --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_2_NET7_0.md @@ -0,0 +1,47 @@ +# Map_Page_To_Connection_With_Dto_2 + +## SQL 0 + +```sql +-- @__p_0='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3) +``` + +## Result 3 + +```json +{ + "data": { + "brands": { + "edges": [ + { + "cursor": "QnJhbmQwOjE=", + "displayName": "BrandDisplay0", + "node": { + "id": 1, + "name": "Brand0" + } + }, + { + "cursor": "QnJhbmQxOjI=", + "displayName": null, + "node": { + "id": 2, + "name": "Brand1" + } + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_2_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_2_NET8_0.md new file mode 100644 index 00000000000..0c6e949bacf --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_2_NET8_0.md @@ -0,0 +1,47 @@ +# Map_Page_To_Connection_With_Dto_2 + +## SQL 0 + +```sql +-- @__p_0='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3) +``` + +## Result 3 + +```json +{ + "data": { + "brands": { + "edges": [ + { + "cursor": "QnJhbmQwOjE=", + "displayName": "BrandDisplay0", + "node": { + "id": 1, + "name": "Brand0" + } + }, + { + "cursor": "QnJhbmQxOjI=", + "displayName": null, + "node": { + "id": 2, + "name": "Brand1" + } + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_NET7_0.md new file mode 100644 index 00000000000..808bca98e86 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_NET7_0.md @@ -0,0 +1,47 @@ +# Map_Page_To_Connection_With_Dto + +## SQL 0 + +```sql +-- @__p_0='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3) +``` + +## Result 3 + +```json +{ + "data": { + "brands": { + "edges": [ + { + "cursor": "QnJhbmQwOjE=", + "displayName": "BrandDisplay0", + "node": { + "id": 1, + "name": "Brand0" + } + }, + { + "cursor": "QnJhbmQxOjI=", + "displayName": null, + "node": { + "id": 2, + "name": "Brand1" + } + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_NET8_0.md new file mode 100644 index 00000000000..808bca98e86 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_NET8_0.md @@ -0,0 +1,47 @@ +# Map_Page_To_Connection_With_Dto + +## SQL 0 + +```sql +-- @__p_0='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3) +``` + +## Result 3 + +```json +{ + "data": { + "brands": { + "edges": [ + { + "cursor": "QnJhbmQwOjE=", + "displayName": "BrandDisplay0", + "node": { + "id": 1, + "name": "Brand0" + } + }, + { + "cursor": "QnJhbmQxOjI=", + "displayName": null, + "node": { + "id": 2, + "name": "Brand1" + } + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2.md index 63d6cb3d64c..43893634212 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2.md @@ -1,16 +1,50 @@ # Nested_Paging_First_2 -## SQL +## SQL 0 ```sql --- @__keys_0={ '2', '1' } (DbType = Object) -SELECT p."Id", p."AvailableStock", p."BrandId", p."Description", p."ImageFileName", p."MaxStockThreshold", p."Name", p."OnReorder", p."Price", p."RestockThreshold", p."TypeId" -FROM "Products" AS p -WHERE p."BrandId" = ANY (@__keys_0) -ORDER BY p."Name", p."Id" +-- @__p_0='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 ``` -## Result 2 +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3) +``` + +## SQL 1 + +```sql +SELECT t."BrandId", t0."Id", t0."AvailableStock", t0."BrandId", t0."Description", t0."ImageFileName", t0."MaxStockThreshold", t0."Name", t0."OnReorder", t0."Price", t0."RestockThreshold", t0."TypeId" +FROM ( + SELECT p."BrandId" + FROM "Products" AS p + WHERE p."BrandId" IN (2, 1) + GROUP BY p."BrandId" +) AS t +LEFT JOIN ( + SELECT t1."Id", t1."AvailableStock", t1."BrandId", t1."Description", t1."ImageFileName", t1."MaxStockThreshold", t1."Name", t1."OnReorder", t1."Price", t1."RestockThreshold", t1."TypeId" + FROM ( + SELECT p0."Id", p0."AvailableStock", p0."BrandId", p0."Description", p0."ImageFileName", p0."MaxStockThreshold", p0."Name", p0."OnReorder", p0."Price", p0."RestockThreshold", p0."TypeId", ROW_NUMBER() OVER(PARTITION BY p0."BrandId" ORDER BY p0."Name", p0."Id") AS row + FROM "Products" AS p0 + WHERE p0."BrandId" IN (2, 1) + ) AS t1 + WHERE t1.row <= 3 +) AS t0 ON t."BrandId" = t0."BrandId" +ORDER BY t."BrandId", t0."BrandId", t0."Name", t0."Id" +``` + +## Expression 1 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].Where(t => value(HotChocolate.Data.IntegrationPagingHelperTests+ProductsByBrandDataLoader+<>c__DisplayClass2_0).keys.Contains(t.BrandId)).GroupBy(t => t.BrandId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3).ToList()}) +``` + +## Result 5 ```json { diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_NET7_0.md new file mode 100644 index 00000000000..7b14e58f1bc --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_NET7_0.md @@ -0,0 +1,103 @@ +# Nested_Paging_First_2 + +## SQL 0 + +```sql +-- @__p_0='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3) +``` + +## SQL 1 + +```sql +SELECT t."BrandId", t0."Id", t0."AvailableStock", t0."BrandId", t0."Description", t0."ImageFileName", t0."MaxStockThreshold", t0."Name", t0."OnReorder", t0."Price", t0."RestockThreshold", t0."TypeId" +FROM ( + SELECT p."BrandId" + FROM "Products" AS p + WHERE p."BrandId" IN (2, 1) + GROUP BY p."BrandId" +) AS t +LEFT JOIN ( + SELECT t1."Id", t1."AvailableStock", t1."BrandId", t1."Description", t1."ImageFileName", t1."MaxStockThreshold", t1."Name", t1."OnReorder", t1."Price", t1."RestockThreshold", t1."TypeId" + FROM ( + SELECT p0."Id", p0."AvailableStock", p0."BrandId", p0."Description", p0."ImageFileName", p0."MaxStockThreshold", p0."Name", p0."OnReorder", p0."Price", p0."RestockThreshold", p0."TypeId", ROW_NUMBER() OVER(PARTITION BY p0."BrandId" ORDER BY p0."Name", p0."Id") AS row + FROM "Products" AS p0 + WHERE p0."BrandId" IN (2, 1) + ) AS t1 + WHERE t1.row <= 3 +) AS t0 ON t."BrandId" = t0."BrandId" +ORDER BY t."BrandId", t0."BrandId", t0."Name", t0."Id" +``` + +## Expression 1 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Where(t => value(HotChocolate.Data.IntegrationPagingHelperTests+ProductsByBrandDataLoader+<>c__DisplayClass2_0).keys.Contains(t.BrandId)).GroupBy(t => t.BrandId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3).ToList()}) +``` + +## Result 5 + +```json +{ + "data": { + "brands": { + "edges": [ + { + "cursor": "QnJhbmQwOjE=" + }, + { + "cursor": "QnJhbmQxOjI=" + } + ], + "nodes": [ + { + "products": { + "nodes": [ + { + "name": "Product 0-0" + }, + { + "name": "Product 0-1" + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "UHJvZHVjdCAwLTA6MQ==", + "endCursor": "UHJvZHVjdCAwLTE6Mg==" + } + } + }, + { + "products": { + "nodes": [ + { + "name": "Product 1-0" + }, + { + "name": "Product 1-1" + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "UHJvZHVjdCAxLTA6MTAx", + "endCursor": "UHJvZHVjdCAxLTE6MTAy" + } + } + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_NET8_0.md new file mode 100644 index 00000000000..79ffbfeb8f4 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_NET8_0.md @@ -0,0 +1,104 @@ +# Nested_Paging_First_2 + +## SQL 0 + +```sql +-- @__p_0='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3) +``` + +## SQL 1 + +```sql +-- @__keys_0={ '2', '1' } (DbType = Object) +SELECT t."BrandId", t0."Id", t0."AvailableStock", t0."BrandId", t0."Description", t0."ImageFileName", t0."MaxStockThreshold", t0."Name", t0."OnReorder", t0."Price", t0."RestockThreshold", t0."TypeId" +FROM ( + SELECT p."BrandId" + FROM "Products" AS p + WHERE p."BrandId" = ANY (@__keys_0) + GROUP BY p."BrandId" +) AS t +LEFT JOIN ( + SELECT t1."Id", t1."AvailableStock", t1."BrandId", t1."Description", t1."ImageFileName", t1."MaxStockThreshold", t1."Name", t1."OnReorder", t1."Price", t1."RestockThreshold", t1."TypeId" + FROM ( + SELECT p0."Id", p0."AvailableStock", p0."BrandId", p0."Description", p0."ImageFileName", p0."MaxStockThreshold", p0."Name", p0."OnReorder", p0."Price", p0."RestockThreshold", p0."TypeId", ROW_NUMBER() OVER(PARTITION BY p0."BrandId" ORDER BY p0."Name", p0."Id") AS row + FROM "Products" AS p0 + WHERE p0."BrandId" = ANY (@__keys_0) + ) AS t1 + WHERE t1.row <= 3 +) AS t0 ON t."BrandId" = t0."BrandId" +ORDER BY t."BrandId", t0."BrandId", t0."Name", t0."Id" +``` + +## Expression 1 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Where(t => value(HotChocolate.Data.IntegrationPagingHelperTests+ProductsByBrandDataLoader+<>c__DisplayClass2_0).keys.Contains(t.BrandId)).GroupBy(t => t.BrandId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3).ToList()}) +``` + +## Result 5 + +```json +{ + "data": { + "brands": { + "edges": [ + { + "cursor": "QnJhbmQwOjE=" + }, + { + "cursor": "QnJhbmQxOjI=" + } + ], + "nodes": [ + { + "products": { + "nodes": [ + { + "name": "Product 0-0" + }, + { + "name": "Product 0-1" + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "UHJvZHVjdCAwLTA6MQ==", + "endCursor": "UHJvZHVjdCAwLTE6Mg==" + } + } + }, + { + "products": { + "nodes": [ + { + "name": "Product 1-0" + }, + { + "name": "Product 1-1" + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "UHJvZHVjdCAxLTA6MTAx", + "endCursor": "UHJvZHVjdCAxLTE6MTAy" + } + } + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_NET9_0.md new file mode 100644 index 00000000000..43893634212 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_NET9_0.md @@ -0,0 +1,103 @@ +# Nested_Paging_First_2 + +## SQL 0 + +```sql +-- @__p_0='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3) +``` + +## SQL 1 + +```sql +SELECT t."BrandId", t0."Id", t0."AvailableStock", t0."BrandId", t0."Description", t0."ImageFileName", t0."MaxStockThreshold", t0."Name", t0."OnReorder", t0."Price", t0."RestockThreshold", t0."TypeId" +FROM ( + SELECT p."BrandId" + FROM "Products" AS p + WHERE p."BrandId" IN (2, 1) + GROUP BY p."BrandId" +) AS t +LEFT JOIN ( + SELECT t1."Id", t1."AvailableStock", t1."BrandId", t1."Description", t1."ImageFileName", t1."MaxStockThreshold", t1."Name", t1."OnReorder", t1."Price", t1."RestockThreshold", t1."TypeId" + FROM ( + SELECT p0."Id", p0."AvailableStock", p0."BrandId", p0."Description", p0."ImageFileName", p0."MaxStockThreshold", p0."Name", p0."OnReorder", p0."Price", p0."RestockThreshold", p0."TypeId", ROW_NUMBER() OVER(PARTITION BY p0."BrandId" ORDER BY p0."Name", p0."Id") AS row + FROM "Products" AS p0 + WHERE p0."BrandId" IN (2, 1) + ) AS t1 + WHERE t1.row <= 3 +) AS t0 ON t."BrandId" = t0."BrandId" +ORDER BY t."BrandId", t0."BrandId", t0."Name", t0."Id" +``` + +## Expression 1 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].Where(t => value(HotChocolate.Data.IntegrationPagingHelperTests+ProductsByBrandDataLoader+<>c__DisplayClass2_0).keys.Contains(t.BrandId)).GroupBy(t => t.BrandId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3).ToList()}) +``` + +## Result 5 + +```json +{ + "data": { + "brands": { + "edges": [ + { + "cursor": "QnJhbmQwOjE=" + }, + { + "cursor": "QnJhbmQxOjI=" + } + ], + "nodes": [ + { + "products": { + "nodes": [ + { + "name": "Product 0-0" + }, + { + "name": "Product 0-1" + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "UHJvZHVjdCAwLTA6MQ==", + "endCursor": "UHJvZHVjdCAwLTE6Mg==" + } + } + }, + { + "products": { + "nodes": [ + { + "name": "Product 1-0" + }, + { + "name": "Product 1-1" + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "UHJvZHVjdCAxLTA6MTAx", + "endCursor": "UHJvZHVjdCAxLTE6MTAy" + } + } + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections.md index de411971bc6..4e9f3ffd421 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections.md @@ -1,16 +1,50 @@ # Nested_Paging_First_2_With_Projections -## SQL +## SQL 0 ```sql --- @__keys_0={ '2', '1' } (DbType = Object) -SELECT p."Id", p."AvailableStock", p."BrandId", p."Description", p."ImageFileName", p."MaxStockThreshold", p."Name", p."OnReorder", p."Price", p."RestockThreshold", p."TypeId" -FROM "Products" AS p -WHERE p."BrandId" = ANY (@__keys_0) -ORDER BY p."Name", p."Id" +-- @__p_0='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 ``` -## Result 2 +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3) +``` + +## SQL 1 + +```sql +SELECT t."BrandId", t0."Name", t0."BrandId", t0."Id" +FROM ( + SELECT p."BrandId" + FROM "Products" AS p + WHERE p."BrandId" IN (2, 1) + GROUP BY p."BrandId" +) AS t +LEFT JOIN ( + SELECT t1."Name", t1."BrandId", t1."Id" + FROM ( + SELECT p0."Name", p0."BrandId", p0."Id", ROW_NUMBER() OVER(PARTITION BY p0."BrandId" ORDER BY p0."Name", p0."Id") AS row + FROM "Products" AS p0 + WHERE p0."BrandId" IN (2, 1) + ) AS t1 + WHERE t1.row <= 3 +) AS t0 ON t."BrandId" = t0."BrandId" +ORDER BY t."BrandId", t0."BrandId", t0."Name", t0."Id" +``` + +## Expression 1 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].Where(t => value(HotChocolate.Data.IntegrationPagingHelperTests+ProductsByBrandDataLoader+<>c__DisplayClass2_0).keys.Contains(t.BrandId)).Select(root => new Product() {Name = IIF((root.Name == null), null, root.Name), BrandId = root.BrandId, Id = root.Id}).GroupBy(t => t.BrandId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3).ToList()}) +``` + +## Result ```json { diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections_NET7_0.md new file mode 100644 index 00000000000..69a894be487 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections_NET7_0.md @@ -0,0 +1,103 @@ +# Nested_Paging_First_2_With_Projections + +## SQL 0 + +```sql +-- @__p_0='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3) +``` + +## SQL 1 + +```sql +SELECT t."BrandId", t0."Name", t0."BrandId", t0."Id" +FROM ( + SELECT p."BrandId" + FROM "Products" AS p + WHERE p."BrandId" IN (2, 1) + GROUP BY p."BrandId" +) AS t +LEFT JOIN ( + SELECT t1."Name", t1."BrandId", t1."Id" + FROM ( + SELECT p0."Name", p0."BrandId", p0."Id", ROW_NUMBER() OVER(PARTITION BY p0."BrandId" ORDER BY p0."Name", p0."Id") AS row + FROM "Products" AS p0 + WHERE p0."BrandId" IN (2, 1) + ) AS t1 + WHERE t1.row <= 3 +) AS t0 ON t."BrandId" = t0."BrandId" +ORDER BY t."BrandId", t0."BrandId", t0."Name", t0."Id" +``` + +## Expression 1 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Where(t => value(HotChocolate.Data.IntegrationPagingHelperTests+ProductsByBrandDataLoader+<>c__DisplayClass2_0).keys.Contains(t.BrandId)).Select(root => new Product() {Name = IIF((root.Name == null), null, root.Name), BrandId = root.BrandId, Id = root.Id}).GroupBy(t => t.BrandId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3).ToList()}) +``` + +## Result + +```json +{ + "data": { + "brands": { + "edges": [ + { + "cursor": "QnJhbmQwOjE=" + }, + { + "cursor": "QnJhbmQxOjI=" + } + ], + "nodes": [ + { + "products": { + "nodes": [ + { + "name": "Product 0-0" + }, + { + "name": "Product 0-1" + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "UHJvZHVjdCAwLTA6MQ==", + "endCursor": "UHJvZHVjdCAwLTE6Mg==" + } + } + }, + { + "products": { + "nodes": [ + { + "name": "Product 1-0" + }, + { + "name": "Product 1-1" + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "UHJvZHVjdCAxLTA6MTAx", + "endCursor": "UHJvZHVjdCAxLTE6MTAy" + } + } + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections_NET8_0.md new file mode 100644 index 00000000000..04fc9c6aee0 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections_NET8_0.md @@ -0,0 +1,104 @@ +# Nested_Paging_First_2_With_Projections + +## SQL 0 + +```sql +-- @__p_0='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3) +``` + +## SQL 1 + +```sql +-- @__keys_0={ '2', '1' } (DbType = Object) +SELECT t."BrandId", t0."Name", t0."BrandId", t0."Id" +FROM ( + SELECT p."BrandId" + FROM "Products" AS p + WHERE p."BrandId" = ANY (@__keys_0) + GROUP BY p."BrandId" +) AS t +LEFT JOIN ( + SELECT t1."Name", t1."BrandId", t1."Id" + FROM ( + SELECT p0."Name", p0."BrandId", p0."Id", ROW_NUMBER() OVER(PARTITION BY p0."BrandId" ORDER BY p0."Name", p0."Id") AS row + FROM "Products" AS p0 + WHERE p0."BrandId" = ANY (@__keys_0) + ) AS t1 + WHERE t1.row <= 3 +) AS t0 ON t."BrandId" = t0."BrandId" +ORDER BY t."BrandId", t0."BrandId", t0."Name", t0."Id" +``` + +## Expression 1 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Where(t => value(HotChocolate.Data.IntegrationPagingHelperTests+ProductsByBrandDataLoader+<>c__DisplayClass2_0).keys.Contains(t.BrandId)).Select(root => new Product() {Name = root.Name, BrandId = root.BrandId, Id = root.Id}).GroupBy(t => t.BrandId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3).ToList()}) +``` + +## Result + +```json +{ + "data": { + "brands": { + "edges": [ + { + "cursor": "QnJhbmQwOjE=" + }, + { + "cursor": "QnJhbmQxOjI=" + } + ], + "nodes": [ + { + "products": { + "nodes": [ + { + "name": "Product 0-0" + }, + { + "name": "Product 0-1" + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "UHJvZHVjdCAwLTA6MQ==", + "endCursor": "UHJvZHVjdCAwLTE6Mg==" + } + } + }, + { + "products": { + "nodes": [ + { + "name": "Product 1-0" + }, + { + "name": "Product 1-1" + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "UHJvZHVjdCAxLTA6MTAx", + "endCursor": "UHJvZHVjdCAxLTE6MTAy" + } + } + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections_NET9_0.md new file mode 100644 index 00000000000..4e9f3ffd421 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections_NET9_0.md @@ -0,0 +1,103 @@ +# Nested_Paging_First_2_With_Projections + +## SQL 0 + +```sql +-- @__p_0='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3) +``` + +## SQL 1 + +```sql +SELECT t."BrandId", t0."Name", t0."BrandId", t0."Id" +FROM ( + SELECT p."BrandId" + FROM "Products" AS p + WHERE p."BrandId" IN (2, 1) + GROUP BY p."BrandId" +) AS t +LEFT JOIN ( + SELECT t1."Name", t1."BrandId", t1."Id" + FROM ( + SELECT p0."Name", p0."BrandId", p0."Id", ROW_NUMBER() OVER(PARTITION BY p0."BrandId" ORDER BY p0."Name", p0."Id") AS row + FROM "Products" AS p0 + WHERE p0."BrandId" IN (2, 1) + ) AS t1 + WHERE t1.row <= 3 +) AS t0 ON t."BrandId" = t0."BrandId" +ORDER BY t."BrandId", t0."BrandId", t0."Name", t0."Id" +``` + +## Expression 1 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].Where(t => value(HotChocolate.Data.IntegrationPagingHelperTests+ProductsByBrandDataLoader+<>c__DisplayClass2_0).keys.Contains(t.BrandId)).Select(root => new Product() {Name = IIF((root.Name == null), null, root.Name), BrandId = root.BrandId, Id = root.Id}).GroupBy(t => t.BrandId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3).ToList()}) +``` + +## Result + +```json +{ + "data": { + "brands": { + "edges": [ + { + "cursor": "QnJhbmQwOjE=" + }, + { + "cursor": "QnJhbmQxOjI=" + } + ], + "nodes": [ + { + "products": { + "nodes": [ + { + "name": "Product 0-0" + }, + { + "name": "Product 0-1" + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "UHJvZHVjdCAwLTA6MQ==", + "endCursor": "UHJvZHVjdCAwLTE6Mg==" + } + } + }, + { + "products": { + "nodes": [ + { + "name": "Product 1-0" + }, + { + "name": "Product 1-1" + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "UHJvZHVjdCAxLTA6MTAx", + "endCursor": "UHJvZHVjdCAxLTE6MTAy" + } + } + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Empty_PagingArgs.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Empty_PagingArgs.md index b633c7e95f9..4f7b7a29cd5 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Empty_PagingArgs.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Empty_PagingArgs.md @@ -1,6 +1,20 @@ # Paging_Empty_PagingArgs -## Result 1 +## SQL 0 + +```sql +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id) +``` + +## Result 3 ```json { @@ -13,7 +27,7 @@ } ``` -## Result 2 +## Result 4 ```json [ diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Empty_PagingArgs_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Empty_PagingArgs_NET7_0.md new file mode 100644 index 00000000000..84ceccc8cab --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Empty_PagingArgs_NET7_0.md @@ -0,0 +1,1236 @@ +# Paging_Empty_PagingArgs + +## SQL 0 + +```sql +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id) +``` + +## Result 3 + +```json +{ + "HasNextPage": false, + "HasPreviousPage": false, + "First": 1, + "FirstCursor": "QnJhbmQwOjE=", + "Last": 100, + "LastCursor": "QnJhbmQ5OToxMDA=" +} +``` + +## Result 4 + +```json +[ + { + "Id": 1, + "Name": "Brand0", + "DisplayName": "BrandDisplay0", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country0" + } + } + }, + { + "Id": 2, + "Name": "Brand1", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country1" + } + } + }, + { + "Id": 11, + "Name": "Brand10", + "DisplayName": "BrandDisplay10", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country10" + } + } + }, + { + "Id": 12, + "Name": "Brand11", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country11" + } + } + }, + { + "Id": 13, + "Name": "Brand12", + "DisplayName": "BrandDisplay12", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country12" + } + } + }, + { + "Id": 14, + "Name": "Brand13", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country13" + } + } + }, + { + "Id": 15, + "Name": "Brand14", + "DisplayName": "BrandDisplay14", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country14" + } + } + }, + { + "Id": 16, + "Name": "Brand15", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country15" + } + } + }, + { + "Id": 17, + "Name": "Brand16", + "DisplayName": "BrandDisplay16", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country16" + } + } + }, + { + "Id": 18, + "Name": "Brand17", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country17" + } + } + }, + { + "Id": 19, + "Name": "Brand18", + "DisplayName": "BrandDisplay18", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country18" + } + } + }, + { + "Id": 20, + "Name": "Brand19", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country19" + } + } + }, + { + "Id": 3, + "Name": "Brand2", + "DisplayName": "BrandDisplay2", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country2" + } + } + }, + { + "Id": 21, + "Name": "Brand20", + "DisplayName": "BrandDisplay20", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country20" + } + } + }, + { + "Id": 22, + "Name": "Brand21", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country21" + } + } + }, + { + "Id": 23, + "Name": "Brand22", + "DisplayName": "BrandDisplay22", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country22" + } + } + }, + { + "Id": 24, + "Name": "Brand23", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country23" + } + } + }, + { + "Id": 25, + "Name": "Brand24", + "DisplayName": "BrandDisplay24", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country24" + } + } + }, + { + "Id": 26, + "Name": "Brand25", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country25" + } + } + }, + { + "Id": 27, + "Name": "Brand26", + "DisplayName": "BrandDisplay26", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country26" + } + } + }, + { + "Id": 28, + "Name": "Brand27", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country27" + } + } + }, + { + "Id": 29, + "Name": "Brand28", + "DisplayName": "BrandDisplay28", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country28" + } + } + }, + { + "Id": 30, + "Name": "Brand29", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country29" + } + } + }, + { + "Id": 4, + "Name": "Brand3", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country3" + } + } + }, + { + "Id": 31, + "Name": "Brand30", + "DisplayName": "BrandDisplay30", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country30" + } + } + }, + { + "Id": 32, + "Name": "Brand31", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country31" + } + } + }, + { + "Id": 33, + "Name": "Brand32", + "DisplayName": "BrandDisplay32", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country32" + } + } + }, + { + "Id": 34, + "Name": "Brand33", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country33" + } + } + }, + { + "Id": 35, + "Name": "Brand34", + "DisplayName": "BrandDisplay34", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country34" + } + } + }, + { + "Id": 36, + "Name": "Brand35", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country35" + } + } + }, + { + "Id": 37, + "Name": "Brand36", + "DisplayName": "BrandDisplay36", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country36" + } + } + }, + { + "Id": 38, + "Name": "Brand37", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country37" + } + } + }, + { + "Id": 39, + "Name": "Brand38", + "DisplayName": "BrandDisplay38", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country38" + } + } + }, + { + "Id": 40, + "Name": "Brand39", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country39" + } + } + }, + { + "Id": 5, + "Name": "Brand4", + "DisplayName": "BrandDisplay4", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country4" + } + } + }, + { + "Id": 41, + "Name": "Brand40", + "DisplayName": "BrandDisplay40", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country40" + } + } + }, + { + "Id": 42, + "Name": "Brand41", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country41" + } + } + }, + { + "Id": 43, + "Name": "Brand42", + "DisplayName": "BrandDisplay42", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country42" + } + } + }, + { + "Id": 44, + "Name": "Brand43", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country43" + } + } + }, + { + "Id": 45, + "Name": "Brand44", + "DisplayName": "BrandDisplay44", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country44" + } + } + }, + { + "Id": 46, + "Name": "Brand45", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country45" + } + } + }, + { + "Id": 47, + "Name": "Brand46", + "DisplayName": "BrandDisplay46", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country46" + } + } + }, + { + "Id": 48, + "Name": "Brand47", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country47" + } + } + }, + { + "Id": 49, + "Name": "Brand48", + "DisplayName": "BrandDisplay48", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country48" + } + } + }, + { + "Id": 50, + "Name": "Brand49", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country49" + } + } + }, + { + "Id": 6, + "Name": "Brand5", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country5" + } + } + }, + { + "Id": 51, + "Name": "Brand50", + "DisplayName": "BrandDisplay50", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country50" + } + } + }, + { + "Id": 52, + "Name": "Brand51", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country51" + } + } + }, + { + "Id": 53, + "Name": "Brand52", + "DisplayName": "BrandDisplay52", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country52" + } + } + }, + { + "Id": 54, + "Name": "Brand53", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country53" + } + } + }, + { + "Id": 55, + "Name": "Brand54", + "DisplayName": "BrandDisplay54", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country54" + } + } + }, + { + "Id": 56, + "Name": "Brand55", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country55" + } + } + }, + { + "Id": 57, + "Name": "Brand56", + "DisplayName": "BrandDisplay56", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country56" + } + } + }, + { + "Id": 58, + "Name": "Brand57", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country57" + } + } + }, + { + "Id": 59, + "Name": "Brand58", + "DisplayName": "BrandDisplay58", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country58" + } + } + }, + { + "Id": 60, + "Name": "Brand59", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country59" + } + } + }, + { + "Id": 7, + "Name": "Brand6", + "DisplayName": "BrandDisplay6", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country6" + } + } + }, + { + "Id": 61, + "Name": "Brand60", + "DisplayName": "BrandDisplay60", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country60" + } + } + }, + { + "Id": 62, + "Name": "Brand61", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country61" + } + } + }, + { + "Id": 63, + "Name": "Brand62", + "DisplayName": "BrandDisplay62", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country62" + } + } + }, + { + "Id": 64, + "Name": "Brand63", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country63" + } + } + }, + { + "Id": 65, + "Name": "Brand64", + "DisplayName": "BrandDisplay64", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country64" + } + } + }, + { + "Id": 66, + "Name": "Brand65", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country65" + } + } + }, + { + "Id": 67, + "Name": "Brand66", + "DisplayName": "BrandDisplay66", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country66" + } + } + }, + { + "Id": 68, + "Name": "Brand67", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country67" + } + } + }, + { + "Id": 69, + "Name": "Brand68", + "DisplayName": "BrandDisplay68", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country68" + } + } + }, + { + "Id": 70, + "Name": "Brand69", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country69" + } + } + }, + { + "Id": 8, + "Name": "Brand7", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country7" + } + } + }, + { + "Id": 71, + "Name": "Brand70", + "DisplayName": "BrandDisplay70", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country70" + } + } + }, + { + "Id": 72, + "Name": "Brand71", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country71" + } + } + }, + { + "Id": 73, + "Name": "Brand72", + "DisplayName": "BrandDisplay72", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country72" + } + } + }, + { + "Id": 74, + "Name": "Brand73", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country73" + } + } + }, + { + "Id": 75, + "Name": "Brand74", + "DisplayName": "BrandDisplay74", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country74" + } + } + }, + { + "Id": 76, + "Name": "Brand75", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country75" + } + } + }, + { + "Id": 77, + "Name": "Brand76", + "DisplayName": "BrandDisplay76", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country76" + } + } + }, + { + "Id": 78, + "Name": "Brand77", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country77" + } + } + }, + { + "Id": 79, + "Name": "Brand78", + "DisplayName": "BrandDisplay78", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country78" + } + } + }, + { + "Id": 80, + "Name": "Brand79", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country79" + } + } + }, + { + "Id": 9, + "Name": "Brand8", + "DisplayName": "BrandDisplay8", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country8" + } + } + }, + { + "Id": 81, + "Name": "Brand80", + "DisplayName": "BrandDisplay80", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country80" + } + } + }, + { + "Id": 82, + "Name": "Brand81", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country81" + } + } + }, + { + "Id": 83, + "Name": "Brand82", + "DisplayName": "BrandDisplay82", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country82" + } + } + }, + { + "Id": 84, + "Name": "Brand83", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country83" + } + } + }, + { + "Id": 85, + "Name": "Brand84", + "DisplayName": "BrandDisplay84", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country84" + } + } + }, + { + "Id": 86, + "Name": "Brand85", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country85" + } + } + }, + { + "Id": 87, + "Name": "Brand86", + "DisplayName": "BrandDisplay86", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country86" + } + } + }, + { + "Id": 88, + "Name": "Brand87", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country87" + } + } + }, + { + "Id": 89, + "Name": "Brand88", + "DisplayName": "BrandDisplay88", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country88" + } + } + }, + { + "Id": 90, + "Name": "Brand89", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country89" + } + } + }, + { + "Id": 10, + "Name": "Brand9", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country9" + } + } + }, + { + "Id": 91, + "Name": "Brand90", + "DisplayName": "BrandDisplay90", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country90" + } + } + }, + { + "Id": 92, + "Name": "Brand91", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country91" + } + } + }, + { + "Id": 93, + "Name": "Brand92", + "DisplayName": "BrandDisplay92", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country92" + } + } + }, + { + "Id": 94, + "Name": "Brand93", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country93" + } + } + }, + { + "Id": 95, + "Name": "Brand94", + "DisplayName": "BrandDisplay94", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country94" + } + } + }, + { + "Id": 96, + "Name": "Brand95", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country95" + } + } + }, + { + "Id": 97, + "Name": "Brand96", + "DisplayName": "BrandDisplay96", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country96" + } + } + }, + { + "Id": 98, + "Name": "Brand97", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country97" + } + } + }, + { + "Id": 99, + "Name": "Brand98", + "DisplayName": "BrandDisplay98", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country98" + } + } + }, + { + "Id": 100, + "Name": "Brand99", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country99" + } + } + } +] +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Empty_PagingArgs_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Empty_PagingArgs_NET8_0.md new file mode 100644 index 00000000000..84ceccc8cab --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Empty_PagingArgs_NET8_0.md @@ -0,0 +1,1236 @@ +# Paging_Empty_PagingArgs + +## SQL 0 + +```sql +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id) +``` + +## Result 3 + +```json +{ + "HasNextPage": false, + "HasPreviousPage": false, + "First": 1, + "FirstCursor": "QnJhbmQwOjE=", + "Last": 100, + "LastCursor": "QnJhbmQ5OToxMDA=" +} +``` + +## Result 4 + +```json +[ + { + "Id": 1, + "Name": "Brand0", + "DisplayName": "BrandDisplay0", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country0" + } + } + }, + { + "Id": 2, + "Name": "Brand1", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country1" + } + } + }, + { + "Id": 11, + "Name": "Brand10", + "DisplayName": "BrandDisplay10", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country10" + } + } + }, + { + "Id": 12, + "Name": "Brand11", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country11" + } + } + }, + { + "Id": 13, + "Name": "Brand12", + "DisplayName": "BrandDisplay12", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country12" + } + } + }, + { + "Id": 14, + "Name": "Brand13", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country13" + } + } + }, + { + "Id": 15, + "Name": "Brand14", + "DisplayName": "BrandDisplay14", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country14" + } + } + }, + { + "Id": 16, + "Name": "Brand15", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country15" + } + } + }, + { + "Id": 17, + "Name": "Brand16", + "DisplayName": "BrandDisplay16", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country16" + } + } + }, + { + "Id": 18, + "Name": "Brand17", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country17" + } + } + }, + { + "Id": 19, + "Name": "Brand18", + "DisplayName": "BrandDisplay18", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country18" + } + } + }, + { + "Id": 20, + "Name": "Brand19", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country19" + } + } + }, + { + "Id": 3, + "Name": "Brand2", + "DisplayName": "BrandDisplay2", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country2" + } + } + }, + { + "Id": 21, + "Name": "Brand20", + "DisplayName": "BrandDisplay20", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country20" + } + } + }, + { + "Id": 22, + "Name": "Brand21", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country21" + } + } + }, + { + "Id": 23, + "Name": "Brand22", + "DisplayName": "BrandDisplay22", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country22" + } + } + }, + { + "Id": 24, + "Name": "Brand23", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country23" + } + } + }, + { + "Id": 25, + "Name": "Brand24", + "DisplayName": "BrandDisplay24", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country24" + } + } + }, + { + "Id": 26, + "Name": "Brand25", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country25" + } + } + }, + { + "Id": 27, + "Name": "Brand26", + "DisplayName": "BrandDisplay26", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country26" + } + } + }, + { + "Id": 28, + "Name": "Brand27", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country27" + } + } + }, + { + "Id": 29, + "Name": "Brand28", + "DisplayName": "BrandDisplay28", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country28" + } + } + }, + { + "Id": 30, + "Name": "Brand29", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country29" + } + } + }, + { + "Id": 4, + "Name": "Brand3", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country3" + } + } + }, + { + "Id": 31, + "Name": "Brand30", + "DisplayName": "BrandDisplay30", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country30" + } + } + }, + { + "Id": 32, + "Name": "Brand31", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country31" + } + } + }, + { + "Id": 33, + "Name": "Brand32", + "DisplayName": "BrandDisplay32", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country32" + } + } + }, + { + "Id": 34, + "Name": "Brand33", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country33" + } + } + }, + { + "Id": 35, + "Name": "Brand34", + "DisplayName": "BrandDisplay34", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country34" + } + } + }, + { + "Id": 36, + "Name": "Brand35", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country35" + } + } + }, + { + "Id": 37, + "Name": "Brand36", + "DisplayName": "BrandDisplay36", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country36" + } + } + }, + { + "Id": 38, + "Name": "Brand37", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country37" + } + } + }, + { + "Id": 39, + "Name": "Brand38", + "DisplayName": "BrandDisplay38", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country38" + } + } + }, + { + "Id": 40, + "Name": "Brand39", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country39" + } + } + }, + { + "Id": 5, + "Name": "Brand4", + "DisplayName": "BrandDisplay4", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country4" + } + } + }, + { + "Id": 41, + "Name": "Brand40", + "DisplayName": "BrandDisplay40", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country40" + } + } + }, + { + "Id": 42, + "Name": "Brand41", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country41" + } + } + }, + { + "Id": 43, + "Name": "Brand42", + "DisplayName": "BrandDisplay42", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country42" + } + } + }, + { + "Id": 44, + "Name": "Brand43", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country43" + } + } + }, + { + "Id": 45, + "Name": "Brand44", + "DisplayName": "BrandDisplay44", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country44" + } + } + }, + { + "Id": 46, + "Name": "Brand45", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country45" + } + } + }, + { + "Id": 47, + "Name": "Brand46", + "DisplayName": "BrandDisplay46", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country46" + } + } + }, + { + "Id": 48, + "Name": "Brand47", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country47" + } + } + }, + { + "Id": 49, + "Name": "Brand48", + "DisplayName": "BrandDisplay48", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country48" + } + } + }, + { + "Id": 50, + "Name": "Brand49", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country49" + } + } + }, + { + "Id": 6, + "Name": "Brand5", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country5" + } + } + }, + { + "Id": 51, + "Name": "Brand50", + "DisplayName": "BrandDisplay50", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country50" + } + } + }, + { + "Id": 52, + "Name": "Brand51", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country51" + } + } + }, + { + "Id": 53, + "Name": "Brand52", + "DisplayName": "BrandDisplay52", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country52" + } + } + }, + { + "Id": 54, + "Name": "Brand53", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country53" + } + } + }, + { + "Id": 55, + "Name": "Brand54", + "DisplayName": "BrandDisplay54", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country54" + } + } + }, + { + "Id": 56, + "Name": "Brand55", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country55" + } + } + }, + { + "Id": 57, + "Name": "Brand56", + "DisplayName": "BrandDisplay56", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country56" + } + } + }, + { + "Id": 58, + "Name": "Brand57", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country57" + } + } + }, + { + "Id": 59, + "Name": "Brand58", + "DisplayName": "BrandDisplay58", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country58" + } + } + }, + { + "Id": 60, + "Name": "Brand59", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country59" + } + } + }, + { + "Id": 7, + "Name": "Brand6", + "DisplayName": "BrandDisplay6", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country6" + } + } + }, + { + "Id": 61, + "Name": "Brand60", + "DisplayName": "BrandDisplay60", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country60" + } + } + }, + { + "Id": 62, + "Name": "Brand61", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country61" + } + } + }, + { + "Id": 63, + "Name": "Brand62", + "DisplayName": "BrandDisplay62", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country62" + } + } + }, + { + "Id": 64, + "Name": "Brand63", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country63" + } + } + }, + { + "Id": 65, + "Name": "Brand64", + "DisplayName": "BrandDisplay64", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country64" + } + } + }, + { + "Id": 66, + "Name": "Brand65", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country65" + } + } + }, + { + "Id": 67, + "Name": "Brand66", + "DisplayName": "BrandDisplay66", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country66" + } + } + }, + { + "Id": 68, + "Name": "Brand67", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country67" + } + } + }, + { + "Id": 69, + "Name": "Brand68", + "DisplayName": "BrandDisplay68", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country68" + } + } + }, + { + "Id": 70, + "Name": "Brand69", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country69" + } + } + }, + { + "Id": 8, + "Name": "Brand7", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country7" + } + } + }, + { + "Id": 71, + "Name": "Brand70", + "DisplayName": "BrandDisplay70", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country70" + } + } + }, + { + "Id": 72, + "Name": "Brand71", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country71" + } + } + }, + { + "Id": 73, + "Name": "Brand72", + "DisplayName": "BrandDisplay72", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country72" + } + } + }, + { + "Id": 74, + "Name": "Brand73", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country73" + } + } + }, + { + "Id": 75, + "Name": "Brand74", + "DisplayName": "BrandDisplay74", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country74" + } + } + }, + { + "Id": 76, + "Name": "Brand75", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country75" + } + } + }, + { + "Id": 77, + "Name": "Brand76", + "DisplayName": "BrandDisplay76", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country76" + } + } + }, + { + "Id": 78, + "Name": "Brand77", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country77" + } + } + }, + { + "Id": 79, + "Name": "Brand78", + "DisplayName": "BrandDisplay78", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country78" + } + } + }, + { + "Id": 80, + "Name": "Brand79", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country79" + } + } + }, + { + "Id": 9, + "Name": "Brand8", + "DisplayName": "BrandDisplay8", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country8" + } + } + }, + { + "Id": 81, + "Name": "Brand80", + "DisplayName": "BrandDisplay80", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country80" + } + } + }, + { + "Id": 82, + "Name": "Brand81", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country81" + } + } + }, + { + "Id": 83, + "Name": "Brand82", + "DisplayName": "BrandDisplay82", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country82" + } + } + }, + { + "Id": 84, + "Name": "Brand83", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country83" + } + } + }, + { + "Id": 85, + "Name": "Brand84", + "DisplayName": "BrandDisplay84", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country84" + } + } + }, + { + "Id": 86, + "Name": "Brand85", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country85" + } + } + }, + { + "Id": 87, + "Name": "Brand86", + "DisplayName": "BrandDisplay86", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country86" + } + } + }, + { + "Id": 88, + "Name": "Brand87", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country87" + } + } + }, + { + "Id": 89, + "Name": "Brand88", + "DisplayName": "BrandDisplay88", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country88" + } + } + }, + { + "Id": 90, + "Name": "Brand89", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country89" + } + } + }, + { + "Id": 10, + "Name": "Brand9", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country9" + } + } + }, + { + "Id": 91, + "Name": "Brand90", + "DisplayName": "BrandDisplay90", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country90" + } + } + }, + { + "Id": 92, + "Name": "Brand91", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country91" + } + } + }, + { + "Id": 93, + "Name": "Brand92", + "DisplayName": "BrandDisplay92", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country92" + } + } + }, + { + "Id": 94, + "Name": "Brand93", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country93" + } + } + }, + { + "Id": 95, + "Name": "Brand94", + "DisplayName": "BrandDisplay94", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country94" + } + } + }, + { + "Id": 96, + "Name": "Brand95", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country95" + } + } + }, + { + "Id": 97, + "Name": "Brand96", + "DisplayName": "BrandDisplay96", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country96" + } + } + }, + { + "Id": 98, + "Name": "Brand97", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country97" + } + } + }, + { + "Id": 99, + "Name": "Brand98", + "DisplayName": "BrandDisplay98", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country98" + } + } + }, + { + "Id": 100, + "Name": "Brand99", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country99" + } + } + } +] +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5.md index 5856b8c206a..363f584a816 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5.md @@ -1,6 +1,22 @@ # Paging_First_5 -## Result 1 +## SQL 0 + +```sql +-- @__p_0='6' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(6) +``` + +## Result 3 ```json { @@ -13,7 +29,7 @@ } ``` -## Result 2 +## Result 4 ```json [ diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_After_Id_13.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_After_Id_13.md index 0566ebf33f6..3f7a63fde55 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_After_Id_13.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_After_Id_13.md @@ -1,6 +1,25 @@ # Paging_First_5_After_Id_13 -## Result 1 +## SQL 0 + +```sql +-- @__p_0='Brand12' +-- @__p_1='13' +-- @__p_2='6' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +WHERE (b."Name" > @__p_0) OR ((b."Name" = @__p_0) AND (b."Id" > @__p_1)) +ORDER BY b."Name", b."Id" +LIMIT @__p_2 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Where(t => ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) > 0) OrElse ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.Id.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.Int32]).value, Int32)) > 0)))).Take(6) +``` + +## Result 3 ```json { @@ -13,7 +32,7 @@ } ``` -## Result 2 +## Result 4 ```json [ diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_After_Id_13_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_After_Id_13_NET7_0.md new file mode 100644 index 00000000000..fbe637b2d28 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_After_Id_13_NET7_0.md @@ -0,0 +1,101 @@ +# Paging_First_5_After_Id_13 + +## SQL 0 + +```sql +-- @__p_0='Brand12' +-- @__p_1='13' +-- @__p_2='6' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +WHERE b."Name" > @__p_0 OR (b."Name" = @__p_0 AND b."Id" > @__p_1) +ORDER BY b."Name", b."Id" +LIMIT @__p_2 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Where(t => ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) > 0) OrElse ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.Id.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.Int32]).value, Int32)) > 0)))).Take(6) +``` + +## Result 3 + +```json +{ + "HasNextPage": true, + "HasPreviousPage": true, + "First": 14, + "FirstCursor": "QnJhbmQxMzoxNA==", + "Last": 18, + "LastCursor": "QnJhbmQxNzoxOA==" +} +``` + +## Result 4 + +```json +[ + { + "Id": 14, + "Name": "Brand13", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country13" + } + } + }, + { + "Id": 15, + "Name": "Brand14", + "DisplayName": "BrandDisplay14", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country14" + } + } + }, + { + "Id": 16, + "Name": "Brand15", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country15" + } + } + }, + { + "Id": 17, + "Name": "Brand16", + "DisplayName": "BrandDisplay16", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country16" + } + } + }, + { + "Id": 18, + "Name": "Brand17", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country17" + } + } + } +] +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_After_Id_13_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_After_Id_13_NET8_0.md new file mode 100644 index 00000000000..fbe637b2d28 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_After_Id_13_NET8_0.md @@ -0,0 +1,101 @@ +# Paging_First_5_After_Id_13 + +## SQL 0 + +```sql +-- @__p_0='Brand12' +-- @__p_1='13' +-- @__p_2='6' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +WHERE b."Name" > @__p_0 OR (b."Name" = @__p_0 AND b."Id" > @__p_1) +ORDER BY b."Name", b."Id" +LIMIT @__p_2 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Where(t => ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) > 0) OrElse ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.Id.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.Int32]).value, Int32)) > 0)))).Take(6) +``` + +## Result 3 + +```json +{ + "HasNextPage": true, + "HasPreviousPage": true, + "First": 14, + "FirstCursor": "QnJhbmQxMzoxNA==", + "Last": 18, + "LastCursor": "QnJhbmQxNzoxOA==" +} +``` + +## Result 4 + +```json +[ + { + "Id": 14, + "Name": "Brand13", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country13" + } + } + }, + { + "Id": 15, + "Name": "Brand14", + "DisplayName": "BrandDisplay14", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country14" + } + } + }, + { + "Id": 16, + "Name": "Brand15", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country15" + } + } + }, + { + "Id": 17, + "Name": "Brand16", + "DisplayName": "BrandDisplay16", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country16" + } + } + }, + { + "Id": 18, + "Name": "Brand17", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country17" + } + } + } +] +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_Before_Id_96.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_Before_Id_96.md index e25afbb7c76..e87fe49bb26 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_Before_Id_96.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_Before_Id_96.md @@ -1,6 +1,25 @@ # Paging_First_5_Before_Id_96 -## Result 1 +## SQL 0 + +```sql +-- @__p_0='Brand95' +-- @__p_1='96' +-- @__p_2='6' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +WHERE (b."Name" < @__p_0) OR ((b."Name" = @__p_0) AND (b."Id" < @__p_1)) +ORDER BY b."Name" DESC, b."Id" DESC +LIMIT @__p_2 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Where(t => ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) < 0) OrElse ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.Id.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.Int32]).value, Int32)) < 0)))).Reverse().Take(6) +``` + +## Result 3 ```json { @@ -13,7 +32,7 @@ } ``` -## Result 2 +## Result 4 ```json [ diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_Before_Id_96_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_Before_Id_96_NET7_0.md new file mode 100644 index 00000000000..b30b11b2e0e --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_Before_Id_96_NET7_0.md @@ -0,0 +1,101 @@ +# Paging_First_5_Before_Id_96 + +## SQL 0 + +```sql +-- @__p_0='Brand95' +-- @__p_1='96' +-- @__p_2='6' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +WHERE b."Name" < @__p_0 OR (b."Name" = @__p_0 AND b."Id" < @__p_1) +ORDER BY b."Name" DESC, b."Id" DESC +LIMIT @__p_2 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Where(t => ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) < 0) OrElse ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.Id.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.Int32]).value, Int32)) < 0)))).Reverse().Take(6) +``` + +## Result 3 + +```json +{ + "HasNextPage": true, + "HasPreviousPage": true, + "First": 91, + "FirstCursor": "QnJhbmQ5MDo5MQ==", + "Last": 95, + "LastCursor": "QnJhbmQ5NDo5NQ==" +} +``` + +## Result 4 + +```json +[ + { + "Id": 91, + "Name": "Brand90", + "DisplayName": "BrandDisplay90", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country90" + } + } + }, + { + "Id": 92, + "Name": "Brand91", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country91" + } + } + }, + { + "Id": 93, + "Name": "Brand92", + "DisplayName": "BrandDisplay92", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country92" + } + } + }, + { + "Id": 94, + "Name": "Brand93", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country93" + } + } + }, + { + "Id": 95, + "Name": "Brand94", + "DisplayName": "BrandDisplay94", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country94" + } + } + } +] +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_Before_Id_96_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_Before_Id_96_NET8_0.md new file mode 100644 index 00000000000..b30b11b2e0e --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_Before_Id_96_NET8_0.md @@ -0,0 +1,101 @@ +# Paging_First_5_Before_Id_96 + +## SQL 0 + +```sql +-- @__p_0='Brand95' +-- @__p_1='96' +-- @__p_2='6' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +WHERE b."Name" < @__p_0 OR (b."Name" = @__p_0 AND b."Id" < @__p_1) +ORDER BY b."Name" DESC, b."Id" DESC +LIMIT @__p_2 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Where(t => ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) < 0) OrElse ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.Id.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.Int32]).value, Int32)) < 0)))).Reverse().Take(6) +``` + +## Result 3 + +```json +{ + "HasNextPage": true, + "HasPreviousPage": true, + "First": 91, + "FirstCursor": "QnJhbmQ5MDo5MQ==", + "Last": 95, + "LastCursor": "QnJhbmQ5NDo5NQ==" +} +``` + +## Result 4 + +```json +[ + { + "Id": 91, + "Name": "Brand90", + "DisplayName": "BrandDisplay90", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country90" + } + } + }, + { + "Id": 92, + "Name": "Brand91", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country91" + } + } + }, + { + "Id": 93, + "Name": "Brand92", + "DisplayName": "BrandDisplay92", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country92" + } + } + }, + { + "Id": 94, + "Name": "Brand93", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country93" + } + } + }, + { + "Id": 95, + "Name": "Brand94", + "DisplayName": "BrandDisplay94", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country94" + } + } + } +] +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_NET7_0.md new file mode 100644 index 00000000000..3d47e9e96a0 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_NET7_0.md @@ -0,0 +1,98 @@ +# Paging_First_5 + +## SQL 0 + +```sql +-- @__p_0='6' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(6) +``` + +## Result 3 + +```json +{ + "HasNextPage": true, + "HasPreviousPage": false, + "First": 1, + "FirstCursor": "QnJhbmQwOjE=", + "Last": 13, + "LastCursor": "QnJhbmQxMjoxMw==" +} +``` + +## Result 4 + +```json +[ + { + "Id": 1, + "Name": "Brand0", + "DisplayName": "BrandDisplay0", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country0" + } + } + }, + { + "Id": 2, + "Name": "Brand1", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country1" + } + } + }, + { + "Id": 11, + "Name": "Brand10", + "DisplayName": "BrandDisplay10", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country10" + } + } + }, + { + "Id": 12, + "Name": "Brand11", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country11" + } + } + }, + { + "Id": 13, + "Name": "Brand12", + "DisplayName": "BrandDisplay12", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country12" + } + } + } +] +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_NET8_0.md new file mode 100644 index 00000000000..3d47e9e96a0 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_NET8_0.md @@ -0,0 +1,98 @@ +# Paging_First_5 + +## SQL 0 + +```sql +-- @__p_0='6' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(6) +``` + +## Result 3 + +```json +{ + "HasNextPage": true, + "HasPreviousPage": false, + "First": 1, + "FirstCursor": "QnJhbmQwOjE=", + "Last": 13, + "LastCursor": "QnJhbmQxMjoxMw==" +} +``` + +## Result 4 + +```json +[ + { + "Id": 1, + "Name": "Brand0", + "DisplayName": "BrandDisplay0", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country0" + } + } + }, + { + "Id": 2, + "Name": "Brand1", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country1" + } + } + }, + { + "Id": 11, + "Name": "Brand10", + "DisplayName": "BrandDisplay10", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country10" + } + } + }, + { + "Id": 12, + "Name": "Brand11", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country11" + } + } + }, + { + "Id": 13, + "Name": "Brand12", + "DisplayName": "BrandDisplay12", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country12" + } + } + } +] +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Last_5.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Last_5.md index d6f164be83a..9906664fc7e 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Last_5.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Last_5.md @@ -1,6 +1,22 @@ # Paging_Last_5 -## Result 1 +## SQL 0 + +```sql +-- @__p_0='6' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name" DESC, b."Id" DESC +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Reverse().Take(6) +``` + +## Result 3 ```json { @@ -13,7 +29,7 @@ } ``` -## Result 2 +## Result 4 ```json [ diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Last_5_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Last_5_NET7_0.md new file mode 100644 index 00000000000..1e0341186cf --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Last_5_NET7_0.md @@ -0,0 +1,98 @@ +# Paging_Last_5 + +## SQL 0 + +```sql +-- @__p_0='6' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name" DESC, b."Id" DESC +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Reverse().Take(6) +``` + +## Result 3 + +```json +{ + "HasNextPage": false, + "HasPreviousPage": true, + "First": 96, + "FirstCursor": "QnJhbmQ5NTo5Ng==", + "Last": 100, + "LastCursor": "QnJhbmQ5OToxMDA=" +} +``` + +## Result 4 + +```json +[ + { + "Id": 96, + "Name": "Brand95", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country95" + } + } + }, + { + "Id": 97, + "Name": "Brand96", + "DisplayName": "BrandDisplay96", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country96" + } + } + }, + { + "Id": 98, + "Name": "Brand97", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country97" + } + } + }, + { + "Id": 99, + "Name": "Brand98", + "DisplayName": "BrandDisplay98", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country98" + } + } + }, + { + "Id": 100, + "Name": "Brand99", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country99" + } + } + } +] +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Last_5_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Last_5_NET8_0.md new file mode 100644 index 00000000000..1e0341186cf --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Last_5_NET8_0.md @@ -0,0 +1,98 @@ +# Paging_Last_5 + +## SQL 0 + +```sql +-- @__p_0='6' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name" DESC, b."Id" DESC +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Reverse().Take(6) +``` + +## Result 3 + +```json +{ + "HasNextPage": false, + "HasPreviousPage": true, + "First": 96, + "FirstCursor": "QnJhbmQ5NTo5Ng==", + "Last": 100, + "LastCursor": "QnJhbmQ5OToxMDA=" +} +``` + +## Result 4 + +```json +[ + { + "Id": 96, + "Name": "Brand95", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country95" + } + } + }, + { + "Id": 97, + "Name": "Brand96", + "DisplayName": "BrandDisplay96", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country96" + } + } + }, + { + "Id": 98, + "Name": "Brand97", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country97" + } + } + }, + { + "Id": 99, + "Name": "Brand98", + "DisplayName": "BrandDisplay98", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country98" + } + } + }, + { + "Id": 100, + "Name": "Brand99", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country99" + } + } + } +] +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals.md index bb2bba71c47..a5196d74d58 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals.md @@ -3,32 +3,47 @@ ## SQL 0 ```sql +-- @__p_0='11' SELECT o."Id", o."Name" FROM "Owners" AS o ORDER BY o."Name", o."Id" +LIMIT @__p_0 ``` ## Expression 0 ```text -[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Select(root => new Owner() {Id = root.Id, Name = root.Name}).OrderBy(t => t.Name).ThenBy(t => t.Id) +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Owner() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name)}).Take(11) ``` ## SQL 1 ```sql --- @__keys_0={ '6', '5', '4', '3', '2', ... } (DbType = Object) -SELECT a."Id", a."AnimalType", a."Name", a."OwnerId", a."IsPurring", a."IsBarking" -FROM "Owners" AS o -INNER JOIN "Animal" AS a ON o."Id" = a."OwnerId" -WHERE o."Id" = ANY (@__keys_0) -ORDER BY a."Name", a."Id" +SELECT t."OwnerId", t0."Id", t0."AnimalType", t0."Name", t0."OwnerId", t0."IsPurring", t0."IsBarking", t0."Id0" +FROM ( + SELECT p."OwnerId" + FROM "Owners" AS o + INNER JOIN "Pets" AS p ON o."Id" = p."OwnerId" + WHERE o."Id" IN (6, 5, 4, 3, 2, 1) + GROUP BY p."OwnerId" +) AS t +LEFT JOIN ( + SELECT t1."Id", t1."AnimalType", t1."Name", t1."OwnerId", t1."IsPurring", t1."IsBarking", t1."Id0" + FROM ( + SELECT p0."Id", p0."AnimalType", p0."Name", p0."OwnerId", p0."IsPurring", p0."IsBarking", o0."Id" AS "Id0", ROW_NUMBER() OVER(PARTITION BY p0."OwnerId" ORDER BY p0."Name", p0."Id") AS row + FROM "Owners" AS o0 + INNER JOIN "Pets" AS p0 ON o0."Id" = p0."OwnerId" + WHERE o0."Id" IN (6, 5, 4, 3, 2, 1) + ) AS t1 + WHERE t1.row <= 11 +) AS t0 ON t."OwnerId" = t0."OwnerId" +ORDER BY t."OwnerId", t0."OwnerId", t0."Name", t0."Id" ``` ## Expression 1 ```text -[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Where(t => value(HotChocolate.Data.InterfaceIntegrationTests+AnimalsByOwnerDataLoader+<>c__DisplayClass2_0).keys.Contains(t.Id)).SelectMany(t => t.Pets).OrderBy(t => t.Name).ThenBy(t => t.Id) +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].Where(t => value(HotChocolate.Data.InterfaceIntegrationTests+AnimalsByOwnerDataLoader+<>c__DisplayClass2_0).keys.Contains(t.Id)).SelectMany(t => t.Pets).GroupBy(t => t.OwnerId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderBy(t => t.Name).ThenBy(t => t.Id).Take(11).ToList()}) ``` ## Result 5 diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_NET7_0.md new file mode 100644 index 00000000000..d59b51987b4 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_NET7_0.md @@ -0,0 +1,163 @@ +# Query_Owner_Animals + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT o."Id", o."Name" +FROM "Owners" AS o +ORDER BY o."Name", o."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Owner() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name)}).Take(11) +``` + +## SQL 1 + +```sql +SELECT t."OwnerId", t0."Id", t0."AnimalType", t0."Name", t0."OwnerId", t0."IsPurring", t0."IsBarking", t0."Id0" +FROM ( + SELECT p."OwnerId" + FROM "Owners" AS o + INNER JOIN "Pets" AS p ON o."Id" = p."OwnerId" + WHERE o."Id" IN (6, 5, 4, 3, 2, 1) + GROUP BY p."OwnerId" +) AS t +LEFT JOIN ( + SELECT t1."Id", t1."AnimalType", t1."Name", t1."OwnerId", t1."IsPurring", t1."IsBarking", t1."Id0" + FROM ( + SELECT p0."Id", p0."AnimalType", p0."Name", p0."OwnerId", p0."IsPurring", p0."IsBarking", o0."Id" AS "Id0", ROW_NUMBER() OVER(PARTITION BY p0."OwnerId" ORDER BY p0."Name", p0."Id") AS row + FROM "Owners" AS o0 + INNER JOIN "Pets" AS p0 ON o0."Id" = p0."OwnerId" + WHERE o0."Id" IN (6, 5, 4, 3, 2, 1) + ) AS t1 + WHERE t1.row <= 11 +) AS t0 ON t."OwnerId" = t0."OwnerId" +ORDER BY t."OwnerId", t0."OwnerId", t0."Name", t0."Id" +``` + +## Expression 1 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Where(t => value(HotChocolate.Data.InterfaceIntegrationTests+AnimalsByOwnerDataLoader+<>c__DisplayClass2_0).keys.Contains(t.Id)).SelectMany(t => t.Pets).GroupBy(t => t.OwnerId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderBy(t => t.Name).ThenBy(t => t.Id).Take(11).ToList()}) +``` + +## Result 5 + +```json +{ + "data": { + "owners": { + "nodes": [ + { + "id": 1, + "name": "Owner 1", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 1, + "name": "Cat 1" + }, + { + "__typename": "Dog", + "id": 5, + "name": "Dog 1" + }, + { + "__typename": "Dog", + "id": 6, + "name": "Dog 2" + } + ] + } + }, + { + "id": 2, + "name": "Owner 2", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 2, + "name": "Cat 2" + }, + { + "__typename": "Dog", + "id": 7, + "name": "Dog 3" + }, + { + "__typename": "Dog", + "id": 8, + "name": "Dog 4" + } + ] + } + }, + { + "id": 3, + "name": "Owner 3", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 3, + "name": "Cat 3 (Not Pure)" + }, + { + "__typename": "Dog", + "id": 9, + "name": "Dog 5" + }, + { + "__typename": "Dog", + "id": 10, + "name": "Dog 6" + } + ] + } + }, + { + "id": 4, + "name": "Owner 4 - No Pets", + "pets": { + "nodes": [] + } + }, + { + "id": 5, + "name": "Owner 5 - Only Cat", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 4, + "name": "Only Cat" + } + ] + } + }, + { + "id": 6, + "name": "Owner 6 - Only Dog", + "pets": { + "nodes": [ + { + "__typename": "Dog", + "id": 11, + "name": "Only Dog" + } + ] + } + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_NET8_0.md new file mode 100644 index 00000000000..4ab695271ef --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_NET8_0.md @@ -0,0 +1,164 @@ +# Query_Owner_Animals + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT o."Id", o."Name" +FROM "Owners" AS o +ORDER BY o."Name", o."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Owner() {Id = root.Id, Name = root.Name}).Take(11) +``` + +## SQL 1 + +```sql +-- @__keys_0={ '6', '5', '4', '3', '2', ... } (DbType = Object) +SELECT t."OwnerId", t0."Id", t0."AnimalType", t0."Name", t0."OwnerId", t0."IsPurring", t0."IsBarking", t0."Id0" +FROM ( + SELECT p."OwnerId" + FROM "Owners" AS o + INNER JOIN "Pets" AS p ON o."Id" = p."OwnerId" + WHERE o."Id" = ANY (@__keys_0) + GROUP BY p."OwnerId" +) AS t +LEFT JOIN ( + SELECT t1."Id", t1."AnimalType", t1."Name", t1."OwnerId", t1."IsPurring", t1."IsBarking", t1."Id0" + FROM ( + SELECT p0."Id", p0."AnimalType", p0."Name", p0."OwnerId", p0."IsPurring", p0."IsBarking", o0."Id" AS "Id0", ROW_NUMBER() OVER(PARTITION BY p0."OwnerId" ORDER BY p0."Name", p0."Id") AS row + FROM "Owners" AS o0 + INNER JOIN "Pets" AS p0 ON o0."Id" = p0."OwnerId" + WHERE o0."Id" = ANY (@__keys_0) + ) AS t1 + WHERE t1.row <= 11 +) AS t0 ON t."OwnerId" = t0."OwnerId" +ORDER BY t."OwnerId", t0."OwnerId", t0."Name", t0."Id" +``` + +## Expression 1 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Where(t => value(HotChocolate.Data.InterfaceIntegrationTests+AnimalsByOwnerDataLoader+<>c__DisplayClass2_0).keys.Contains(t.Id)).SelectMany(t => t.Pets).GroupBy(t => t.OwnerId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderBy(t => t.Name).ThenBy(t => t.Id).Take(11).ToList()}) +``` + +## Result 5 + +```json +{ + "data": { + "owners": { + "nodes": [ + { + "id": 1, + "name": "Owner 1", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 1, + "name": "Cat 1" + }, + { + "__typename": "Dog", + "id": 5, + "name": "Dog 1" + }, + { + "__typename": "Dog", + "id": 6, + "name": "Dog 2" + } + ] + } + }, + { + "id": 2, + "name": "Owner 2", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 2, + "name": "Cat 2" + }, + { + "__typename": "Dog", + "id": 7, + "name": "Dog 3" + }, + { + "__typename": "Dog", + "id": 8, + "name": "Dog 4" + } + ] + } + }, + { + "id": 3, + "name": "Owner 3", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 3, + "name": "Cat 3 (Not Pure)" + }, + { + "__typename": "Dog", + "id": 9, + "name": "Dog 5" + }, + { + "__typename": "Dog", + "id": 10, + "name": "Dog 6" + } + ] + } + }, + { + "id": 4, + "name": "Owner 4 - No Pets", + "pets": { + "nodes": [] + } + }, + { + "id": 5, + "name": "Owner 5 - Only Cat", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 4, + "name": "Only Cat" + } + ] + } + }, + { + "id": 6, + "name": "Owner 6 - Only Dog", + "pets": { + "nodes": [ + { + "__typename": "Dog", + "id": 11, + "name": "Only Dog" + } + ] + } + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_NET_9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_NET_9_0.md new file mode 100644 index 00000000000..a0d36730082 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_NET_9_0.md @@ -0,0 +1,164 @@ +# Query_Owner_Animals + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT o."Id", o."Name" +FROM "Owners" AS o +ORDER BY o."Name", o."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Owner() {Id = root.Id, Name = root.Name}).Take(11) +``` + +## SQL 1 + +```sql +-- @__keys_0={ '6', '5', '4', '3', '2', ... } (DbType = Object) +SELECT s."OwnerId", s1."Id", s1."AnimalType", s1."Name", s1."OwnerId", s1."IsPurring", s1."IsBarking", s1."Id0" +FROM ( + SELECT p."OwnerId" + FROM "Owners" AS o + INNER JOIN "Pets" AS p ON o."Id" = p."OwnerId" + WHERE o."Id" = ANY (@__keys_0) + GROUP BY p."OwnerId" +) AS s +LEFT JOIN ( + SELECT s0."Id", s0."AnimalType", s0."Name", s0."OwnerId", s0."IsPurring", s0."IsBarking", s0."Id0" + FROM ( + SELECT p0."Id", p0."AnimalType", p0."Name", p0."OwnerId", p0."IsPurring", p0."IsBarking", o0."Id" AS "Id0", ROW_NUMBER() OVER(PARTITION BY p0."OwnerId" ORDER BY p0."Name", p0."Id") AS row + FROM "Owners" AS o0 + INNER JOIN "Pets" AS p0 ON o0."Id" = p0."OwnerId" + WHERE o0."Id" = ANY (@__keys_0) + ) AS s0 + WHERE s0.row <= 11 +) AS s1 ON s."OwnerId" = s1."OwnerId" +ORDER BY s."OwnerId", s1."OwnerId", s1."Name", s1."Id" +``` + +## Expression 1 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Where(t => value(HotChocolate.Data.InterfaceIntegrationTests+AnimalsByOwnerDataLoader+<>c__DisplayClass2_0).keys.Contains(t.Id)).SelectMany(t => t.Pets).GroupBy(t => t.OwnerId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderBy(t => t.Name).ThenBy(t => t.Id).Take(11).ToList()}) +``` + +## Result 5 + +```json +{ + "data": { + "owners": { + "nodes": [ + { + "id": 1, + "name": "Owner 1", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 1, + "name": "Cat 1" + }, + { + "__typename": "Dog", + "id": 5, + "name": "Dog 1" + }, + { + "__typename": "Dog", + "id": 6, + "name": "Dog 2" + } + ] + } + }, + { + "id": 2, + "name": "Owner 2", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 2, + "name": "Cat 2" + }, + { + "__typename": "Dog", + "id": 7, + "name": "Dog 3" + }, + { + "__typename": "Dog", + "id": 8, + "name": "Dog 4" + } + ] + } + }, + { + "id": 3, + "name": "Owner 3", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 3, + "name": "Cat 3 (Not Pure)" + }, + { + "__typename": "Dog", + "id": 9, + "name": "Dog 5" + }, + { + "__typename": "Dog", + "id": 10, + "name": "Dog 6" + } + ] + } + }, + { + "id": 4, + "name": "Owner 4 - No Pets", + "pets": { + "nodes": [] + } + }, + { + "id": 5, + "name": "Owner 5 - Only Cat", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 4, + "name": "Only Cat" + } + ] + } + }, + { + "id": 6, + "name": "Owner 6 - Only Dog", + "pets": { + "nodes": [ + { + "__typename": "Dog", + "id": 11, + "name": "Only Dog" + } + ] + } + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments.md index f08d748670a..8b569cf5bbb 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments.md @@ -3,32 +3,47 @@ ## SQL 0 ```sql +-- @__p_0='11' SELECT o."Id", o."Name" FROM "Owners" AS o ORDER BY o."Name", o."Id" +LIMIT @__p_0 ``` ## Expression 0 ```text -[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Select(root => new Owner() {Id = root.Id, Name = root.Name}).OrderBy(t => t.Name).ThenBy(t => t.Id) +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Owner() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name)}).Take(11) ``` ## SQL 1 ```sql --- @__keys_0={ '6', '5', '4', '3', '2', ... } (DbType = Object) -SELECT a."Id", a."AnimalType", a."Name", a."OwnerId", a."IsPurring", a."IsBarking" -FROM "Owners" AS o -INNER JOIN "Animal" AS a ON o."Id" = a."OwnerId" -WHERE o."Id" = ANY (@__keys_0) -ORDER BY a."Name", a."Id" +SELECT t."OwnerId", t0."Id", t0."AnimalType", t0."Name", t0."OwnerId", t0."IsPurring", t0."IsBarking", t0."Id0" +FROM ( + SELECT p."OwnerId" + FROM "Owners" AS o + INNER JOIN "Pets" AS p ON o."Id" = p."OwnerId" + WHERE o."Id" IN (6, 5, 4, 3, 2, 1) + GROUP BY p."OwnerId" +) AS t +LEFT JOIN ( + SELECT t1."Id", t1."AnimalType", t1."Name", t1."OwnerId", t1."IsPurring", t1."IsBarking", t1."Id0" + FROM ( + SELECT p0."Id", p0."AnimalType", p0."Name", p0."OwnerId", p0."IsPurring", p0."IsBarking", o0."Id" AS "Id0", ROW_NUMBER() OVER(PARTITION BY p0."OwnerId" ORDER BY p0."Name", p0."Id") AS row + FROM "Owners" AS o0 + INNER JOIN "Pets" AS p0 ON o0."Id" = p0."OwnerId" + WHERE o0."Id" IN (6, 5, 4, 3, 2, 1) + ) AS t1 + WHERE t1.row <= 11 +) AS t0 ON t."OwnerId" = t0."OwnerId" +ORDER BY t."OwnerId", t0."OwnerId", t0."Name", t0."Id" ``` ## Expression 1 ```text -[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Where(t => value(HotChocolate.Data.InterfaceIntegrationTests+AnimalsByOwnerDataLoader+<>c__DisplayClass2_0).keys.Contains(t.Id)).SelectMany(t => t.Pets).OrderBy(t => t.Name).ThenBy(t => t.Id) +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].Where(t => value(HotChocolate.Data.InterfaceIntegrationTests+AnimalsByOwnerDataLoader+<>c__DisplayClass2_0).keys.Contains(t.Id)).SelectMany(t => t.Pets).GroupBy(t => t.OwnerId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderBy(t => t.Name).ThenBy(t => t.Id).Take(11).ToList()}) ``` ## Result 5 diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments_NET7_0.md new file mode 100644 index 00000000000..f11bae0324e --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments_NET7_0.md @@ -0,0 +1,174 @@ +# Query_Owner_Animals_With_Fragments + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT o."Id", o."Name" +FROM "Owners" AS o +ORDER BY o."Name", o."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Owner() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name)}).Take(11) +``` + +## SQL 1 + +```sql +SELECT t."OwnerId", t0."Id", t0."AnimalType", t0."Name", t0."OwnerId", t0."IsPurring", t0."IsBarking", t0."Id0" +FROM ( + SELECT p."OwnerId" + FROM "Owners" AS o + INNER JOIN "Pets" AS p ON o."Id" = p."OwnerId" + WHERE o."Id" IN (6, 5, 4, 3, 2, 1) + GROUP BY p."OwnerId" +) AS t +LEFT JOIN ( + SELECT t1."Id", t1."AnimalType", t1."Name", t1."OwnerId", t1."IsPurring", t1."IsBarking", t1."Id0" + FROM ( + SELECT p0."Id", p0."AnimalType", p0."Name", p0."OwnerId", p0."IsPurring", p0."IsBarking", o0."Id" AS "Id0", ROW_NUMBER() OVER(PARTITION BY p0."OwnerId" ORDER BY p0."Name", p0."Id") AS row + FROM "Owners" AS o0 + INNER JOIN "Pets" AS p0 ON o0."Id" = p0."OwnerId" + WHERE o0."Id" IN (6, 5, 4, 3, 2, 1) + ) AS t1 + WHERE t1.row <= 11 +) AS t0 ON t."OwnerId" = t0."OwnerId" +ORDER BY t."OwnerId", t0."OwnerId", t0."Name", t0."Id" +``` + +## Expression 1 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Where(t => value(HotChocolate.Data.InterfaceIntegrationTests+AnimalsByOwnerDataLoader+<>c__DisplayClass2_0).keys.Contains(t.Id)).SelectMany(t => t.Pets).GroupBy(t => t.OwnerId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderBy(t => t.Name).ThenBy(t => t.Id).Take(11).ToList()}) +``` + +## Result 5 + +```json +{ + "data": { + "owners": { + "nodes": [ + { + "id": 1, + "name": "Owner 1", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 1, + "name": "Cat 1", + "isPurring": false + }, + { + "__typename": "Dog", + "id": 5, + "name": "Dog 1", + "isBarking": true + }, + { + "__typename": "Dog", + "id": 6, + "name": "Dog 2", + "isBarking": false + } + ] + } + }, + { + "id": 2, + "name": "Owner 2", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 2, + "name": "Cat 2", + "isPurring": false + }, + { + "__typename": "Dog", + "id": 7, + "name": "Dog 3", + "isBarking": true + }, + { + "__typename": "Dog", + "id": 8, + "name": "Dog 4", + "isBarking": false + } + ] + } + }, + { + "id": 3, + "name": "Owner 3", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 3, + "name": "Cat 3 (Not Pure)", + "isPurring": true + }, + { + "__typename": "Dog", + "id": 9, + "name": "Dog 5", + "isBarking": true + }, + { + "__typename": "Dog", + "id": 10, + "name": "Dog 6", + "isBarking": false + } + ] + } + }, + { + "id": 4, + "name": "Owner 4 - No Pets", + "pets": { + "nodes": [] + } + }, + { + "id": 5, + "name": "Owner 5 - Only Cat", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 4, + "name": "Only Cat", + "isPurring": false + } + ] + } + }, + { + "id": 6, + "name": "Owner 6 - Only Dog", + "pets": { + "nodes": [ + { + "__typename": "Dog", + "id": 11, + "name": "Only Dog", + "isBarking": true + } + ] + } + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments_NET8_0.md new file mode 100644 index 00000000000..6521c666725 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments_NET8_0.md @@ -0,0 +1,175 @@ +# Query_Owner_Animals_With_Fragments + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT o."Id", o."Name" +FROM "Owners" AS o +ORDER BY o."Name", o."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Owner() {Id = root.Id, Name = root.Name}).Take(11) +``` + +## SQL 1 + +```sql +-- @__keys_0={ '6', '5', '4', '3', '2', ... } (DbType = Object) +SELECT t."OwnerId", t0."Id", t0."AnimalType", t0."Name", t0."OwnerId", t0."IsPurring", t0."IsBarking", t0."Id0" +FROM ( + SELECT p."OwnerId" + FROM "Owners" AS o + INNER JOIN "Pets" AS p ON o."Id" = p."OwnerId" + WHERE o."Id" = ANY (@__keys_0) + GROUP BY p."OwnerId" +) AS t +LEFT JOIN ( + SELECT t1."Id", t1."AnimalType", t1."Name", t1."OwnerId", t1."IsPurring", t1."IsBarking", t1."Id0" + FROM ( + SELECT p0."Id", p0."AnimalType", p0."Name", p0."OwnerId", p0."IsPurring", p0."IsBarking", o0."Id" AS "Id0", ROW_NUMBER() OVER(PARTITION BY p0."OwnerId" ORDER BY p0."Name", p0."Id") AS row + FROM "Owners" AS o0 + INNER JOIN "Pets" AS p0 ON o0."Id" = p0."OwnerId" + WHERE o0."Id" = ANY (@__keys_0) + ) AS t1 + WHERE t1.row <= 11 +) AS t0 ON t."OwnerId" = t0."OwnerId" +ORDER BY t."OwnerId", t0."OwnerId", t0."Name", t0."Id" +``` + +## Expression 1 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Where(t => value(HotChocolate.Data.InterfaceIntegrationTests+AnimalsByOwnerDataLoader+<>c__DisplayClass2_0).keys.Contains(t.Id)).SelectMany(t => t.Pets).GroupBy(t => t.OwnerId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderBy(t => t.Name).ThenBy(t => t.Id).Take(11).ToList()}) +``` + +## Result 5 + +```json +{ + "data": { + "owners": { + "nodes": [ + { + "id": 1, + "name": "Owner 1", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 1, + "name": "Cat 1", + "isPurring": false + }, + { + "__typename": "Dog", + "id": 5, + "name": "Dog 1", + "isBarking": true + }, + { + "__typename": "Dog", + "id": 6, + "name": "Dog 2", + "isBarking": false + } + ] + } + }, + { + "id": 2, + "name": "Owner 2", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 2, + "name": "Cat 2", + "isPurring": false + }, + { + "__typename": "Dog", + "id": 7, + "name": "Dog 3", + "isBarking": true + }, + { + "__typename": "Dog", + "id": 8, + "name": "Dog 4", + "isBarking": false + } + ] + } + }, + { + "id": 3, + "name": "Owner 3", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 3, + "name": "Cat 3 (Not Pure)", + "isPurring": true + }, + { + "__typename": "Dog", + "id": 9, + "name": "Dog 5", + "isBarking": true + }, + { + "__typename": "Dog", + "id": 10, + "name": "Dog 6", + "isBarking": false + } + ] + } + }, + { + "id": 4, + "name": "Owner 4 - No Pets", + "pets": { + "nodes": [] + } + }, + { + "id": 5, + "name": "Owner 5 - Only Cat", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 4, + "name": "Only Cat", + "isPurring": false + } + ] + } + }, + { + "id": 6, + "name": "Owner 6 - Only Dog", + "pets": { + "nodes": [ + { + "__typename": "Dog", + "id": 11, + "name": "Only Dog", + "isBarking": true + } + ] + } + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments_NET_9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments_NET_9_0.md new file mode 100644 index 00000000000..425c3bb3f54 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments_NET_9_0.md @@ -0,0 +1,175 @@ +# Query_Owner_Animals_With_Fragments + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT o."Id", o."Name" +FROM "Owners" AS o +ORDER BY o."Name", o."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Owner() {Id = root.Id, Name = root.Name}).Take(11) +``` + +## SQL 1 + +```sql +-- @__keys_0={ '6', '5', '4', '3', '2', ... } (DbType = Object) +SELECT s."OwnerId", s1."Id", s1."AnimalType", s1."Name", s1."OwnerId", s1."IsPurring", s1."IsBarking", s1."Id0" +FROM ( + SELECT p."OwnerId" + FROM "Owners" AS o + INNER JOIN "Pets" AS p ON o."Id" = p."OwnerId" + WHERE o."Id" = ANY (@__keys_0) + GROUP BY p."OwnerId" +) AS s +LEFT JOIN ( + SELECT s0."Id", s0."AnimalType", s0."Name", s0."OwnerId", s0."IsPurring", s0."IsBarking", s0."Id0" + FROM ( + SELECT p0."Id", p0."AnimalType", p0."Name", p0."OwnerId", p0."IsPurring", p0."IsBarking", o0."Id" AS "Id0", ROW_NUMBER() OVER(PARTITION BY p0."OwnerId" ORDER BY p0."Name", p0."Id") AS row + FROM "Owners" AS o0 + INNER JOIN "Pets" AS p0 ON o0."Id" = p0."OwnerId" + WHERE o0."Id" = ANY (@__keys_0) + ) AS s0 + WHERE s0.row <= 11 +) AS s1 ON s."OwnerId" = s1."OwnerId" +ORDER BY s."OwnerId", s1."OwnerId", s1."Name", s1."Id" +``` + +## Expression 1 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Where(t => value(HotChocolate.Data.InterfaceIntegrationTests+AnimalsByOwnerDataLoader+<>c__DisplayClass2_0).keys.Contains(t.Id)).SelectMany(t => t.Pets).GroupBy(t => t.OwnerId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderBy(t => t.Name).ThenBy(t => t.Id).Take(11).ToList()}) +``` + +## Result 5 + +```json +{ + "data": { + "owners": { + "nodes": [ + { + "id": 1, + "name": "Owner 1", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 1, + "name": "Cat 1", + "isPurring": false + }, + { + "__typename": "Dog", + "id": 5, + "name": "Dog 1", + "isBarking": true + }, + { + "__typename": "Dog", + "id": 6, + "name": "Dog 2", + "isBarking": false + } + ] + } + }, + { + "id": 2, + "name": "Owner 2", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 2, + "name": "Cat 2", + "isPurring": false + }, + { + "__typename": "Dog", + "id": 7, + "name": "Dog 3", + "isBarking": true + }, + { + "__typename": "Dog", + "id": 8, + "name": "Dog 4", + "isBarking": false + } + ] + } + }, + { + "id": 3, + "name": "Owner 3", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 3, + "name": "Cat 3 (Not Pure)", + "isPurring": true + }, + { + "__typename": "Dog", + "id": 9, + "name": "Dog 5", + "isBarking": true + }, + { + "__typename": "Dog", + "id": 10, + "name": "Dog 6", + "isBarking": false + } + ] + } + }, + { + "id": 4, + "name": "Owner 4 - No Pets", + "pets": { + "nodes": [] + } + }, + { + "id": 5, + "name": "Owner 5 - Only Cat", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 4, + "name": "Only Cat", + "isPurring": false + } + ] + } + }, + { + "id": 6, + "name": "Owner 6 - Only Dog", + "pets": { + "nodes": [ + { + "__typename": "Dog", + "id": 11, + "name": "Only Dog", + "isBarking": true + } + ] + } + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Pets.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Pets.md new file mode 100644 index 00000000000..68032883f6d --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Pets.md @@ -0,0 +1,71 @@ +# Query_Pets + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT p."AnimalType" = 'Cat', p."Id", p."Name", p."AnimalType" = 'Dog' +FROM "Pets" AS p +ORDER BY p."Name", p."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => IIF((root Is Cat), Convert(new Cat() {Id = Convert(root, Cat).Id, Name = Convert(root, Cat).Name}, Animal), IIF((root Is Dog), Convert(new Dog() {Id = Convert(root, Dog).Id, Name = Convert(root, Dog).Name}, Animal), null))).Take(11) +``` + +## Result 3 + +```json +{ + "data": { + "pets": { + "nodes": [ + { + "id": 1, + "name": "Cat 1" + }, + { + "id": 2, + "name": "Cat 2" + }, + { + "id": 3, + "name": "Cat 3 (Not Pure)" + }, + { + "id": 5, + "name": "Dog 1" + }, + { + "id": 6, + "name": "Dog 2" + }, + { + "id": 7, + "name": "Dog 3" + }, + { + "id": 8, + "name": "Dog 4" + }, + { + "id": 9, + "name": "Dog 5" + }, + { + "id": 10, + "name": "Dog 6" + }, + { + "id": 4, + "name": "Only Cat" + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Pets_NET_6_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Pets_NET_6_0.md new file mode 100644 index 00000000000..e8a08a56b6f --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Pets_NET_6_0.md @@ -0,0 +1,71 @@ +# Query_Pets + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT p."AnimalType" = 'Cat', p."Id", p."Name", p."AnimalType" = 'Dog' +FROM "Pets" AS p +ORDER BY p."Name", p."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => IIF((root Is Cat), Convert(new Cat() {Id = Convert(root, Cat).Id, Name = IIF((Convert(root, Cat).Name == null), null, Convert(root, Cat).Name)}, Animal), IIF((root Is Dog), Convert(new Dog() {Id = Convert(root, Dog).Id, Name = IIF((Convert(root, Dog).Name == null), null, Convert(root, Dog).Name)}, Animal), null))).Take(11) +``` + +## Result 3 + +```json +{ + "data": { + "pets": { + "nodes": [ + { + "id": 1, + "name": "Cat 1" + }, + { + "id": 2, + "name": "Cat 2" + }, + { + "id": 3, + "name": "Cat 3 (Not Pure)" + }, + { + "id": 5, + "name": "Dog 1" + }, + { + "id": 6, + "name": "Dog 2" + }, + { + "id": 7, + "name": "Dog 3" + }, + { + "id": 8, + "name": "Dog 4" + }, + { + "id": 9, + "name": "Dog 5" + }, + { + "id": 10, + "name": "Dog 6" + }, + { + "id": 4, + "name": "Only Cat" + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Pets_NET_7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Pets_NET_7_0.md new file mode 100644 index 00000000000..882426891ca --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Pets_NET_7_0.md @@ -0,0 +1,71 @@ +# Query_Pets + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT p."AnimalType" = 'Cat', p."Id", p."Name", p."AnimalType" = 'Dog' +FROM "Pets" AS p +ORDER BY p."Name", p."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => IIF((root Is Cat), Convert(new Cat() {Id = Convert(root, Cat).Id, Name = IIF((Convert(root, Cat).Name == null), null, Convert(root, Cat).Name)}, Animal), IIF((root Is Dog), Convert(new Dog() {Id = Convert(root, Dog).Id, Name = IIF((Convert(root, Dog).Name == null), null, Convert(root, Dog).Name)}, Animal), null))).Take(11) +``` + +## Result 3 + +```json +{ + "data": { + "pets": { + "nodes": [ + { + "id": 1, + "name": "Cat 1" + }, + { + "id": 2, + "name": "Cat 2" + }, + { + "id": 3, + "name": "Cat 3 (Not Pure)" + }, + { + "id": 5, + "name": "Dog 1" + }, + { + "id": 6, + "name": "Dog 2" + }, + { + "id": 7, + "name": "Dog 3" + }, + { + "id": 8, + "name": "Dog 4" + }, + { + "id": 9, + "name": "Dog 5" + }, + { + "id": 10, + "name": "Dog 6" + }, + { + "id": 4, + "name": "Only Cat" + } + ] + } + } +} +``` + From b3569e7087bdc44c8807562ecc01e765459168c1 Mon Sep 17 00:00:00 2001 From: Glen Date: Sun, 6 Oct 2024 00:48:54 +0200 Subject: [PATCH 049/154] Added Codecov configuration file to set the mode to informational (#7564) --- .github/codecov.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .github/codecov.yml diff --git a/.github/codecov.yml b/.github/codecov.yml new file mode 100644 index 00000000000..bfdc9877d9a --- /dev/null +++ b/.github/codecov.yml @@ -0,0 +1,8 @@ +coverage: + status: + project: + default: + informational: true + patch: + default: + informational: true From bc1148ea2e422365ecdb056a0e44755ef922d1cb Mon Sep 17 00:00:00 2001 From: Glen Date: Sun, 6 Oct 2024 14:10:24 +0200 Subject: [PATCH 050/154] Simplified Codecov coverage upload (#7565) --- .github/workflows/ci.yml | 40 ++------------------- .github/workflows/coverage.yml | 65 +++++++++------------------------- 2 files changed, 19 insertions(+), 86 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 27c286aa55b..0b41eb47de5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -227,34 +227,12 @@ jobs: name: mismatch-files-${{ matrix.name }} path: ${{ matrix.directoryPath }}/**/__mismatch__/* - merge-coverage: - name: Merge and Upload Coverage + upload-coverage: + name: Upload Coverage needs: library-tests if: always() && needs.library-tests.result != 'cancelled' runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - show-progress: false - - - name: Install .NET - if: ${{ !cancelled() }} - uses: actions/setup-dotnet@v4 - with: - dotnet-version: | - 6.x - 7.x - 8.x - - - name: Create All.sln - if: ${{ !cancelled() }} - run: ./build.sh CreateAllSln - - - name: Build - if: ${{ !cancelled() }} - run: dotnet build ./src/All.sln - - name: Download all coverage artifacts if: ${{ !cancelled() }} uses: actions/download-artifact@v4 @@ -262,26 +240,14 @@ jobs: path: ./output/download pattern: coverage-* - - name: Merge Coverage Files - if: ${{ !cancelled() }} - timeout-minutes: 10 - run: | - dotnet tool install -g dotnet-reportgenerator-globaltool - reportgenerator "-reports:./output/download/**/*.xml" "-targetdir:./output/coverage/merged" -reporttypes:Opencover -license:$ReportGenerator_License - env: - ReportGenerator_License: ${{ secrets.REPORTGENERATOR_LICENSE }} - - - name: Upload Combined Coverage to Codecov - if: ${{ !cancelled() }} + - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 timeout-minutes: 10 with: - file: ./output/coverage/merged/OpenCover.xml token: ${{ secrets.CODECOV_TOKEN }} name: graphql-platform flags: unittests fail_ci_if_error: true - disable_search: true ci-status-check: name: "CI Status Check" diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 74a4d053a7d..77e34fa58f9 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -112,56 +112,23 @@ jobs: steps.run-tests.outcome == 'failure' || steps.run-tests.outcome == 'cancelled' - merge-coverage: - name: Merge and Upload Coverage + upload-coverage: + name: Upload Coverage needs: library-tests if: always() && needs.library-tests.result != 'cancelled' runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - show-progress: false - - - name: Install .NET - uses: actions/setup-dotnet@v4 - with: - dotnet-version: | - 6.x - 7.x - 8.x - - - name: Create All.sln - if: ${{ !cancelled() }} - run: ./build.sh CreateAllSln - - - name: Build - if: ${{ !cancelled() }} - run: dotnet build ./src/All.sln - - - name: Download all coverage artifacts - uses: actions/download-artifact@v4 - with: - path: ./output/download - pattern: coverage-* - - - name: Merge Coverage Files - if: ${{ !cancelled() }} - timeout-minutes: 10 - run: | - dotnet tool install -g dotnet-reportgenerator-globaltool - reportgenerator "-reports:./output/download/**/*.xml" "-targetdir:./output/coverage/merged" -reporttypes:Opencover -license:$ReportGenerator_License - env: - ReportGenerator_License: ${{ secrets.REPORTGENERATOR_LICENSE }} - - - name: Upload Combined Coverage to Codecov - if: ${{ !cancelled() }} - uses: codecov/codecov-action@v4 - timeout-minutes: 10 - with: - file: ./output/coverage/merged/OpenCover.xml - token: ${{ secrets.CODECOV_TOKEN }} - name: graphql-platform - flags: unittests - fail_ci_if_error: true - disable_search: true + - name: Download all coverage artifacts + uses: actions/download-artifact@v4 + with: + path: ./output/download + pattern: coverage-* + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + timeout-minutes: 10 + with: + token: ${{ secrets.CODECOV_TOKEN }} + name: graphql-platform + flags: unittests + fail_ci_if_error: true From 32b8fb04db287771aa8eb325371a52490c196213 Mon Sep 17 00:00:00 2001 From: Glen Date: Sun, 6 Oct 2024 17:58:19 +0200 Subject: [PATCH 051/154] Used non-escaped text from XML doc summary for descriptions (#7563) --- .../Conventions/XmlDocumentationProvider.cs | 9 ++++++--- .../Types.Tests.Documentation/ClassWithSummary.cs | 15 ++++++--------- .../Conventions/XmlDocumentationProviderTests.cs | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/XmlDocumentationProvider.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/XmlDocumentationProvider.cs index ccb11ace68f..e8bf71564a1 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/XmlDocumentationProvider.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/XmlDocumentationProvider.cs @@ -156,10 +156,13 @@ private static void AppendText( foreach (var node in element.Nodes()) { - var currentElement = node as XElement; - if (currentElement is null) + if (node is not XElement currentElement) { - description.Append(node); + if (node is XText text) + { + description.Append(text.Value); + } + continue; } diff --git a/src/HotChocolate/Core/test/Types.Tests.Documentation/ClassWithSummary.cs b/src/HotChocolate/Core/test/Types.Tests.Documentation/ClassWithSummary.cs index 69a44f6f2b9..e692706f768 100644 --- a/src/HotChocolate/Core/test/Types.Tests.Documentation/ClassWithSummary.cs +++ b/src/HotChocolate/Core/test/Types.Tests.Documentation/ClassWithSummary.cs @@ -1,9 +1,6 @@ -namespace HotChocolate.Types.Descriptors -{ - /// - /// I am a test class. - /// - public class ClassWithSummary - { - } -} +namespace HotChocolate.Types.Descriptors; + +/// +/// I am a test class. This should not be escaped: > +/// +public class ClassWithSummary; diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/Conventions/XmlDocumentationProviderTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/Conventions/XmlDocumentationProviderTests.cs index fafb8d54bd0..8f8788622dd 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/Conventions/XmlDocumentationProviderTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/Conventions/XmlDocumentationProviderTests.cs @@ -316,7 +316,7 @@ public void When_class_has_description_then_it_is_converted() typeof(ClassWithSummary)); // assert - Assert.Equal("I am a test class.", description); + Assert.Equal("I am a test class. This should not be escaped: >", description); } [Fact] From 3e4db7403a23a8acd00315f9f6694a8111be59b7 Mon Sep 17 00:00:00 2001 From: Glen Date: Tue, 8 Oct 2024 10:22:15 +0200 Subject: [PATCH 052/154] Improved field selection error messages (#7573) --- .../GraphQLOverHttpSpecTests.cs | 30 +----- .../Core/src/Validation/ErrorHelper.cs | 6 +- .../Properties/Resources.Designer.cs | 4 +- .../src/Validation/Properties/Resources.resx | 4 +- ...operty_Return_UnexpectedErrorWithPath.json | 2 +- .../DocumentValidatorTests.cs | 4 +- .../LeafFieldSelectionsRuleTests.cs | 101 ++++++++++-------- .../test/Validation.Tests/Types/QueryType.cs | 8 ++ ...Tests.ScalarSelectionsNotAllowedOnInt.snap | 2 +- ...rTests.SkipDirectiveIsInTheWrongPlace.snap | 2 +- ...lidatorTests.Type_query_repeated_6250.snap | 8 +- ...irectQueryOnInterfaceWithoutSubFields.snap | 4 +- ...terfaceWithoutSubFieldsEmptySelection.snap | 4 +- ...s.DirectQueryOnObjectWithoutSubFields.snap | 4 +- ...nObjectWithoutSubFieldsEmptySelection.snap | 4 +- ...ts.DirectQueryOnUnionWithoutSubFields.snap | 4 +- ...OnUnionWithoutSubFieldsEmptySelection.snap | 4 +- ...leTests.InterfaceTypeMissingSelection.snap | 4 +- ...aceTypeMissingSelectionEmptySelection.snap | 4 +- ...ts.ScalarSelectionNotAllowedOnBoolean.snap | 4 +- ...Tests.ScalarSelectionNotAllowedOnEnum.snap | 4 +- ...ectionNotAllowedOnListOfListOfScalars.snap | 21 ++++ ...larSelectionNotAllowedOnListOfScalars.snap | 21 ++++ ...sts.ScalarSelectionNotAllowedWithArgs.snap | 4 +- ...alarSelectionNotAllowedWithDirectives.snap | 4 +- ...ectionNotAllowedWithDirectivesAndArgs.snap | 4 +- ...Tests.ScalarSelectionsNotAllowedOnInt.snap | 4 +- 27 files changed, 158 insertions(+), 111 deletions(-) create mode 100644 src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedOnListOfListOfScalars.snap create mode 100644 src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedOnListOfScalars.snap diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/GraphQLOverHttpSpecTests.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/GraphQLOverHttpSpecTests.cs index 4c94ca26b4e..48d927b0afc 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/GraphQLOverHttpSpecTests.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/GraphQLOverHttpSpecTests.cs @@ -241,32 +241,10 @@ public async Task ValidationError2(string? acceptHeader, HttpTransportVersion tr using var response = await client.SendAsync(request); // assert - Snapshot - .Create() - .Add(response) - .MatchInline( - @$"Headers: - Content-Type: {expectedContentType} - --------------------------> - Status Code: {expectedStatusCode} - --------------------------> - " + - @"{""errors"":[{""message"":""`__type` is an object, interface or " + - @"union type field. Leaf selections on objects, interfaces, and unions without " + - @"subfields are disallowed."",""locations"":[{""line"":1,""column"":3}]," + - @"""extensions"":{""declaringType"":""Query"",""field"":""__type""," + - @"""type"":""__Type"",""responseName"":""__type""," + - @"""specifiedBy"":""https://spec.graphql.org/October2021/#sec-Field-Selections-" + - @"on-Objects-Interfaces-and-Unions-Types""}},{""message"":""The field `name" + - @"` does not exist on the type `Query`."",""locations"":[{" + - @"""line"":1,""column"":10}],""extensions"":{""type"":""Query""," + - @"""field"":""name"",""responseName"":""name"",""specifiedBy"":" + - @"""https://spec.graphql.org/October2021/#sec-Field-Selections-on-Objects-" + - @"Interfaces-and-Unions-Types""}},{""message"":""The argument `name` " + - @"is required."",""locations"":[{""line"":1,""column"":3}],""extensions"":{" + - @"""type"":""Query"",""field"":""__type"",""argument"":""name""," + - @"""specifiedBy"":""https://spec.graphql.org/October2021/#sec-Required-Arguments""" + - "}}]}"); + Assert.Equal( + expectedContentType, + response.Content.Headers.GetValues("Content-Type").Single()); + Assert.Equal(expectedStatusCode, response.StatusCode); } [Fact] diff --git a/src/HotChocolate/Core/src/Validation/ErrorHelper.cs b/src/HotChocolate/Core/src/Validation/ErrorHelper.cs index 1417160af04..4f84b35b050 100644 --- a/src/HotChocolate/Core/src/Validation/ErrorHelper.cs +++ b/src/HotChocolate/Core/src/Validation/ErrorHelper.cs @@ -143,7 +143,8 @@ public static IError LeafFieldsCannotHaveSelections( return ErrorBuilder.New() .SetMessage( Resources.ErrorHelper_LeafFieldsCannotHaveSelections, - node.Name.Value, fieldType.IsScalarType() ? "a scalar" : "an enum") + node.Name.Value, + fieldType.Print()) .SetLocations([node]) .SetPath(context.CreateErrorPath()) .SetExtension("declaringType", declaringType.Name) @@ -216,7 +217,8 @@ public static IError NoSelectionOnCompositeField( return ErrorBuilder.New() .SetMessage( Resources.ErrorHelper_NoSelectionOnCompositeField, - node.Name.Value) + node.Name.Value, + fieldType.Print()) .SetLocations([node]) .SetPath(context.CreateErrorPath()) .SetExtension("declaringType", declaringType.Name) diff --git a/src/HotChocolate/Core/src/Validation/Properties/Resources.Designer.cs b/src/HotChocolate/Core/src/Validation/Properties/Resources.Designer.cs index 5cfd918f987..7324aafa346 100644 --- a/src/HotChocolate/Core/src/Validation/Properties/Resources.Designer.cs +++ b/src/HotChocolate/Core/src/Validation/Properties/Resources.Designer.cs @@ -303,7 +303,7 @@ internal static string ErrorHelper_IntrospectionNotAllowed { } /// - /// Looks up a localized string similar to `{0}` returns {1} value. Selections on scalars or enums are never allowed, because they are the leaf nodes of any GraphQL query.. + /// Looks up a localized string similar to Field "{0}" must not have a selection since type "{1}" has no subfields.. /// internal static string ErrorHelper_LeafFieldsCannotHaveSelections { get { @@ -321,7 +321,7 @@ internal static string ErrorHelper_MaxExecutionDepth { } /// - /// Looks up a localized string similar to `{0}` is an object, interface or union type field. Leaf selections on objects, interfaces, and unions without subfields are disallowed.. + /// Looks up a localized string similar to Field "{0}" of type "{1}" must have a selection of subfields. Did you mean "{0} {{ ... }}"?. /// internal static string ErrorHelper_NoSelectionOnCompositeField { get { diff --git a/src/HotChocolate/Core/src/Validation/Properties/Resources.resx b/src/HotChocolate/Core/src/Validation/Properties/Resources.resx index 1197e1a53a1..fd84901b3e8 100644 --- a/src/HotChocolate/Core/src/Validation/Properties/Resources.resx +++ b/src/HotChocolate/Core/src/Validation/Properties/Resources.resx @@ -126,7 +126,7 @@ The field `{0}` does not exist on the type `{1}`. - `{0}` returns {1} value. Selections on scalars or enums are never allowed, because they are the leaf nodes of any GraphQL query. + Field "{0}" must not have a selection since type "{1}" has no subfields. The specified argument value does not match the argument type. @@ -138,7 +138,7 @@ The specified value type of variable `{0}` does not match the variable type. - `{0}` is an object, interface or union type field. Leaf selections on objects, interfaces, and unions without subfields are disallowed. + Field "{0}" of type "{1}" must have a selection of subfields. Did you mean "{0} {{ ... }}"? Operation `{0}` has a empty selection set. Root types without subfields are disallowed. diff --git a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.Property_Return_UnexpectedErrorWithPath.json b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.Property_Return_UnexpectedErrorWithPath.json index a8e52e94df4..29f0800e0ff 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.Property_Return_UnexpectedErrorWithPath.json +++ b/src/HotChocolate/Core/test/Execution.Tests/Errors/__snapshots__/ErrorBehaviorTests.Property_Return_UnexpectedErrorWithPath.json @@ -1,7 +1,7 @@ { "errors": [ { - "message": "`error13` is an object, interface or union type field. Leaf selections on objects, interfaces, and unions without subfields are disallowed.", + "message": "Field \"error13\" of type \"Foo\" must have a selection of subfields. Did you mean \"error13 { ... }\"?", "locations": [ { "line": 1, diff --git a/src/HotChocolate/Core/test/Validation.Tests/DocumentValidatorTests.cs b/src/HotChocolate/Core/test/Validation.Tests/DocumentValidatorTests.cs index 9744480ffe7..49e2abd6360 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/DocumentValidatorTests.cs +++ b/src/HotChocolate/Core/test/Validation.Tests/DocumentValidatorTests.cs @@ -441,8 +441,8 @@ await ExpectErrors( } ", t => Assert.Equal( - "`barkVolume` returns a scalar value. Selections on scalars or enums" + - " are never allowed, because they are the leaf nodes of any GraphQL query.", + "Field \"barkVolume\" must not have a selection since type \"Int\" has no " + + "subfields.", t.Message)); } diff --git a/src/HotChocolate/Core/test/Validation.Tests/LeafFieldSelectionsRuleTests.cs b/src/HotChocolate/Core/test/Validation.Tests/LeafFieldSelectionsRuleTests.cs index 123c5afe8d3..b43ba58a711 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/LeafFieldSelectionsRuleTests.cs +++ b/src/HotChocolate/Core/test/Validation.Tests/LeafFieldSelectionsRuleTests.cs @@ -45,9 +45,8 @@ public void ScalarSelectionsNotAllowedOnInt() } ", t => Assert.Equal( - "`barkVolume` returns a scalar value. Selections on scalars " + - "or enums are never allowed, because they are the leaf " + - "nodes of any GraphQL query.", + "Field \"barkVolume\" must not have a selection since type \"Int\" has no " + + "subfields.", t.Message)); } @@ -60,9 +59,8 @@ query directQueryOnObjectWithoutSubFields { } ", t => Assert.Equal( - "`human` is an object, interface or union type " + - "field. Leaf selections on objects, interfaces, and " + - "unions without subfields are disallowed.", + "Field \"human\" of type \"Human\" must have a selection of subfields. Did you " + + "mean \"human { ... }\"?", t.Message)); } @@ -75,9 +73,8 @@ query directQueryOnObjectWithoutSubFields { } ", t => Assert.Equal( - "`human` is an object, interface or union type " + - "field. Leaf selections on objects, interfaces, and " + - "unions without subfields are disallowed.", + "Field \"human\" of type \"Human\" must have a selection of subfields. Did you " + + "mean \"human { ... }\"?", t.Message)); } @@ -90,9 +87,8 @@ query directQueryOnInterfaceWithoutSubFields { } ", t => Assert.Equal( - "`pet` is an object, interface or union type " + - "field. Leaf selections on objects, interfaces, and " + - "unions without subfields are disallowed.", + "Field \"pet\" of type \"Human\" must have a selection of subfields. Did you mean " + + "\"pet { ... }\"?", t.Message)); } @@ -105,9 +101,8 @@ query directQueryOnInterfaceWithoutSubFields { } ", t => Assert.Equal( - "`pet` is an object, interface or union type " + - "field. Leaf selections on objects, interfaces, and " + - "unions without subfields are disallowed.", + "Field \"pet\" of type \"Human\" must have a selection of subfields. Did you mean " + + "\"pet { ... }\"?", t.Message)); } @@ -120,9 +115,8 @@ query directQueryOnUnionWithoutSubFields { } ", t => Assert.Equal( - "`catOrDog` is an object, interface or union type " + - "field. Leaf selections on objects, interfaces, and " + - "unions without subfields are disallowed.", + "Field \"catOrDog\" of type \"CatOrDog\" must have a selection of subfields. Did " + + "you mean \"catOrDog { ... }\"?", t.Message)); } @@ -135,9 +129,8 @@ query directQueryOnUnionWithoutSubFields { } ", t => Assert.Equal( - "`catOrDog` is an object, interface or union type " + - "field. Leaf selections on objects, interfaces, and " + - "unions without subfields are disallowed.", + "Field \"catOrDog\" of type \"CatOrDog\" must have a selection of subfields. Did " + + "you mean \"catOrDog { ... }\"?", t.Message)); } @@ -150,9 +143,8 @@ public void InterfaceTypeMissingSelection() } ", t => Assert.Equal( - "`pets` is an object, interface or union type " + - "field. Leaf selections on objects, interfaces, and " + - "unions without subfields are disallowed.", + "Field \"pets\" of type \"[Pet]\" must have a selection of subfields. Did you " + + "mean \"pets { ... }\"?", t.Message)); } @@ -165,9 +157,8 @@ public void InterfaceTypeMissingSelectionEmptySelection() } ", t => Assert.Equal( - "`pets` is an object, interface or union type " + - "field. Leaf selections on objects, interfaces, and " + - "unions without subfields are disallowed.", + "Field \"pets\" of type \"[Pet]\" must have a selection of subfields. Did you " + + "mean \"pets { ... }\"?", t.Message)); } @@ -256,9 +247,8 @@ public void ScalarSelectionNotAllowedOnBoolean() } ", t => Assert.Equal( - "`barks` returns a scalar value. Selections on scalars " + - "or enums are never allowed, because they are the leaf " + - "nodes of any GraphQL query.", + "Field \"barks\" must not have a selection since type \"Boolean!\" has no " + + "subfields.", t.Message)); } @@ -277,9 +267,40 @@ ... on Cat { } ", t => Assert.Equal( - "`furColor` returns an enum value. Selections on scalars " + - "or enums are never allowed, because they are the leaf " + - "nodes of any GraphQL query.", + "Field \"furColor\" must not have a selection since type \"FurColor\" has no " + + "subfields.", + t.Message)); + } + + [Fact] + public void ScalarSelectionNotAllowedOnListOfScalars() + { + ExpectErrors(@" + { + listOfScalars { + x + } + } + ", + t => Assert.Equal( + "Field \"listOfScalars\" must not have a selection since type \"[String]\" has " + + "no subfields.", + t.Message)); + } + + [Fact] + public void ScalarSelectionNotAllowedOnListOfListOfScalars() + { + ExpectErrors(@" + { + listOfListOfScalars { + x + } + } + ", + t => Assert.Equal( + "Field \"listOfListOfScalars\" must not have a selection since type " + + "\"[[String]]\" has no subfields.", t.Message)); } @@ -294,9 +315,8 @@ public void ScalarSelectionNotAllowedWithArgs() } ", t => Assert.Equal( - "`doesKnowCommand` returns a scalar value. Selections on scalars " + - "or enums are never allowed, because they are the leaf " + - "nodes of any GraphQL query.", + "Field \"doesKnowCommand\" must not have a selection since type \"Boolean!\" has " + + "no subfields.", t.Message)); } @@ -311,9 +331,7 @@ name @include(if: true) { isAlsoHumanName } } ", t => Assert.Equal( - "`name` returns a scalar value. Selections on scalars " + - "or enums are never allowed, because they are the leaf " + - "nodes of any GraphQL query.", + "Field \"name\" must not have a selection since type \"String!\" has no subfields.", t.Message)); } @@ -328,9 +346,8 @@ public void ScalarSelectionNotAllowedWithDirectivesAndArgs() } ", t => Assert.Equal( - "`doesKnowCommand` returns a scalar value. Selections on scalars " + - "or enums are never allowed, because they are the leaf " + - "nodes of any GraphQL query.", + "Field \"doesKnowCommand\" must not have a selection since type \"Boolean!\" has " + + "no subfields.", t.Message)); } } diff --git a/src/HotChocolate/Core/test/Validation.Tests/Types/QueryType.cs b/src/HotChocolate/Core/test/Validation.Tests/Types/QueryType.cs index 63886121777..d1c6e600d4a 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/Types/QueryType.cs +++ b/src/HotChocolate/Core/test/Validation.Tests/Types/QueryType.cs @@ -38,5 +38,13 @@ protected override void Configure(IObjectTypeDescriptor descriptor) descriptor.Field("nonNull") .Argument("a", a => a.Type>().DefaultValue("abc")) .Resolve("foo"); + + descriptor.Field("listOfScalars") + .Type>() + .Resolve(_ => []); + + descriptor.Field("listOfListOfScalars") + .Type>>() + .Resolve(_ => []); } } diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/DocumentValidatorTests.ScalarSelectionsNotAllowedOnInt.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/DocumentValidatorTests.ScalarSelectionsNotAllowedOnInt.snap index 13c8a0309a1..0a186109727 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/DocumentValidatorTests.ScalarSelectionsNotAllowedOnInt.snap +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/DocumentValidatorTests.ScalarSelectionsNotAllowedOnInt.snap @@ -1,6 +1,6 @@ [ { - "Message": "`barkVolume` returns a scalar value. Selections on scalars or enums are never allowed, because they are the leaf nodes of any GraphQL query.", + "Message": "Field \"barkVolume\" must not have a selection since type \"Int\" has no subfields.", "Code": null, "Path": { "Name": "dog", diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/DocumentValidatorTests.SkipDirectiveIsInTheWrongPlace.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/DocumentValidatorTests.SkipDirectiveIsInTheWrongPlace.snap index 2e5419608c7..cc38cc18786 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/DocumentValidatorTests.SkipDirectiveIsInTheWrongPlace.snap +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/DocumentValidatorTests.SkipDirectiveIsInTheWrongPlace.snap @@ -1,6 +1,6 @@ [ { - "Message": "`field` is an object, interface or union type field. Leaf selections on objects, interfaces, and unions without subfields are disallowed.", + "Message": "Field \"field\" of type \"Query\" must have a selection of subfields. Did you mean \"field { ... }\"?", "Code": null, "Path": null, "Locations": [ diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/DocumentValidatorTests.Type_query_repeated_6250.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/DocumentValidatorTests.Type_query_repeated_6250.snap index f272b738bcd..881ae1734dc 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/DocumentValidatorTests.Type_query_repeated_6250.snap +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/DocumentValidatorTests.Type_query_repeated_6250.snap @@ -16,7 +16,7 @@ "Exception": null }, { - "Message": "`__type` is an object, interface or union type field. Leaf selections on objects, interfaces, and unions without subfields are disallowed.", + "Message": "Field \"__type\" of type \"__Type\" must have a selection of subfields. Did you mean \"__type { ... }\"?", "Code": null, "Path": null, "Locations": [ @@ -35,7 +35,7 @@ "Exception": null }, { - "Message": "`__type` is an object, interface or union type field. Leaf selections on objects, interfaces, and unions without subfields are disallowed.", + "Message": "Field \"__type\" of type \"__Type\" must have a selection of subfields. Did you mean \"__type { ... }\"?", "Code": null, "Path": null, "Locations": [ @@ -54,7 +54,7 @@ "Exception": null }, { - "Message": "`__type` is an object, interface or union type field. Leaf selections on objects, interfaces, and unions without subfields are disallowed.", + "Message": "Field \"__type\" of type \"__Type\" must have a selection of subfields. Did you mean \"__type { ... }\"?", "Code": null, "Path": null, "Locations": [ @@ -73,7 +73,7 @@ "Exception": null }, { - "Message": "`__type` is an object, interface or union type field. Leaf selections on objects, interfaces, and unions without subfields are disallowed.", + "Message": "Field \"__type\" of type \"__Type\" must have a selection of subfields. Did you mean \"__type { ... }\"?", "Code": null, "Path": null, "Locations": [ diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.DirectQueryOnInterfaceWithoutSubFields.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.DirectQueryOnInterfaceWithoutSubFields.snap index 5b2bf1840f4..e8f3abfcf83 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.DirectQueryOnInterfaceWithoutSubFields.snap +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.DirectQueryOnInterfaceWithoutSubFields.snap @@ -1,6 +1,6 @@ -[ +[ { - "Message": "`pet` is an object, interface or union type field. Leaf selections on objects, interfaces, and unions without subfields are disallowed.", + "Message": "Field \"pet\" of type \"Human\" must have a selection of subfields. Did you mean \"pet { ... }\"?", "Code": null, "Path": null, "Locations": [ diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.DirectQueryOnInterfaceWithoutSubFieldsEmptySelection.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.DirectQueryOnInterfaceWithoutSubFieldsEmptySelection.snap index 5b2bf1840f4..e8f3abfcf83 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.DirectQueryOnInterfaceWithoutSubFieldsEmptySelection.snap +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.DirectQueryOnInterfaceWithoutSubFieldsEmptySelection.snap @@ -1,6 +1,6 @@ -[ +[ { - "Message": "`pet` is an object, interface or union type field. Leaf selections on objects, interfaces, and unions without subfields are disallowed.", + "Message": "Field \"pet\" of type \"Human\" must have a selection of subfields. Did you mean \"pet { ... }\"?", "Code": null, "Path": null, "Locations": [ diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.DirectQueryOnObjectWithoutSubFields.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.DirectQueryOnObjectWithoutSubFields.snap index 61b5716e23f..892b2fb340f 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.DirectQueryOnObjectWithoutSubFields.snap +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.DirectQueryOnObjectWithoutSubFields.snap @@ -1,6 +1,6 @@ -[ +[ { - "Message": "`human` is an object, interface or union type field. Leaf selections on objects, interfaces, and unions without subfields are disallowed.", + "Message": "Field \"human\" of type \"Human\" must have a selection of subfields. Did you mean \"human { ... }\"?", "Code": null, "Path": null, "Locations": [ diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.DirectQueryOnObjectWithoutSubFieldsEmptySelection.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.DirectQueryOnObjectWithoutSubFieldsEmptySelection.snap index 61b5716e23f..892b2fb340f 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.DirectQueryOnObjectWithoutSubFieldsEmptySelection.snap +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.DirectQueryOnObjectWithoutSubFieldsEmptySelection.snap @@ -1,6 +1,6 @@ -[ +[ { - "Message": "`human` is an object, interface or union type field. Leaf selections on objects, interfaces, and unions without subfields are disallowed.", + "Message": "Field \"human\" of type \"Human\" must have a selection of subfields. Did you mean \"human { ... }\"?", "Code": null, "Path": null, "Locations": [ diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.DirectQueryOnUnionWithoutSubFields.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.DirectQueryOnUnionWithoutSubFields.snap index ddf92acfee1..d15e8294c28 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.DirectQueryOnUnionWithoutSubFields.snap +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.DirectQueryOnUnionWithoutSubFields.snap @@ -1,6 +1,6 @@ -[ +[ { - "Message": "`catOrDog` is an object, interface or union type field. Leaf selections on objects, interfaces, and unions without subfields are disallowed.", + "Message": "Field \"catOrDog\" of type \"CatOrDog\" must have a selection of subfields. Did you mean \"catOrDog { ... }\"?", "Code": null, "Path": null, "Locations": [ diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.DirectQueryOnUnionWithoutSubFieldsEmptySelection.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.DirectQueryOnUnionWithoutSubFieldsEmptySelection.snap index ddf92acfee1..d15e8294c28 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.DirectQueryOnUnionWithoutSubFieldsEmptySelection.snap +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.DirectQueryOnUnionWithoutSubFieldsEmptySelection.snap @@ -1,6 +1,6 @@ -[ +[ { - "Message": "`catOrDog` is an object, interface or union type field. Leaf selections on objects, interfaces, and unions without subfields are disallowed.", + "Message": "Field \"catOrDog\" of type \"CatOrDog\" must have a selection of subfields. Did you mean \"catOrDog { ... }\"?", "Code": null, "Path": null, "Locations": [ diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.InterfaceTypeMissingSelection.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.InterfaceTypeMissingSelection.snap index 25fe5023c4b..073a370f45c 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.InterfaceTypeMissingSelection.snap +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.InterfaceTypeMissingSelection.snap @@ -1,6 +1,6 @@ -[ +[ { - "Message": "`pets` is an object, interface or union type field. Leaf selections on objects, interfaces, and unions without subfields are disallowed.", + "Message": "Field \"pets\" of type \"[Pet]\" must have a selection of subfields. Did you mean \"pets { ... }\"?", "Code": null, "Path": { "Name": "human", diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.InterfaceTypeMissingSelectionEmptySelection.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.InterfaceTypeMissingSelectionEmptySelection.snap index 25fe5023c4b..073a370f45c 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.InterfaceTypeMissingSelectionEmptySelection.snap +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.InterfaceTypeMissingSelectionEmptySelection.snap @@ -1,6 +1,6 @@ -[ +[ { - "Message": "`pets` is an object, interface or union type field. Leaf selections on objects, interfaces, and unions without subfields are disallowed.", + "Message": "Field \"pets\" of type \"[Pet]\" must have a selection of subfields. Did you mean \"pets { ... }\"?", "Code": null, "Path": { "Name": "human", diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedOnBoolean.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedOnBoolean.snap index cd27fc2f644..931694e2be6 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedOnBoolean.snap +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedOnBoolean.snap @@ -1,6 +1,6 @@ -[ +[ { - "Message": "`barks` returns a scalar value. Selections on scalars or enums are never allowed, because they are the leaf nodes of any GraphQL query.", + "Message": "Field \"barks\" must not have a selection since type \"Boolean!\" has no subfields.", "Code": null, "Path": { "Name": "dog", diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedOnEnum.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedOnEnum.snap index da76d554150..654d4535a99 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedOnEnum.snap +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedOnEnum.snap @@ -1,6 +1,6 @@ -[ +[ { - "Message": "`furColor` returns an enum value. Selections on scalars or enums are never allowed, because they are the leaf nodes of any GraphQL query.", + "Message": "Field \"furColor\" must not have a selection since type \"FurColor\" has no subfields.", "Code": null, "Path": { "Name": "catOrDog", diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedOnListOfListOfScalars.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedOnListOfListOfScalars.snap new file mode 100644 index 00000000000..7e4006a260a --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedOnListOfListOfScalars.snap @@ -0,0 +1,21 @@ +[ + { + "Message": "Field \"listOfListOfScalars\" must not have a selection since type \"[[String]]\" has no subfields.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 3, + "Column": 21 + } + ], + "Extensions": { + "declaringType": "Query", + "field": "listOfListOfScalars", + "type": "[[String]]", + "responseName": "listOfListOfScalars", + "specifiedBy": "https://spec.graphql.org/October2021/#sec-Leaf-Field-Selections" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedOnListOfScalars.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedOnListOfScalars.snap new file mode 100644 index 00000000000..fc370927ea6 --- /dev/null +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedOnListOfScalars.snap @@ -0,0 +1,21 @@ +[ + { + "Message": "Field \"listOfScalars\" must not have a selection since type \"[String]\" has no subfields.", + "Code": null, + "Path": null, + "Locations": [ + { + "Line": 3, + "Column": 21 + } + ], + "Extensions": { + "declaringType": "Query", + "field": "listOfScalars", + "type": "[String]", + "responseName": "listOfScalars", + "specifiedBy": "https://spec.graphql.org/October2021/#sec-Leaf-Field-Selections" + }, + "Exception": null + } +] diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedWithArgs.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedWithArgs.snap index 49211dd5c60..de69f7157f3 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedWithArgs.snap +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedWithArgs.snap @@ -1,6 +1,6 @@ -[ +[ { - "Message": "`doesKnowCommand` returns a scalar value. Selections on scalars or enums are never allowed, because they are the leaf nodes of any GraphQL query.", + "Message": "Field \"doesKnowCommand\" must not have a selection since type \"Boolean!\" has no subfields.", "Code": null, "Path": { "Name": "dog", diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedWithDirectives.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedWithDirectives.snap index c5e65904a2d..187725622d9 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedWithDirectives.snap +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedWithDirectives.snap @@ -1,6 +1,6 @@ -[ +[ { - "Message": "`name` returns a scalar value. Selections on scalars or enums are never allowed, because they are the leaf nodes of any GraphQL query.", + "Message": "Field \"name\" must not have a selection since type \"String!\" has no subfields.", "Code": null, "Path": { "Name": "dog", diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedWithDirectivesAndArgs.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedWithDirectivesAndArgs.snap index 49211dd5c60..de69f7157f3 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedWithDirectivesAndArgs.snap +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionNotAllowedWithDirectivesAndArgs.snap @@ -1,6 +1,6 @@ -[ +[ { - "Message": "`doesKnowCommand` returns a scalar value. Selections on scalars or enums are never allowed, because they are the leaf nodes of any GraphQL query.", + "Message": "Field \"doesKnowCommand\" must not have a selection since type \"Boolean!\" has no subfields.", "Code": null, "Path": { "Name": "dog", diff --git a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionsNotAllowedOnInt.snap b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionsNotAllowedOnInt.snap index a21725f8486..0a186109727 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionsNotAllowedOnInt.snap +++ b/src/HotChocolate/Core/test/Validation.Tests/__snapshots__/LeafFieldSelectionsRuleTests.ScalarSelectionsNotAllowedOnInt.snap @@ -1,6 +1,6 @@ -[ +[ { - "Message": "`barkVolume` returns a scalar value. Selections on scalars or enums are never allowed, because they are the leaf nodes of any GraphQL query.", + "Message": "Field \"barkVolume\" must not have a selection since type \"Int\" has no subfields.", "Code": null, "Path": { "Name": "dog", From c964eb58a455485182949f3fe82eff57e3023792 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Tue, 8 Oct 2024 15:28:54 -0400 Subject: [PATCH 053/154] Renamed Banana Cake Pop to Nitro. --- .build/Build.Environment.cs | 16 +- ...{Build.BananaCakePop.cs => Build.Nitro.cs} | 8 +- .build/Build.Publish.cs | 12 +- src/Directory.Packages.props | 2 +- .../src/AspNetCore/DefaultHttpMethod.cs | 2 +- .../EndpointRouteBuilderExtensions.cs | 50 ++-- ...s => NitroAppEndpointConventionBuilder.cs} | 6 +- .../src/AspNetCore/GraphQLServerOptions.cs | 2 +- .../src/AspNetCore/GraphQLToolOptions.cs | 8 +- .../src/AspNetCore/GraphQLToolServeMode.cs | 4 +- .../AspNetCore/HotChocolate.AspNetCore.csproj | 4 +- .../ToolConfigurationFileMiddlewareTests.cs | 31 +- ...reTests.Fetch_MapNitroApp_Tool_Config.snap | 14 + .../DefaultGraphQLRequestExecutor.cs | 8 +- ...zureFunctionServiceCollectionExtensions.cs | 16 +- .../HotChocolate.AzureFunctions.csproj | 2 +- .../Helpers/TestHttpRequestDataHelper.cs | 6 +- .../IsolatedProcessEndToEndTests.cs | 4 +- .../Helpers/TestHttpContextHelper.cs | 6 +- .../InProcessEndToEndTests.cs | 8 +- .../TestConstants.cs | 2 +- ...ests.Brand_With_Default_Field_Over_Node.md | 3 +- ...rTests.Brand_With_Id_And_Name_Over_Node.md | 3 +- ...taLoaderTests.Brand_With_Name_Over_Node.md | 3 +- src/HotChocolate/Directory.Build.props | 2 +- .../src/Core/HotChocolate.Fusion.csproj | 1 - .../{v12 => }/HotChocolate.Templates.nuspec | 0 .../HotChocolate.Templates.StarWars.nuspec | 15 - .../content/.template.config/template.json | 16 -- .../content/Characters/CharacterQueries.cs | 62 ---- .../content/Characters/CharacterResolvers.cs | 22 -- .../StarWars/content/Characters/Droid.cs | 49 ---- .../StarWars/content/Characters/Episode.cs | 23 -- .../StarWars/content/Characters/Human.cs | 49 ---- .../StarWars/content/Characters/ICharacter.cs | 39 --- .../content/Characters/ISearchResult.cs | 9 - .../StarWars/content/Characters/Starship.cs | 31 -- templates/StarWars/content/Characters/Unit.cs | 11 - .../Characters/UseConvertUnitAttribute.cs | 48 ---- templates/StarWars/content/Program.cs | 17 -- .../Repositories/CharacterRepository.cs | 137 --------- .../Repositories/ICharacterRepository.cs | 17 -- .../content/Repositories/IReviewRepository.cs | 12 - .../content/Repositories/ReviewRepository.cs | 33 --- .../content/Reviews/CreateReviewInput.cs | 44 --- .../content/Reviews/CreateReviewPayload.cs | 35 --- templates/StarWars/content/Reviews/Review.cs | 41 --- .../content/Reviews/ReviewMutations.cs | 31 -- .../StarWars/content/Reviews/ReviewQueries.cs | 24 -- .../content/Reviews/ReviewSubscriptions.cs | 31 -- templates/StarWars/content/StarWars.csproj | 20 -- templates/StarWars/content/Startup.cs | 84 ------ .../.gitignore | 0 .../.template.config/template.json | 16 +- .../GraphQLFunction.cs | 2 +- ...tChocolate.Template.AzureFunctions.csproj} | 10 +- .../Program.cs | 0 .../{v12/function => azure-function}/Query.cs | 0 .../host.json | 0 .../.template.config/template.json | 10 +- ...otChocolate.Template.Gateway.Aspire.csproj | 6 +- templates/{v12 => }/gateway-aspire/Program.cs | 0 .../Properties/launchSettings.json | 0 .../appsettings.Development.json | 0 .../{v12 => }/gateway-aspire/appsettings.json | 0 .../.template.config/template.json | 10 +- ...tChocolate.Template.Gateway.Managed.csproj | 6 +- .../Program.cs | 0 .../Properties/launchSettings.json | 0 .../appsettings.Development.json | 0 .../appsettings.json | 0 .../gateway/.template.config/template.json | 10 +- .../HotChocolate.Template.Gateway.csproj | 4 +- templates/{v12 => }/gateway/Program.cs | 0 .../gateway/Properties/launchSettings.json | 0 .../gateway/appsettings.Development.json | 0 templates/{v12 => }/gateway/appsettings.json | 0 .../server/.template.config/template.json | 14 +- .../HotChocolate.Template.Server.csproj | 2 +- templates/{v12 => }/server/Program.cs | 0 .../{v12 => }/server/Properties/ModuleInfo.cs | 0 .../server/Properties/launchSettings.json | 0 templates/{v12 => }/server/Types/Author.cs | 0 templates/{v12 => }/server/Types/Book.cs | 0 templates/{v12 => }/server/Types/Query.cs | 0 .../server/appsettings.Development.json | 0 templates/{v12 => }/server/appsettings.json | 0 .../.template.config/template.json | 44 --- templates/v12/function-isolated/Query.cs | 16 -- .../v12/function-isolated/local.settings.json | 7 - templates/v12/function/.gitignore | 264 ------------------ templates/v12/function/GraphQLFunction.cs | 17 -- ...otChocolate.Template.AzureFunctions.csproj | 36 --- templates/v12/function/Startup.cs | 11 - templates/v12/function/host.json | 11 - templates/v12/function/local.settings.json | 6 - .../v12/gateway-aspire/.vscode/tasks.json | 24 -- templates/v12/gateway-bcp/.vscode/tasks.json | 24 -- templates/v12/gateway/.vscode/tasks.json | 24 -- templates/v12/server/.vscode/tasks.json | 24 -- 100 files changed, 161 insertions(+), 1560 deletions(-) rename .build/{Build.BananaCakePop.cs => Build.Nitro.cs} (82%) rename src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/{BananaCakePopEndpointConventionBuilder.cs => NitroAppEndpointConventionBuilder.cs} (57%) create mode 100644 src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/ToolConfigurationFileMiddlewareTests.Fetch_MapNitroApp_Tool_Config.snap rename templates/{v12 => }/HotChocolate.Templates.nuspec (100%) delete mode 100644 templates/StarWars/HotChocolate.Templates.StarWars.nuspec delete mode 100644 templates/StarWars/content/.template.config/template.json delete mode 100644 templates/StarWars/content/Characters/CharacterQueries.cs delete mode 100644 templates/StarWars/content/Characters/CharacterResolvers.cs delete mode 100644 templates/StarWars/content/Characters/Droid.cs delete mode 100644 templates/StarWars/content/Characters/Episode.cs delete mode 100644 templates/StarWars/content/Characters/Human.cs delete mode 100644 templates/StarWars/content/Characters/ICharacter.cs delete mode 100644 templates/StarWars/content/Characters/ISearchResult.cs delete mode 100644 templates/StarWars/content/Characters/Starship.cs delete mode 100644 templates/StarWars/content/Characters/Unit.cs delete mode 100644 templates/StarWars/content/Characters/UseConvertUnitAttribute.cs delete mode 100644 templates/StarWars/content/Program.cs delete mode 100644 templates/StarWars/content/Repositories/CharacterRepository.cs delete mode 100644 templates/StarWars/content/Repositories/ICharacterRepository.cs delete mode 100644 templates/StarWars/content/Repositories/IReviewRepository.cs delete mode 100644 templates/StarWars/content/Repositories/ReviewRepository.cs delete mode 100644 templates/StarWars/content/Reviews/CreateReviewInput.cs delete mode 100644 templates/StarWars/content/Reviews/CreateReviewPayload.cs delete mode 100644 templates/StarWars/content/Reviews/Review.cs delete mode 100644 templates/StarWars/content/Reviews/ReviewMutations.cs delete mode 100644 templates/StarWars/content/Reviews/ReviewQueries.cs delete mode 100644 templates/StarWars/content/Reviews/ReviewSubscriptions.cs delete mode 100644 templates/StarWars/content/StarWars.csproj delete mode 100644 templates/StarWars/content/Startup.cs rename templates/{v12/function-isolated => azure-function}/.gitignore (100%) rename templates/{v12/function => azure-function}/.template.config/template.json (75%) rename templates/{v12/function-isolated => azure-function}/GraphQLFunction.cs (90%) rename templates/{v12/function-isolated/HotChocolate.Template.AzureFunctions.Isolated.csproj => azure-function/HotChocolate.Template.AzureFunctions.csproj} (88%) rename templates/{v12/function-isolated => azure-function}/Program.cs (100%) rename templates/{v12/function => azure-function}/Query.cs (100%) rename templates/{v12/function-isolated => azure-function}/host.json (100%) rename templates/{v12 => }/gateway-aspire/.template.config/template.json (91%) rename templates/{v12 => }/gateway-aspire/HotChocolate.Template.Gateway.Aspire.csproj (81%) rename templates/{v12 => }/gateway-aspire/Program.cs (100%) rename templates/{v12 => }/gateway-aspire/Properties/launchSettings.json (100%) rename templates/{v12 => }/gateway-aspire/appsettings.Development.json (100%) rename templates/{v12 => }/gateway-aspire/appsettings.json (100%) rename templates/{v12/gateway-bcp => gateway-managed}/.template.config/template.json (92%) rename templates/{v12/gateway-bcp => gateway-managed}/HotChocolate.Template.Gateway.Managed.csproj (81%) rename templates/{v12/gateway-bcp => gateway-managed}/Program.cs (100%) rename templates/{v12/gateway-bcp => gateway-managed}/Properties/launchSettings.json (100%) rename templates/{v12/gateway-bcp => gateway-managed}/appsettings.Development.json (100%) rename templates/{v12/gateway-bcp => gateway-managed}/appsettings.json (100%) rename templates/{v12 => }/gateway/.template.config/template.json (91%) rename templates/{v12 => }/gateway/HotChocolate.Template.Gateway.csproj (79%) rename templates/{v12 => }/gateway/Program.cs (100%) rename templates/{v12 => }/gateway/Properties/launchSettings.json (100%) rename templates/{v12 => }/gateway/appsettings.Development.json (100%) rename templates/{v12 => }/gateway/appsettings.json (100%) rename templates/{v12 => }/server/.template.config/template.json (84%) rename templates/{v12 => }/server/HotChocolate.Template.Server.csproj (94%) rename templates/{v12 => }/server/Program.cs (100%) rename templates/{v12 => }/server/Properties/ModuleInfo.cs (100%) rename templates/{v12 => }/server/Properties/launchSettings.json (100%) rename templates/{v12 => }/server/Types/Author.cs (100%) rename templates/{v12 => }/server/Types/Book.cs (100%) rename templates/{v12 => }/server/Types/Query.cs (100%) rename templates/{v12 => }/server/appsettings.Development.json (100%) rename templates/{v12 => }/server/appsettings.json (100%) delete mode 100644 templates/v12/function-isolated/.template.config/template.json delete mode 100644 templates/v12/function-isolated/Query.cs delete mode 100644 templates/v12/function-isolated/local.settings.json delete mode 100644 templates/v12/function/.gitignore delete mode 100644 templates/v12/function/GraphQLFunction.cs delete mode 100644 templates/v12/function/HotChocolate.Template.AzureFunctions.csproj delete mode 100644 templates/v12/function/Startup.cs delete mode 100644 templates/v12/function/host.json delete mode 100644 templates/v12/function/local.settings.json delete mode 100644 templates/v12/gateway-aspire/.vscode/tasks.json delete mode 100644 templates/v12/gateway-bcp/.vscode/tasks.json delete mode 100644 templates/v12/gateway/.vscode/tasks.json delete mode 100644 templates/v12/server/.vscode/tasks.json diff --git a/.build/Build.Environment.cs b/.build/Build.Environment.cs index 39d62a7e379..4eb17d19391 100644 --- a/.build/Build.Environment.cs +++ b/.build/Build.Environment.cs @@ -6,7 +6,6 @@ partial class Build const string Release = "Release"; const string Net50 = "net5.0"; const string Net60 = "net6.0"; - const string Net70 = "net7.0"; readonly int DegreeOfParallelism = 2; @@ -19,15 +18,12 @@ partial class Build AbsolutePath TestResultDirectory => OutputDirectory / "test-results"; AbsolutePath CoverageReportDirectory => OutputDirectory / "coverage-reports"; AbsolutePath PackageDirectory => OutputDirectory / "packages"; - AbsolutePath StarWarsTemplateNuSpec => RootDirectory / "templates" / "StarWars" / "HotChocolate.Templates.StarWars.nuspec"; AbsolutePath HotChocolateDirectoryBuildProps => SourceDirectory / "HotChocolate" / "Directory.Build.Props"; - AbsolutePath StarWarsProj => RootDirectory / "templates" / "StarWars" / "content" / "StarWars.csproj"; - AbsolutePath TemplatesNuSpec => RootDirectory / "templates" / "v12" / "HotChocolate.Templates.nuspec"; - AbsolutePath EmptyServer12Proj => RootDirectory / "templates" / "v12" / "server" / "HotChocolate.Template.Server.csproj"; - AbsolutePath EmptyAzf12Proj => RootDirectory / "templates" / "v12" / "function" / "HotChocolate.Template.AzureFunctions.csproj"; - AbsolutePath EmptyAzfUp12Proj => RootDirectory / "templates" / "v12" / "function-isolated" / "HotChocolate.Template.AzureFunctions.Isolated.csproj"; - AbsolutePath Gateway13Proj => RootDirectory / "templates" / "v12" / "gateway" / "HotChocolate.Template.Gateway.csproj"; - AbsolutePath GatewayAspire13Proj => RootDirectory / "templates" / "v12" / "gateway-aspire" / "HotChocolate.Template.Gateway.Aspire.csproj"; - AbsolutePath GatewayManaged13Proj => RootDirectory / "templates" / "v12" / "gateway-bcp" / "HotChocolate.Template.Gateway.Managed.csproj"; + AbsolutePath TemplatesNuSpec => RootDirectory / "templates" / "HotChocolate.Templates.nuspec"; + AbsolutePath EmptyServer12Proj => RootDirectory / "templates" / "server" / "HotChocolate.Template.Server.csproj"; + AbsolutePath EmptyAzf12Proj => RootDirectory / "templates" / "azure-function" / "HotChocolate.Template.AzureFunctions.csproj"; + AbsolutePath Gateway13Proj => RootDirectory / "templates" / "gateway" / "HotChocolate.Template.Gateway.csproj"; + AbsolutePath GatewayAspire13Proj => RootDirectory / "templates" / "gateway-aspire" / "HotChocolate.Template.Gateway.Aspire.csproj"; + AbsolutePath GatewayManaged13Proj => RootDirectory / "templates" / "gateway-managed" / "HotChocolate.Template.Gateway.Managed.csproj"; AbsolutePath FSharpTypes => SourceDirectory/"HotChocolate" /"Core" / "src" / "Types.FSharp" / "HotChocolate.Types.FSharp.fsproj"; } diff --git a/.build/Build.BananaCakePop.cs b/.build/Build.Nitro.cs similarity index 82% rename from .build/Build.BananaCakePop.cs rename to .build/Build.Nitro.cs index 5892a9d462a..a00217232e1 100644 --- a/.build/Build.BananaCakePop.cs +++ b/.build/Build.Nitro.cs @@ -11,14 +11,14 @@ partial class Build { - Target UpdateBananaCakePop => _ => _ + Target UpdateNitro => _ => _ .Executes(async () => { - var latestVersion = await GetLatestVersion("BananaCakePop.Middleware"); - Log.Information($"Latest BCP Version: {latestVersion}"); + var latestVersion = await GetLatestVersion("ChilliCream.Nitro.App"); + Log.Information($"Latest Nitro Version: {latestVersion}"); var project = Project.FromFile(HotChocolateDirectoryBuildProps, new ProjectOptions()); - project.SetProperty("BananaCakePopVersion", latestVersion); + project.SetProperty("NitroVersion", latestVersion); project.Save(); }); diff --git a/.build/Build.Publish.cs b/.build/Build.Publish.cs index 16b91554cc9..c51a826ece0 100644 --- a/.build/Build.Publish.cs +++ b/.build/Build.Publish.cs @@ -46,18 +46,12 @@ partial class Build .Produces(PackageDirectory / "*.snupkg") .Executes(() => { - var projFile = File.ReadAllText(StarWarsProj); - File.WriteAllText(StarWarsProj, projFile.Replace("14.0.0-preview.build.0", SemVersion)); - - projFile = File.ReadAllText(EmptyServer12Proj); + var projFile = File.ReadAllText(EmptyServer12Proj); File.WriteAllText(EmptyServer12Proj, projFile.Replace("14.0.0-preview.build.0", SemVersion)); projFile = File.ReadAllText(EmptyAzf12Proj); File.WriteAllText(EmptyAzf12Proj, projFile.Replace("14.0.0-preview.build.0", SemVersion)); - projFile = File.ReadAllText(EmptyAzfUp12Proj); - File.WriteAllText(EmptyAzfUp12Proj, projFile.Replace("14.0.0-preview.build.0", SemVersion)); - projFile = File.ReadAllText(Gateway13Proj); File.WriteAllText(Gateway13Proj, projFile.Replace("14.0.0-preview.build.0", SemVersion)); @@ -108,9 +102,7 @@ partial class Build .SetVersion(SemVersion) .SetOutputDirectory(PackageDirectory) .SetConfiguration(Configuration) - .CombineWith( - t => t.SetTargetPath(StarWarsTemplateNuSpec), - t => t.SetTargetPath(TemplatesNuSpec))); + .CombineWith(t => t.SetTargetPath(TemplatesNuSpec))); }); Target Publish => _ => _ diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index bd5c3fc3255..b9a57951277 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -11,7 +11,7 @@ - + diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore/DefaultHttpMethod.cs b/src/HotChocolate/AspNetCore/src/AspNetCore/DefaultHttpMethod.cs index 5a4dee90c48..219dbee499f 100644 --- a/src/HotChocolate/AspNetCore/src/AspNetCore/DefaultHttpMethod.cs +++ b/src/HotChocolate/AspNetCore/src/AspNetCore/DefaultHttpMethod.cs @@ -1,7 +1,7 @@ namespace HotChocolate.AspNetCore; /// -/// The default HTTP fetch method for Banana Cake Pop. +/// The default HTTP fetch method for Nitro. /// public enum DefaultHttpMethod { diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/EndpointRouteBuilderExtensions.cs b/src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/EndpointRouteBuilderExtensions.cs index 2293321f723..7c9c83db878 100644 --- a/src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/EndpointRouteBuilderExtensions.cs +++ b/src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/EndpointRouteBuilderExtensions.cs @@ -1,10 +1,12 @@ +#if NET7_0_OR_GREATER using System.Diagnostics.CodeAnalysis; +#endif +using ChilliCream.Nitro.App; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing.Patterns; using HotChocolate.AspNetCore; using HotChocolate.AspNetCore.Extensions; -using BananaCakePop.Middleware; using static HotChocolate.AspNetCore.MiddlewareRoutingType; using static Microsoft.AspNetCore.Routing.Patterns.RoutePatternFactory; @@ -20,7 +22,9 @@ public static class EndpointRouteBuilderExtensions private const string _graphQLWebSocketPath = "/graphql/ws"; private const string _graphQLSchemaPath = "/graphql/sdl"; private const string _graphQLToolPath = "/graphql/ui"; +#if NET8_0_OR_GREATER private const string _graphQLPersistedOperationPath = "/graphql/persisted"; +#endif private const string _graphQLToolRelativeRequestPath = ".."; /// @@ -124,7 +128,7 @@ public static IApplicationBuilder MapGraphQL( .UseMiddleware(schemaName) .UseMiddleware(schemaName) .UseMiddleware(schemaName, path, Integrated) - .UseBananaCakePop(path) + .UseNitroApp(path) .Use( _ => context => { @@ -374,13 +378,13 @@ public static IEndpointConventionBuilder MapGraphQLSchema( } /// - /// Adds a Banana Cake Pop endpoint to the endpoint configurations. + /// Adds a Nitro endpoint to the endpoint configurations. /// /// /// The . /// /// - /// The path to which Banana Cake Pop is mapped. + /// The path to which Nitro is mapped. /// /// /// The relative path on which the server is listening for GraphQL requests. @@ -389,20 +393,20 @@ public static IEndpointConventionBuilder MapGraphQLSchema( /// Returns the so that /// configuration can be chained. /// - public static BananaCakePopEndpointConventionBuilder MapBananaCakePop( + public static NitroAppEndpointConventionBuilder MapNitroApp( this IEndpointRouteBuilder endpointRouteBuilder, string toolPath = _graphQLToolPath, string? relativeRequestPath = _graphQLToolRelativeRequestPath) - => MapBananaCakePop(endpointRouteBuilder, new PathString(toolPath), relativeRequestPath); + => MapNitroApp(endpointRouteBuilder, new PathString(toolPath), relativeRequestPath); /// - /// Adds a Banana Cake Pop endpoint to the endpoint configurations. + /// Adds a Nitro endpoint to the endpoint configurations. /// /// /// The . /// /// - /// The path to which Banana Cake Pop is mapped. + /// The path to which Nitro is mapped. /// /// /// The relative path on which the server is listening for GraphQL requests. @@ -411,7 +415,7 @@ public static BananaCakePopEndpointConventionBuilder MapBananaCakePop( /// Returns the so that /// configuration can be chained. /// - public static BananaCakePopEndpointConventionBuilder MapBananaCakePop( + public static NitroAppEndpointConventionBuilder MapNitroApp( this IEndpointRouteBuilder endpointRouteBuilder, PathString toolPath, string? relativeRequestPath = _graphQLToolRelativeRequestPath) @@ -428,7 +432,7 @@ public static BananaCakePopEndpointConventionBuilder MapBananaCakePop( var requestPipeline = endpointRouteBuilder.CreateApplicationBuilder(); requestPipeline - .UseBananaCakePop(toolPath) + .UseNitroApp(toolPath) .Use( _ => context => { @@ -438,13 +442,13 @@ public static BananaCakePopEndpointConventionBuilder MapBananaCakePop( var builder = endpointRouteBuilder .Map(pattern, requestPipeline.Build()) - .WithDisplayName("Banana Cake Pop Pipeline") - .WithMetadata(new BananaCakePopOptions { GraphQLEndpoint = relativeRequestPath, }); + .WithDisplayName("Nitro Pipeline") + .WithMetadata(new NitroAppOptions { GraphQLEndpoint = relativeRequestPath, }); - return new BananaCakePopEndpointConventionBuilder(builder); + return new NitroAppEndpointConventionBuilder(builder); } - #if NET8_0_OR_GREATER + /// /// Adds a persisted operation endpoint to the endpoint configurations. /// @@ -520,7 +524,7 @@ public static GraphQLEndpointConventionBuilder WithOptions( GraphQLServerOptions serverOptions) => builder .WithMetadata(serverOptions) - .WithMetadata(serverOptions.Tool.ToBcpOptions()); + .WithMetadata(serverOptions.Tool.ToNitroAppOptions()); /// /// Specifies the GraphQL HTTP request options. @@ -547,23 +551,23 @@ public static GraphQLHttpEndpointConventionBuilder WithOptions( }); /// - /// Specifies the Banana Cake Pop tooling options. + /// Specifies the Nitro tooling options. /// /// - /// The . + /// The . /// /// - /// The Banana Cake Pop tooling options. + /// The Nitro tooling options. /// /// - /// Returns the so that + /// Returns the so that /// configuration can be chained. /// - public static BananaCakePopEndpointConventionBuilder WithOptions( - this BananaCakePopEndpointConventionBuilder builder, + public static NitroAppEndpointConventionBuilder WithOptions( + this NitroAppEndpointConventionBuilder builder, GraphQLToolOptions toolOptions) { - builder.Add(c => c.Metadata.Add(toolOptions.ToBcpOptions())); + builder.Add(c => c.Metadata.Add(toolOptions.ToNitroAppOptions())); return builder; } @@ -599,7 +603,7 @@ private static IApplicationBuilder UseCancellation(this IApplicationBuilder buil } }); - internal static BananaCakePopOptions ToBcpOptions(this GraphQLToolOptions options) + internal static NitroAppOptions ToNitroAppOptions(this GraphQLToolOptions options) => new() { ServeMode = ServeMode.Version(options.ServeMode.Mode), diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/BananaCakePopEndpointConventionBuilder.cs b/src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/NitroAppEndpointConventionBuilder.cs similarity index 57% rename from src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/BananaCakePopEndpointConventionBuilder.cs rename to src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/NitroAppEndpointConventionBuilder.cs index b202af2f2fe..827858bffd5 100644 --- a/src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/BananaCakePopEndpointConventionBuilder.cs +++ b/src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/NitroAppEndpointConventionBuilder.cs @@ -3,13 +3,13 @@ namespace HotChocolate.AspNetCore.Extensions; /// -/// Represents the endpoint convention builder for Banana Cake Pop. +/// Represents the endpoint convention builder for Nitro. /// -public sealed class BananaCakePopEndpointConventionBuilder : IEndpointConventionBuilder +public sealed class NitroAppEndpointConventionBuilder : IEndpointConventionBuilder { private readonly IEndpointConventionBuilder _builder; - internal BananaCakePopEndpointConventionBuilder(IEndpointConventionBuilder builder) + internal NitroAppEndpointConventionBuilder(IEndpointConventionBuilder builder) { _builder = builder; } diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore/GraphQLServerOptions.cs b/src/HotChocolate/AspNetCore/src/AspNetCore/GraphQLServerOptions.cs index 74e789fe4f9..389d604c445 100644 --- a/src/HotChocolate/AspNetCore/src/AspNetCore/GraphQLServerOptions.cs +++ b/src/HotChocolate/AspNetCore/src/AspNetCore/GraphQLServerOptions.cs @@ -6,7 +6,7 @@ namespace HotChocolate.AspNetCore; public sealed class GraphQLServerOptions { /// - /// Gets the GraphQL tool options for Banana Cake Pop. + /// Gets the GraphQL tool options for Nitro. /// public GraphQLToolOptions Tool { get; internal set; } = new(); diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore/GraphQLToolOptions.cs b/src/HotChocolate/AspNetCore/src/AspNetCore/GraphQLToolOptions.cs index 59191104216..f702d468880 100644 --- a/src/HotChocolate/AspNetCore/src/AspNetCore/GraphQLToolOptions.cs +++ b/src/HotChocolate/AspNetCore/src/AspNetCore/GraphQLToolOptions.cs @@ -3,7 +3,7 @@ namespace HotChocolate.AspNetCore; /// -/// Represents the GraphQL tool options for Banana Cake Pop. +/// Represents the GraphQL tool options for Nitro. /// public sealed class GraphQLToolOptions { @@ -38,7 +38,7 @@ public sealed class GraphQLToolOptions public bool? IncludeCookies { get; set; } /// - /// Gets or sets the default http headers for Banana Cake Pop. + /// Gets or sets the default http headers for Nitro. /// public IHeaderDictionary? HttpHeaders { get; set; } @@ -48,12 +48,12 @@ public sealed class GraphQLToolOptions public DefaultHttpMethod? HttpMethod { get; set; } /// - /// Defines if Banana Cake Pop is enabled. + /// Defines if Nitro is enabled. /// public bool Enable { get; set; } = true; /// - /// Specifies the Google analytics tracking ID for Banana Cake Pop. + /// Specifies the Google analytics tracking ID for Nitro. /// public string? GaTrackingId { get; set; } diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore/GraphQLToolServeMode.cs b/src/HotChocolate/AspNetCore/src/AspNetCore/GraphQLToolServeMode.cs index 4d4d7544df1..be20089305a 100644 --- a/src/HotChocolate/AspNetCore/src/AspNetCore/GraphQLToolServeMode.cs +++ b/src/HotChocolate/AspNetCore/src/AspNetCore/GraphQLToolServeMode.cs @@ -1,9 +1,9 @@ -using BananaCakePop.Middleware; +using ChilliCream.Nitro.App; namespace HotChocolate.AspNetCore; /// -/// Represents the different modes of serving the Banana Cake Pop GraphQL tool. This class enables +/// Represents the different modes of serving the Nitro GraphQL tool. This class enables /// serving the tool in a variety of predefined ways: /// /// diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore/HotChocolate.AspNetCore.csproj b/src/HotChocolate/AspNetCore/src/AspNetCore/HotChocolate.AspNetCore.csproj index b545e3b8cc8..0554c3ebfcc 100644 --- a/src/HotChocolate/AspNetCore/src/AspNetCore/HotChocolate.AspNetCore.csproj +++ b/src/HotChocolate/AspNetCore/src/AspNetCore/HotChocolate.AspNetCore.csproj @@ -4,7 +4,7 @@ HotChocolate.AspNetCore HotChocolate.AspNetCore HotChocolate.AspNetCore - This package contains the GraphQL ASP.NET Core middleware for Hot Chocolate. Moreover, this package includes the Banana Cake Pop middleware, which provides you with our beloved GraphQL IDE middleware. + This package contains the GraphQL ASP.NET Core middleware for Hot Chocolate. Moreover, this package includes the Nitro middleware, which provides you with our beloved GraphQL IDE middleware. @@ -23,7 +23,7 @@ - + diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/ToolConfigurationFileMiddlewareTests.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/ToolConfigurationFileMiddlewareTests.cs index 037c2445a25..7ec7dae1496 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/ToolConfigurationFileMiddlewareTests.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/ToolConfigurationFileMiddlewareTests.cs @@ -1,3 +1,4 @@ + using System.Net; using System.Net.Http.Headers; using CookieCrumble; @@ -22,7 +23,7 @@ public async Task Fetch_Tool_Config_Without_Options() var server = CreateStarWarsServer(); // act - var result = await GetBcpConfigAsync(server); + var result = await GetNitroConfigAsync(server); // assert result.MatchSnapshot(); @@ -33,10 +34,10 @@ public async Task Fetch_Tool_Config_Without_Options_Explicit_Route() { // arrange var options = new GraphQLToolOptions { ServeMode = GraphQLToolServeMode.Embedded, }; - var server = CreateServer(builder => builder.MapBananaCakePop().WithOptions(options)); + var server = CreateServer(builder => builder.MapNitroApp().WithOptions(options)); // act - var result = await GetBcpConfigAsync(server, "/graphql/ui"); + var result = await GetNitroConfigAsync(server, "/graphql/ui"); // assert result.MatchSnapshot(); @@ -50,11 +51,11 @@ public async Task Fetch_Tool_Config_Without_Options_Explicit_Route_Combined() var server = CreateServer(builder => { builder.MapGraphQLHttp(); - builder.MapBananaCakePop().WithOptions(options); + builder.MapNitroApp().WithOptions(options); }); // act - var result = await GetBcpConfigAsync(server, "/graphql/ui"); + var result = await GetNitroConfigAsync(server, "/graphql/ui"); // assert result.MatchSnapshot(); @@ -65,10 +66,10 @@ public async Task Fetch_Tool_Config_Without_Options_Explicit_Route_Explicit_Path { // arrange var options = new GraphQLToolOptions { ServeMode = GraphQLToolServeMode.Embedded, }; - var server = CreateServer(b => b.MapBananaCakePop("/foo/bar").WithOptions(options)); + var server = CreateServer(b => b.MapNitroApp("/foo/bar").WithOptions(options)); // act - var result = await GetBcpConfigAsync(server, "/foo/bar"); + var result = await GetNitroConfigAsync(server, "/foo/bar"); // assert result.MatchSnapshot(); @@ -121,35 +122,35 @@ public async Task Fetch_Tool_Config_With_Options() var server = CreateStarWarsServer("/graphql", configureConventions: builder => builder.WithOptions(options)); // act - var result = await GetBcpConfigAsync(server); + var result = await GetNitroConfigAsync(server); // assert result.MatchSnapshot(); } [Fact] - public async Task Fetch_MapBananaCakePop_Tool_Config() + public async Task Fetch_MapNitroApp_Tool_Config() { // arrange var options = new GraphQLToolOptions { ServeMode = GraphQLToolServeMode.Embedded, }; - var server = CreateServer(endpoint => endpoint.MapBananaCakePop().WithOptions(options)); + var server = CreateServer(endpoint => endpoint.MapNitroApp().WithOptions(options)); // act - var result = await GetBcpConfigAsync(server, "/graphql/ui"); + var result = await GetNitroConfigAsync(server, "/graphql/ui"); // assert result.MatchSnapshot(); } [Fact] - public async Task Fetch_MapBananaCakePop_Tool_FromCdn() + public async Task Fetch_MapNitroApp_Tool_FromCdn() { // arrange var options = new GraphQLToolOptions { ServeMode = GraphQLToolServeMode.Version("5.0.8"), }; - var server = CreateServer(endpoint => endpoint.MapBananaCakePop().WithOptions(options)); + var server = CreateServer(endpoint => endpoint.MapNitroApp().WithOptions(options)); // act var result = await GetAsync(server, "/graphql/ui/index.html"); @@ -158,9 +159,9 @@ public async Task Fetch_MapBananaCakePop_Tool_FromCdn() Assert.Contains("static/js/main.98391269.js", result.Content); } - private Task GetBcpConfigAsync(TestServer server, string url = "/graphql") + private Task GetNitroConfigAsync(TestServer server, string url = "/graphql") { - return GetAsync(server, $"{url}/bcp-config.json"); + return GetAsync(server, $"{url}/nitro-config.json"); } private async Task GetAsync(TestServer server, string url = "/graphql") diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/ToolConfigurationFileMiddlewareTests.Fetch_MapNitroApp_Tool_Config.snap b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/ToolConfigurationFileMiddlewareTests.Fetch_MapNitroApp_Tool_Config.snap new file mode 100644 index 00000000000..dc92955d57a --- /dev/null +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/ToolConfigurationFileMiddlewareTests.Fetch_MapNitroApp_Tool_Config.snap @@ -0,0 +1,14 @@ +{ + "Content": "{\"useBrowserUrlAsEndpoint\":true,\"endpoint\":\"..\",\"useGet\":false}", + "ContentType": { + "CharSet": "utf-8", + "Parameters": [ + { + "Name": "charset", + "Value": "utf-8" + } + ], + "MediaType": "application/json" + }, + "StatusCode": "OK" +} diff --git a/src/HotChocolate/AzureFunctions/src/HotChocolate.AzureFunctions/DefaultGraphQLRequestExecutor.cs b/src/HotChocolate/AzureFunctions/src/HotChocolate.AzureFunctions/DefaultGraphQLRequestExecutor.cs index 2f21f65b2cb..f0056e9960d 100644 --- a/src/HotChocolate/AzureFunctions/src/HotChocolate.AzureFunctions/DefaultGraphQLRequestExecutor.cs +++ b/src/HotChocolate/AzureFunctions/src/HotChocolate.AzureFunctions/DefaultGraphQLRequestExecutor.cs @@ -1,4 +1,4 @@ -using BananaCakePop.Middleware; +using ChilliCream.Nitro.App; using HotChocolate.AspNetCore; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -11,13 +11,13 @@ internal sealed class DefaultGraphQLRequestExecutor : IGraphQLRequestExecutor private readonly EmptyResult _result = new(); private readonly RequestDelegate _pipeline; private readonly GraphQLServerOptions _options; - private readonly BananaCakePopOptions _bcpOptions; + private readonly NitroAppOptions _nitroAppOptions; public DefaultGraphQLRequestExecutor(RequestDelegate pipeline, GraphQLServerOptions options) { _pipeline = pipeline ?? throw new ArgumentNullException(nameof(pipeline)); _options = options ?? throw new ArgumentNullException(nameof(options)); - _bcpOptions = _options.Tool.ToBcpOptions(); + _nitroAppOptions = _options.Tool.ToNitroAppOptions(); } public async Task ExecuteAsync(HttpContext context) @@ -29,7 +29,7 @@ public async Task ExecuteAsync(HttpContext context) // First we need to populate the HttpContext with the current GraphQL server options ... context.Items.Add(nameof(GraphQLServerOptions), _options); - context.Items.Add(nameof(BananaCakePopOptions), _bcpOptions); + context.Items.Add(nameof(NitroAppOptions), _nitroAppOptions); // after that we can execute the pipeline ... await _pipeline.Invoke(context).ConfigureAwait(false); diff --git a/src/HotChocolate/AzureFunctions/src/HotChocolate.AzureFunctions/Extensions/HotChocolateAzureFunctionServiceCollectionExtensions.cs b/src/HotChocolate/AzureFunctions/src/HotChocolate.AzureFunctions/Extensions/HotChocolateAzureFunctionServiceCollectionExtensions.cs index 9894204ad20..d464b371eea 100644 --- a/src/HotChocolate/AzureFunctions/src/HotChocolate.AzureFunctions/Extensions/HotChocolateAzureFunctionServiceCollectionExtensions.cs +++ b/src/HotChocolate/AzureFunctions/src/HotChocolate.AzureFunctions/Extensions/HotChocolateAzureFunctionServiceCollectionExtensions.cs @@ -1,4 +1,4 @@ -using BananaCakePop.Middleware; +using ChilliCream.Nitro.App; using HotChocolate; using HotChocolate.AspNetCore; using HotChocolate.AzureFunctions; @@ -99,7 +99,7 @@ private static IServiceCollection AddAzureFunctionsGraphQLRequestExecutor( .UseMiddleware(schemaNameOrDefault) .UseMiddleware(schemaNameOrDefault) .UseMiddleware(schemaNameOrDefault) - .UseBananaCakePop(path) + .UseNitroApp(path) .UseMiddleware( schemaNameOrDefault, path, @@ -142,7 +142,7 @@ public static IRequestExecutorBuilder ModifyFunctionOptions( return builder; } - private static PipelineBuilder UseBananaCakePop( + private static PipelineBuilder UseNitroApp( this PipelineBuilder requestPipeline, PathString path) { @@ -156,15 +156,15 @@ private static PipelineBuilder UseBananaCakePop( var forwarderAccessor = new HttpForwarderAccessor(); return requestPipeline - .UseMiddleware(path) - .UseMiddleware(path, forwarderAccessor) - .UseMiddleware(fileProvider, path) - .UseMiddleware(fileProvider, path); + .UseMiddleware(path) + .UseMiddleware(path, forwarderAccessor) + .UseMiddleware(fileProvider, path) + .UseMiddleware(fileProvider, path); } private static IFileProvider CreateFileProvider() { - var type = typeof(BananaCakePopStaticFileMiddleware); + var type = typeof(NitroAppStaticFileMiddleware); var resourceNamespace = type.Namespace + ".Resources"; return new EmbeddedFileProvider(type.Assembly, resourceNamespace); diff --git a/src/HotChocolate/AzureFunctions/src/HotChocolate.AzureFunctions/HotChocolate.AzureFunctions.csproj b/src/HotChocolate/AzureFunctions/src/HotChocolate.AzureFunctions/HotChocolate.AzureFunctions.csproj index 5d0c942eec9..082a352bfa7 100644 --- a/src/HotChocolate/AzureFunctions/src/HotChocolate.AzureFunctions/HotChocolate.AzureFunctions.csproj +++ b/src/HotChocolate/AzureFunctions/src/HotChocolate.AzureFunctions/HotChocolate.AzureFunctions.csproj @@ -18,7 +18,7 @@ - + diff --git a/src/HotChocolate/AzureFunctions/test/HotChocolate.AzureFunctions.IsolatedProcess.Tests/Helpers/TestHttpRequestDataHelper.cs b/src/HotChocolate/AzureFunctions/test/HotChocolate.AzureFunctions.IsolatedProcess.Tests/Helpers/TestHttpRequestDataHelper.cs index 553e2f05c67..a1dd32d736e 100644 --- a/src/HotChocolate/AzureFunctions/test/HotChocolate.AzureFunctions.IsolatedProcess.Tests/Helpers/TestHttpRequestDataHelper.cs +++ b/src/HotChocolate/AzureFunctions/test/HotChocolate.AzureFunctions.IsolatedProcess.Tests/Helpers/TestHttpRequestDataHelper.cs @@ -24,7 +24,7 @@ public static HttpRequestData NewGraphQLHttpRequestData( return httpRequestData; } - public static HttpRequestData NewBcpHttpRequestData( + public static HttpRequestData NewNitroHttpRequestData( IServiceProvider serviceProvider, string path) { @@ -33,8 +33,8 @@ public static HttpRequestData NewBcpHttpRequestData( HttpMethods.Get, new Uri(IO.Path.Combine(DefaultAzFuncGraphQLUri.ToString(), path))); - //Ensure we accept Text/Html for BCP requests... - httpRequestData.Headers.Add(Accept, TestConstants.DefaultBcpContentType); + //Ensure we accept Text/Html for Nitro requests... + httpRequestData.Headers.Add(Accept, TestConstants.DefaultNitroContentType); return httpRequestData; } diff --git a/src/HotChocolate/AzureFunctions/test/HotChocolate.AzureFunctions.IsolatedProcess.Tests/IsolatedProcessEndToEndTests.cs b/src/HotChocolate/AzureFunctions/test/HotChocolate.AzureFunctions.IsolatedProcess.Tests/IsolatedProcessEndToEndTests.cs index 731ffed74bb..43a3223b060 100644 --- a/src/HotChocolate/AzureFunctions/test/HotChocolate.AzureFunctions.IsolatedProcess.Tests/IsolatedProcessEndToEndTests.cs +++ b/src/HotChocolate/AzureFunctions/test/HotChocolate.AzureFunctions.IsolatedProcess.Tests/IsolatedProcessEndToEndTests.cs @@ -96,7 +96,7 @@ public async Task AzFuncIsolatedProcess_FunctionsContextItemsTestAsync() } [Fact] - public async Task AzFuncIsolatedProcess_BananaCakePopTestAsync() + public async Task AzFuncIsolatedProcess_NitroTestAsync() { var host = new MockIsolatedProcessHostBuilder() .AddGraphQLFunction( @@ -108,7 +108,7 @@ public async Task AzFuncIsolatedProcess_BananaCakePopTestAsync() var requestExecutor = host.Services.GetRequiredService(); // Build an HttpRequestData that is valid for the Isolated Process to execute with... - var httpRequestData = TestHttpRequestDataHelper.NewBcpHttpRequestData(host.Services, "index.html"); + var httpRequestData = TestHttpRequestDataHelper.NewNitroHttpRequestData(host.Services, "index.html"); // Execute Query Test for end-to-end validation... // NOTE: This uses the new Az Func Isolated Process extension to execute diff --git a/src/HotChocolate/AzureFunctions/test/HotChocolate.AzureFunctions.Tests/Helpers/TestHttpContextHelper.cs b/src/HotChocolate/AzureFunctions/test/HotChocolate.AzureFunctions.Tests/Helpers/TestHttpContextHelper.cs index c6d3342e141..ad8f857a7a9 100644 --- a/src/HotChocolate/AzureFunctions/test/HotChocolate.AzureFunctions.Tests/Helpers/TestHttpContextHelper.cs +++ b/src/HotChocolate/AzureFunctions/test/HotChocolate.AzureFunctions.Tests/Helpers/TestHttpContextHelper.cs @@ -31,7 +31,7 @@ public static HttpContext NewGraphQLHttpContext(string query) return httpContext; } - public static HttpContext NewBcpHttpContext() + public static HttpContext NewNitroHttpContext() { var uri = new Uri(IO.Path.Combine(DefaultAzFuncGraphQLUri.ToString(), "index.html")); @@ -44,8 +44,8 @@ public static HttpContext NewBcpHttpContext() request.Path = new PathString(uri.AbsolutePath); request.QueryString = new QueryString(uri.Query); - // Ensure we accept Text/Html for BCP requests... - httpContext.Request.Headers[HeaderNames.Accept] = TestConstants.DefaultBcpContentType; + // Ensure we accept Text/Html for Nitro requests... + httpContext.Request.Headers[HeaderNames.Accept] = TestConstants.DefaultNitroContentType; httpContext.Response.Body = new MemoryStream(); diff --git a/src/HotChocolate/AzureFunctions/test/HotChocolate.AzureFunctions.Tests/InProcessEndToEndTests.cs b/src/HotChocolate/AzureFunctions/test/HotChocolate.AzureFunctions.Tests/InProcessEndToEndTests.cs index f69b39d04a2..8cf80ed8ab8 100644 --- a/src/HotChocolate/AzureFunctions/test/HotChocolate.AzureFunctions.Tests/InProcessEndToEndTests.cs +++ b/src/HotChocolate/AzureFunctions/test/HotChocolate.AzureFunctions.Tests/InProcessEndToEndTests.cs @@ -40,7 +40,7 @@ public async Task AzFuncInProcess_EndToEndTestAsync() } [Fact] - public async Task AzFuncInProcess_BananaCakePopTestAsync() + public async Task AzFuncInProcess_NitroTestAsync() { var hostBuilder = new MockInProcessFunctionsHostBuilder(); @@ -50,15 +50,15 @@ public async Task AzFuncInProcess_BananaCakePopTestAsync() .AddGraphQLFunction() .AddQueryType( d => d.Name("Query") - .Field("BcpTest") - .Resolve("This is a test for BCP File Serving...")); + .Field("NitroTest") + .Resolve("This is a test for Nitro File Serving...")); var serviceProvider = hostBuilder.BuildServiceProvider(); // The executor should resolve without error as a Required service... var requestExecutor = serviceProvider.GetRequiredService(); - var httpContext = TestHttpContextHelper.NewBcpHttpContext(); + var httpContext = TestHttpContextHelper.NewNitroHttpContext(); // Execute Query Test for end-to-end validation... await requestExecutor.ExecuteAsync(httpContext.Request); diff --git a/src/HotChocolate/AzureFunctions/test/HotChocolate.AzureFunctions.Tests/TestConstants.cs b/src/HotChocolate/AzureFunctions/test/HotChocolate.AzureFunctions.Tests/TestConstants.cs index 2229d033bd1..f31e1416463 100644 --- a/src/HotChocolate/AzureFunctions/test/HotChocolate.AzureFunctions.Tests/TestConstants.cs +++ b/src/HotChocolate/AzureFunctions/test/HotChocolate.AzureFunctions.Tests/TestConstants.cs @@ -3,5 +3,5 @@ namespace HotChocolate.AzureFunctions; public static class TestConstants { public const string DefaultJsonContentType = "application/json"; - public const string DefaultBcpContentType = "text/html"; + public const string DefaultNitroContentType = "text/html"; } diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Default_Field_Over_Node.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Default_Field_Over_Node.md index 358196bf1aa..a533e21fac4 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Default_Field_Over_Node.md +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Default_Field_Over_Node.md @@ -3,9 +3,10 @@ ## SQL ```text +-- @__keys_0={ '1' } (DbType = Object) SELECT b."Id" FROM "Brands" AS b -WHERE b."Id" = 1 +WHERE b."Id" = ANY (@__keys_0) ``` ## Result diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Id_And_Name_Over_Node.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Id_And_Name_Over_Node.md index e501abd2fba..aba267d352a 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Id_And_Name_Over_Node.md +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Id_And_Name_Over_Node.md @@ -3,9 +3,10 @@ ## SQL ```text +-- @__keys_0={ '1' } (DbType = Object) SELECT b."Id", b."Name" FROM "Brands" AS b -WHERE b."Id" = 1 +WHERE b."Id" = ANY (@__keys_0) ``` ## Result diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Over_Node.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Over_Node.md index c9d15e2d389..b0130913f49 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Over_Node.md +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Brand_With_Name_Over_Node.md @@ -3,9 +3,10 @@ ## SQL ```text +-- @__keys_0={ '1' } (DbType = Object) SELECT b."Name", b."Id" FROM "Brands" AS b -WHERE b."Id" = 1 +WHERE b."Id" = ANY (@__keys_0) ``` ## Result diff --git a/src/HotChocolate/Directory.Build.props b/src/HotChocolate/Directory.Build.props index 86a922cc61f..04d61431827 100644 --- a/src/HotChocolate/Directory.Build.props +++ b/src/HotChocolate/Directory.Build.props @@ -4,6 +4,6 @@ hotchocolate-signet.png - 16.0.1 + 19.0.1 diff --git a/src/HotChocolate/Fusion/src/Core/HotChocolate.Fusion.csproj b/src/HotChocolate/Fusion/src/Core/HotChocolate.Fusion.csproj index 7fe4f027e57..f2d956febe2 100644 --- a/src/HotChocolate/Fusion/src/Core/HotChocolate.Fusion.csproj +++ b/src/HotChocolate/Fusion/src/Core/HotChocolate.Fusion.csproj @@ -12,7 +12,6 @@ - diff --git a/templates/v12/HotChocolate.Templates.nuspec b/templates/HotChocolate.Templates.nuspec similarity index 100% rename from templates/v12/HotChocolate.Templates.nuspec rename to templates/HotChocolate.Templates.nuspec diff --git a/templates/StarWars/HotChocolate.Templates.StarWars.nuspec b/templates/StarWars/HotChocolate.Templates.StarWars.nuspec deleted file mode 100644 index f3ed6eaf756..00000000000 --- a/templates/StarWars/HotChocolate.Templates.StarWars.nuspec +++ /dev/null @@ -1,15 +0,0 @@ - - - - HotChocolate.Templates.StarWars - 0.0.0 - - Creates a GraphQL Star Wars Demo - - Michael Staib - https://chillicream.com/resources/hotchocolate-signet.png - - - - - diff --git a/templates/StarWars/content/.template.config/template.json b/templates/StarWars/content/.template.config/template.json deleted file mode 100644 index d69567d8d14..00000000000 --- a/templates/StarWars/content/.template.config/template.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/template", - "author": "Michael Staib", - "classifications": [ - "ChilliCream", - "HotChocolate", - "Demos" - ], - "identity": "HotChocolate.Templates.StarWars", - "name": "HotChocolate GraphQL Star Wars Demo", - "shortName": "starwars", - "tags": { - "language": "C#", - "type": "project" - } -} diff --git a/templates/StarWars/content/Characters/CharacterQueries.cs b/templates/StarWars/content/Characters/CharacterQueries.cs deleted file mode 100644 index a3f43a95e5f..00000000000 --- a/templates/StarWars/content/Characters/CharacterQueries.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System.Collections.Generic; -using HotChocolate; -using HotChocolate.Data; -using HotChocolate.Types; -using StarWars.Repositories; - -namespace StarWars.Characters -{ - /// - /// The queries related to characters. - /// - [ExtendObjectType(OperationTypeNames.Query)] - public class CharacterQueries - { - /// - /// Retrieve a hero by a particular Star Wars episode. - /// - /// The episode to retrieve the hero. - /// The character repository. - /// The hero character. - public ICharacter GetHero( - Episode episode, - [Service] ICharacterRepository repository) => - repository.GetHero(episode); - - /// - /// Gets all character. - /// - /// The character repository. - /// The character. - [UsePaging(typeof(InterfaceType))] - [UseFiltering] - [UseSorting] - public IEnumerable GetCharacters( - [Service] ICharacterRepository repository) => - repository.GetCharacters(); - - /// - /// Gets a character by it`s id. - /// - /// The ids of the human to retrieve. - /// The character repository. - /// The character. - public IEnumerable GetCharacter( - int[] ids, - [Service] ICharacterRepository repository) => - repository.GetCharacters(ids); - - /// - /// Search the repository for objects that contain the text. - /// - /// - /// The text we are searching for. - /// - /// The character repository. - /// Returns the union type . - public IEnumerable Search( - string text, - [Service] ICharacterRepository repository) => - repository.Search(text); - } -} diff --git a/templates/StarWars/content/Characters/CharacterResolvers.cs b/templates/StarWars/content/Characters/CharacterResolvers.cs deleted file mode 100644 index 27b12e8aab8..00000000000 --- a/templates/StarWars/content/Characters/CharacterResolvers.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using HotChocolate; -using HotChocolate.Types; -using StarWars.Repositories; - -namespace StarWars.Characters -{ - /// - /// This resolver class extends all object types implementing . - /// - [ExtendObjectType(typeof(ICharacter))] - public class CharacterResolvers - { - [UsePaging(typeof(InterfaceType))] - [BindMember(nameof(ICharacter.Friends))] - public IEnumerable GetFriends( - [Parent] ICharacter character, - [Service] ICharacterRepository repository) => - repository.GetCharacters(character.Friends.ToArray()); - } -} diff --git a/templates/StarWars/content/Characters/Droid.cs b/templates/StarWars/content/Characters/Droid.cs deleted file mode 100644 index 447c26c53c1..00000000000 --- a/templates/StarWars/content/Characters/Droid.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Collections.Generic; -using HotChocolate.Types; -using HotChocolate.Types.Relay; - -namespace StarWars.Characters -{ - /// - /// A droid in the Star Wars universe. - /// - public class Droid : ICharacter - { - public Droid( - int id, - string name, - IReadOnlyList friends, - IReadOnlyList appearsIn, - string primaryFunction, - double height = 1.72d) - { - Id = id; - Name = name; - Friends = friends; - AppearsIn = appearsIn; - PrimaryFunction = primaryFunction; - Height = height; - } - - /// - public int Id { get; } - - /// - public string Name { get; } - - /// - public IReadOnlyList Friends { get; } - - /// - public IReadOnlyList AppearsIn { get; } - - /// - /// The droid's primary function. - /// - public string PrimaryFunction { get; } - - /// - [UseConvertUnit] - public double Height { get; } - } -} diff --git a/templates/StarWars/content/Characters/Episode.cs b/templates/StarWars/content/Characters/Episode.cs deleted file mode 100644 index aa2ba91cae9..00000000000 --- a/templates/StarWars/content/Characters/Episode.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace StarWars.Characters -{ - /// - /// The Star Wars episodes. - /// - public enum Episode - { - /// - /// Star Wars Episode IV: A New Hope - /// - NewHope, - - /// - /// Star Wars Episode V: Empire Strikes Back - /// - Empire, - - /// - /// Star Wars Episode VI: Return of the Jedi - /// - Jedi - } -} diff --git a/templates/StarWars/content/Characters/Human.cs b/templates/StarWars/content/Characters/Human.cs deleted file mode 100644 index 3d20f0ef91f..00000000000 --- a/templates/StarWars/content/Characters/Human.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Collections.Generic; -using HotChocolate.Types; -using HotChocolate.Types.Relay; - -namespace StarWars.Characters -{ - /// - /// A human character in the Star Wars universe. - /// - public class Human : ICharacter - { - public Human( - int id, - string name, - IReadOnlyList friends, - IReadOnlyList appearsIn, - string? homePlanet = null, - double height = 1.72d) - { - Id = id; - Name = name; - Friends = friends; - AppearsIn = appearsIn; - HomePlanet = homePlanet; - Height = height; - } - - /// - public int Id { get; } - - /// - public string Name { get; } - - /// - public IReadOnlyList Friends { get; } - - /// - public IReadOnlyList AppearsIn { get; } - - /// - /// The planet the character is originally from. - /// - public string? HomePlanet { get; } - - /// - [UseConvertUnit] - public double Height { get; } - } -} diff --git a/templates/StarWars/content/Characters/ICharacter.cs b/templates/StarWars/content/Characters/ICharacter.cs deleted file mode 100644 index 8dc65acd6f9..00000000000 --- a/templates/StarWars/content/Characters/ICharacter.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections.Generic; -using HotChocolate.Types; - -namespace StarWars.Characters -{ - /// - /// A character in the Star Wars universe. - /// - [InterfaceType("Character")] - public interface ICharacter : ISearchResult - { - /// - /// The unique identifier for the character. - /// - int Id { get; } - - /// - /// The name of the character. - /// - string Name { get; } - - /// - /// The ids of the character's friends. - /// - [UsePaging(typeof(InterfaceType))] - IReadOnlyList Friends { get; } - - /// - /// The episodes the character appears in. - /// - IReadOnlyList AppearsIn { get; } - - /// - /// The height of the character. - /// - [UseConvertUnit] - double Height { get; } - } -} diff --git a/templates/StarWars/content/Characters/ISearchResult.cs b/templates/StarWars/content/Characters/ISearchResult.cs deleted file mode 100644 index 90adb2ed5a4..00000000000 --- a/templates/StarWars/content/Characters/ISearchResult.cs +++ /dev/null @@ -1,9 +0,0 @@ -using HotChocolate.Types; - -namespace StarWars.Characters -{ - [UnionType("SearchResult")] - public interface ISearchResult - { - } -} diff --git a/templates/StarWars/content/Characters/Starship.cs b/templates/StarWars/content/Characters/Starship.cs deleted file mode 100644 index 62fecba94b5..00000000000 --- a/templates/StarWars/content/Characters/Starship.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace StarWars.Characters -{ - /// - /// A starship in the Star Wars universe. - /// - public class Starship : ISearchResult - { - public Starship(int id, string name, double length) - { - Id = id; - Name = name; - Length = length; - } - - /// - /// The Id of the starship. - /// - public int Id { get; } - - /// - /// The name of the starship. - /// - public string Name { get; } - - /// - /// The length of the starship. - /// - [UseConvertUnit] - public double Length { get; } - } -} diff --git a/templates/StarWars/content/Characters/Unit.cs b/templates/StarWars/content/Characters/Unit.cs deleted file mode 100644 index 421d2724d8a..00000000000 --- a/templates/StarWars/content/Characters/Unit.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace StarWars.Characters -{ - /// - /// Different units of measurement. - /// - public enum Unit - { - Foot, - Meters - } -} diff --git a/templates/StarWars/content/Characters/UseConvertUnitAttribute.cs b/templates/StarWars/content/Characters/UseConvertUnitAttribute.cs deleted file mode 100644 index 4e34aa329e2..00000000000 --- a/templates/StarWars/content/Characters/UseConvertUnitAttribute.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Reflection; -using HotChocolate.Types; -using HotChocolate.Types.Descriptors; - -namespace StarWars.Characters -{ - [AttributeUsage( - AttributeTargets.Property | AttributeTargets.Method, - AllowMultiple = false)] - public sealed class UseConvertUnitAttribute : DescriptorAttribute - { - protected override void TryConfigure( - IDescriptorContext context, - IDescriptor descriptor, - ICustomAttributeProvider element) - { - if (descriptor is IObjectFieldDescriptor objectField) - { - objectField - .Argument("unit", a => a.Type>().DefaultValue(Unit.Meters)) - .Use(next => async context => - { - await next(context).ConfigureAwait(false); - - if (context.Result is double length) - { - context.Result = ConvertToUnit(length, context.ArgumentValue("unit")); - } - }); - } - else if (descriptor is IInterfaceFieldDescriptor interfaceField) - { - interfaceField - .Argument("unit", a => a.Type>().DefaultValue(Unit.Meters)); - } - } - - private double ConvertToUnit(double length, Unit unit) - { - if (unit == Unit.Foot) - { - return length * 3.28084d; - } - return length; - } - } -} diff --git a/templates/StarWars/content/Program.cs b/templates/StarWars/content/Program.cs deleted file mode 100644 index 2b54de3a183..00000000000 --- a/templates/StarWars/content/Program.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Hosting; - -namespace StarWars -{ - public class Program - { - public static Task Main(string[] args) => - CreateHostBuilder(args).Build().RunAsync(); - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - webBuilder.UseStartup()); - } -} diff --git a/templates/StarWars/content/Repositories/CharacterRepository.cs b/templates/StarWars/content/Repositories/CharacterRepository.cs deleted file mode 100644 index ffac72f45c0..00000000000 --- a/templates/StarWars/content/Repositories/CharacterRepository.cs +++ /dev/null @@ -1,137 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using StarWars.Characters; - -namespace StarWars.Repositories -{ - public class CharacterRepository : ICharacterRepository - { - private Dictionary _characters; - private Dictionary _starships; - - public CharacterRepository() - { - _characters = CreateCharacters().ToDictionary(t => t.Id); - _starships = CreateStarships().ToDictionary(t => t.Id); - } - - public IQueryable GetCharacters() => - _characters.Values.AsQueryable(); - - public IEnumerable GetCharacters(int[] ids) - { - foreach (int id in ids) - { - if (_characters.TryGetValue(id, out ICharacter? c)) - { - yield return c; - } - } - } - - public ICharacter GetHero(Episode episode) - { - if (episode == Episode.Empire) - { - return _characters[1000]; - } - return _characters[2001]; - } - - public IEnumerable Search(string text) - { - IEnumerable filteredCharacters = _characters.Values - .Where(t => t.Name.Contains(text, - StringComparison.OrdinalIgnoreCase)); - - foreach (ICharacter character in filteredCharacters) - { - yield return character; - } - - IEnumerable filteredStarships = _starships.Values - .Where(t => t.Name.Contains(text, - StringComparison.OrdinalIgnoreCase)); - - foreach (Starship starship in filteredStarships) - { - yield return starship; - } - } - - private static IEnumerable CreateCharacters() - { - yield return new Human - ( - 1000, - "Luke Skywalker", - new[] { 1002, 1003, 2000, 2001 }, - new[] { Episode.NewHope, Episode.Empire, Episode.Jedi }, - "Tatooine" - ); - - yield return new Human - ( - 1001, - "Darth Vader", - new[] { 1004 }, - new[] { Episode.NewHope, Episode.Empire, Episode.Jedi }, - "Tatooine" - ); - - yield return new Human - ( - 1002, - "Han Solo", - new[] { 1000, 1003, 2001 }, - new[] { Episode.NewHope, Episode.Empire, Episode.Jedi } - ); - - yield return new Human - ( - 1003, - "Leia Organa", - new[] { 1000, 1002, 2000, 2001 }, - new[] { Episode.NewHope, Episode.Empire, Episode.Jedi }, - "Alderaan" - ); - - yield return new Human - ( - 1004, - "Wilhuff Tarkin", - new[] { 1001 }, - new[] { Episode.NewHope } - ); - - yield return new Droid - ( - 2000, - "C-3PO", - new[] { 1000, 1002, 1003, 2001 }, - new[] { Episode.NewHope, Episode.Empire, Episode.Jedi }, - "Protocol" - ); - - yield return new Droid - ( - 2001, - "R2-D2", - new[] { 1000, 1002, 1003 }, - new[] { Episode.NewHope, Episode.Empire, Episode.Jedi }, - "Astromech" - ); - } - - private static IEnumerable CreateStarships() - { - yield return new Starship - ( - 3000, - "TIE Advanced x1", - 9.2 - ); - } - } -} diff --git a/templates/StarWars/content/Repositories/ICharacterRepository.cs b/templates/StarWars/content/Repositories/ICharacterRepository.cs deleted file mode 100644 index 50e8aa7392f..00000000000 --- a/templates/StarWars/content/Repositories/ICharacterRepository.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using StarWars.Characters; - -namespace StarWars.Repositories -{ - public interface ICharacterRepository - { - IQueryable GetCharacters(); - - IEnumerable GetCharacters(params int[] ids); - - ICharacter GetHero(Episode episode); - - IEnumerable Search(string text); - } -} diff --git a/templates/StarWars/content/Repositories/IReviewRepository.cs b/templates/StarWars/content/Repositories/IReviewRepository.cs deleted file mode 100644 index 3cbaec56740..00000000000 --- a/templates/StarWars/content/Repositories/IReviewRepository.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; -using StarWars.Characters; -using StarWars.Reviews; - -namespace StarWars.Repositories -{ - public interface IReviewRepository - { - void AddReview(Episode episode, Review review); - IEnumerable GetReviews(Episode episode); - } -} diff --git a/templates/StarWars/content/Repositories/ReviewRepository.cs b/templates/StarWars/content/Repositories/ReviewRepository.cs deleted file mode 100644 index 8c4e475d2dc..00000000000 --- a/templates/StarWars/content/Repositories/ReviewRepository.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using StarWars.Characters; -using StarWars.Reviews; - -namespace StarWars.Repositories -{ - public class ReviewRepository : IReviewRepository - { - private readonly Dictionary> _data = - new Dictionary>(); - - public void AddReview(Episode episode, Review review) - { - if (!_data.TryGetValue(episode, out List? reviews)) - { - reviews = new List(); - _data[episode] = reviews; - } - - reviews.Add(review); - } - - public IEnumerable GetReviews(Episode episode) - { - if (_data.TryGetValue(episode, out List? reviews)) - { - return reviews; - } - return Array.Empty(); - } - } -} diff --git a/templates/StarWars/content/Reviews/CreateReviewInput.cs b/templates/StarWars/content/Reviews/CreateReviewInput.cs deleted file mode 100644 index afc9763b210..00000000000 --- a/templates/StarWars/content/Reviews/CreateReviewInput.cs +++ /dev/null @@ -1,44 +0,0 @@ -using StarWars.Characters; - -namespace StarWars.Reviews -{ - /// - /// This input represents the data needed to create a review. - /// - public class CreateReviewInput - { - /// - /// Creates a new instance of . - /// - /// - /// The review for which to create the review. - /// - /// - /// The number of stars given for this review. - /// - /// - /// An explanation for the rating. - /// - public CreateReviewInput(Episode episode, int stars, string commentary) - { - Episode = episode; - Stars = stars; - Commentary = commentary; - } - - /// - /// The review for which to create the review. - /// - public Episode Episode { get; } - - /// - /// The number of stars given for this review. - /// - public int Stars { get; } - - /// - /// An explanation for the rating. - /// - public string Commentary { get; } - } -} diff --git a/templates/StarWars/content/Reviews/CreateReviewPayload.cs b/templates/StarWars/content/Reviews/CreateReviewPayload.cs deleted file mode 100644 index 66db69ac2e5..00000000000 --- a/templates/StarWars/content/Reviews/CreateReviewPayload.cs +++ /dev/null @@ -1,35 +0,0 @@ -using StarWars.Characters; - -namespace StarWars.Reviews -{ - /// - /// This payload allows us to query the created review object. - /// - public class CreateReviewPayload - { - /// - /// Creates a new instance of . - /// - /// - /// The episode for which a review was created. - /// - /// - /// The review that was being created. - /// - public CreateReviewPayload(Episode episode, Review review) - { - Episode = episode; - Review = review; - } - - /// - /// The episode for which a review was created. - /// - public Episode Episode { get; } - - /// - /// The review that was being created. - /// - public Review Review { get; } - } -} diff --git a/templates/StarWars/content/Reviews/Review.cs b/templates/StarWars/content/Reviews/Review.cs deleted file mode 100644 index 8ba7192380f..00000000000 --- a/templates/StarWars/content/Reviews/Review.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; - -namespace StarWars.Reviews -{ - /// - /// A review of a particular movie. - /// - public class Review - { - /// - /// Creates a new instance of . - /// - /// - /// The number of stars given for this review. - /// - /// - /// The explanation for the rating. - /// - public Review(int stars, string commentary) - { - Id = Guid.NewGuid(); - Stars = stars; - Commentary = commentary; - } - - /// - /// The ID of the review. - /// - public Guid Id { get; } - - /// - /// The number of stars given for this review. - /// - public int Stars { get; } - - /// - /// An explanation for the rating. - /// - public string Commentary { get; } - } -} diff --git a/templates/StarWars/content/Reviews/ReviewMutations.cs b/templates/StarWars/content/Reviews/ReviewMutations.cs deleted file mode 100644 index 300d3799884..00000000000 --- a/templates/StarWars/content/Reviews/ReviewMutations.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Threading.Tasks; -using HotChocolate; -using HotChocolate.Subscriptions; -using HotChocolate.Types; -using StarWars.Repositories; - -namespace StarWars.Reviews -{ - /// - /// The mutations related to reviews. - /// - [ExtendObjectType(OperationTypeNames.Mutation)] - public class ReviewMutations - { - /// - /// Creates a review for a given Star Wars episode. - /// - public async Task CreateReview( - CreateReviewInput input, - [Service]IReviewRepository repository, - [Service]ITopicEventSender eventSender) - { - var review = new Review(input.Stars, input.Commentary); - repository.AddReview(input.Episode, review); - await eventSender - .SendAsync(input.Episode, review) - .ConfigureAwait(false); - return new CreateReviewPayload(input.Episode, review); - } - } -} diff --git a/templates/StarWars/content/Reviews/ReviewQueries.cs b/templates/StarWars/content/Reviews/ReviewQueries.cs deleted file mode 100644 index b9246633ae9..00000000000 --- a/templates/StarWars/content/Reviews/ReviewQueries.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Collections.Generic; -using HotChocolate; -using HotChocolate.Data; -using HotChocolate.Types; -using StarWars.Characters; -using StarWars.Repositories; - -namespace StarWars.Reviews -{ - /// - /// The queries related to reviews. - /// - [ExtendObjectType(OperationTypeNames.Query)] - public class ReviewQueries - { - [UsePaging] - [UseFiltering] - [UseSorting] - public IEnumerable GetReviews( - Episode episode, - [Service]IReviewRepository repository) => - repository.GetReviews(episode); - } -} diff --git a/templates/StarWars/content/Reviews/ReviewSubscriptions.cs b/templates/StarWars/content/Reviews/ReviewSubscriptions.cs deleted file mode 100644 index cdf9486f063..00000000000 --- a/templates/StarWars/content/Reviews/ReviewSubscriptions.cs +++ /dev/null @@ -1,31 +0,0 @@ -using HotChocolate; -using HotChocolate.Types; -using StarWars.Characters; - -namespace StarWars.Reviews -{ - /// - /// The subscriptions related to reviews. - /// - [ExtendObjectType(OperationTypeNames.Subscription)] - public class ReviewSubscriptions - { - /// - /// The OnReview event is invoked whenever a new review is being created. - /// - /// - /// The episode to which you want to subscribe to. - /// - /// - /// The event message. - /// - /// - /// The review that was created. - /// - [Subscribe] - public Review OnReview( - [Topic]Episode episode, - [EventMessage]Review message) => - message; - } -} diff --git a/templates/StarWars/content/StarWars.csproj b/templates/StarWars/content/StarWars.csproj deleted file mode 100644 index ea59d979c2e..00000000000 --- a/templates/StarWars/content/StarWars.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - - net6.0 - enable - - - - false - true - $(NoWarn);1591 - - - - - - - - - diff --git a/templates/StarWars/content/Startup.cs b/templates/StarWars/content/Startup.cs deleted file mode 100644 index 49b7e8bf37d..00000000000 --- a/templates/StarWars/content/Startup.cs +++ /dev/null @@ -1,84 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using StarWars.Characters; -using StarWars.Repositories; -using StarWars.Reviews; - -namespace StarWars -{ - public class Startup - { - // This method gets called by the runtime. Use this method to add services to the container. - // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 - public void ConfigureServices(IServiceCollection services) - { - services - // In order to use the ASP.NET Core routing we need to add the routing services. - .AddRouting() - - // We will add some in-memory repositories that hold the in-memory data for this GraphQL server. - .AddSingleton() - .AddSingleton() - - // Next we are adding our GraphQL server configuration. - // We can host multiple named GraphQL server configurations - // that can be exposed on different routes. - .AddGraphQLServer() - - // The query types are split into two classes, - // by splitting the types into several class we can organize - // our query fields by topics and also am able to test - // them separately. - .AddQueryType() - .AddTypeExtension() - .AddTypeExtension() - .AddMutationType() - .AddTypeExtension() - .AddSubscriptionType() - .AddTypeExtension() - - // The type discover is very good in exploring the types that we are using - // but sometimes when a GraphQL field for instance only exposes an interface - // we need to provide we need to provide the implementation types that we want - // to host in our GraphQL schema. - .AddType() - .AddType() - .AddType() - .AddTypeExtension() - - // Add filtering and sorting capabilities. - .AddFiltering() - .AddSorting() - - // if you wanted to control the pagination settings globally you could - // do so by setting the paging options. - // .ModifyPagingOptions() - - // Since we are exposing a subscription type we also need a pub/sub system - // handling the subscription events. For our little demo here we will use - // an in-memory pub/sub system. - .AddInMemorySubscriptions() - - // Last we will add apollo tracing to our server which by default is - // only activated through the X-APOLLO-TRACING:1 header. - .AddApolloTracing(); - } - - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - // in order to expose our GraphQL schema we need to map the GraphQL server - // to a specific route. By default it is mapped onto /graphql. - app - .UseWebSockets() - .UseRouting() - .UseEndpoints(endpoint => endpoint.MapGraphQL()); - } - } -} diff --git a/templates/v12/function-isolated/.gitignore b/templates/azure-function/.gitignore similarity index 100% rename from templates/v12/function-isolated/.gitignore rename to templates/azure-function/.gitignore diff --git a/templates/v12/function/.template.config/template.json b/templates/azure-function/.template.config/template.json similarity index 75% rename from templates/v12/function/.template.config/template.json rename to templates/azure-function/.template.config/template.json index a65146d974d..640bf4d2f4b 100644 --- a/templates/v12/function/.template.config/template.json +++ b/templates/azure-function/.template.config/template.json @@ -4,9 +4,9 @@ "classifications": ["Web", "GraphQL", "Azure"], "identity": "HotChocolate.Template.AzureFunctions", "sourceName": "HotChocolate.Template.AzureFunctions", - "name": "GraphQL Function", + "name": "GraphQL Azure Function", "shortName": "graphql-azf", - "defaultName": "GraphQL Function", + "defaultName": "GraphQL Azure Function", "description": "", "preferNameDirectory": true, "tags": { @@ -20,12 +20,16 @@ "datatype": "choice", "choices": [ { - "choice": "net6.0", - "description": "Target .NET 6" + "choice": "net8.0", + "description": "Target .NET 8" + }, + { + "choice": "net9.0", + "description": "Target .NET 9" } ], - "replaces": "net6.0", - "defaultValue": "net6.0" + "replaces": "net8.0", + "defaultValue": "net8.0" } }, "postActions": [ diff --git a/templates/v12/function-isolated/GraphQLFunction.cs b/templates/azure-function/GraphQLFunction.cs similarity index 90% rename from templates/v12/function-isolated/GraphQLFunction.cs rename to templates/azure-function/GraphQLFunction.cs index 95c882527e2..dffa0d67b1b 100644 --- a/templates/v12/function-isolated/GraphQLFunction.cs +++ b/templates/azure-function/GraphQLFunction.cs @@ -2,7 +2,7 @@ using Microsoft.Azure.Functions.Worker; using Microsoft.Azure.Functions.Worker.Http; -namespace HotChocolate.Template.AzureFunctions.Isolated; +namespace HotChocolate.Template.AzureFunctions; public class GraphQLFunction { diff --git a/templates/v12/function-isolated/HotChocolate.Template.AzureFunctions.Isolated.csproj b/templates/azure-function/HotChocolate.Template.AzureFunctions.csproj similarity index 88% rename from templates/v12/function-isolated/HotChocolate.Template.AzureFunctions.Isolated.csproj rename to templates/azure-function/HotChocolate.Template.AzureFunctions.csproj index efa15e739cc..16670308d61 100644 --- a/templates/v12/function-isolated/HotChocolate.Template.AzureFunctions.Isolated.csproj +++ b/templates/azure-function/HotChocolate.Template.AzureFunctions.csproj @@ -1,6 +1,6 @@ - net6.0 + net8.0 v4 Exe enable @@ -11,13 +11,13 @@ - + - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/templates/v12/function-isolated/Program.cs b/templates/azure-function/Program.cs similarity index 100% rename from templates/v12/function-isolated/Program.cs rename to templates/azure-function/Program.cs diff --git a/templates/v12/function/Query.cs b/templates/azure-function/Query.cs similarity index 100% rename from templates/v12/function/Query.cs rename to templates/azure-function/Query.cs diff --git a/templates/v12/function-isolated/host.json b/templates/azure-function/host.json similarity index 100% rename from templates/v12/function-isolated/host.json rename to templates/azure-function/host.json diff --git a/templates/v12/gateway-aspire/.template.config/template.json b/templates/gateway-aspire/.template.config/template.json similarity index 91% rename from templates/v12/gateway-aspire/.template.config/template.json rename to templates/gateway-aspire/.template.config/template.json index 657a069d55f..b5b420074be 100644 --- a/templates/v12/gateway-aspire/.template.config/template.json +++ b/templates/gateway-aspire/.template.config/template.json @@ -19,16 +19,16 @@ "description": "The target framework for the project.", "datatype": "choice", "choices": [ - { - "choice": "net7.0", - "description": "Target .NET 7" - }, { "choice": "net8.0", "description": "Target .NET 8" + }, + { + "choice": "net9.0", + "description": "Target .NET 9" } ], - "replaces": "net7.0", + "replaces": "net8.0", "defaultValue": "net8.0" } }, diff --git a/templates/v12/gateway-aspire/HotChocolate.Template.Gateway.Aspire.csproj b/templates/gateway-aspire/HotChocolate.Template.Gateway.Aspire.csproj similarity index 81% rename from templates/v12/gateway-aspire/HotChocolate.Template.Gateway.Aspire.csproj rename to templates/gateway-aspire/HotChocolate.Template.Gateway.Aspire.csproj index 16b01acc902..715e307a171 100644 --- a/templates/v12/gateway-aspire/HotChocolate.Template.Gateway.Aspire.csproj +++ b/templates/gateway-aspire/HotChocolate.Template.Gateway.Aspire.csproj @@ -1,15 +1,15 @@ - net7.0 + net8.0 enable enable Gateway - - + + diff --git a/templates/v12/gateway-aspire/Program.cs b/templates/gateway-aspire/Program.cs similarity index 100% rename from templates/v12/gateway-aspire/Program.cs rename to templates/gateway-aspire/Program.cs diff --git a/templates/v12/gateway-aspire/Properties/launchSettings.json b/templates/gateway-aspire/Properties/launchSettings.json similarity index 100% rename from templates/v12/gateway-aspire/Properties/launchSettings.json rename to templates/gateway-aspire/Properties/launchSettings.json diff --git a/templates/v12/gateway-aspire/appsettings.Development.json b/templates/gateway-aspire/appsettings.Development.json similarity index 100% rename from templates/v12/gateway-aspire/appsettings.Development.json rename to templates/gateway-aspire/appsettings.Development.json diff --git a/templates/v12/gateway-aspire/appsettings.json b/templates/gateway-aspire/appsettings.json similarity index 100% rename from templates/v12/gateway-aspire/appsettings.json rename to templates/gateway-aspire/appsettings.json diff --git a/templates/v12/gateway-bcp/.template.config/template.json b/templates/gateway-managed/.template.config/template.json similarity index 92% rename from templates/v12/gateway-bcp/.template.config/template.json rename to templates/gateway-managed/.template.config/template.json index 8c0477fa567..c1b186013e7 100644 --- a/templates/v12/gateway-bcp/.template.config/template.json +++ b/templates/gateway-managed/.template.config/template.json @@ -19,16 +19,16 @@ "description": "The target framework for the project.", "datatype": "choice", "choices": [ - { - "choice": "net7.0", - "description": "Target .NET 7" - }, { "choice": "net8.0", "description": "Target .NET 8" + }, + { + "choice": "net9.0", + "description": "Target .NET 9" } ], - "replaces": "net7.0", + "replaces": "net8.0", "defaultValue": "net8.0" } }, diff --git a/templates/v12/gateway-bcp/HotChocolate.Template.Gateway.Managed.csproj b/templates/gateway-managed/HotChocolate.Template.Gateway.Managed.csproj similarity index 81% rename from templates/v12/gateway-bcp/HotChocolate.Template.Gateway.Managed.csproj rename to templates/gateway-managed/HotChocolate.Template.Gateway.Managed.csproj index 16b01acc902..715e307a171 100644 --- a/templates/v12/gateway-bcp/HotChocolate.Template.Gateway.Managed.csproj +++ b/templates/gateway-managed/HotChocolate.Template.Gateway.Managed.csproj @@ -1,15 +1,15 @@ - net7.0 + net8.0 enable enable Gateway - - + + diff --git a/templates/v12/gateway-bcp/Program.cs b/templates/gateway-managed/Program.cs similarity index 100% rename from templates/v12/gateway-bcp/Program.cs rename to templates/gateway-managed/Program.cs diff --git a/templates/v12/gateway-bcp/Properties/launchSettings.json b/templates/gateway-managed/Properties/launchSettings.json similarity index 100% rename from templates/v12/gateway-bcp/Properties/launchSettings.json rename to templates/gateway-managed/Properties/launchSettings.json diff --git a/templates/v12/gateway-bcp/appsettings.Development.json b/templates/gateway-managed/appsettings.Development.json similarity index 100% rename from templates/v12/gateway-bcp/appsettings.Development.json rename to templates/gateway-managed/appsettings.Development.json diff --git a/templates/v12/gateway-bcp/appsettings.json b/templates/gateway-managed/appsettings.json similarity index 100% rename from templates/v12/gateway-bcp/appsettings.json rename to templates/gateway-managed/appsettings.json diff --git a/templates/v12/gateway/.template.config/template.json b/templates/gateway/.template.config/template.json similarity index 91% rename from templates/v12/gateway/.template.config/template.json rename to templates/gateway/.template.config/template.json index a413fb41456..f89792d844f 100644 --- a/templates/v12/gateway/.template.config/template.json +++ b/templates/gateway/.template.config/template.json @@ -19,16 +19,16 @@ "description": "The target framework for the project.", "datatype": "choice", "choices": [ - { - "choice": "net7.0", - "description": "Target .NET 7" - }, { "choice": "net8.0", "description": "Target .NET 8" + }, + { + "choice": "net9.0", + "description": "Target .NET 9" } ], - "replaces": "net7.0", + "replaces": "net8.0", "defaultValue": "net8.0" } }, diff --git a/templates/v12/gateway/HotChocolate.Template.Gateway.csproj b/templates/gateway/HotChocolate.Template.Gateway.csproj similarity index 79% rename from templates/v12/gateway/HotChocolate.Template.Gateway.csproj rename to templates/gateway/HotChocolate.Template.Gateway.csproj index d2c8d413903..f29ed8b2693 100644 --- a/templates/v12/gateway/HotChocolate.Template.Gateway.csproj +++ b/templates/gateway/HotChocolate.Template.Gateway.csproj @@ -1,13 +1,13 @@ - net7.0 + net8.0 enable enable - + diff --git a/templates/v12/gateway/Program.cs b/templates/gateway/Program.cs similarity index 100% rename from templates/v12/gateway/Program.cs rename to templates/gateway/Program.cs diff --git a/templates/v12/gateway/Properties/launchSettings.json b/templates/gateway/Properties/launchSettings.json similarity index 100% rename from templates/v12/gateway/Properties/launchSettings.json rename to templates/gateway/Properties/launchSettings.json diff --git a/templates/v12/gateway/appsettings.Development.json b/templates/gateway/appsettings.Development.json similarity index 100% rename from templates/v12/gateway/appsettings.Development.json rename to templates/gateway/appsettings.Development.json diff --git a/templates/v12/gateway/appsettings.json b/templates/gateway/appsettings.json similarity index 100% rename from templates/v12/gateway/appsettings.json rename to templates/gateway/appsettings.json diff --git a/templates/v12/server/.template.config/template.json b/templates/server/.template.config/template.json similarity index 84% rename from templates/v12/server/.template.config/template.json rename to templates/server/.template.config/template.json index a522ec108b1..9e367e66592 100644 --- a/templates/v12/server/.template.config/template.json +++ b/templates/server/.template.config/template.json @@ -19,20 +19,16 @@ "description": "The target framework for the project.", "datatype": "choice", "choices": [ - { - "choice": "net6.0", - "description": "Target .NET 6" - }, - { - "choice": "net7.0", - "description": "Target .NET 7" - }, { "choice": "net8.0", "description": "Target .NET 8" + }, + { + "choice": "net9.0", + "description": "Target .NET 9" } ], - "replaces": "net6.0", + "replaces": "net8.0", "defaultValue": "net8.0" } }, diff --git a/templates/v12/server/HotChocolate.Template.Server.csproj b/templates/server/HotChocolate.Template.Server.csproj similarity index 94% rename from templates/v12/server/HotChocolate.Template.Server.csproj rename to templates/server/HotChocolate.Template.Server.csproj index f94695cec1a..78fdd691c0b 100644 --- a/templates/v12/server/HotChocolate.Template.Server.csproj +++ b/templates/server/HotChocolate.Template.Server.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 enable enable preview diff --git a/templates/v12/server/Program.cs b/templates/server/Program.cs similarity index 100% rename from templates/v12/server/Program.cs rename to templates/server/Program.cs diff --git a/templates/v12/server/Properties/ModuleInfo.cs b/templates/server/Properties/ModuleInfo.cs similarity index 100% rename from templates/v12/server/Properties/ModuleInfo.cs rename to templates/server/Properties/ModuleInfo.cs diff --git a/templates/v12/server/Properties/launchSettings.json b/templates/server/Properties/launchSettings.json similarity index 100% rename from templates/v12/server/Properties/launchSettings.json rename to templates/server/Properties/launchSettings.json diff --git a/templates/v12/server/Types/Author.cs b/templates/server/Types/Author.cs similarity index 100% rename from templates/v12/server/Types/Author.cs rename to templates/server/Types/Author.cs diff --git a/templates/v12/server/Types/Book.cs b/templates/server/Types/Book.cs similarity index 100% rename from templates/v12/server/Types/Book.cs rename to templates/server/Types/Book.cs diff --git a/templates/v12/server/Types/Query.cs b/templates/server/Types/Query.cs similarity index 100% rename from templates/v12/server/Types/Query.cs rename to templates/server/Types/Query.cs diff --git a/templates/v12/server/appsettings.Development.json b/templates/server/appsettings.Development.json similarity index 100% rename from templates/v12/server/appsettings.Development.json rename to templates/server/appsettings.Development.json diff --git a/templates/v12/server/appsettings.json b/templates/server/appsettings.json similarity index 100% rename from templates/v12/server/appsettings.json rename to templates/server/appsettings.json diff --git a/templates/v12/function-isolated/.template.config/template.json b/templates/v12/function-isolated/.template.config/template.json deleted file mode 100644 index 346765e515d..00000000000 --- a/templates/v12/function-isolated/.template.config/template.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "$schema": "http://json.schemastore.org/template", - "author": "Michael Staib", - "classifications": ["Web", "GraphQL", "Azure"], - "identity": "HotChocolate.Template.AzureFunctions.Isolated", - "sourceName": "HotChocolate.Template.AzureFunctions.Isolated", - "name": "GraphQL Function Isolated", - "shortName": "graphql-azf-ip", - "defaultName": "GraphQL Function Isolated", - "description": "", - "preferNameDirectory": true, - "tags": { - "language": "C#", - "type": "project" - }, - "symbols": { - "Framework": { - "type": "parameter", - "description": "The target framework for the project.", - "datatype": "choice", - "choices": [ - { - "choice": "net6.0", - "description": "Target .NET 6" - } - ], - "replaces": "net6.0", - "defaultValue": "net6.0" - } - }, - "postActions": [ - { - "condition": "(!skipRestore)", - "description": "Restore NuGet packages required by this project.", - "manualInstructions": [ - { - "text": "Run 'dotnet restore'" - } - ], - "actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025", - "continueOnError": true - } - ] -} diff --git a/templates/v12/function-isolated/Query.cs b/templates/v12/function-isolated/Query.cs deleted file mode 100644 index f3e92558253..00000000000 --- a/templates/v12/function-isolated/Query.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace HotChocolate.Template.AzureFunctions.Isolated; - -public class Query -{ - public Person GetPerson() => new Person("Luke Skywalker"); -} - -public class Person -{ - public Person(string name) - { - Name = name; - } - - public string Name { get; } -} diff --git a/templates/v12/function-isolated/local.settings.json b/templates/v12/function-isolated/local.settings.json deleted file mode 100644 index 9803d637f40..00000000000 --- a/templates/v12/function-isolated/local.settings.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "IsEncrypted": false, - "Values": { - "AzureWebJobsStorage": "UseDevelopmentStorage=true", - "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated" - } -} \ No newline at end of file diff --git a/templates/v12/function/.gitignore b/templates/v12/function/.gitignore deleted file mode 100644 index bd4e82a2277..00000000000 --- a/templates/v12/function/.gitignore +++ /dev/null @@ -1,264 +0,0 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. - -# Azure Functions localsettings file -local.settings.json - -# User-specific files -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ - -# Visual Studio 2015 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUNIT -*.VisualState.xml -TestResult.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# DNX -project.lock.json -project.fragment.lock.json -artifacts/ - -*_i.c -*_p.c -*_i.h -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# JustCode is a .NET coding add-in -.JustCode - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# TODO: Comment the next line if you want to checkin your web deploy settings -# but database connection strings (with potential passwords) will be unencrypted -#*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# The packages folder can be ignored because of Package Restore -**/packages/* -# except build/, which is used as an MSBuild target. -!**/packages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config -# NuGet v3's project.json files produces more ignoreable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -node_modules/ -orleans.codegen.cs - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm - -# SQL Server files -*.mdf -*.ldf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# JetBrains Rider -.idea/ -*.sln.iml - -# CodeRush -.cr/ - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc diff --git a/templates/v12/function/GraphQLFunction.cs b/templates/v12/function/GraphQLFunction.cs deleted file mode 100644 index 1717c0bf7ed..00000000000 --- a/templates/v12/function/GraphQLFunction.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Azure.WebJobs; -using Microsoft.Azure.WebJobs.Extensions.Http; - -namespace HotChocolate.Template.AzureFunctions; - -public class GraphQLFunction -{ - [FunctionName("GraphQLHttpFunction")] - public Task Run( - [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "graphql/{**slug}")] - HttpRequest request, - [GraphQL] - IGraphQLRequestExecutor executor) - => executor.ExecuteAsync(request); -} diff --git a/templates/v12/function/HotChocolate.Template.AzureFunctions.csproj b/templates/v12/function/HotChocolate.Template.AzureFunctions.csproj deleted file mode 100644 index 6cef41cfe71..00000000000 --- a/templates/v12/function/HotChocolate.Template.AzureFunctions.csproj +++ /dev/null @@ -1,36 +0,0 @@ - - - - net6.0 - enable - enable - v4 - - - - - - - - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - PreserveNewest - - - PreserveNewest - Never - - - - diff --git a/templates/v12/function/Startup.cs b/templates/v12/function/Startup.cs deleted file mode 100644 index 11bca511a9b..00000000000 --- a/templates/v12/function/Startup.cs +++ /dev/null @@ -1,11 +0,0 @@ -[assembly: FunctionsStartup(typeof(Startup))] - -public class Startup : FunctionsStartup -{ - public override void Configure(IFunctionsHostBuilder builder) - { - builder - .AddGraphQLFunction() - .AddQueryType(); - } -} diff --git a/templates/v12/function/host.json b/templates/v12/function/host.json deleted file mode 100644 index 809a3f20b92..00000000000 --- a/templates/v12/function/host.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": "2.0", - "logging": { - "applicationInsights": { - "samplingSettings": { - "isEnabled": true, - "excludedTypes": "Request" - } - } - } -} \ No newline at end of file diff --git a/templates/v12/function/local.settings.json b/templates/v12/function/local.settings.json deleted file mode 100644 index 893616561c6..00000000000 --- a/templates/v12/function/local.settings.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "IsEncrypted": false, - "Values": { - "FUNCTIONS_WORKER_RUNTIME": "dotnet" - } -} diff --git a/templates/v12/gateway-aspire/.vscode/tasks.json b/templates/v12/gateway-aspire/.vscode/tasks.json deleted file mode 100644 index 31c32bd3457..00000000000 --- a/templates/v12/gateway-aspire/.vscode/tasks.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - // See https://go.microsoft.com/fwlink/?LinkId=733558 - // for the documentation about the tasks.json format - "version": "2.0.0", - "tasks": [ - { - "label": "build", - "command": "dotnet", - "type": "shell", - "args": [ - "build", - // Ask dotnet build to generate full paths for file names. - "/property:GenerateFullPaths=true", - // Do not generate summary otherwise it leads to duplicate errors in Problems panel - "/consoleloggerparameters:NoSummary" - ], - "group": "build", - "presentation": { - "reveal": "silent" - }, - "problemMatcher": "$msCompile" - } - ] -} \ No newline at end of file diff --git a/templates/v12/gateway-bcp/.vscode/tasks.json b/templates/v12/gateway-bcp/.vscode/tasks.json deleted file mode 100644 index 31c32bd3457..00000000000 --- a/templates/v12/gateway-bcp/.vscode/tasks.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - // See https://go.microsoft.com/fwlink/?LinkId=733558 - // for the documentation about the tasks.json format - "version": "2.0.0", - "tasks": [ - { - "label": "build", - "command": "dotnet", - "type": "shell", - "args": [ - "build", - // Ask dotnet build to generate full paths for file names. - "/property:GenerateFullPaths=true", - // Do not generate summary otherwise it leads to duplicate errors in Problems panel - "/consoleloggerparameters:NoSummary" - ], - "group": "build", - "presentation": { - "reveal": "silent" - }, - "problemMatcher": "$msCompile" - } - ] -} \ No newline at end of file diff --git a/templates/v12/gateway/.vscode/tasks.json b/templates/v12/gateway/.vscode/tasks.json deleted file mode 100644 index 31c32bd3457..00000000000 --- a/templates/v12/gateway/.vscode/tasks.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - // See https://go.microsoft.com/fwlink/?LinkId=733558 - // for the documentation about the tasks.json format - "version": "2.0.0", - "tasks": [ - { - "label": "build", - "command": "dotnet", - "type": "shell", - "args": [ - "build", - // Ask dotnet build to generate full paths for file names. - "/property:GenerateFullPaths=true", - // Do not generate summary otherwise it leads to duplicate errors in Problems panel - "/consoleloggerparameters:NoSummary" - ], - "group": "build", - "presentation": { - "reveal": "silent" - }, - "problemMatcher": "$msCompile" - } - ] -} \ No newline at end of file diff --git a/templates/v12/server/.vscode/tasks.json b/templates/v12/server/.vscode/tasks.json deleted file mode 100644 index 31c32bd3457..00000000000 --- a/templates/v12/server/.vscode/tasks.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - // See https://go.microsoft.com/fwlink/?LinkId=733558 - // for the documentation about the tasks.json format - "version": "2.0.0", - "tasks": [ - { - "label": "build", - "command": "dotnet", - "type": "shell", - "args": [ - "build", - // Ask dotnet build to generate full paths for file names. - "/property:GenerateFullPaths=true", - // Do not generate summary otherwise it leads to duplicate errors in Problems panel - "/consoleloggerparameters:NoSummary" - ], - "group": "build", - "presentation": { - "reveal": "silent" - }, - "problemMatcher": "$msCompile" - } - ] -} \ No newline at end of file From f5fc2f1e1816fa08721f690e930c801f66daa34a Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Tue, 8 Oct 2024 10:23:56 -0400 Subject: [PATCH 054/154] Added support for n:m projections in DataLoader. (#7577) --- .../src/Core/Projections/ExpressionHelpers.cs | 4 +- .../src/Core/Projections/KeyValueResult.cs | 11 ++ .../SelectionDataLoaderExtensions.cs | 117 ++++++++++++++++-- .../Projections/ProjectableDataLoaderTests.cs | 65 +++++++++- ...ts.Project_Key_To_Collection_Expression.md | 10 ++ .../Types/DataLoaders.cs | 2 +- .../PagingHelperIntegrationTests.cs | 2 +- 7 files changed, 196 insertions(+), 15 deletions(-) create mode 100644 src/GreenDonut/src/Core/Projections/KeyValueResult.cs create mode 100644 src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Project_Key_To_Collection_Expression.md diff --git a/src/GreenDonut/src/Core/Projections/ExpressionHelpers.cs b/src/GreenDonut/src/Core/Projections/ExpressionHelpers.cs index 23e9598c438..777bf2f3ae3 100644 --- a/src/GreenDonut/src/Core/Projections/ExpressionHelpers.cs +++ b/src/GreenDonut/src/Core/Projections/ExpressionHelpers.cs @@ -169,8 +169,8 @@ private static Expression CombineConditionalAndMemberInit( return Expression.Condition(condition.Test, ifTrue, ifFalse); } - public static Expression> Rewrite( - Expression> selector) + public static Expression> Rewrite( + Expression> selector) { var parameter = selector.Parameters[0]; var bindings = new List(); diff --git a/src/GreenDonut/src/Core/Projections/KeyValueResult.cs b/src/GreenDonut/src/Core/Projections/KeyValueResult.cs new file mode 100644 index 00000000000..f66858d4923 --- /dev/null +++ b/src/GreenDonut/src/Core/Projections/KeyValueResult.cs @@ -0,0 +1,11 @@ +namespace GreenDonut.Projections; + +/// +/// This class is a helper that is used to project a key value pair. +/// +public sealed class KeyValueResult +{ + public TKey Key { get; set; } = default!; + + public TValue Value { get; set; } = default!; +} diff --git a/src/GreenDonut/src/Core/Projections/SelectionDataLoaderExtensions.cs b/src/GreenDonut/src/Core/Projections/SelectionDataLoaderExtensions.cs index 95298fe4265..93a42f2095d 100644 --- a/src/GreenDonut/src/Core/Projections/SelectionDataLoaderExtensions.cs +++ b/src/GreenDonut/src/Core/Projections/SelectionDataLoaderExtensions.cs @@ -14,6 +14,12 @@ namespace GreenDonut.Projections; #endif public static class SelectionDataLoaderExtensions { + private static readonly MethodInfo _selectMethod = + typeof(Enumerable) + .GetMethods() + .Where(m => m.Name == nameof(Enumerable.Select) && m.GetParameters().Length == 2) + .First(m => m.GetParameters()[1].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>)); + /// /// Branches a DataLoader and applies a selector to load the data. /// @@ -65,7 +71,7 @@ static IDataLoader CreateBranch( IDataLoader dataLoader, Expression> selector) { - var branch = new SelectionDataLoader( + var branch = new SelectionDataLoader( (DataLoaderBase)dataLoader, key); var context = new DefaultSelectorBuilder(); @@ -140,14 +146,14 @@ public static IDataLoader Include( /// Applies the selector from the DataLoader state to a queryable. /// /// - /// The queryable to apply the selector to. - /// - /// - /// The selector builder. + /// The queryable to apply the selector to. /// /// /// The DataLoader key. /// + /// + /// The selector builder. + /// /// /// The queryable type. /// @@ -157,10 +163,9 @@ public static IDataLoader Include( /// /// Throws if is null. /// - public static IQueryable Select( - this IQueryable query, - ISelectorBuilder builder, - Expression> key) + public static IQueryable Select(this IQueryable query, + Expression> key, + ISelectorBuilder builder) { if (query is null) { @@ -181,5 +186,99 @@ public static IQueryable Select( return query; } + + /// + /// Applies the selector from the DataLoader state to a queryable. + /// + /// + /// The queryable to apply the selector to. + /// + /// + /// The DataLoader key. + /// + /// + /// The list selector. + /// + /// + /// The element selector. + /// + /// + /// The queryable type. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns a selector query on which a key must be applied to fetch the data. + /// + public static IQueryable>> Select( + this IQueryable query, + Expression> key, + Expression>> list, + ISelectorBuilder elementSelector) + { + // we first create a new parameter expression for the root as we need + // a unified parameter for both expressions (key and list) + var parameter = Expression.Parameter(typeof(T), "root"); + + // next we replace the parameter within the key and list selectors with the + // unified parameter. + var rewrittenKey = ReplaceParameter(key, key.Parameters[0], parameter); + var rewrittenList = ReplaceParameter(list, list.Parameters[0], parameter); + + // next we try to compile an element selector expression. + var elementSelectorExpr = elementSelector.TryCompile(); + + // if we have a element selector to project properties on the list expression + // we will need to combine this into the list expression. + if (elementSelectorExpr is not null) + { + var selectMethod = _selectMethod.MakeGenericMethod(typeof(TValue), typeof(TValue)); + + list = Expression.Lambda>>( + Expression.Call( + selectMethod, + rewrittenList.Body, + elementSelectorExpr), + parameter); + } + + // finally we combine key and list expression into a single selector expression + var keyValueSelectorExpr = Expression.Lambda>>>( + Expression.MemberInit( + Expression.New(typeof(KeyValueResult>)), + Expression.Bind( + typeof(KeyValueResult>).GetProperty( + nameof(KeyValueResult>.Key))!, + rewrittenKey.Body), + Expression.Bind( + typeof(KeyValueResult>).GetProperty( + nameof(KeyValueResult>.Value))!, + list.Body)), + parameter); + + // lastly we apply the selector expression to the queryable. + return query.Select(keyValueSelectorExpr); + } + + private static Expression ReplaceParameter( + Expression expression, + ParameterExpression oldParameter, + ParameterExpression newParameter) + => (Expression)new ReplaceParameterVisitor(oldParameter, newParameter).Visit(expression); +} + +file sealed class ReplaceParameterVisitor( + ParameterExpression oldParameter, + ParameterExpression newParameter) + : ExpressionVisitor +{ + protected override Expression VisitParameter(ParameterExpression node) + => node == oldParameter + ? newParameter + : base.VisitParameter(node); } #endif diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs index 5c785890636..24db8271ae8 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs @@ -779,6 +779,36 @@ public async Task Brand_With_Default_Field_Over_Node() .MatchMarkdownSnapshot(); } + [Fact] + public async Task Project_Key_To_Collection_Expression() + { + // Arrange + var queries = new List(); + var connectionString = CreateConnectionString(); + await CatalogContext.SeedAsync(connectionString); + + var services = new ServiceCollection() + .AddScoped(_ => queries) + .AddTransient(_ => new CatalogContext(connectionString)) + .AddDataLoader( + sp => new ProductByBrandIdDataLoader( + sp, + sp.GetRequiredService>(), + sp.GetRequiredService(), + sp.GetRequiredService())) + .BuildServiceProvider(); + + // Act + await using var scope = services.CreateAsyncScope(); + var dataLoader = scope.ServiceProvider.GetRequiredService(); + await dataLoader.LoadAsync(1); + + // Assert + Snapshot.Create() + .AddSql(queries) + .MatchMarkdownSnapshot(); + } + public class Query { public async Task GetBrandByIdAsync( @@ -930,7 +960,7 @@ protected override async Task> LoadBatchAsync( var query = catalogContext.Brands .Where(t => keys.Contains(t.Id)) - .Select(context.GetSelector(), b => b.Id); + .Select(b => b.Id, context.GetSelector()); lock (_queries) { @@ -959,7 +989,7 @@ protected override async Task> LoadBatchAsync( var query = catalogContext.Products .Where(t => keys.Contains(t.Id)) - .Select(context.GetSelector(), b => b.Id); + .Select(b => b.Id, context.GetSelector()); lock (queries) { @@ -971,6 +1001,37 @@ protected override async Task> LoadBatchAsync( return x; } } + + public class ProductByBrandIdDataLoader( + IServiceProvider services, + List queries, + IBatchScheduler batchScheduler, + DataLoaderOptions options) + : StatefulBatchDataLoader(batchScheduler, options) + { + protected override async Task> LoadBatchAsync( + IReadOnlyList keys, + DataLoaderFetchContext context, + CancellationToken cancellationToken) + { + var catalogContext = services.GetRequiredService(); + var selector = new DefaultSelectorBuilder(); + selector.Add(t => new Product { Name = t.Name }); + + var query = catalogContext.Brands + .Where(t => keys.Contains(t.Id)) + .Select(t => t.Id, t => t.Products, selector); + + lock (queries) + { + queries.Add(query.ToQueryString()); + } + + var x = await query.ToDictionaryAsync(t => t.Key, t => t.Value.ToArray(), cancellationToken); + + return x; + } + } } file static class Extensions diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Project_Key_To_Collection_Expression.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Project_Key_To_Collection_Expression.md new file mode 100644 index 00000000000..6cf1c5c4768 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Project_Key_To_Collection_Expression.md @@ -0,0 +1,10 @@ +# Project_Key_To_Collection_Expression + +```text +-- @__keys_0={ '1' } (DbType = Object) +SELECT b."Id", p."Name", p."Id" +FROM "Brands" AS b +LEFT JOIN "Products" AS p ON b."Id" = p."BrandId" +WHERE b."Id" = ANY (@__keys_0) +ORDER BY b."Id" +``` diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/Types/DataLoaders.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/Types/DataLoaders.cs index e05996f78f9..c76eb982919 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/Types/DataLoaders.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/Types/DataLoaders.cs @@ -74,7 +74,7 @@ public static async Task> GetAuthorById( IQueryable query, ISelectorBuilder selector, CancellationToken ct) - => await Task.FromResult(query.Select(selector, t => t.Id).ToDictionary(t => t.Id)); + => await Task.FromResult(query.Select(t => t.Id, selector).ToDictionary(t => t.Id)); [DataLoader] public static async Task> GetAuthorWithPagingById( diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs index 3d2f3a32f0d..b28de084d3d 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs @@ -1511,7 +1511,7 @@ protected override async Task>> LoadBatch return await catalogContext.Products .Where(t => keys.Contains(t.BrandId)) - .Select(context.GetSelector(), b => b.BrandId) + .Select(b => b.BrandId, context.GetSelector()) .OrderBy(t => t.Name).ThenBy(t => t.Id) .ToBatchPageAsync(t => t.BrandId, pagingArgs, cancellationToken); } From 4cbcb07eef794d3b443d6a29cd01fac755c74c89 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Tue, 8 Oct 2024 17:19:00 -0400 Subject: [PATCH 055/154] Added more overloads for list selects. (#7578) --- .../src/Core/DataLoaderFetchContext.cs | 2 +- .../Projections/DefaultSelectorBuilder.cs | 15 +- .../SelectionDataLoaderExtensions.cs | 19 +- ...otChocolateExecutionSelectionExtensions.cs | 4 +- ...tChocolateExecutionDataLoaderExtensions.cs | 112 ++++ .../FileBuilders/DataLoaderFileBuilder.cs | 3 +- .../Projections/ProjectableDataLoaderTests.cs | 88 ++- ...ey_To_Collection_Expression_Integration.md | 632 ++++++++++++++++++ ...ePaginationBatchingDataLoaderExtensions.cs | 12 +- .../Extensions/PagingQueryableExtensions.cs | 48 +- 10 files changed, 898 insertions(+), 37 deletions(-) create mode 100644 src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Project_Key_To_Collection_Expression_Integration.md diff --git a/src/GreenDonut/src/Core/DataLoaderFetchContext.cs b/src/GreenDonut/src/Core/DataLoaderFetchContext.cs index 9629960df32..74419f42e67 100644 --- a/src/GreenDonut/src/Core/DataLoaderFetchContext.cs +++ b/src/GreenDonut/src/Core/DataLoaderFetchContext.cs @@ -159,7 +159,7 @@ public ISelectorBuilder GetSelector() // if no selector was found we will just return // a new default selector builder. - return new DefaultSelectorBuilder(); + return new DefaultSelectorBuilder(); } #endif } diff --git a/src/GreenDonut/src/Core/Projections/DefaultSelectorBuilder.cs b/src/GreenDonut/src/Core/Projections/DefaultSelectorBuilder.cs index b1989282a33..24308a644e9 100644 --- a/src/GreenDonut/src/Core/Projections/DefaultSelectorBuilder.cs +++ b/src/GreenDonut/src/Core/Projections/DefaultSelectorBuilder.cs @@ -7,24 +7,16 @@ namespace GreenDonut.Projections; /// /// A default implementation of the . /// -/// #if NET8_0_OR_GREATER [Experimental(Experiments.Projections)] #endif -public sealed class DefaultSelectorBuilder : ISelectorBuilder +public sealed class DefaultSelectorBuilder : ISelectorBuilder { private List? _selectors; /// public void Add(Expression> selector) { - if (typeof(T) != typeof(TValue)) - { - throw new ArgumentException( - "The projection type must match the DataLoader value type.", - nameof(selector)); - } - _selectors ??= new List(); if (!_selectors.Contains(selector)) { @@ -40,11 +32,6 @@ public void Add(Expression> selector) return null; } - if (typeof(T) != typeof(TValue)) - { - return null; - } - if (_selectors.Count == 1) { return (Expression>)_selectors[0]; diff --git a/src/GreenDonut/src/Core/Projections/SelectionDataLoaderExtensions.cs b/src/GreenDonut/src/Core/Projections/SelectionDataLoaderExtensions.cs index 93a42f2095d..286e2289bdd 100644 --- a/src/GreenDonut/src/Core/Projections/SelectionDataLoaderExtensions.cs +++ b/src/GreenDonut/src/Core/Projections/SelectionDataLoaderExtensions.cs @@ -35,15 +35,18 @@ public static class SelectionDataLoaderExtensions /// /// The value type. /// + /// + /// The element type. + /// /// /// Returns a branched DataLoader with the selector applied. /// /// /// Throws if is null. /// - public static IDataLoader Select( + public static IDataLoader Select( this IDataLoader dataLoader, - Expression>? selector) + Expression>? selector) where TKey : notnull { if (dataLoader is null) @@ -58,7 +61,7 @@ public static IDataLoader Select( if (dataLoader is ISelectionDataLoader) { - var context = (DefaultSelectorBuilder)dataLoader.ContextData[typeof(ISelectorBuilder).FullName!]!; + var context = (DefaultSelectorBuilder)dataLoader.ContextData[typeof(ISelectorBuilder).FullName!]!; context.Add(selector); return dataLoader; } @@ -69,12 +72,12 @@ public static IDataLoader Select( static IDataLoader CreateBranch( string key, IDataLoader dataLoader, - Expression> selector) + Expression> selector) { var branch = new SelectionDataLoader( (DataLoaderBase)dataLoader, key); - var context = new DefaultSelectorBuilder(); + var context = new DefaultSelectorBuilder(); branch.ContextData = branch.ContextData.SetItem(typeof(ISelectorBuilder).FullName!, context); context.Add(selector); return branch; @@ -137,7 +140,7 @@ public static IDataLoader Include( var context = dataLoader.GetOrSetState( typeof(ISelectorBuilder).FullName!, - _ => new DefaultSelectorBuilder()); + _ => new DefaultSelectorBuilder()); context.Add(Rewrite(includeSelector)); return dataLoader; } @@ -238,7 +241,7 @@ public static IQueryable>> Select>>( + rewrittenList = Expression.Lambda>>( Expression.Call( selectMethod, rewrittenList.Body, @@ -257,7 +260,7 @@ public static IQueryable>> Select>).GetProperty( nameof(KeyValueResult>.Value))!, - list.Body)), + rewrittenList.Body)), parameter); // lastly we apply the selector expression to the queryable. diff --git a/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs b/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs index 841c448a24a..d548a0c0b4f 100644 --- a/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs +++ b/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs @@ -53,7 +53,7 @@ public static Expression> AsSelector( if ((flags & FieldFlags.Connection) == FieldFlags.Connection) { - var builder = new DefaultSelectorBuilder(); + var builder = new DefaultSelectorBuilder(); var buffer = ArrayPool.Shared.Rent(16); var count = GetConnectionSelections(selection, buffer); for (var i = 0; i < count; i++) @@ -66,7 +66,7 @@ public static Expression> AsSelector( if ((flags & FieldFlags.CollectionSegment) == FieldFlags.CollectionSegment) { - var builder = new DefaultSelectorBuilder(); + var builder = new DefaultSelectorBuilder(); var buffer = ArrayPool.Shared.Rent(16); var count = GetCollectionSelections(selection, buffer); for (var i = 0; i < count; i++) diff --git a/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs b/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs index 0ba273ab3b4..f417866ca03 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs @@ -54,6 +54,118 @@ public static IDataLoader Select( return dataLoader.Select(expression); } + /// + /// Selects the fields that where selected in the GraphQL selection tree. + /// + /// + /// The data loader. + /// + /// + /// The selection that shall be applied to the data loader. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns a new data loader that applies the selection. + /// + public static IDataLoader Select( + this IDataLoader dataLoader, + ISelection selection) + where TKey : notnull + where TValue : notnull + { + var expression = selection.AsSelector(); + return dataLoader.Select(expression); + } + + /// + /// Selects the fields that where selected in the GraphQL selection tree. + /// + /// + /// The data loader. + /// + /// + /// The selection that shall be applied to the data loader. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns a new data loader that applies the selection. + /// + public static IDataLoader> Select( + this IDataLoader> dataLoader, + ISelection selection) + where TKey : notnull + where TValue : notnull + { + var expression = selection.AsSelector(); + return dataLoader.Select(expression); + } + + /// + /// Selects the fields that where selected in the GraphQL selection tree. + /// + /// + /// The data loader. + /// + /// + /// The selection that shall be applied to the data loader. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns a new data loader that applies the selection. + /// + public static IDataLoader> Select( + this IDataLoader> dataLoader, + ISelection selection) + where TKey : notnull + where TValue : notnull + { + var expression = selection.AsSelector(); + return dataLoader.Select(expression); + } + + /// + /// Selects the fields that where selected in the GraphQL selection tree. + /// + /// + /// The data loader. + /// + /// + /// The selection that shall be applied to the data loader. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns a new data loader that applies the selection. + /// + public static IDataLoader> Select( + this IDataLoader> dataLoader, + ISelection selection) + where TKey : notnull + where TValue : notnull + { + var expression = selection.AsSelector(); + return dataLoader.Select(expression); + } + /// /// Selects the fields that where selected in the GraphQL selection tree. /// diff --git a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs index 16e2679bda7..2f97e844fd3 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs @@ -264,8 +264,7 @@ public void WriteDataLoaderLoadMethod( parameter.StateKey); _writer.IncreaseIndent(); _writer.WriteIndentedLine( - "?? new global::GreenDonut.Projections.DefaultSelectorBuilder<{0}>();", - value.ToFullyQualified()); + "?? new global::GreenDonut.Projections.DefaultSelectorBuilder();"); _writer.DecreaseIndent(); } else if (parameter.Kind is DataLoaderParameterKind.PagingArguments) diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs index 24db8271ae8..c115c9a8e4f 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs @@ -809,6 +809,38 @@ public async Task Project_Key_To_Collection_Expression() .MatchMarkdownSnapshot(); } + [Fact] + public async Task Project_Key_To_Collection_Expression_Integration() + { + // Arrange + var queries = new List(); + var connectionString = CreateConnectionString(); + await CatalogContext.SeedAsync(connectionString); + + // Act + var result = await new ServiceCollection() + .AddScoped(_ => queries) + .AddTransient(_ => new CatalogContext(connectionString)) + .AddGraphQLServer() + .AddQueryType() + .AddTypeExtension(typeof(BrandListExtensions)) + .ExecuteRequestAsync( + """ + { + brandById(id: 1) { + products { + name + } + } + } + """); + // Assert + Snapshot.Create() + .AddSql(queries) + .Add(result, "Result") + .MatchMarkdownSnapshot(); + } + public class Query { public async Task GetBrandByIdAsync( @@ -873,6 +905,19 @@ public class NodeQuery } } + public class BrandsQuery + { + public async Task> GetBrandByIdAsync( + int id, + ISelection selection, + CatalogContext context, + CancellationToken cancellationToken) + => await context.Brands + .Select(selection.AsSelector()) + .Take(2) + .ToListAsync(cancellationToken); + } + [ExtendObjectType] public class BrandExtensions { @@ -891,6 +936,18 @@ public string GetDetails( => "Brand Name:" + brand.Name; } + [ExtendObjectType] + public class BrandListExtensions + { + [BindMember(nameof(Brand.Products))] + public async Task?> GetProductsAsync( + [Parent] Brand brand, + ProductByBrandIdDataLoader2 productByBrandId, + ISelection selection, + CancellationToken cancellationToken) + => await productByBrandId.Select(selection).LoadAsync(brand.Id, cancellationToken); + } + public class BrandWithRequirementType : ObjectType { protected override void Configure(IObjectTypeDescriptor descriptor) @@ -1015,7 +1072,7 @@ protected override async Task> LoadBatchAsyn CancellationToken cancellationToken) { var catalogContext = services.GetRequiredService(); - var selector = new DefaultSelectorBuilder(); + var selector = new DefaultSelectorBuilder(); selector.Add(t => new Product { Name = t.Name }); var query = catalogContext.Brands @@ -1032,6 +1089,35 @@ protected override async Task> LoadBatchAsyn return x; } } + + public class ProductByBrandIdDataLoader2( + IServiceProvider services, + List queries, + IBatchScheduler batchScheduler, + DataLoaderOptions options) + : StatefulBatchDataLoader(batchScheduler, options) + { + protected override async Task> LoadBatchAsync( + IReadOnlyList keys, + DataLoaderFetchContext context, + CancellationToken cancellationToken) + { + var catalogContext = services.GetRequiredService(); + + var query = catalogContext.Brands + .Where(t => keys.Contains(t.Id)) + .Select(t => t.Id, t => t.Products, context.GetSelector()); + + lock (queries) + { + queries.Add(query.ToQueryString()); + } + + var x = await query.ToDictionaryAsync(t => t.Key, t => t.Value.ToArray(), cancellationToken); + + return x; + } + } } file static class Extensions diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Project_Key_To_Collection_Expression_Integration.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Project_Key_To_Collection_Expression_Integration.md new file mode 100644 index 00000000000..9cb6d640200 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Project_Key_To_Collection_Expression_Integration.md @@ -0,0 +1,632 @@ +# Project_Key_To_Collection_Expression_Integration + +## SQL + +```text +-- @__keys_0={ '2', '1' } (DbType = Object) +SELECT b."Id", p."Name", p."Id" +FROM "Brands" AS b +LEFT JOIN "Products" AS p ON b."Id" = p."BrandId" +WHERE b."Id" = ANY (@__keys_0) +ORDER BY b."Id" +``` + +## Result + +```json +{ + "data": { + "brandById": [ + { + "products": [ + { + "name": "Product 0-0" + }, + { + "name": "Product 0-1" + }, + { + "name": "Product 0-2" + }, + { + "name": "Product 0-3" + }, + { + "name": "Product 0-4" + }, + { + "name": "Product 0-5" + }, + { + "name": "Product 0-6" + }, + { + "name": "Product 0-7" + }, + { + "name": "Product 0-8" + }, + { + "name": "Product 0-9" + }, + { + "name": "Product 0-10" + }, + { + "name": "Product 0-11" + }, + { + "name": "Product 0-12" + }, + { + "name": "Product 0-13" + }, + { + "name": "Product 0-14" + }, + { + "name": "Product 0-15" + }, + { + "name": "Product 0-16" + }, + { + "name": "Product 0-17" + }, + { + "name": "Product 0-18" + }, + { + "name": "Product 0-19" + }, + { + "name": "Product 0-20" + }, + { + "name": "Product 0-21" + }, + { + "name": "Product 0-22" + }, + { + "name": "Product 0-23" + }, + { + "name": "Product 0-24" + }, + { + "name": "Product 0-25" + }, + { + "name": "Product 0-26" + }, + { + "name": "Product 0-27" + }, + { + "name": "Product 0-28" + }, + { + "name": "Product 0-29" + }, + { + "name": "Product 0-30" + }, + { + "name": "Product 0-31" + }, + { + "name": "Product 0-32" + }, + { + "name": "Product 0-33" + }, + { + "name": "Product 0-34" + }, + { + "name": "Product 0-35" + }, + { + "name": "Product 0-36" + }, + { + "name": "Product 0-37" + }, + { + "name": "Product 0-38" + }, + { + "name": "Product 0-39" + }, + { + "name": "Product 0-40" + }, + { + "name": "Product 0-41" + }, + { + "name": "Product 0-42" + }, + { + "name": "Product 0-43" + }, + { + "name": "Product 0-44" + }, + { + "name": "Product 0-45" + }, + { + "name": "Product 0-46" + }, + { + "name": "Product 0-47" + }, + { + "name": "Product 0-48" + }, + { + "name": "Product 0-49" + }, + { + "name": "Product 0-50" + }, + { + "name": "Product 0-51" + }, + { + "name": "Product 0-52" + }, + { + "name": "Product 0-53" + }, + { + "name": "Product 0-54" + }, + { + "name": "Product 0-55" + }, + { + "name": "Product 0-56" + }, + { + "name": "Product 0-57" + }, + { + "name": "Product 0-58" + }, + { + "name": "Product 0-59" + }, + { + "name": "Product 0-60" + }, + { + "name": "Product 0-61" + }, + { + "name": "Product 0-62" + }, + { + "name": "Product 0-63" + }, + { + "name": "Product 0-64" + }, + { + "name": "Product 0-65" + }, + { + "name": "Product 0-66" + }, + { + "name": "Product 0-67" + }, + { + "name": "Product 0-68" + }, + { + "name": "Product 0-69" + }, + { + "name": "Product 0-70" + }, + { + "name": "Product 0-71" + }, + { + "name": "Product 0-72" + }, + { + "name": "Product 0-73" + }, + { + "name": "Product 0-74" + }, + { + "name": "Product 0-75" + }, + { + "name": "Product 0-76" + }, + { + "name": "Product 0-77" + }, + { + "name": "Product 0-78" + }, + { + "name": "Product 0-79" + }, + { + "name": "Product 0-80" + }, + { + "name": "Product 0-81" + }, + { + "name": "Product 0-82" + }, + { + "name": "Product 0-83" + }, + { + "name": "Product 0-84" + }, + { + "name": "Product 0-85" + }, + { + "name": "Product 0-86" + }, + { + "name": "Product 0-87" + }, + { + "name": "Product 0-88" + }, + { + "name": "Product 0-89" + }, + { + "name": "Product 0-90" + }, + { + "name": "Product 0-91" + }, + { + "name": "Product 0-92" + }, + { + "name": "Product 0-93" + }, + { + "name": "Product 0-94" + }, + { + "name": "Product 0-95" + }, + { + "name": "Product 0-96" + }, + { + "name": "Product 0-97" + }, + { + "name": "Product 0-98" + }, + { + "name": "Product 0-99" + } + ] + }, + { + "products": [ + { + "name": "Product 1-0" + }, + { + "name": "Product 1-1" + }, + { + "name": "Product 1-2" + }, + { + "name": "Product 1-3" + }, + { + "name": "Product 1-4" + }, + { + "name": "Product 1-5" + }, + { + "name": "Product 1-6" + }, + { + "name": "Product 1-7" + }, + { + "name": "Product 1-8" + }, + { + "name": "Product 1-9" + }, + { + "name": "Product 1-10" + }, + { + "name": "Product 1-11" + }, + { + "name": "Product 1-12" + }, + { + "name": "Product 1-13" + }, + { + "name": "Product 1-14" + }, + { + "name": "Product 1-15" + }, + { + "name": "Product 1-16" + }, + { + "name": "Product 1-17" + }, + { + "name": "Product 1-18" + }, + { + "name": "Product 1-19" + }, + { + "name": "Product 1-20" + }, + { + "name": "Product 1-21" + }, + { + "name": "Product 1-22" + }, + { + "name": "Product 1-23" + }, + { + "name": "Product 1-24" + }, + { + "name": "Product 1-25" + }, + { + "name": "Product 1-26" + }, + { + "name": "Product 1-27" + }, + { + "name": "Product 1-28" + }, + { + "name": "Product 1-29" + }, + { + "name": "Product 1-30" + }, + { + "name": "Product 1-31" + }, + { + "name": "Product 1-32" + }, + { + "name": "Product 1-33" + }, + { + "name": "Product 1-34" + }, + { + "name": "Product 1-35" + }, + { + "name": "Product 1-36" + }, + { + "name": "Product 1-37" + }, + { + "name": "Product 1-38" + }, + { + "name": "Product 1-39" + }, + { + "name": "Product 1-40" + }, + { + "name": "Product 1-41" + }, + { + "name": "Product 1-42" + }, + { + "name": "Product 1-43" + }, + { + "name": "Product 1-44" + }, + { + "name": "Product 1-45" + }, + { + "name": "Product 1-46" + }, + { + "name": "Product 1-47" + }, + { + "name": "Product 1-48" + }, + { + "name": "Product 1-49" + }, + { + "name": "Product 1-50" + }, + { + "name": "Product 1-51" + }, + { + "name": "Product 1-52" + }, + { + "name": "Product 1-53" + }, + { + "name": "Product 1-54" + }, + { + "name": "Product 1-55" + }, + { + "name": "Product 1-56" + }, + { + "name": "Product 1-57" + }, + { + "name": "Product 1-58" + }, + { + "name": "Product 1-59" + }, + { + "name": "Product 1-60" + }, + { + "name": "Product 1-61" + }, + { + "name": "Product 1-62" + }, + { + "name": "Product 1-63" + }, + { + "name": "Product 1-64" + }, + { + "name": "Product 1-65" + }, + { + "name": "Product 1-66" + }, + { + "name": "Product 1-67" + }, + { + "name": "Product 1-68" + }, + { + "name": "Product 1-69" + }, + { + "name": "Product 1-70" + }, + { + "name": "Product 1-71" + }, + { + "name": "Product 1-72" + }, + { + "name": "Product 1-73" + }, + { + "name": "Product 1-74" + }, + { + "name": "Product 1-75" + }, + { + "name": "Product 1-76" + }, + { + "name": "Product 1-77" + }, + { + "name": "Product 1-78" + }, + { + "name": "Product 1-79" + }, + { + "name": "Product 1-80" + }, + { + "name": "Product 1-81" + }, + { + "name": "Product 1-82" + }, + { + "name": "Product 1-83" + }, + { + "name": "Product 1-84" + }, + { + "name": "Product 1-85" + }, + { + "name": "Product 1-86" + }, + { + "name": "Product 1-87" + }, + { + "name": "Product 1-88" + }, + { + "name": "Product 1-89" + }, + { + "name": "Product 1-90" + }, + { + "name": "Product 1-91" + }, + { + "name": "Product 1-92" + }, + { + "name": "Product 1-93" + }, + { + "name": "Product 1-94" + }, + { + "name": "Product 1-95" + }, + { + "name": "Product 1-96" + }, + { + "name": "Product 1-97" + }, + { + "name": "Product 1-98" + }, + { + "name": "Product 1-99" + } + ] + } + ] + } +} +``` + diff --git a/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs b/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs index 0c94ed32f08..dedb0edd9ad 100644 --- a/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs +++ b/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs @@ -5,7 +5,7 @@ using GreenDonut.Projections; using HotChocolate.Pagination; -namespace GreenDonut; +namespace GreenDonut.Projections; /// /// Provides extension methods to pass a pagination context to a DataLoader. @@ -77,6 +77,9 @@ static IDataLoader CreatePagingDataLoader( /// /// The value type of the DataLoader. /// + /// + /// The element type of the projection. + /// /// /// Returns the DataLoader with the added projection. /// @@ -85,10 +88,9 @@ static IDataLoader CreatePagingDataLoader( /// #if NET8_0_OR_GREATER [Experimental(Experiments.Projections)] -#endif - public static IPagingDataLoader> Select( + public static IPagingDataLoader> Select( this IPagingDataLoader> dataLoader, - Expression>? selector) + Expression>? selector) where TKey : notnull { if (dataLoader is null) @@ -103,7 +105,7 @@ public static IPagingDataLoader> Select( var builder = dataLoader.GetOrSetState( typeof(ISelectorBuilder).FullName!, - _ => new DefaultSelectorBuilder()); + _ => new DefaultSelectorBuilder()); builder.Add(selector); return dataLoader; } diff --git a/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs index 4622b1ce8b2..9c692438b9b 100644 --- a/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs +++ b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs @@ -200,12 +200,52 @@ public static async ValueTask> ToPageAsync( /// /// If the queryable does not have any keys specified. /// - public static async ValueTask>> ToBatchPageAsync( + public static ValueTask>> ToBatchPageAsync( this IQueryable source, Expression> keySelector, PagingArguments arguments, CancellationToken cancellationToken = default) where TKey : notnull + => ToBatchPageAsync(source, keySelector, t => t, arguments, cancellationToken); + + /// + /// Executes a batch query with paging and returns the selected pages for each parent. + /// + /// + /// The queryable to be paged. + /// + /// + /// A function to select the key of the parent. + /// + /// + /// A function to select the value of the items in the queryable. + /// + /// + /// The paging arguments. + /// + /// + /// The cancellation token. + /// + /// + /// The type of the parent key. + /// + /// + /// The type of the items in the queryable. + /// + /// + /// The type of the items in the queryable. + /// + /// + /// + /// If the queryable does not have any keys specified. + /// + public static async ValueTask>> ToBatchPageAsync( + this IQueryable source, + Expression> keySelector, + Func valueSelector, + PagingArguments arguments, + CancellationToken cancellationToken = default) + where TKey : notnull { var keys = ParseDataSetKeys(source); @@ -233,7 +273,7 @@ public static async ValueTask>> ToBatchPageAsync( + BuildBatchSelectExpression( arguments, keys, ordering.OrderExpressions, @@ -243,7 +283,7 @@ public static async ValueTask>> ToBatchPageAsync>(); // we apply our new expression here. - source = source.Provider.CreateQuery(ordering.Expression); + source = source.Provider.CreateQuery(ordering.Expression); TryGetQueryInterceptor()?.OnBeforeExecute(source.GroupBy(keySelector).Select(selectExpression)); @@ -265,7 +305,7 @@ public static async ValueTask>> ToBatchPageAsync Date: Tue, 8 Oct 2024 17:49:24 -0400 Subject: [PATCH 056/154] Fixed Compile Issue --- .../HotChocolatePaginationBatchingDataLoaderExtensions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs b/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs index dedb0edd9ad..ae6280876f8 100644 --- a/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs +++ b/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs @@ -62,6 +62,7 @@ static IDataLoader CreatePagingDataLoader( } } +#if NET8_0_OR_GREATER /// /// Adds a projection as state to the DataLoader. /// @@ -86,7 +87,6 @@ static IDataLoader CreatePagingDataLoader( /// /// Throws if the is null. /// -#if NET8_0_OR_GREATER [Experimental(Experiments.Projections)] public static IPagingDataLoader> Select( this IPagingDataLoader> dataLoader, @@ -109,6 +109,7 @@ public static IPagingDataLoader> Select Date: Tue, 8 Oct 2024 18:00:43 -0400 Subject: [PATCH 057/154] Fixed Test Issues --- .../Projections/ProjectableDataLoaderTests.cs | 8 + ...ollection_Expression_Integration_NET7_0.md | 631 ++++++++++++++++++ ...ect_Key_To_Collection_Expression_NET7_0.md | 9 + ...ePaginationBatchingDataLoaderExtensions.cs | 6 +- 4 files changed, 651 insertions(+), 3 deletions(-) create mode 100644 src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Project_Key_To_Collection_Expression_Integration_NET7_0.md create mode 100644 src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Project_Key_To_Collection_Expression_NET7_0.md diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs index c115c9a8e4f..672993a2907 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs @@ -804,7 +804,11 @@ public async Task Project_Key_To_Collection_Expression() await dataLoader.LoadAsync(1); // Assert +#if NET7_0 + Snapshot.Create("NET7_0") +#else Snapshot.Create() +#endif .AddSql(queries) .MatchMarkdownSnapshot(); } @@ -835,7 +839,11 @@ public async Task Project_Key_To_Collection_Expression_Integration() } """); // Assert +#if NET7_0 + Snapshot.Create("NET7_0") +#else Snapshot.Create() +#endif .AddSql(queries) .Add(result, "Result") .MatchMarkdownSnapshot(); diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Project_Key_To_Collection_Expression_Integration_NET7_0.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Project_Key_To_Collection_Expression_Integration_NET7_0.md new file mode 100644 index 00000000000..6e939f70dda --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Project_Key_To_Collection_Expression_Integration_NET7_0.md @@ -0,0 +1,631 @@ +# Project_Key_To_Collection_Expression_Integration + +## SQL + +```text +SELECT b."Id", p."Name", p."Id" +FROM "Brands" AS b +LEFT JOIN "Products" AS p ON b."Id" = p."BrandId" +WHERE b."Id" IN (2, 1) +ORDER BY b."Id" +``` + +## Result + +```json +{ + "data": { + "brandById": [ + { + "products": [ + { + "name": "Product 0-0" + }, + { + "name": "Product 0-1" + }, + { + "name": "Product 0-2" + }, + { + "name": "Product 0-3" + }, + { + "name": "Product 0-4" + }, + { + "name": "Product 0-5" + }, + { + "name": "Product 0-6" + }, + { + "name": "Product 0-7" + }, + { + "name": "Product 0-8" + }, + { + "name": "Product 0-9" + }, + { + "name": "Product 0-10" + }, + { + "name": "Product 0-11" + }, + { + "name": "Product 0-12" + }, + { + "name": "Product 0-13" + }, + { + "name": "Product 0-14" + }, + { + "name": "Product 0-15" + }, + { + "name": "Product 0-16" + }, + { + "name": "Product 0-17" + }, + { + "name": "Product 0-18" + }, + { + "name": "Product 0-19" + }, + { + "name": "Product 0-20" + }, + { + "name": "Product 0-21" + }, + { + "name": "Product 0-22" + }, + { + "name": "Product 0-23" + }, + { + "name": "Product 0-24" + }, + { + "name": "Product 0-25" + }, + { + "name": "Product 0-26" + }, + { + "name": "Product 0-27" + }, + { + "name": "Product 0-28" + }, + { + "name": "Product 0-29" + }, + { + "name": "Product 0-30" + }, + { + "name": "Product 0-31" + }, + { + "name": "Product 0-32" + }, + { + "name": "Product 0-33" + }, + { + "name": "Product 0-34" + }, + { + "name": "Product 0-35" + }, + { + "name": "Product 0-36" + }, + { + "name": "Product 0-37" + }, + { + "name": "Product 0-38" + }, + { + "name": "Product 0-39" + }, + { + "name": "Product 0-40" + }, + { + "name": "Product 0-41" + }, + { + "name": "Product 0-42" + }, + { + "name": "Product 0-43" + }, + { + "name": "Product 0-44" + }, + { + "name": "Product 0-45" + }, + { + "name": "Product 0-46" + }, + { + "name": "Product 0-47" + }, + { + "name": "Product 0-48" + }, + { + "name": "Product 0-49" + }, + { + "name": "Product 0-50" + }, + { + "name": "Product 0-51" + }, + { + "name": "Product 0-52" + }, + { + "name": "Product 0-53" + }, + { + "name": "Product 0-54" + }, + { + "name": "Product 0-55" + }, + { + "name": "Product 0-56" + }, + { + "name": "Product 0-57" + }, + { + "name": "Product 0-58" + }, + { + "name": "Product 0-59" + }, + { + "name": "Product 0-60" + }, + { + "name": "Product 0-61" + }, + { + "name": "Product 0-62" + }, + { + "name": "Product 0-63" + }, + { + "name": "Product 0-64" + }, + { + "name": "Product 0-65" + }, + { + "name": "Product 0-66" + }, + { + "name": "Product 0-67" + }, + { + "name": "Product 0-68" + }, + { + "name": "Product 0-69" + }, + { + "name": "Product 0-70" + }, + { + "name": "Product 0-71" + }, + { + "name": "Product 0-72" + }, + { + "name": "Product 0-73" + }, + { + "name": "Product 0-74" + }, + { + "name": "Product 0-75" + }, + { + "name": "Product 0-76" + }, + { + "name": "Product 0-77" + }, + { + "name": "Product 0-78" + }, + { + "name": "Product 0-79" + }, + { + "name": "Product 0-80" + }, + { + "name": "Product 0-81" + }, + { + "name": "Product 0-82" + }, + { + "name": "Product 0-83" + }, + { + "name": "Product 0-84" + }, + { + "name": "Product 0-85" + }, + { + "name": "Product 0-86" + }, + { + "name": "Product 0-87" + }, + { + "name": "Product 0-88" + }, + { + "name": "Product 0-89" + }, + { + "name": "Product 0-90" + }, + { + "name": "Product 0-91" + }, + { + "name": "Product 0-92" + }, + { + "name": "Product 0-93" + }, + { + "name": "Product 0-94" + }, + { + "name": "Product 0-95" + }, + { + "name": "Product 0-96" + }, + { + "name": "Product 0-97" + }, + { + "name": "Product 0-98" + }, + { + "name": "Product 0-99" + } + ] + }, + { + "products": [ + { + "name": "Product 1-0" + }, + { + "name": "Product 1-1" + }, + { + "name": "Product 1-2" + }, + { + "name": "Product 1-3" + }, + { + "name": "Product 1-4" + }, + { + "name": "Product 1-5" + }, + { + "name": "Product 1-6" + }, + { + "name": "Product 1-7" + }, + { + "name": "Product 1-8" + }, + { + "name": "Product 1-9" + }, + { + "name": "Product 1-10" + }, + { + "name": "Product 1-11" + }, + { + "name": "Product 1-12" + }, + { + "name": "Product 1-13" + }, + { + "name": "Product 1-14" + }, + { + "name": "Product 1-15" + }, + { + "name": "Product 1-16" + }, + { + "name": "Product 1-17" + }, + { + "name": "Product 1-18" + }, + { + "name": "Product 1-19" + }, + { + "name": "Product 1-20" + }, + { + "name": "Product 1-21" + }, + { + "name": "Product 1-22" + }, + { + "name": "Product 1-23" + }, + { + "name": "Product 1-24" + }, + { + "name": "Product 1-25" + }, + { + "name": "Product 1-26" + }, + { + "name": "Product 1-27" + }, + { + "name": "Product 1-28" + }, + { + "name": "Product 1-29" + }, + { + "name": "Product 1-30" + }, + { + "name": "Product 1-31" + }, + { + "name": "Product 1-32" + }, + { + "name": "Product 1-33" + }, + { + "name": "Product 1-34" + }, + { + "name": "Product 1-35" + }, + { + "name": "Product 1-36" + }, + { + "name": "Product 1-37" + }, + { + "name": "Product 1-38" + }, + { + "name": "Product 1-39" + }, + { + "name": "Product 1-40" + }, + { + "name": "Product 1-41" + }, + { + "name": "Product 1-42" + }, + { + "name": "Product 1-43" + }, + { + "name": "Product 1-44" + }, + { + "name": "Product 1-45" + }, + { + "name": "Product 1-46" + }, + { + "name": "Product 1-47" + }, + { + "name": "Product 1-48" + }, + { + "name": "Product 1-49" + }, + { + "name": "Product 1-50" + }, + { + "name": "Product 1-51" + }, + { + "name": "Product 1-52" + }, + { + "name": "Product 1-53" + }, + { + "name": "Product 1-54" + }, + { + "name": "Product 1-55" + }, + { + "name": "Product 1-56" + }, + { + "name": "Product 1-57" + }, + { + "name": "Product 1-58" + }, + { + "name": "Product 1-59" + }, + { + "name": "Product 1-60" + }, + { + "name": "Product 1-61" + }, + { + "name": "Product 1-62" + }, + { + "name": "Product 1-63" + }, + { + "name": "Product 1-64" + }, + { + "name": "Product 1-65" + }, + { + "name": "Product 1-66" + }, + { + "name": "Product 1-67" + }, + { + "name": "Product 1-68" + }, + { + "name": "Product 1-69" + }, + { + "name": "Product 1-70" + }, + { + "name": "Product 1-71" + }, + { + "name": "Product 1-72" + }, + { + "name": "Product 1-73" + }, + { + "name": "Product 1-74" + }, + { + "name": "Product 1-75" + }, + { + "name": "Product 1-76" + }, + { + "name": "Product 1-77" + }, + { + "name": "Product 1-78" + }, + { + "name": "Product 1-79" + }, + { + "name": "Product 1-80" + }, + { + "name": "Product 1-81" + }, + { + "name": "Product 1-82" + }, + { + "name": "Product 1-83" + }, + { + "name": "Product 1-84" + }, + { + "name": "Product 1-85" + }, + { + "name": "Product 1-86" + }, + { + "name": "Product 1-87" + }, + { + "name": "Product 1-88" + }, + { + "name": "Product 1-89" + }, + { + "name": "Product 1-90" + }, + { + "name": "Product 1-91" + }, + { + "name": "Product 1-92" + }, + { + "name": "Product 1-93" + }, + { + "name": "Product 1-94" + }, + { + "name": "Product 1-95" + }, + { + "name": "Product 1-96" + }, + { + "name": "Product 1-97" + }, + { + "name": "Product 1-98" + }, + { + "name": "Product 1-99" + } + ] + } + ] + } +} +``` + diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Project_Key_To_Collection_Expression_NET7_0.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Project_Key_To_Collection_Expression_NET7_0.md new file mode 100644 index 00000000000..8071555af10 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Project_Key_To_Collection_Expression_NET7_0.md @@ -0,0 +1,9 @@ +# Project_Key_To_Collection_Expression + +```text +SELECT b."Id", p."Name", p."Id" +FROM "Brands" AS b +LEFT JOIN "Products" AS p ON b."Id" = p."BrandId" +WHERE b."Id" = 1 +ORDER BY b."Id" +``` diff --git a/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs b/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs index ae6280876f8..109f8a8c728 100644 --- a/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs +++ b/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs @@ -62,7 +62,6 @@ static IDataLoader CreatePagingDataLoader( } } -#if NET8_0_OR_GREATER /// /// Adds a projection as state to the DataLoader. /// @@ -87,7 +86,9 @@ static IDataLoader CreatePagingDataLoader( /// /// Throws if the is null. /// +#if NET8_0_OR_GREATER [Experimental(Experiments.Projections)] +#endif public static IPagingDataLoader> Select( this IPagingDataLoader> dataLoader, Expression>? selector) @@ -109,8 +110,7 @@ public static IPagingDataLoader> Select Date: Wed, 9 Oct 2024 07:59:51 -0400 Subject: [PATCH 058/154] Fixed read-only property projection issue. (#7579) --- .../HotChocolateExecutionSelectionExtensions.cs | 0 .../src/Execution/Projections/SelectionExpressionBuilder.cs | 6 ++++-- 2 files changed, 4 insertions(+), 2 deletions(-) rename src/HotChocolate/Core/src/Execution/{Extensions => Projections}/HotChocolateExecutionSelectionExtensions.cs (100%) diff --git a/src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs b/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionSelectionExtensions.cs similarity index 100% rename from src/HotChocolate/Core/src/Execution/Extensions/HotChocolateExecutionSelectionExtensions.cs rename to src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionSelectionExtensions.cs diff --git a/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs b/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs index 8d85f8d996b..d368c0a1a2d 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs @@ -40,7 +40,9 @@ public Expression> BuildNodeExpression(ISelection sele var context = new Context(parameter, rootType, requirements); var root = new TypeContainer(); - var entityType = selection.DeclaringOperation.GetPossibleTypes(selection).FirstOrDefault(t => t.RuntimeType == typeof(TRoot)); + var entityType = selection.DeclaringOperation + .GetPossibleTypes(selection) + .FirstOrDefault(t => t.RuntimeType == typeof(TRoot)); if (entityType is null) { @@ -176,7 +178,7 @@ private void CollectSelection( return; } - if (selection.Field.Member is not PropertyInfo property) + if (selection.Field.Member is not PropertyInfo { CanRead: true, CanWrite: true } property) { return; } From a0a06c6b73f77a3fc17d2ad5d88786f18fb65319 Mon Sep 17 00:00:00 2001 From: Glen Date: Thu, 10 Oct 2024 10:32:38 +0200 Subject: [PATCH 059/154] Added migration section for MutationResult -> FieldResult (#7580) --- .../hotchocolate/v14/migrating/migrate-from-13-to-14.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/website/src/docs/hotchocolate/v14/migrating/migrate-from-13-to-14.md b/website/src/docs/hotchocolate/v14/migrating/migrate-from-13-to-14.md index cef75cca68e..6196cae0779 100644 --- a/website/src/docs/hotchocolate/v14/migrating/migrate-from-13-to-14.md +++ b/website/src/docs/hotchocolate/v14/migrating/migrate-from-13-to-14.md @@ -141,6 +141,13 @@ Please ensure that your clients are sending date/time strings in the correct for | -------------- | ------------------- | ---------------------- | | cacheDirectory | "persisted_queries" | "persisted_operations" | +## MutationResult renamed to FieldResult + +| Old name | New name | +| ----------------------------- | -------------------------- | +| MutationResult<TResult> | FieldResult<TResult> | +| IMutationResult | IFieldResult | + # Deprecations Things that will continue to function this release, but we encourage you to move away from. From 049ee3737094a8d8ad378b91a4f9778e92e53622 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Thu, 10 Oct 2024 04:37:12 -0400 Subject: [PATCH 060/154] Allow branching on paging- and selection- DataLoader. (#7581) --- .../Core/Projections/SelectionDataLoader.cs | 3 +- .../SelectionDataLoaderExtensions.cs | 4 +- ...tChocolateExecutionDataLoaderExtensions.cs | 3 +- ...ePaginationBatchingDataLoaderExtensions.cs | 41 +++++++++++++------ .../Pagination.Batching/PagingDataLoader.cs | 27 +++++++++++- .../PagingHelperIntegrationTests.cs | 4 +- 6 files changed, 62 insertions(+), 20 deletions(-) diff --git a/src/GreenDonut/src/Core/Projections/SelectionDataLoader.cs b/src/GreenDonut/src/Core/Projections/SelectionDataLoader.cs index 67271f86479..2b411a551b7 100644 --- a/src/GreenDonut/src/Core/Projections/SelectionDataLoader.cs +++ b/src/GreenDonut/src/Core/Projections/SelectionDataLoader.cs @@ -15,6 +15,7 @@ public SelectionDataLoader( { _root = root; CacheKeyType = $"{root.CacheKeyType}:{selectionKey}"; + ContextData = root.ContextData; } public IDataLoader Root => _root; @@ -23,7 +24,7 @@ public SelectionDataLoader( protected override bool AllowCachePropagation => false; - protected override bool AllowBranching => false; + protected override bool AllowBranching => true; protected internal override ValueTask FetchAsync( IReadOnlyList keys, diff --git a/src/GreenDonut/src/Core/Projections/SelectionDataLoaderExtensions.cs b/src/GreenDonut/src/Core/Projections/SelectionDataLoaderExtensions.cs index 286e2289bdd..385eb713c24 100644 --- a/src/GreenDonut/src/Core/Projections/SelectionDataLoaderExtensions.cs +++ b/src/GreenDonut/src/Core/Projections/SelectionDataLoaderExtensions.cs @@ -59,9 +59,9 @@ public static IDataLoader Select( return dataLoader; } - if (dataLoader is ISelectionDataLoader) + if (dataLoader.ContextData.TryGetValue(typeof(ISelectorBuilder).FullName!, out var value)) { - var context = (DefaultSelectorBuilder)dataLoader.ContextData[typeof(ISelectorBuilder).FullName!]!; + var context = (DefaultSelectorBuilder)value!; context.Add(selector); return dataLoader; } diff --git a/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs b/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs index f417866ca03..2b0a63bb5c1 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs @@ -200,8 +200,7 @@ public static IPagingDataLoader> Select( } var expression = selection.AsSelector(); - dataLoader.Select(expression); - return dataLoader; + return dataLoader.Select(expression); } } #endif diff --git a/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs b/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs index 109f8a8c728..bbb33613241 100644 --- a/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs +++ b/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs @@ -54,9 +54,7 @@ static IDataLoader CreatePagingDataLoader( IDataLoader> root, PagingArguments pagingArguments) { - var branch = new PagingDataLoader>( - (DataLoaderBase>)root, - branchKey); + var branch = new PagingDataLoader>(root, branchKey); branch.SetState(pagingArguments); return branch; } @@ -104,11 +102,27 @@ public static IPagingDataLoader> Select new DefaultSelectorBuilder()); - builder.Add(selector); - return dataLoader; + if (dataLoader.ContextData.TryGetValue(typeof(ISelectorBuilder).FullName!, out var value)) + { + var context = (DefaultSelectorBuilder)value!; + context.Add(selector); + return dataLoader; + } + + var branchKey = selector.ToString(); + return (IPagingDataLoader>)dataLoader.Branch(branchKey, CreateBranch, selector); + + static IDataLoader CreateBranch( + string key, + IDataLoader> dataLoader, + Expression> selector) + { + var branch = new PagingDataLoader>(dataLoader, key); + var context = new DefaultSelectorBuilder(); + branch.ContextData = branch.ContextData.SetItem(typeof(ISelectorBuilder).FullName!, context); + context.Add(selector); + return branch; + } } private static string CreateBranchKey( @@ -117,14 +131,15 @@ private static string CreateBranchKey( var requiredBufferSize = 1; requiredBufferSize += EstimateIntLength(pagingArguments.First); - if(pagingArguments.After is not null) + if (pagingArguments.After is not null) { requiredBufferSize += pagingArguments.After?.Length ?? 0; requiredBufferSize += 2; } + requiredBufferSize += EstimateIntLength(pagingArguments.Last); - if(pagingArguments.Before is not null) + if (pagingArguments.Before is not null) { requiredBufferSize += pagingArguments.Before?.Length ?? 0; requiredBufferSize += 2; @@ -136,7 +151,7 @@ private static string CreateBranchKey( } char[]? rentedBuffer = null; - Span buffer = requiredBufferSize <= 128 + var buffer = requiredBufferSize <= 128 ? stackalloc char[requiredBufferSize] : (rentedBuffer = ArrayPool.Shared.Rent(requiredBufferSize)); @@ -154,6 +169,7 @@ private static string CreateBranchKey( { throw new InvalidOperationException("Buffer is too small."); } + written += charsWritten; } @@ -180,6 +196,7 @@ private static string CreateBranchKey( { throw new InvalidOperationException("Buffer is too small."); } + written += charsWritten; } @@ -209,7 +226,7 @@ private static string CreateBranchKey( private static int EstimateIntLength(int? value) { // if the value is null we need 0 digits. - if(value is null) + if (value is null) { return 0; } diff --git a/src/HotChocolate/Pagination/src/Pagination.Batching/PagingDataLoader.cs b/src/HotChocolate/Pagination/src/Pagination.Batching/PagingDataLoader.cs index 90515824148..9b31c7ef6d4 100644 --- a/src/HotChocolate/Pagination/src/Pagination.Batching/PagingDataLoader.cs +++ b/src/HotChocolate/Pagination/src/Pagination.Batching/PagingDataLoader.cs @@ -11,12 +11,20 @@ internal sealed class PagingDataLoader private readonly DataLoaderBase _root; public PagingDataLoader( + IDataLoader root, + string pagingKey) + : this(GetRoot(root), pagingKey) + { + } + + private PagingDataLoader( DataLoaderBase root, string pagingKey) : base(DataLoaderHelper.GetBatchScheduler(root), DataLoaderHelper.GetOptions(root)) { _root = root; CacheKeyType = $"{DataLoaderHelper.GetCacheKeyType(root)}:{pagingKey}"; + ContextData = root.ContextData; } public IDataLoader Root => _root; @@ -28,7 +36,7 @@ public PagingArguments PagingArguments protected override bool AllowCachePropagation => false; - protected override bool AllowBranching => false; + protected override bool AllowBranching => true; protected internal override ValueTask FetchAsync( IReadOnlyList keys, @@ -36,4 +44,21 @@ protected internal override ValueTask FetchAsync( DataLoaderFetchContext context, CancellationToken cancellationToken) => DataLoaderHelper.FetchAsync(_root, keys, results, context, cancellationToken); + + private static DataLoaderBase GetRoot(IDataLoader root) + { + if(root is DataLoaderBase baseRoot) + { + return baseRoot; + } + + if(root is PagingDataLoader pagingRoot) + { + return pagingRoot._root; + } + + throw new ArgumentException( + "The root data loader must be a DataLoaderBase.", + nameof(root)); + } } diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs index b28de084d3d..a8f9653bb1f 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs @@ -1477,7 +1477,6 @@ public static async Task> GetProducts( ProductsByBrandDataLoader dataLoader, ISelection selection, PagingArguments arguments, - IResolverContext context, CancellationToken cancellationToken) => await dataLoader .WithPagingArguments(arguments) @@ -1505,13 +1504,14 @@ protected override async Task>> LoadBatch CancellationToken cancellationToken) { var pagingArgs = context.GetPagingArguments(); + var selector = context.GetSelector(); await using var scope = _services.CreateAsyncScope(); await using var catalogContext = scope.ServiceProvider.GetRequiredService(); return await catalogContext.Products .Where(t => keys.Contains(t.BrandId)) - .Select(b => b.BrandId, context.GetSelector()) + .Select(b => b.BrandId, selector) .OrderBy(t => t.Name).ThenBy(t => t.Id) .ToBatchPageAsync(t => t.BrandId, pagingArgs, cancellationToken); } From 3de3c4d6597282215b1576dd7b8c4bd8d4c0059c Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Thu, 10 Oct 2024 21:05:27 -0400 Subject: [PATCH 061/154] Refined Slicing Argument Validation (#7584) --- .../Utilities/CostAnalyzerUtilities.cs | 5 + .../SlicingArgumentsTests.cs | 255 ++++++++++++++++++ 2 files changed, 260 insertions(+) create mode 100644 src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/SlicingArgumentsTests.cs diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/Utilities/CostAnalyzerUtilities.cs b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Utilities/CostAnalyzerUtilities.cs index 6fa07716213..edde29c1f76 100644 --- a/src/HotChocolate/CostAnalysis/src/CostAnalysis/Utilities/CostAnalyzerUtilities.cs +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Utilities/CostAnalyzerUtilities.cs @@ -172,6 +172,11 @@ public static void ValidateRequireOneSlicingArgument( { if (listSizeDirective.SlicingArguments.Contains(argumentNode.Name.Value)) { + if(argumentNode.Value.Kind == SyntaxKind.NullValue) + { + continue; + } + argumentCount++; if(argumentNode.Value.Kind == SyntaxKind.Variable) diff --git a/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/SlicingArgumentsTests.cs b/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/SlicingArgumentsTests.cs new file mode 100644 index 00000000000..ecb42312c96 --- /dev/null +++ b/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/SlicingArgumentsTests.cs @@ -0,0 +1,255 @@ +using CookieCrumble; +using HotChocolate.Execution; +using HotChocolate.Types; +using Microsoft.Extensions.DependencyInjection; + +namespace HotChocolate.CostAnalysis; + +public class SlicingArgumentsTests +{ + [Fact] + public async Task SlicingArguments_Non_Specified() + { + var result = + await new ServiceCollection() + .AddGraphQLServer() + .AddQueryType() + .ExecuteRequestAsync( + """ + { + foos { + nodes + } + } + """); + + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Exactly one slicing argument must be defined.", + "locations": [ + { + "line": 2, + "column": 5 + } + ], + "path": [ + "foos" + ], + "extensions": { + "code": "HC0082" + } + } + ] + } + """); + } + + [Fact] + public async Task SlicingArguments_Set_To_Null() + { + var result = + await new ServiceCollection() + .AddGraphQLServer() + .AddQueryType() + .ExecuteRequestAsync( + """ + { + foos(first: null) { + nodes + } + } + """); + + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Exactly one slicing argument must be defined.", + "locations": [ + { + "line": 2, + "column": 5 + } + ], + "path": [ + "foos" + ], + "extensions": { + "code": "HC0082" + } + } + ] + } + """); + } + + [Fact] + public async Task SlicingArguments_Set_First_To_Null_And_Last_To_Null() + { + var result = + await new ServiceCollection() + .AddGraphQLServer() + .AddQueryType() + .ExecuteRequestAsync( + """ + { + foos(first: null last: null) { + nodes + } + } + """); + + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Exactly one slicing argument must be defined.", + "locations": [ + { + "line": 2, + "column": 5 + } + ], + "path": [ + "foos" + ], + "extensions": { + "code": "HC0082" + } + } + ] + } + """); + } + + [Fact] + public async Task SlicingArguments_First_Is_Null_And_Last_Is_1() + { + var result = + await new ServiceCollection() + .AddGraphQLServer() + .AddQueryType() + .ExecuteRequestAsync( + """ + { + foos(first: null, last: 1) { + nodes + } + } + """); + + result.MatchInlineSnapshot( + """ + { + "data": { + "foos": { + "nodes": [ + 100 + ] + } + } + } + """); + } + + [Fact] + public async Task SlicingArguments_First_Is_1_And_Last_Is_Null() + { + var result = + await new ServiceCollection() + .AddGraphQLServer() + .AddQueryType() + .ExecuteRequestAsync( + """ + { + foos(first: 1, last: null) { + nodes + } + } + """); + + result.MatchInlineSnapshot( + """ + { + "data": { + "foos": { + "nodes": [ + 1 + ] + } + } + } + """); + } + + [Fact] + public async Task SlicingArguments_First_Is_Variable_And_Last_Is_Null() + { + var result = + await new ServiceCollection() + .AddGraphQLServer() + .AddQueryType() + .ExecuteRequestAsync( + """ + query($first: Int = 1) { + foos(first: $first, last: null) { + nodes + } + } + """); + + result.MatchInlineSnapshot( + """ + { + "data": { + "foos": { + "nodes": [ + 1 + ] + } + } + } + """); + } + + [Fact] + public async Task SlicingArguments_First_Is_Variable_And_Last_Is_Variable() + { + var result = + await new ServiceCollection() + .AddGraphQLServer() + .AddQueryType() + .ExecuteRequestAsync( + """ + query($first: Int = 1, $last: Int = null) { + foos(first: $first, last: $last) { + nodes + } + } + """); + + result.MatchInlineSnapshot( + """ + { + "data": { + "foos": { + "nodes": [ + 1 + ] + } + } + } + """); + } + + public class Query + { + [UsePaging] + public IEnumerable GetFoos() => Enumerable.Range(1, 100); + } +} From b72edcec47f3b9a9657e905e2d6cf258fe7a13a7 Mon Sep 17 00:00:00 2001 From: Tobias Tengler <45513122+tobias-tengler@users.noreply.github.com> Date: Fri, 11 Oct 2024 13:28:56 +0200 Subject: [PATCH 062/154] [Fusion] Support type-system directives (#7585) --- .../Extensions/DirectiveLocationExtensions.cs | 4 + .../src/Abstractions/FusionTypeBaseNames.cs | 10 + .../src/Abstractions/FusionTypeNames.cs | 36 ++ .../Extensions/ComplexTypeMergeExtensions.cs | 6 + .../Extensions/InputObjectMergeExtensions.cs | 2 + .../Composition/Extensions/MergeExtensions.cs | 43 ++ .../src/Composition/FusionGraphComposer.cs | 4 +- .../Fusion/src/Composition/FusionTypes.cs | 5 + .../HotChocolate.Fusion.Composition.csproj | 1 + .../Fusion/src/Composition/LogEntryHelper.cs | 13 + .../Pipeline/MergeEntityMiddleware.cs | 7 +- .../MergeHandler/EnumTypeMergeHandler.cs | 4 + .../InputObjectTypeMergeHandler.cs | 2 + .../MergeHandler/InterfaceTypeMergeHandler.cs | 2 + .../MergeHandler/ScalarTypeMergeHandler.cs | 2 + .../MergeHandler/UnionTypeMergeHandler.cs | 2 + .../MergeQueryAndMutationTypeMiddleware.cs | 2 + .../MergeSchemaDefinitionMiddleware.cs | 19 + .../MergeSubscriptionTypeMiddleware.cs | 1 + .../Pipeline/MergeTypeMiddleware.cs | 77 ++++ .../Pipeline/PrepareFusionSchemaMiddleware.cs | 29 ++ ...moveDirectivesWithoutLocationMiddleware.cs | 25 ++ .../CompositionResources.Designer.cs | 6 + .../Properties/CompositionResources.resx | 3 + .../src/Core/HotChocolate.Fusion.csproj | 1 + .../test/Composition.Tests/DirectiveTests.cs | 369 ++++++++++++++++++ .../Fusion/test/Shared/TestSubgraph.cs | 2 +- .../test/Shared/TestSubgraphCollection.cs | 46 +-- .../BuiltIns/DeprecatedDirectiveDefinition.cs | 2 +- .../BuiltIns/IncludeDirectiveDefinition.cs | 2 +- .../BuiltIns/SkipDirectiveDefinition.cs | 2 +- .../src/Skimmed/DirectiveDefinition.cs | 10 +- .../Extensions/DirectiveLocationExtensions.cs | 8 + .../src/Skimmed/Serialization/SchemaParser.cs | 2 + 34 files changed, 712 insertions(+), 37 deletions(-) create mode 100644 src/HotChocolate/Fusion/src/Composition/Pipeline/MergeSchemaDefinitionMiddleware.cs create mode 100644 src/HotChocolate/Fusion/src/Composition/Pipeline/RemoveDirectivesWithoutLocationMiddleware.cs create mode 100644 src/HotChocolate/Fusion/test/Composition.Tests/DirectiveTests.cs diff --git a/src/HotChocolate/Core/src/Types/Types/Extensions/DirectiveLocationExtensions.cs b/src/HotChocolate/Core/src/Types/Types/Extensions/DirectiveLocationExtensions.cs index 73280594014..b8ff9401385 100644 --- a/src/HotChocolate/Core/src/Types/Types/Extensions/DirectiveLocationExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Types/Extensions/DirectiveLocationExtensions.cs @@ -36,6 +36,10 @@ public static class DirectiveLocationExtensions DirectiveLocation.InlineFragment, Language.DirectiveLocation.InlineFragment }, + { + DirectiveLocation.VariableDefinition, + Language.DirectiveLocation.VariableDefinition + }, { DirectiveLocation.Schema, Language.DirectiveLocation.Schema diff --git a/src/HotChocolate/Fusion/src/Abstractions/FusionTypeBaseNames.cs b/src/HotChocolate/Fusion/src/Abstractions/FusionTypeBaseNames.cs index 9fa373f0ec4..95ebdc1bc23 100644 --- a/src/HotChocolate/Fusion/src/Abstractions/FusionTypeBaseNames.cs +++ b/src/HotChocolate/Fusion/src/Abstractions/FusionTypeBaseNames.cs @@ -45,6 +45,16 @@ internal static class FusionTypeBaseNames /// public const string FusionDirective = "fusion"; + public const string InternalDirective = "internal"; + + public const string RenameDirective = "rename"; + + public const string RemoveDirective = "remove"; + + public const string LookupDirective = "lookup"; + + public const string RequireDirective = "require"; + /// /// The base name of the GraphQL selection directive. /// diff --git a/src/HotChocolate/Fusion/src/Abstractions/FusionTypeNames.cs b/src/HotChocolate/Fusion/src/Abstractions/FusionTypeNames.cs index d2fc162d6a3..4128297ddde 100644 --- a/src/HotChocolate/Fusion/src/Abstractions/FusionTypeNames.cs +++ b/src/HotChocolate/Fusion/src/Abstractions/FusionTypeNames.cs @@ -22,6 +22,11 @@ private FusionTypeNames( string reEncodeIdDirective, string transportDirective, string fusionDirective, + string internalDirective, + string renameDirective, + string removeDirective, + string lookupDirective, + string requireDirective, string selectionScalar, string selectionSetScalar, string typeNameScalar, @@ -39,6 +44,11 @@ private FusionTypeNames( ReEncodeIdDirective = reEncodeIdDirective; TransportDirective = transportDirective; FusionDirective = fusionDirective; + InternalDirective = internalDirective; + RenameDirective = renameDirective; + RemoveDirective = removeDirective; + LookupDirective = lookupDirective; + RequireDirective = requireDirective; SelectionScalar = selectionScalar; SelectionSetScalar = selectionSetScalar; TypeNameScalar = typeNameScalar; @@ -56,6 +66,12 @@ private FusionTypeNames( _fusionDirectives.Add(transportDirective); _fusionDirectives.Add(fusionDirective); + _fusionDirectives.Add(internalDirective); + _fusionDirectives.Add(renameDirective); + _fusionDirectives.Add(removeDirective); + _fusionDirectives.Add(lookupDirective); + _fusionDirectives.Add(requireDirective); + _fusionTypes.Add(selectionScalar); _fusionTypes.Add(selectionSetScalar); _fusionTypes.Add(typeNameScalar); @@ -110,6 +126,16 @@ private FusionTypeNames( /// public string FusionDirective { get; } + public string InternalDirective { get; } + + public string RenameDirective { get; } + + public string RemoveDirective { get; } + + public string LookupDirective { get; } + + public string RequireDirective { get; } + /// /// Gets the name of the GraphQL selection scalar. /// @@ -199,6 +225,11 @@ public static FusionTypeNames Create(string? prefix = null, bool prefixSelf = fa prefixSelf ? $"{prefix}_{FusionTypeBaseNames.FusionDirective}" : FusionTypeBaseNames.FusionDirective, + FusionTypeBaseNames.InternalDirective, + FusionTypeBaseNames.RenameDirective, + FusionTypeBaseNames.RemoveDirective, + FusionTypeBaseNames.LookupDirective, + FusionTypeBaseNames.RequireDirective, $"{prefix}_{FusionTypeBaseNames.Selection}", $"{prefix}_{FusionTypeBaseNames.SelectionSet}", $"{prefix}_{FusionTypeBaseNames.TypeName}", @@ -218,6 +249,11 @@ public static FusionTypeNames Create(string? prefix = null, bool prefixSelf = fa FusionTypeBaseNames.ReEncodeIdDirective, FusionTypeBaseNames.TransportDirective, FusionTypeBaseNames.FusionDirective, + FusionTypeBaseNames.InternalDirective, + FusionTypeBaseNames.RenameDirective, + FusionTypeBaseNames.RemoveDirective, + FusionTypeBaseNames.LookupDirective, + FusionTypeBaseNames.RequireDirective, $"_{FusionTypeBaseNames.Selection}", $"_{FusionTypeBaseNames.SelectionSet}", $"_{FusionTypeBaseNames.TypeName}", diff --git a/src/HotChocolate/Fusion/src/Composition/Extensions/ComplexTypeMergeExtensions.cs b/src/HotChocolate/Fusion/src/Composition/Extensions/ComplexTypeMergeExtensions.cs index f302601203e..9a2ff4430c2 100644 --- a/src/HotChocolate/Fusion/src/Composition/Extensions/ComplexTypeMergeExtensions.cs +++ b/src/HotChocolate/Fusion/src/Composition/Extensions/ComplexTypeMergeExtensions.cs @@ -15,6 +15,7 @@ public static OutputFieldDefinition CreateField( var target = new OutputFieldDefinition(source.Name); target.MergeDescriptionWith(source); target.MergeDeprecationWith(source); + target.MergeDirectivesWith(source, context); // Replace the type name of the field in the source with the corresponding type name // in the target schema. @@ -33,6 +34,7 @@ public static OutputFieldDefinition CreateField( targetArgument.Type = sourceArgument.Type.ReplaceNameType(n => targetSchema.Types[n]); targetArgument.MergeDeprecationWith(sourceArgument); + targetArgument.MergeDirectivesWith(sourceArgument, context); target.Arguments.Add(targetArgument); } @@ -126,6 +128,8 @@ public static void MergeField( // If the target field is not deprecated and the source field is deprecated, copy over the target.MergeDeprecationWith(source); + target.MergeDirectivesWith(source, context); + foreach (var sourceArgument in source.Arguments) { var targetArgument = target.Arguments[sourceArgument.Name]; @@ -137,6 +141,8 @@ public static void MergeField( // If the target argument is not deprecated and the source argument is deprecated, targetArgument.MergeDeprecationWith(sourceArgument); + targetArgument.MergeDirectivesWith(sourceArgument, context); + // If the target argument does not have a default value and the source argument does, if (sourceArgument.DefaultValue is not null && targetArgument.DefaultValue is null) diff --git a/src/HotChocolate/Fusion/src/Composition/Extensions/InputObjectMergeExtensions.cs b/src/HotChocolate/Fusion/src/Composition/Extensions/InputObjectMergeExtensions.cs index 0401434b8c7..80ef9e0ba49 100644 --- a/src/HotChocolate/Fusion/src/Composition/Extensions/InputObjectMergeExtensions.cs +++ b/src/HotChocolate/Fusion/src/Composition/Extensions/InputObjectMergeExtensions.cs @@ -20,6 +20,7 @@ public static InputFieldDefinition CreateField( var target = new InputFieldDefinition(source.Name, targetFieldType); target.MergeDescriptionWith(source); target.MergeDeprecationWith(source); + target.MergeDirectivesWith(source, context); target.DefaultValue = source.DefaultValue; return target; } @@ -55,5 +56,6 @@ public static void MergeField( target.MergeDescriptionWith(source); target.MergeDeprecationWith(source); + target.MergeDirectivesWith(source, context); } } diff --git a/src/HotChocolate/Fusion/src/Composition/Extensions/MergeExtensions.cs b/src/HotChocolate/Fusion/src/Composition/Extensions/MergeExtensions.cs index a269c77bfaa..c3d060f28d9 100644 --- a/src/HotChocolate/Fusion/src/Composition/Extensions/MergeExtensions.cs +++ b/src/HotChocolate/Fusion/src/Composition/Extensions/MergeExtensions.cs @@ -165,6 +165,49 @@ internal static void MergeDescriptionWith(this INamedTypeDefinition target, INam } } + internal static void MergeDirectivesWith( + this IDirectivesProvider target, + IDirectivesProvider source, + CompositionContext context) + { + foreach (var directive in source.Directives) + { + if (context.FusionTypes.IsFusionDirective(directive.Name) + || BuiltIns.IsBuiltInDirective(directive.Name) + // @tag is handled separately + || directive.Name == "tag") + { + continue; + } + + if (context.FusionGraph.DirectiveDefinitions.TryGetDirective(directive.Name, out var directiveDefinition) + && directiveDefinition.IsSpecDirective) + { + continue; + } + + if (!target.Directives.ContainsName(directive.Name)) + { + target.Directives.Add(directive); + } + else + { + if (directiveDefinition is not null && directiveDefinition.IsRepeatable) + { + target.Directives.Add(directive); + } + } + } + } + + internal static void MergeDescriptionWith(this DirectiveDefinition target, DirectiveDefinition source) + { + if (string.IsNullOrWhiteSpace(target.Description) && !string.IsNullOrWhiteSpace(source.Description)) + { + target.Description = source.Description; + } + } + internal static void MergeDescriptionWith(this EnumValue target, EnumValue source) { if (string.IsNullOrWhiteSpace(target.Description) && !string.IsNullOrWhiteSpace(source.Description)) diff --git a/src/HotChocolate/Fusion/src/Composition/FusionGraphComposer.cs b/src/HotChocolate/Fusion/src/Composition/FusionGraphComposer.cs index 535b6e3e0c9..4d87c747ab5 100644 --- a/src/HotChocolate/Fusion/src/Composition/FusionGraphComposer.cs +++ b/src/HotChocolate/Fusion/src/Composition/FusionGraphComposer.cs @@ -42,7 +42,7 @@ public FusionGraphComposer( [ new InterfaceTypeMergeHandler(), new UnionTypeMergeHandler(), new InputObjectTypeMergeHandler(), new EnumTypeMergeHandler(), - new ScalarTypeMergeHandler(), + new ScalarTypeMergeHandler() ], fusionTypePrefix, fusionTypeSelf, @@ -68,7 +68,9 @@ internal FusionGraphComposer( .Use() .Use() .Use(() => new MergeTypeMiddleware(mergeHandlers)) + .Use() .Use() + .Use() .Use() .Use() .Use() diff --git a/src/HotChocolate/Fusion/src/Composition/FusionTypes.cs b/src/HotChocolate/Fusion/src/Composition/FusionTypes.cs index 2e72b04979a..6d0ec753c34 100644 --- a/src/HotChocolate/Fusion/src/Composition/FusionTypes.cs +++ b/src/HotChocolate/Fusion/src/Composition/FusionTypes.cs @@ -1,3 +1,4 @@ +using System.Collections.Frozen; using HotChocolate.Fusion.Composition.Properties; using HotChocolate.Language; using HotChocolate.Skimmed; @@ -13,6 +14,7 @@ public sealed class FusionTypes { private readonly SchemaDefinition _fusionGraph; private readonly bool _prefixSelf; + private readonly FusionTypeNames _fusionTypeNames; public FusionTypes(SchemaDefinition fusionGraph, string? prefix = null, bool prefixSelf = false) { @@ -22,6 +24,7 @@ public FusionTypes(SchemaDefinition fusionGraph, string? prefix = null, bool pre } var names = FusionTypeNames.Create(prefix, prefixSelf); + _fusionTypeNames = names; _fusionGraph = fusionGraph; _prefixSelf = prefixSelf; @@ -120,6 +123,8 @@ public FusionTypes(SchemaDefinition fusionGraph, string? prefix = null, bool pre public DirectiveDefinition Fusion { get; } + public bool IsFusionDirective(string directiveName) => _fusionTypeNames.IsFusionDirective(directiveName); + private ScalarTypeDefinition RegisterScalarType(string name) { var scalarType = new ScalarTypeDefinition(name); diff --git a/src/HotChocolate/Fusion/src/Composition/HotChocolate.Fusion.Composition.csproj b/src/HotChocolate/Fusion/src/Composition/HotChocolate.Fusion.Composition.csproj index 510aa801f92..3b776133b88 100644 --- a/src/HotChocolate/Fusion/src/Composition/HotChocolate.Fusion.Composition.csproj +++ b/src/HotChocolate/Fusion/src/Composition/HotChocolate.Fusion.Composition.csproj @@ -11,6 +11,7 @@ + diff --git a/src/HotChocolate/Fusion/src/Composition/LogEntryHelper.cs b/src/HotChocolate/Fusion/src/Composition/LogEntryHelper.cs index 4e9a40b7936..93523bb8d5d 100644 --- a/src/HotChocolate/Fusion/src/Composition/LogEntryHelper.cs +++ b/src/HotChocolate/Fusion/src/Composition/LogEntryHelper.cs @@ -86,6 +86,16 @@ public static LogEntry OutputFieldArgumentMismatch( coordinate: coordinate, member: field); + public static LogEntry DirectiveDefinitionArgumentMismatch( + SchemaCoordinate coordinate, + DirectiveDefinition directiveDefinition) + => new LogEntry( + LogEntryHelper_DirectiveDefinitionArgumentMismatch, + code: LogEntryCodes.DirectiveDefinitionArgumentMismatch, + severity: LogSeverity.Error, + coordinate: coordinate, + member: directiveDefinition); + public static LogEntry OutputFieldArgumentSetMismatch( SchemaCoordinate coordinate, OutputFieldDefinition field, @@ -193,5 +203,8 @@ static file class LogEntryCodes public const string FieldDependencyCannotBeResolved = "HF0008"; public const string TypeNotDeclared = "HF0009"; + public const string RootNameMismatch = "HF0010"; + + public const string DirectiveDefinitionArgumentMismatch = "HF0011"; } diff --git a/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeEntityMiddleware.cs b/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeEntityMiddleware.cs index e81a28843a3..2f91f0ccd5d 100644 --- a/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeEntityMiddleware.cs +++ b/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeEntityMiddleware.cs @@ -34,10 +34,9 @@ public static void Merge(this CompositionContext context, EntityPart source, Obj { context.TryApplySource(source.Type, source.Schema, target); - if (string.IsNullOrEmpty(target.Description)) - { - target.Description = source.Type.Description; - } + target.MergeDescriptionWith(source.Type); + + target.MergeDirectivesWith(source.Type, context); foreach (var interfaceType in source.Type.Implements) { diff --git a/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeHandler/EnumTypeMergeHandler.cs b/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeHandler/EnumTypeMergeHandler.cs index 210452bd91d..465eabebeee 100644 --- a/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeHandler/EnumTypeMergeHandler.cs +++ b/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeHandler/EnumTypeMergeHandler.cs @@ -47,6 +47,8 @@ private static void MergeType( // description target.MergeDescriptionWith(source); + target.MergeDirectivesWith(source, context); + // Merge each value of the enum type foreach (var sourceValue in source.Values) { @@ -64,6 +66,8 @@ private static void MergeType( // value's deprecation reason targetValue.MergeDeprecationWith(sourceValue); + targetValue.MergeDirectivesWith(sourceValue, context); + // Apply the source value to the target value context.ApplySource(sourceValue, sourceSchema, targetValue); } diff --git a/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeHandler/InputObjectTypeMergeHandler.cs b/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeHandler/InputObjectTypeMergeHandler.cs index f6b0b4c163b..5a493f6f5b2 100644 --- a/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeHandler/InputObjectTypeMergeHandler.cs +++ b/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeHandler/InputObjectTypeMergeHandler.cs @@ -47,6 +47,8 @@ private static void MergeType( // If the target input object type doesn't have a description, use the source input // object type's description target.MergeDescriptionWith(source); + + target.MergeDirectivesWith(source, context); // Merge each field of the input object type foreach (var sourceField in source.Fields) diff --git a/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeHandler/InterfaceTypeMergeHandler.cs b/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeHandler/InterfaceTypeMergeHandler.cs index 9f5e5a207c4..c8251e5cdbb 100644 --- a/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeHandler/InterfaceTypeMergeHandler.cs +++ b/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeHandler/InterfaceTypeMergeHandler.cs @@ -47,6 +47,8 @@ private static void MergeType( // If the target type does not have a description, use the source type's description. target.MergeDescriptionWith(source); + target.MergeDirectivesWith(source, context); + // Add all of the interfaces that the source type implements to the target type. foreach (var interfaceType in source.Implements) { diff --git a/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeHandler/ScalarTypeMergeHandler.cs b/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeHandler/ScalarTypeMergeHandler.cs index 352e0c3bcac..f20eda146f3 100644 --- a/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeHandler/ScalarTypeMergeHandler.cs +++ b/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeHandler/ScalarTypeMergeHandler.cs @@ -32,6 +32,8 @@ public ValueTask MergeAsync( // Try to apply the source scalar type to the target scalar type. context.TryApplySource(source, part.Schema, target); + target.MergeDirectivesWith(source, context); + // If the target scalar type has no description, // set it to the source scalar type's description. target.MergeDescriptionWith(source); diff --git a/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeHandler/UnionTypeMergeHandler.cs b/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeHandler/UnionTypeMergeHandler.cs index 3b6c8691275..6095b2bb309 100644 --- a/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeHandler/UnionTypeMergeHandler.cs +++ b/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeHandler/UnionTypeMergeHandler.cs @@ -41,6 +41,8 @@ private static void MergeType( { context.TryApplySource(source, sourceSchema, target); + target.MergeDirectivesWith(source, context); + target.MergeDescriptionWith(source); foreach (var sourceType in source.Types) diff --git a/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeQueryAndMutationTypeMiddleware.cs b/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeQueryAndMutationTypeMiddleware.cs index 7fe1051e26a..d45a45a28cc 100644 --- a/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeQueryAndMutationTypeMiddleware.cs +++ b/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeQueryAndMutationTypeMiddleware.cs @@ -32,6 +32,7 @@ public async ValueTask InvokeAsync(CompositionContext context, MergeDelegate nex { targetType = context.FusionGraph.QueryType = new ObjectTypeDefinition(schema.QueryType.Name); targetType.MergeDescriptionWith(schema.QueryType); + targetType.MergeDirectivesWith(schema.QueryType, context); } MergeRootFields( @@ -51,6 +52,7 @@ public async ValueTask InvokeAsync(CompositionContext context, MergeDelegate nex { targetType = context.FusionGraph.MutationType = new ObjectTypeDefinition(schema.MutationType.Name); targetType.MergeDescriptionWith(schema.MutationType); + targetType.MergeDirectivesWith(schema.MutationType, context); context.FusionGraph.Types.Add(targetType); } diff --git a/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeSchemaDefinitionMiddleware.cs b/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeSchemaDefinitionMiddleware.cs new file mode 100644 index 00000000000..8428a39f116 --- /dev/null +++ b/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeSchemaDefinitionMiddleware.cs @@ -0,0 +1,19 @@ +namespace HotChocolate.Fusion.Composition.Pipeline; + +internal sealed class MergeSchemaDefinitionMiddleware : IMergeMiddleware +{ + public async ValueTask InvokeAsync(CompositionContext context, MergeDelegate next) + { + var target = context.FusionGraph; + + foreach (var schema in context.Subgraphs) + { + target.MergeDirectivesWith(schema, context); + } + + if (!context.Log.HasErrors) + { + await next(context).ConfigureAwait(false); + } + } +} diff --git a/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeSubscriptionTypeMiddleware.cs b/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeSubscriptionTypeMiddleware.cs index b86b1172ecd..9a67ac2a549 100644 --- a/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeSubscriptionTypeMiddleware.cs +++ b/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeSubscriptionTypeMiddleware.cs @@ -21,6 +21,7 @@ public async ValueTask InvokeAsync(CompositionContext context, MergeDelegate nex { subscriptionType = context.FusionGraph.SubscriptionType = new ObjectTypeDefinition(schema.SubscriptionType.Name); subscriptionType.MergeDescriptionWith(schema.SubscriptionType); + subscriptionType.MergeDirectivesWith(schema.SubscriptionType, context); context.FusionGraph.Types.Add(subscriptionType); } diff --git a/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeTypeMiddleware.cs b/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeTypeMiddleware.cs index 6c5cf2882c6..6f143cbe935 100644 --- a/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeTypeMiddleware.cs +++ b/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeTypeMiddleware.cs @@ -1,3 +1,4 @@ +using HotChocolate.Skimmed; using HotChocolate.Types; namespace HotChocolate.Fusion.Composition.Pipeline; @@ -19,6 +20,7 @@ public MergeTypeMiddleware(IEnumerable mergeHandlers) public async ValueTask InvokeAsync(CompositionContext context, MergeDelegate next) { var groupedTypes = new Dictionary>(); + var groupedDirectives = new Dictionary>(); foreach (var schema in context.Subgraphs) { @@ -31,6 +33,36 @@ public async ValueTask InvokeAsync(CompositionContext context, MergeDelegate nex } types.Add(new TypePart(type, schema)); } + + foreach (var directiveDefinition in schema.DirectiveDefinitions) + { + if (!groupedDirectives.TryGetValue(directiveDefinition.Name, out var directiveDefinitions)) + { + directiveDefinitions = []; + groupedDirectives.Add(directiveDefinition.Name, directiveDefinitions); + } + directiveDefinitions.Add(directiveDefinition); + } + } + + foreach (var (directiveName, directiveDefinitions) in groupedDirectives) + { + if (context.FusionTypes.IsFusionDirective(directiveName) + || BuiltIns.IsBuiltInDirective(directiveName) + // @tag is handled separately + || directiveName == "tag") + { + continue; + } + + var target = context.FusionGraph.DirectiveDefinitions[directiveName]; + + foreach (var directiveDefinition in directiveDefinitions) + { + var source = directiveDefinition; + + MergeDirectiveDefinition(source, target, context); + } } foreach (var types in groupedTypes) @@ -70,4 +102,49 @@ public async ValueTask InvokeAsync(CompositionContext context, MergeDelegate nex await next(context).ConfigureAwait(false); } } + + private static void MergeDirectiveDefinition( + DirectiveDefinition source, + DirectiveDefinition target, + CompositionContext context) + { + if (!target.IsRepeatable) + { + target.IsRepeatable = source.IsRepeatable; + } + + foreach (var sourceArgument in source.Arguments) + { + if (!target.Arguments.TryGetField(sourceArgument.Name, out var targetArgument)) + { + context.Log.Write(LogEntryHelper.DirectiveDefinitionArgumentMismatch(new SchemaCoordinate(source.Name), source)); + return; + } + + if (!sourceArgument.Type.Equals(targetArgument.Type, TypeComparison.Structural)) + { + context.Log.Write(LogEntryHelper.DirectiveDefinitionArgumentMismatch(new SchemaCoordinate(source.Name), source)); + return; + } + } + + // Directive definitions without a location will be removed by RemoveDirectivesWithoutLocationMiddleware + // in a later stage. + target.Locations = RemoveExecutableLocations(source.Locations & target.Locations); + + target.MergeDescriptionWith(source); + } + + private static DirectiveLocation RemoveExecutableLocations(DirectiveLocation location) + { + return location + & ~DirectiveLocation.Query + & ~DirectiveLocation.Mutation + & ~DirectiveLocation.Subscription + & ~DirectiveLocation.Field + & ~DirectiveLocation.FragmentDefinition + & ~DirectiveLocation.FragmentSpread + & ~DirectiveLocation.InlineFragment + & ~DirectiveLocation.VariableDefinition; + } } diff --git a/src/HotChocolate/Fusion/src/Composition/Pipeline/PrepareFusionSchemaMiddleware.cs b/src/HotChocolate/Fusion/src/Composition/Pipeline/PrepareFusionSchemaMiddleware.cs index 1c806cc12ee..7f2db96264b 100644 --- a/src/HotChocolate/Fusion/src/Composition/Pipeline/PrepareFusionSchemaMiddleware.cs +++ b/src/HotChocolate/Fusion/src/Composition/Pipeline/PrepareFusionSchemaMiddleware.cs @@ -46,6 +46,35 @@ public async ValueTask InvokeAsync(CompositionContext context, MergeDelegate nex } } } + + foreach (var directiveDefinition in schema.DirectiveDefinitions) + { + if (context.FusionTypes.IsFusionDirective(directiveDefinition.Name) + // @tag is handled separately + || directiveDefinition.Name == "tag") + { + continue; + } + + if (context.FusionGraph.DirectiveDefinitions.ContainsName(directiveDefinition.Name)) + { + continue; + } + + var initialDirectiveDefinition = new DirectiveDefinition(directiveDefinition.Name) + { + Locations = directiveDefinition.Locations, + IsRepeatable = directiveDefinition.IsRepeatable, + IsSpecDirective = directiveDefinition.IsSpecDirective + }; + + foreach (var argument in directiveDefinition.Arguments) + { + initialDirectiveDefinition.Arguments.Add(context.CreateField(argument, schema)); + } + + context.FusionGraph.DirectiveDefinitions.Add(initialDirectiveDefinition); + } } await next(context); diff --git a/src/HotChocolate/Fusion/src/Composition/Pipeline/RemoveDirectivesWithoutLocationMiddleware.cs b/src/HotChocolate/Fusion/src/Composition/Pipeline/RemoveDirectivesWithoutLocationMiddleware.cs new file mode 100644 index 00000000000..57a4fb52cea --- /dev/null +++ b/src/HotChocolate/Fusion/src/Composition/Pipeline/RemoveDirectivesWithoutLocationMiddleware.cs @@ -0,0 +1,25 @@ +namespace HotChocolate.Fusion.Composition.Pipeline; + +internal sealed class RemoveDirectivesWithoutLocationMiddleware : IMergeMiddleware +{ + public async ValueTask InvokeAsync(CompositionContext context, MergeDelegate next) + { + foreach(var directive in context.FusionGraph.DirectiveDefinitions) + { + if (context.FusionTypes.IsFusionDirective(directive.Name)) + { + continue; + } + + if (directive.Locations == 0) + { + context.FusionGraph.DirectiveDefinitions.Remove(directive); + } + } + + if (!context.Log.HasErrors) + { + await next(context).ConfigureAwait(false); + } + } +} diff --git a/src/HotChocolate/Fusion/src/Composition/Properties/CompositionResources.Designer.cs b/src/HotChocolate/Fusion/src/Composition/Properties/CompositionResources.Designer.cs index 89e9bd9fce3..ed5a400c280 100644 --- a/src/HotChocolate/Fusion/src/Composition/Properties/CompositionResources.Designer.cs +++ b/src/HotChocolate/Fusion/src/Composition/Properties/CompositionResources.Designer.cs @@ -146,5 +146,11 @@ internal static string LogEntryHelper_RootTypeNameMismatch { return ResourceManager.GetString("LogEntryHelper_RootTypeNameMismatch", resourceCulture); } } + + internal static string LogEntryHelper_DirectiveDefinitionArgumentMismatch { + get { + return ResourceManager.GetString("LogEntryHelper_DirectiveDefinitionArgumentMismatch", resourceCulture); + } + } } } diff --git a/src/HotChocolate/Fusion/src/Composition/Properties/CompositionResources.resx b/src/HotChocolate/Fusion/src/Composition/Properties/CompositionResources.resx index 27a5cfb7115..3f965f13478 100644 --- a/src/HotChocolate/Fusion/src/Composition/Properties/CompositionResources.resx +++ b/src/HotChocolate/Fusion/src/Composition/Properties/CompositionResources.resx @@ -72,4 +72,7 @@ Source Arguments: {2} The {0} type name `{1}` does not match to the {0} type name `{2}` on subgraph `{3}`. Make sure that the root type names on the various subgraph match. + + The number of arguments in a directive definition does not match the number of arguments in the same directive definition in another subgraph schema. + diff --git a/src/HotChocolate/Fusion/src/Core/HotChocolate.Fusion.csproj b/src/HotChocolate/Fusion/src/Core/HotChocolate.Fusion.csproj index f2d956febe2..b5e5d259a77 100644 --- a/src/HotChocolate/Fusion/src/Core/HotChocolate.Fusion.csproj +++ b/src/HotChocolate/Fusion/src/Core/HotChocolate.Fusion.csproj @@ -10,6 +10,7 @@ + diff --git a/src/HotChocolate/Fusion/test/Composition.Tests/DirectiveTests.cs b/src/HotChocolate/Fusion/test/Composition.Tests/DirectiveTests.cs new file mode 100644 index 00000000000..d873c094378 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Composition.Tests/DirectiveTests.cs @@ -0,0 +1,369 @@ +using CookieCrumble; +using HotChocolate.Fusion.Metadata; +using HotChocolate.Fusion.Shared; +using HotChocolate.Language; +using HotChocolate.Skimmed; +using HotChocolate.Skimmed.Serialization; +using HotChocolate.Types; +using Microsoft.Extensions.DependencyInjection; +using Xunit.Abstractions; + +namespace HotChocolate.Fusion.Composition; + +public class DirectiveTests(ITestOutputHelper output) +{ + private readonly Func _logFactory = () => new TestCompositionLog(output); + + [Fact] + public async Task Expose_TypeSystem_Directives_In_Fusion_Graph() + { + var schemaText = """ + schema @test { + query: Query + mutation: Mutation + subscription: Subscription + } + + type Query @test { + enumField: TestEnum + objectField(input: TestInput @test): TestOutput + scalarField: Test @test + } + + type Mutation @test { + mutationField(input: TestInput @test): String @test + } + + type Subscription @test { + subscriptionField(input: TestInput @test): String @test + } + + type TestOutput implements TestInterface @test { + field: Int @test + } + + interface TestInterface @test { + field: Int @test + } + + union TestUnion @test = TestOutput + + input TestInput @test { + inputField: Int @test + } + + enum TestEnum @test { + ENUM_VALUE @test + } + + scalar Test @test + + "A test directive" + directive @test(arg: String = "test") repeatable on SCHEMA | SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION + """; + + // arrange + var subgraph = await TestSubgraph.CreateAsync( + builder => builder + .AddDocumentFromString(schemaText) + .AddType(new AnyType("Test")) + .AddResolverMocking()); + + using var subgraphs = new TestSubgraphCollection(output, [subgraph]); + + // act + var fusionGraph = await subgraphs.GetFusionGraphAsync(); + + // assert + GetSchemaWithoutFusion(fusionGraph).MatchInlineSnapshot(schemaText); + } + + [Fact] + public async Task Remove_Executable_Locations_From_Directive_Definition() + { + // arrange + var subgraph = await TestSubgraph.CreateAsync( + """ + type Query { + field: String @test + } + + "A test directive" + directive @test on SCHEMA | SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION | QUERY | MUTATION | SUBSCRIPTION | FIELD | FRAGMENT_DEFINITION | FRAGMENT_SPREAD | INLINE_FRAGMENT | VARIABLE_DEFINITION + """); + + using var subgraphs = new TestSubgraphCollection(output, [subgraph]); + + // act + var fusionGraph = await subgraphs.GetFusionGraphAsync(); + + // assert + GetSchemaWithoutFusion(fusionGraph).MatchInlineSnapshot( + """ + schema { + query: Query + } + + type Query { + field: String @test + } + + "A test directive" + directive @test on SCHEMA | SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | INTERFACE | UNION | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION + """); + } + + [Fact] + public async Task Ignore_Purely_Executable_Directive_Definitions() + { + // arrange + var subgraph = await TestSubgraph.CreateAsync( + """ + type Query { + field: String + } + + "A test directive" + directive @test on QUERY | MUTATION | SUBSCRIPTION | FIELD | FRAGMENT_DEFINITION | FRAGMENT_SPREAD | INLINE_FRAGMENT | VARIABLE_DEFINITION + """); + + using var subgraphs = new TestSubgraphCollection(output, [subgraph]); + + // act + var fusionGraph = await subgraphs.GetFusionGraphAsync(); + + // assert + GetSchemaWithoutFusion(fusionGraph).MatchInlineSnapshot( + """ + schema { + query: Query + } + + type Query { + field: String + } + """); + } + + [Fact] + public async Task Merge_Directive_Definitions_With_Same_Arguments() + { + // arrange + var subgraphA = await TestSubgraph.CreateAsync( + """ + type Query { + field1: String @test(arg: "test") + } + + directive @test(arg: [String!]!) on FIELD_DEFINITION + """); + + var subgraphB = await TestSubgraph.CreateAsync( + """ + type Query { + field2: String @test(arg: "test") + } + + directive @test(arg: [String!]!) on FIELD_DEFINITION + """); + + using var subgraphs = new TestSubgraphCollection(output, [subgraphA, subgraphB]); + + // act + var fusionGraph = await subgraphs.GetFusionGraphAsync(); + + // assert + GetSchemaWithoutFusion(fusionGraph).MatchInlineSnapshot( + """ + schema { + query: Query + } + + type Query { + field1: String @test(arg: "test") + field2: String @test(arg: "test") + } + + directive @test(arg: [String!]!) on FIELD_DEFINITION + """); + } + + [Fact] + public async Task Throw_On_Directive_Definition_Argument_Name_Mismatch() + { + // arrange + var subgraphA = await TestSubgraph.CreateAsync( + """ + type Query { + field1: String @test(arg1: "test") + } + + directive @test(arg1: String) on FIELD_DEFINITION + """); + + var subgraphB = await TestSubgraph.CreateAsync( + """ + type Query { + field2: String @test(arg2: "test") + } + + directive @test(arg2: String) on FIELD_DEFINITION + """); + + var subgraphs = new TestSubgraphCollection(output, [subgraphA, subgraphB]); + + // act + var act = () => subgraphs.GetFusionGraphAsync(); + + // assert + await Assert.ThrowsAsync(act); + } + + [Fact] + public async Task Throw_On_Directive_Argument_Type_Mismatch() + { + // arrange + var subgraphA = await TestSubgraph.CreateAsync( + """ + type Query { + field1: String @test(arg: 1) + } + + directive @test(arg: Int) on FIELD_DEFINITION + """); + + var subgraphB = await TestSubgraph.CreateAsync( + """ + type Query { + field2: String @test(arg: "test") + } + + directive @test(arg: String) on FIELD_DEFINITION + """); + + var subgraphs = new TestSubgraphCollection(output, [subgraphA, subgraphB]); + + // act + var act = () => subgraphs.GetFusionGraphAsync(); + + // assert + await Assert.ThrowsAsync(act); + } + + [Fact] + public async Task Merge_Non_Repeatable_Directive_On_Same_Field() + { + // arrange + var subgraphA = await TestSubgraph.CreateAsync( + """ + type Query { + foo: SubType @test + } + + type SubType { + bar: String @test + } + + directive @test on FIELD_DEFINITION + """); + + var subgraphB = await TestSubgraph.CreateAsync( + """ + type Query { + foo: SubType @test + } + + type SubType { + bar: String @test + } + + directive @test on FIELD_DEFINITION + """); + + using var subgraphs = new TestSubgraphCollection(output, [subgraphA, subgraphB]); + + // act + var fusionGraph = await subgraphs.GetFusionGraphAsync(); + + // assert + GetSchemaWithoutFusion(fusionGraph).MatchInlineSnapshot( + """ + schema { + query: Query + } + + type Query { + foo: SubType @test + } + + type SubType { + bar: String @test + } + + directive @test on FIELD_DEFINITION + """); + } + + [Fact] + public async Task Repeat_Repeatable_Directive_On_Same_Field() + { + // arrange + var subgraphA = await TestSubgraph.CreateAsync( + """ + type Query { + foo: SubType @test(arg: "A") + } + + type SubType { + bar: String @test(arg: "A") + } + + directive @test(arg: String) repeatable on FIELD_DEFINITION + """); + + var subgraphB = await TestSubgraph.CreateAsync( + """ + type Query { + foo: SubType @test(arg: "B") + } + + type SubType { + bar: String @test(arg: "B") + } + + directive @test(arg: String) repeatable on FIELD_DEFINITION + """); + + using var subgraphs = new TestSubgraphCollection(output, [subgraphA, subgraphB]); + + // act + var fusionGraph = await subgraphs.GetFusionGraphAsync(); + + // assert + GetSchemaWithoutFusion(fusionGraph).MatchInlineSnapshot( + """ + schema { + query: Query + } + + type Query { + foo: SubType @test(arg: "A") @test(arg: "B") + } + + type SubType { + bar: String @test(arg: "A") @test(arg: "B") + } + + directive @test(arg: String) repeatable on FIELD_DEFINITION + """); + } + + private static DocumentNode GetSchemaWithoutFusion(SchemaDefinition fusionGraph) + { + var fusionGraphDoc = Utf8GraphQLParser.Parse(SchemaFormatter.FormatAsString(fusionGraph)); + var typeNames = FusionTypeNames.From(fusionGraphDoc); + var rewriter = new FusionGraphConfigurationToSchemaRewriter(); + + return (DocumentNode)rewriter.Rewrite(fusionGraphDoc, new(typeNames))!; + } +} diff --git a/src/HotChocolate/Fusion/test/Shared/TestSubgraph.cs b/src/HotChocolate/Fusion/test/Shared/TestSubgraph.cs index 0d697df1de1..cd84c8bdcd9 100644 --- a/src/HotChocolate/Fusion/test/Shared/TestSubgraph.cs +++ b/src/HotChocolate/Fusion/test/Shared/TestSubgraph.cs @@ -37,7 +37,7 @@ public static async Task CreateAsync( { var builder = services .AddRouting() - .AddGraphQLServer(); + .AddGraphQLServer(disableDefaultSecurity: true); configureBuilder(builder); }, diff --git a/src/HotChocolate/Fusion/test/Shared/TestSubgraphCollection.cs b/src/HotChocolate/Fusion/test/Shared/TestSubgraphCollection.cs index ce575f58348..da9f8cd6229 100644 --- a/src/HotChocolate/Fusion/test/Shared/TestSubgraphCollection.cs +++ b/src/HotChocolate/Fusion/test/Shared/TestSubgraphCollection.cs @@ -23,11 +23,33 @@ public async Task GetExecutorAsync( FusionFeatureCollection? features = null, Action? configureBuilder = null) { - var fusionGraph = await ComposeFusionGraphAsync(features); + var fusionGraph = await GetFusionGraphAsync(features); return await GetExecutorAsync(fusionGraph, configureBuilder); } + public async Task GetFusionGraphAsync(FusionFeatureCollection? features = null) + { + features ??= new FusionFeatureCollection(FusionFeatures.NodeField); + + var configurations = GetSubgraphs() + .Select(s => + { + return new SubgraphConfiguration( + s.SubgraphName, + s.Subgraph.Schema.ToString(), + s.Subgraph.SchemaExtensions, + new IClientConfiguration[] + { + new HttpClientConfiguration(new Uri("http://localhost:5000/graphql")), + }, + null); + }); + + return await new FusionGraphComposer(logFactory: () => new TestCompositionLog(outputHelper)) + .ComposeAsync(configurations, features); + } + public void Dispose() { foreach (var subgraph in subgraphs) @@ -52,28 +74,6 @@ private async Task GetExecutorAsync( return await builder.BuildRequestExecutorAsync(); } - private async Task ComposeFusionGraphAsync(FusionFeatureCollection? features = null) - { - features ??= new FusionFeatureCollection(FusionFeatures.NodeField); - - var configurations = GetSubgraphs() - .Select(s => - { - return new SubgraphConfiguration( - s.SubgraphName, - s.Subgraph.Schema.ToString(), - s.Subgraph.SchemaExtensions, - new IClientConfiguration[] - { - new HttpClientConfiguration(new Uri("http://localhost:5000/graphql")), - }, - null); - }); - - return await new FusionGraphComposer(logFactory: () => new TestCompositionLog(outputHelper)) - .ComposeAsync(configurations, features); - } - private IEnumerable<(string SubgraphName, TestSubgraph Subgraph)> GetSubgraphs() => subgraphs.Select((s, i) => ($"Subgraph_{++i}", s)); diff --git a/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/DeprecatedDirectiveDefinition.cs b/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/DeprecatedDirectiveDefinition.cs index 64669436e20..136b296c024 100644 --- a/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/DeprecatedDirectiveDefinition.cs +++ b/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/DeprecatedDirectiveDefinition.cs @@ -5,7 +5,7 @@ public sealed class DeprecatedDirectiveDefinition : DirectiveDefinition internal DeprecatedDirectiveDefinition(StringTypeDefinition stringType) : base(BuiltIns.Deprecated.Name) { - IsSpecScalar = true; + IsSpecDirective = true; Arguments.Add(new InputFieldDefinition(BuiltIns.Deprecated.Reason, stringType)); } diff --git a/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/IncludeDirectiveDefinition.cs b/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/IncludeDirectiveDefinition.cs index 784925925d5..597b418f769 100644 --- a/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/IncludeDirectiveDefinition.cs +++ b/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/IncludeDirectiveDefinition.cs @@ -5,7 +5,7 @@ public sealed class IncludeDirectiveDefinition : DirectiveDefinition internal IncludeDirectiveDefinition(BooleanTypeDefinition booleanType) : base(BuiltIns.Include.Name) { - IsSpecScalar = true; + IsSpecDirective = true; Arguments.Add(new InputFieldDefinition(BuiltIns.Include.If, booleanType)); } diff --git a/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/SkipDirectiveDefinition.cs b/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/SkipDirectiveDefinition.cs index 4b7f43daa3d..72dbbe528aa 100644 --- a/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/SkipDirectiveDefinition.cs +++ b/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/SkipDirectiveDefinition.cs @@ -5,7 +5,7 @@ public sealed class SkipDirectiveDefinition : DirectiveDefinition internal SkipDirectiveDefinition(BooleanTypeDefinition booleanType) : base(BuiltIns.Skip.Name) { - IsSpecScalar = true; + IsSpecDirective = true; Arguments.Add(new InputFieldDefinition(BuiltIns.Skip.If, booleanType)); } diff --git a/src/HotChocolate/Skimmed/src/Skimmed/DirectiveDefinition.cs b/src/HotChocolate/Skimmed/src/Skimmed/DirectiveDefinition.cs index a63a3b5a24b..604c28c0edb 100644 --- a/src/HotChocolate/Skimmed/src/Skimmed/DirectiveDefinition.cs +++ b/src/HotChocolate/Skimmed/src/Skimmed/DirectiveDefinition.cs @@ -18,7 +18,7 @@ public class DirectiveDefinition(string name) private IInputFieldDefinitionCollection? _arguments; private IFeatureCollection? _features; private string? _description; - private bool _isSpecScalar; + private bool _isSpecDirective; private bool _isRepeatable; private DirectiveLocation _locations; private bool _isReadOnly; @@ -66,11 +66,11 @@ public string? Description } /// - /// Gets or sets a value indicating whether this scalar type is a spec scalar. + /// Gets or sets a value indicating whether this directive type is a spec directive. /// - public bool IsSpecScalar + public bool IsSpecDirective { - get => _isSpecScalar; + get => _isSpecDirective; set { if (_isReadOnly) @@ -79,7 +79,7 @@ public bool IsSpecScalar "The directive is sealed and cannot be modified."); } - _isSpecScalar = value; + _isSpecDirective = value; } } diff --git a/src/HotChocolate/Skimmed/src/Skimmed/Extensions/DirectiveLocationExtensions.cs b/src/HotChocolate/Skimmed/src/Skimmed/Extensions/DirectiveLocationExtensions.cs index b194b3d8ba3..6adfed32995 100644 --- a/src/HotChocolate/Skimmed/src/Skimmed/Extensions/DirectiveLocationExtensions.cs +++ b/src/HotChocolate/Skimmed/src/Skimmed/Extensions/DirectiveLocationExtensions.cs @@ -36,6 +36,10 @@ internal static class DirectiveLocationExtensions DirectiveLocation.InlineFragment, Language.DirectiveLocation.InlineFragment }, + { + DirectiveLocation.VariableDefinition, + Language.DirectiveLocation.VariableDefinition + }, { DirectiveLocation.Schema, Language.DirectiveLocation.Schema @@ -113,6 +117,10 @@ internal static class DirectiveLocationExtensions Language.DirectiveLocation.InlineFragment, DirectiveLocation.InlineFragment }, + { + Language.DirectiveLocation.VariableDefinition, + DirectiveLocation.VariableDefinition + }, { Language.DirectiveLocation.Schema, DirectiveLocation.Schema diff --git a/src/HotChocolate/Skimmed/src/Skimmed/Serialization/SchemaParser.cs b/src/HotChocolate/Skimmed/src/Skimmed/Serialization/SchemaParser.cs index a3418ff74a0..509f745a27c 100644 --- a/src/HotChocolate/Skimmed/src/Skimmed/Serialization/SchemaParser.cs +++ b/src/HotChocolate/Skimmed/src/Skimmed/Serialization/SchemaParser.cs @@ -44,6 +44,7 @@ private static void DiscoverDirectives(SchemaDefinition schema, DocumentNode doc throw new Exception("duplicate"); } + // TODO: This is problematic schema.DirectiveDefinitions.Add(new DirectiveDefinition(def.Name.Value)); } } @@ -634,6 +635,7 @@ private static void BuildDirectiveCollection( out var directiveType)) { directiveType = new DirectiveDefinition(directiveNode.Name.Value); + // TODO: This is problematic directiveType.IsRepeatable = true; schema.DirectiveDefinitions.Add(directiveType); } From 96150f901b39f57b07cfa5e82aa53c0f984fbfa0 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Mon, 14 Oct 2024 05:25:12 -0400 Subject: [PATCH 063/154] Added SSH server for JetBrains Gateway with Codespaces. (#7592) --- .devcontainer/dockerfile | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/.devcontainer/dockerfile b/.devcontainer/dockerfile index 28be44b87b4..52ac0fff18f 100644 --- a/.devcontainer/dockerfile +++ b/.devcontainer/dockerfile @@ -8,7 +8,21 @@ RUN apt-get update && apt-get install -y \ curl \ gnupg2 \ lsb-release \ - software-properties-common + software-properties-common \ + openssh-server + +# Configure SSH server +RUN mkdir /var/run/sshd \ + && echo 'root:Docker!' | chpasswd \ + && sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config \ + && sed -i 's@session required pam_loginuid.so@session optional pam_loginuid.so@g' /etc/pam.d/sshd \ + && echo "export VISIBLE=now" >> /etc/profile + +# Expose SSH port +EXPOSE 22 + +# Start SSH service +CMD ["/usr/sbin/sshd", "-D"] # Install .NET SDKs (6, 7, and 8) RUN wget https://dot.net/v1/dotnet-install.sh -O dotnet-install.sh \ @@ -41,4 +55,4 @@ RUN curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | # Clean up RUN rm dotnet-install.sh \ && apt-get clean \ - && rm -rf /var/lib/apt/lists/* + && rm -rf /var/lib/apt/lists/* \ No newline at end of file From f7fa8cf5f87102b89eead020a930cf4850f2050f Mon Sep 17 00:00:00 2001 From: Heath Provost Date: Mon, 14 Oct 2024 08:41:02 -0500 Subject: [PATCH 064/154] Fixed incorrect handling of IntermediateOutputPath by using GraphQLCodeGenerationRoot consistently (#7590) --- .../MetaPackages/Common/MSBuild/StrawberryShake.targets | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/StrawberryShake/MetaPackages/Common/MSBuild/StrawberryShake.targets b/src/StrawberryShake/MetaPackages/Common/MSBuild/StrawberryShake.targets index 92d006a20a1..f688ecb8098 100644 --- a/src/StrawberryShake/MetaPackages/Common/MSBuild/StrawberryShake.targets +++ b/src/StrawberryShake/MetaPackages/Common/MSBuild/StrawberryShake.targets @@ -34,7 +34,7 @@ @@ -72,8 +72,8 @@ - - + + From b544457a8da9093b1bfd4250c62c08d254543fea Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Mon, 14 Oct 2024 17:10:21 +0200 Subject: [PATCH 065/154] Fixed issue with global context data being disposed to early. (#7593) --- .../Pipeline/OperationExecutionMiddleware.cs | 2 +- .../Core/src/Execution/RequestExecutor.cs | 14 +- .../DeferAndStreamTestSchema.cs | 15 +++ .../Core/test/Execution.Tests/DeferTests.cs | 124 ++++++++++++++---- ...s_Passed_To_DeferContext_Single_Defer.snap | 7 + ..._Passed_To_DeferContext_Stacked_Defer.snap | 7 + 6 files changed, 138 insertions(+), 31 deletions(-) create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Stacked_Defer.snap diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/OperationExecutionMiddleware.cs b/src/HotChocolate/Core/src/Execution/Pipeline/OperationExecutionMiddleware.cs index a409129a5da..b157416165d 100644 --- a/src/HotChocolate/Core/src/Execution/Pipeline/OperationExecutionMiddleware.cs +++ b/src/HotChocolate/Core/src/Execution/Pipeline/OperationExecutionMiddleware.cs @@ -90,7 +90,7 @@ private async Task ExecuteOperationRequestAsync( if (operation.Definition.Operation is OperationType.Subscription) { // since the request context is pooled we need to clone the context for - // long running executions. + // long-running executions. var cloned = context.Clone(); context.Result = await _subscriptionExecutor diff --git a/src/HotChocolate/Core/src/Execution/RequestExecutor.cs b/src/HotChocolate/Core/src/Execution/RequestExecutor.cs index 621b4ee822d..2e0b85db5d7 100644 --- a/src/HotChocolate/Core/src/Execution/RequestExecutor.cs +++ b/src/HotChocolate/Core/src/Execution/RequestExecutor.cs @@ -103,7 +103,7 @@ internal async Task ExecuteAsync( services.InitializeDataLoaderScope(); } - var context = _contextPool.Get(); + RequestContext? context = _contextPool.Get(); try { @@ -128,15 +128,23 @@ internal async Task ExecuteAsync( if (context.Result.IsStreamResult()) { + var localContext = context; context.Result.RegisterForCleanup(scope); + context.Result.RegisterForCleanup(() => _contextPool.Return(localContext)); scope = null; + context = null; + return localContext.Result; } return context.Result; } finally { - _contextPool.Return(context); + if (context is not null) + { + _contextPool.Return(context); + } + scope?.Dispose(); } } @@ -297,7 +305,7 @@ private static async Task UnwrapBatchItemResultAsync( private void EnrichContext(IRequestContext context) { - if (_enricher.Length <= 0) + if (_enricher.Length == 0) { return; } diff --git a/src/HotChocolate/Core/test/Execution.Tests/DeferAndStreamTestSchema.cs b/src/HotChocolate/Core/test/Execution.Tests/DeferAndStreamTestSchema.cs index eb63e3ae1bf..ff978ec370b 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/DeferAndStreamTestSchema.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/DeferAndStreamTestSchema.cs @@ -61,6 +61,12 @@ public async Task Wait(int m, CancellationToken ct) await Task.Delay(m, ct); return true; } + + public async Task EnsureState() + { + await Task.Delay(150); + return new Stateful(); + } } public class Person @@ -81,4 +87,13 @@ public async Task GetNameAsync(CancellationToken cancellationToken) return _name; } } + + public class Stateful + { + public async Task GetState([GlobalState] string requestState) + { + await Task.Delay(250); + return requestState; + } + } } diff --git a/src/HotChocolate/Core/test/Execution.Tests/DeferTests.cs b/src/HotChocolate/Core/test/Execution.Tests/DeferTests.cs index 9c2cb94b802..dc28740f3b0 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/DeferTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/DeferTests.cs @@ -12,13 +12,15 @@ public async Task InlineFragment_Defer() // act var result = await executor.ExecuteAsync( - @"{ + """ + { ... @defer { - person(id: ""UGVyc29uOjE="") { + person(id: "UGVyc29uOjE=") { id } } - }"); + } + """); Assert.IsType(result).MatchSnapshot(); } @@ -31,16 +33,18 @@ public async Task InlineFragment_Defer_Nested() // act var result = await executor.ExecuteAsync( - @"{ + """ + { ... @defer { - person(id: ""UGVyc29uOjE="") { + person(id: "UGVyc29uOjE=") { id ... @defer { name } } } - }"); + } + """); Assert.IsType(result).MatchSnapshot(); } @@ -72,13 +76,15 @@ public async Task InlineFragment_Defer_If_Set_To_false() // act var result = await executor.ExecuteAsync( - @"{ + """ + { ... @defer(if: false) { - person(id: ""UGVyc29uOjE="") { + person(id: "UGVyc29uOjE=") { id } } - }"); + } + """); Assert.IsType(result).MatchSnapshot(); } @@ -94,14 +100,16 @@ public async Task InlineFragment_Defer_If_Variable_Set_To_false() OperationRequestBuilder .New() .SetDocument( - @"query($defer: Boolean!) { + """ + query($defer: Boolean!) { ... @defer(if: $defer) { - person(id: ""UGVyc29uOjE="") { + person(id: "UGVyc29uOjE=") { id } } - }") - .SetVariableValues(new Dictionary { {"defer", false }, }) + } + """) + .SetVariableValues(new Dictionary { { "defer", false }, }) .Build()); Assert.IsType(result).MatchSnapshot(); @@ -115,15 +123,17 @@ public async Task FragmentSpread_Defer() // act var result = await executor.ExecuteAsync( - @"{ + """ + { ... Foo @defer } fragment Foo on Query { - person(id: ""UGVyc29uOjE="") { + person(id: "UGVyc29uOjE=") { id } - }"); + } + """); Assert.IsType(result).MatchSnapshot(); } @@ -162,15 +172,17 @@ public async Task FragmentSpread_Defer_Label_Set_To_abc() // act var result = await executor.ExecuteAsync( - @"{ - ... Foo @defer(label: ""abc"") + """ + { + ... Foo @defer(label: "abc") } fragment Foo on Query { - person(id: ""UGVyc29uOjE="") { + person(id: "UGVyc29uOjE=") { id } - }"); + } + """); Assert.IsType(result).MatchSnapshot(); } @@ -183,15 +195,17 @@ public async Task FragmentSpread_Defer_If_Set_To_false() // act var result = await executor.ExecuteAsync( - @"{ + """ + { ... Foo @defer(if: false) } fragment Foo on Query { - person(id: ""UGVyc29uOjE="") { + person(id: "UGVyc29uOjE=") { id } - }"); + } + """); Assert.IsType(result).MatchSnapshot(); } @@ -207,18 +221,74 @@ public async Task FragmentSpread_Defer_If_Variable_Set_To_false() OperationRequestBuilder .New() .SetDocument( - @"query ($defer: Boolean!) { + """ + query ($defer: Boolean!) { ... Foo @defer(if: $defer) } fragment Foo on Query { - person(id: ""UGVyc29uOjE="") { + person(id: "UGVyc29uOjE=") { id } - }") - .SetVariableValues(new Dictionary { {"defer", false }, }) + } + """) + .SetVariableValues(new Dictionary { { "defer", false }, }) .Build()); Assert.IsType(result).MatchSnapshot(); } + + [Fact] + public async Task Ensure_GlobalState_Is_Passed_To_DeferContext_Stacked_Defer() + { + // arrange + var executor = await DeferAndStreamTestSchema.CreateAsync(); + + // act + var result = await executor.ExecuteAsync( + OperationRequestBuilder + .New() + .SetDocument( + """ + { + ... @defer { + ensureState { + ... @defer { + state + } + } + } + } + """) + .SetGlobalState("requestState", "state 123") + .Build()); + + Assert.IsType(result).MatchSnapshot(); + } + + [Fact] + public async Task Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer() + { + // arrange + var executor = await DeferAndStreamTestSchema.CreateAsync(); + + // act + var result = await executor.ExecuteAsync( + OperationRequestBuilder + .New() + .SetDocument( + """ + { + ensureState { + ... @defer { + state + } + } + } + """) + .SetGlobalState("requestState", "state 123") + .Build()); + + Assert.IsType(result).MatchSnapshot(); + } } diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer.snap new file mode 100644 index 00000000000..b312149a5b4 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer.snap @@ -0,0 +1,7 @@ +{ + "data": { + "ensureState": { + "state": "state 123" + } + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Stacked_Defer.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Stacked_Defer.snap new file mode 100644 index 00000000000..b312149a5b4 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Stacked_Defer.snap @@ -0,0 +1,7 @@ +{ + "data": { + "ensureState": { + "state": "state 123" + } + } +} From accf41bb98b3ca3a931373e3ba25d7f103a2da9e Mon Sep 17 00:00:00 2001 From: PascalSenn Date: Mon, 14 Oct 2024 22:07:21 +0200 Subject: [PATCH 066/154] Added predicates to DataLoader (#7589) Co-authored-by: Michael Staib Co-authored-by: Glen --- .../src/Core/DataLoaderFetchContext.cs | 27 +- src/GreenDonut/src/Core/Experiments.cs | 3 +- .../{Projections => }/ExpressionHelpers.cs | 13 +- .../Predicates/DefaultPredicateBuilder.cs | 51 ++++ .../src/Core/Predicates/IPredicateBuilder.cs | 35 +++ .../Core/Predicates/IPredicateDataLoader.cs | 23 ++ .../Core/Predicates/PredicateDataLoader.cs | 34 +++ .../PredicateDataLoaderExtensions.cs | 115 ++++++++ .../DefaultSelectorBuilder.cs | 4 +- .../ISelectionDataLoader.cs | 3 +- .../ISelectorBuilder.cs | 4 +- .../KeyValueResult.cs | 2 +- .../SelectionDataLoader.cs | 2 +- .../SelectionDataLoaderExtensions.cs | 6 +- .../Core/src/Execution/Experiments.cs | 3 +- ...tChocolateExecutionDataLoaderExtensions.cs | 4 +- ...otChocolateExecutionSelectionExtensions.cs | 6 +- .../Types.Analyzers/Models/DataLoaderInfo.cs | 18 ++ .../Models/DataLoaderParameterKind.cs | 3 +- .../src/Types.Analyzers/WellKnownTypes.cs | 1 + .../Projections/ProjectableDataLoaderTests.cs | 2 +- .../Types/DataLoaders.cs | 3 +- .../src/Data/Filters/Context/FilterContext.cs | 15 + .../Data/Filters/Context/IFilterContext.cs | 7 + .../Expressions/QueryableFilterProvider.cs | 119 +++++--- ...eExecutionPredicateDataLoaderExtensions.cs | 68 +++++ .../Data/src/Data/HotChocolate.Data.csproj | 1 + .../DataLoaderTests.cs | 279 ++++++++++++++++++ ...colate.Data.Filters.SqlServer.Tests.csproj | 1 + .../TestContext/Brand.cs | 30 ++ .../TestContext/CatalogContext.cs | 62 ++++ .../BrandEntityTypeConfiguration.cs | 20 ++ .../ProductEntityTypeConfiguration.cs | 42 +++ .../ProductTypeEntityTypeConfiguration.cs | 17 ++ .../TestContext/Product.cs | 102 +++++++ .../TestContext/ProductImage.cs | 3 + .../TestContext/ProductType.cs | 14 + .../DataLoaderTests.Filter_With_Expression.md | 27 ++ ...LoaderTests.Filter_With_Expression_Null.md | 27 ++ .../DataLoaderTests.Filter_With_Filtering.md | 29 ++ ...oaderTests.Filter_With_Multi_Expression.md | 27 ++ ...onBatchingDataLoaderPredicateExtensions.cs | 75 +++++ ...onBatchingDataLoaderSelectorExtensions.cs} | 12 +- .../InterfaceIntegrationTests.cs | 2 +- .../PagingHelperIntegrationTests.cs | 2 +- 45 files changed, 1268 insertions(+), 75 deletions(-) rename src/GreenDonut/src/Core/{Projections => }/ExpressionHelpers.cs (93%) create mode 100644 src/GreenDonut/src/Core/Predicates/DefaultPredicateBuilder.cs create mode 100644 src/GreenDonut/src/Core/Predicates/IPredicateBuilder.cs create mode 100644 src/GreenDonut/src/Core/Predicates/IPredicateDataLoader.cs create mode 100644 src/GreenDonut/src/Core/Predicates/PredicateDataLoader.cs create mode 100644 src/GreenDonut/src/Core/Predicates/PredicateDataLoaderExtensions.cs rename src/GreenDonut/src/Core/{Projections => Selectors}/DefaultSelectorBuilder.cs (94%) rename src/GreenDonut/src/Core/{Projections => Selectors}/ISelectionDataLoader.cs (96%) rename src/GreenDonut/src/Core/{Projections => Selectors}/ISelectorBuilder.cs (94%) rename src/GreenDonut/src/Core/{Projections => Selectors}/KeyValueResult.cs (87%) rename src/GreenDonut/src/Core/{Projections => Selectors}/SelectionDataLoader.cs (96%) rename src/GreenDonut/src/Core/{Projections => Selectors}/SelectionDataLoaderExtensions.cs (98%) create mode 100644 src/HotChocolate/Data/src/Data/Filters/Extensions/HotChocolateExecutionPredicateDataLoaderExtensions.cs create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/DataLoaderTests.cs create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/Brand.cs create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/CatalogContext.cs create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/EntityConfigurations/BrandEntityTypeConfiguration.cs create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/EntityConfigurations/ProductEntityTypeConfiguration.cs create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/EntityConfigurations/ProductTypeEntityTypeConfiguration.cs create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/Product.cs create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/ProductImage.cs create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/ProductType.cs create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Expression.md create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Expression_Null.md create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Filtering.md create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Multi_Expression.md create mode 100644 src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderPredicateExtensions.cs rename src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/{HotChocolatePaginationBatchingDataLoaderExtensions.cs => HotChocolatePaginationBatchingDataLoaderSelectorExtensions.cs} (96%) diff --git a/src/GreenDonut/src/Core/DataLoaderFetchContext.cs b/src/GreenDonut/src/Core/DataLoaderFetchContext.cs index 74419f42e67..e2d8a1621ec 100644 --- a/src/GreenDonut/src/Core/DataLoaderFetchContext.cs +++ b/src/GreenDonut/src/Core/DataLoaderFetchContext.cs @@ -3,7 +3,7 @@ using System.Diagnostics.CodeAnalysis; #endif #if NET6_0_OR_GREATER -using GreenDonut.Projections; +using GreenDonut.Selectors; #endif namespace GreenDonut; @@ -147,7 +147,7 @@ public TState GetStateOrDefault(string key, TState defaultValue) /// Returns the selector builder if it exists. /// #if NET8_0_OR_GREATER - [Experimental(Experiments.Projections)] + [Experimental(Experiments.Selectors)] #endif public ISelectorBuilder GetSelector() { @@ -161,5 +161,28 @@ public ISelectorBuilder GetSelector() // a new default selector builder. return new DefaultSelectorBuilder(); } + + /// + /// Gets the predicate builder from the DataLoader state snapshot. + /// The state builder can be used to create a predicate expression. + /// + /// + /// Returns the predicate builder if it exists. + /// +#if NET8_0_OR_GREATER + [Experimental(Experiments.Predicates)] +#endif + public IPredicateBuilder GetPredicate() + { + if (ContextData.TryGetValue(typeof(IPredicateBuilder).FullName!, out var value) + && value is DefaultPredicateBuilder casted) + { + return casted; + } + + // if no predicate was found we will just return + // a new default predicate builder. + return new DefaultPredicateBuilder(); + } #endif } diff --git a/src/GreenDonut/src/Core/Experiments.cs b/src/GreenDonut/src/Core/Experiments.cs index cdc251db5dc..d2e1fb348ff 100644 --- a/src/GreenDonut/src/Core/Experiments.cs +++ b/src/GreenDonut/src/Core/Experiments.cs @@ -2,5 +2,6 @@ namespace GreenDonut; internal static class Experiments { - public const string Projections = "GD0001"; + public const string Selectors = "GD0001"; + public const string Predicates = "GD0002"; } diff --git a/src/GreenDonut/src/Core/Projections/ExpressionHelpers.cs b/src/GreenDonut/src/Core/ExpressionHelpers.cs similarity index 93% rename from src/GreenDonut/src/Core/Projections/ExpressionHelpers.cs rename to src/GreenDonut/src/Core/ExpressionHelpers.cs index 777bf2f3ae3..69263ce02c3 100644 --- a/src/GreenDonut/src/Core/Projections/ExpressionHelpers.cs +++ b/src/GreenDonut/src/Core/ExpressionHelpers.cs @@ -2,7 +2,7 @@ using System.Linq.Expressions; using System.Reflection; -namespace GreenDonut.Projections; +namespace GreenDonut; internal static class ExpressionHelpers { @@ -17,6 +17,17 @@ public static Expression> Combine( return Expression.Lambda>(combinedBody, parameter); } + public static Expression> And( + Expression> first, + Expression> second) + { + var parameter = Expression.Parameter(typeof(T), "entity"); + var firstBody = ReplaceParameter(first.Body, first.Parameters[0], parameter); + var secondBody = ReplaceParameter(second.Body, second.Parameters[0], parameter); + var combinedBody = Expression.AndAlso(firstBody, secondBody); + return Expression.Lambda>(combinedBody, parameter); + } + private static Expression ReplaceParameter( Expression body, ParameterExpression toReplace, diff --git a/src/GreenDonut/src/Core/Predicates/DefaultPredicateBuilder.cs b/src/GreenDonut/src/Core/Predicates/DefaultPredicateBuilder.cs new file mode 100644 index 00000000000..bc80389c66b --- /dev/null +++ b/src/GreenDonut/src/Core/Predicates/DefaultPredicateBuilder.cs @@ -0,0 +1,51 @@ +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; + +namespace GreenDonut.Predicates; + +[Experimental(Experiments.Predicates)] +internal sealed class DefaultPredicateBuilder : IPredicateBuilder +{ + private List? _predicates; + + /// + public void Add(Expression> selector) + { + _predicates ??= new List(); + if (!_predicates.Contains(selector)) + { + _predicates.Add(selector); + } + } + + /// + public Expression>? TryCompile() + { + if (_predicates is null) + { + return null; + } + + if (_predicates.Count == 1) + { + return (Expression>)_predicates[0]; + } + + if (_predicates.Count == 2) + { + return ExpressionHelpers.And( + (Expression>)_predicates[0], + (Expression>)_predicates[1]); + } + + var expression = (Expression>)_predicates[0]; + for (var i = 1; i < _predicates.Count; i++) + { + expression = ExpressionHelpers.And( + expression, + (Expression>)_predicates[i]); + } + + return expression; + } +} diff --git a/src/GreenDonut/src/Core/Predicates/IPredicateBuilder.cs b/src/GreenDonut/src/Core/Predicates/IPredicateBuilder.cs new file mode 100644 index 00000000000..545ba2c9655 --- /dev/null +++ b/src/GreenDonut/src/Core/Predicates/IPredicateBuilder.cs @@ -0,0 +1,35 @@ +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; + +namespace GreenDonut.Predicates; + +/// +/// The predicate builder helps you create a combined predicate expression +/// by adding multiple expressions together. +/// +[Experimental(Experiments.Predicates)] +public interface IPredicateBuilder +{ + /// + /// Adds a predicate expression to the builder. + /// + /// + /// An expression that defines how to select data from the data source. + /// + /// + /// The type of the data source. + /// + void Add(Expression> selector); + + /// + /// Combines all the added predicate expressions into one. + /// Returns null if no expressions were added. + /// + /// + /// The type of the data source. + /// + /// + /// A combined predicate expression, or null if none were added. + /// + Expression>? TryCompile(); +} diff --git a/src/GreenDonut/src/Core/Predicates/IPredicateDataLoader.cs b/src/GreenDonut/src/Core/Predicates/IPredicateDataLoader.cs new file mode 100644 index 00000000000..7514e47396b --- /dev/null +++ b/src/GreenDonut/src/Core/Predicates/IPredicateDataLoader.cs @@ -0,0 +1,23 @@ +namespace GreenDonut.Predicates; + +/// +/// A predicate DataLoader is a specialized version of a DataLoader that +/// selects a subset of data based on a given predicate from the original DataLoader. +/// The data retrieved by this DataLoader is not shared with other DataLoaders and +/// remains isolated within this instance. +/// +/// +/// The type of the key. +/// +/// +/// The type of the value. +/// +public interface IPredicateDataLoader + : IDataLoader + where TKey : notnull +{ + /// + /// Gets the root DataLoader instance from which this instance was derived. + /// + IDataLoader Root { get; } +} diff --git a/src/GreenDonut/src/Core/Predicates/PredicateDataLoader.cs b/src/GreenDonut/src/Core/Predicates/PredicateDataLoader.cs new file mode 100644 index 00000000000..0c0a03241a0 --- /dev/null +++ b/src/GreenDonut/src/Core/Predicates/PredicateDataLoader.cs @@ -0,0 +1,34 @@ +namespace GreenDonut.Predicates; + +internal sealed class PredicateDataLoader + : DataLoaderBase + , IPredicateDataLoader + where TKey : notnull +{ + private readonly DataLoaderBase _root; + + public PredicateDataLoader( + DataLoaderBase root, + string predicateKey) + : base(root.BatchScheduler, root.Options) + { + _root = root; + CacheKeyType = $"{root.CacheKeyType}:{predicateKey}"; + ContextData = root.ContextData; + } + + public IDataLoader Root => _root; + + protected internal override string CacheKeyType { get; } + + protected override bool AllowCachePropagation => false; + + protected override bool AllowBranching => false; + + protected internal override ValueTask FetchAsync( + IReadOnlyList keys, + Memory> results, + DataLoaderFetchContext context, + CancellationToken cancellationToken) + => _root.FetchAsync(keys, results, context, cancellationToken); +} diff --git a/src/GreenDonut/src/Core/Predicates/PredicateDataLoaderExtensions.cs b/src/GreenDonut/src/Core/Predicates/PredicateDataLoaderExtensions.cs new file mode 100644 index 00000000000..53999442221 --- /dev/null +++ b/src/GreenDonut/src/Core/Predicates/PredicateDataLoaderExtensions.cs @@ -0,0 +1,115 @@ +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; + +namespace GreenDonut.Predicates; + +/// +/// Data loader extensions for predicates. +/// +[Experimental(Experiments.Predicates)] +public static class PredicateDataLoaderExtensions +{ + /// + /// Branches a DataLoader and applies a predicate to filter the data. + /// + /// + /// The DataLoader to branch. + /// + /// + /// The data predicate. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns a branched DataLoader with the predicate applied. + /// + /// + /// Throws if is null. + /// + public static IDataLoader Where( + this IDataLoader dataLoader, + Expression>? predicate) + where TKey : notnull + { + if (dataLoader is null) + { + throw new ArgumentNullException(nameof(dataLoader)); + } + + if (predicate is null) + { + return dataLoader; + } + + if (dataLoader.ContextData.TryGetValue(typeof(IPredicateBuilder).FullName!, out var value)) + { + var context = (DefaultPredicateBuilder)value!; + context.Add(predicate); + return dataLoader; + } + + var branchKey = predicate.ToString(); + return (IDataLoader)dataLoader.Branch(branchKey, CreateBranch, predicate); + + static IDataLoader CreateBranch( + string key, + IDataLoader dataLoader, + Expression> predicate) + { + var branch = new PredicateDataLoader( + (DataLoaderBase)dataLoader, + key); + var context = new DefaultPredicateBuilder(); + branch.ContextData = + branch.ContextData.SetItem(typeof(IPredicateBuilder).FullName!, context); + context.Add(predicate); + return branch; + } + } + + /// + /// Applies the predicate from the DataLoader state to a queryable. + /// + /// + /// The queryable to apply the predicate to. + /// + /// + /// The predicate builder. + /// + /// + /// The queryable type. + /// + /// + /// Returns a query with the predicate applied, ready to fetch data with the key. + /// + /// + /// Throws if is null. + /// + public static IQueryable Where( + this IQueryable query, + IPredicateBuilder builder) + { + if (query is null) + { + throw new ArgumentNullException(nameof(query)); + } + + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + var predicate = builder.TryCompile(); + + if (predicate is not null) + { + query = query.Where(predicate); + } + + return query; + } +} diff --git a/src/GreenDonut/src/Core/Projections/DefaultSelectorBuilder.cs b/src/GreenDonut/src/Core/Selectors/DefaultSelectorBuilder.cs similarity index 94% rename from src/GreenDonut/src/Core/Projections/DefaultSelectorBuilder.cs rename to src/GreenDonut/src/Core/Selectors/DefaultSelectorBuilder.cs index 24308a644e9..2857ce89172 100644 --- a/src/GreenDonut/src/Core/Projections/DefaultSelectorBuilder.cs +++ b/src/GreenDonut/src/Core/Selectors/DefaultSelectorBuilder.cs @@ -2,13 +2,13 @@ using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; -namespace GreenDonut.Projections; +namespace GreenDonut.Selectors; /// /// A default implementation of the . /// #if NET8_0_OR_GREATER -[Experimental(Experiments.Projections)] +[Experimental(Experiments.Selectors)] #endif public sealed class DefaultSelectorBuilder : ISelectorBuilder { diff --git a/src/GreenDonut/src/Core/Projections/ISelectionDataLoader.cs b/src/GreenDonut/src/Core/Selectors/ISelectionDataLoader.cs similarity index 96% rename from src/GreenDonut/src/Core/Projections/ISelectionDataLoader.cs rename to src/GreenDonut/src/Core/Selectors/ISelectionDataLoader.cs index 04c34f05945..156b3e737a7 100644 --- a/src/GreenDonut/src/Core/Projections/ISelectionDataLoader.cs +++ b/src/GreenDonut/src/Core/Selectors/ISelectionDataLoader.cs @@ -1,6 +1,5 @@ #if NET6_0_OR_GREATER - -namespace GreenDonut.Projections; +namespace GreenDonut.Selectors; /// /// A selection DataLoader is a version of a DataLoader that diff --git a/src/GreenDonut/src/Core/Projections/ISelectorBuilder.cs b/src/GreenDonut/src/Core/Selectors/ISelectorBuilder.cs similarity index 94% rename from src/GreenDonut/src/Core/Projections/ISelectorBuilder.cs rename to src/GreenDonut/src/Core/Selectors/ISelectorBuilder.cs index 0733a3ebd0e..010274b01d1 100644 --- a/src/GreenDonut/src/Core/Projections/ISelectorBuilder.cs +++ b/src/GreenDonut/src/Core/Selectors/ISelectorBuilder.cs @@ -2,7 +2,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; -namespace GreenDonut.Projections; +namespace GreenDonut.Selectors; /// /// The selector builder allows to build up a selector expression @@ -10,7 +10,7 @@ namespace GreenDonut.Projections; /// single selector expression. /// #if NET8_0_OR_GREATER -[Experimental(Experiments.Projections)] +[Experimental(Experiments.Selectors)] #endif public interface ISelectorBuilder { diff --git a/src/GreenDonut/src/Core/Projections/KeyValueResult.cs b/src/GreenDonut/src/Core/Selectors/KeyValueResult.cs similarity index 87% rename from src/GreenDonut/src/Core/Projections/KeyValueResult.cs rename to src/GreenDonut/src/Core/Selectors/KeyValueResult.cs index f66858d4923..1902bec371e 100644 --- a/src/GreenDonut/src/Core/Projections/KeyValueResult.cs +++ b/src/GreenDonut/src/Core/Selectors/KeyValueResult.cs @@ -1,4 +1,4 @@ -namespace GreenDonut.Projections; +namespace GreenDonut.Selectors; /// /// This class is a helper that is used to project a key value pair. diff --git a/src/GreenDonut/src/Core/Projections/SelectionDataLoader.cs b/src/GreenDonut/src/Core/Selectors/SelectionDataLoader.cs similarity index 96% rename from src/GreenDonut/src/Core/Projections/SelectionDataLoader.cs rename to src/GreenDonut/src/Core/Selectors/SelectionDataLoader.cs index 2b411a551b7..2be4d5effa4 100644 --- a/src/GreenDonut/src/Core/Projections/SelectionDataLoader.cs +++ b/src/GreenDonut/src/Core/Selectors/SelectionDataLoader.cs @@ -1,5 +1,5 @@ #if NET6_0_OR_GREATER -namespace GreenDonut.Projections; +namespace GreenDonut.Selectors; internal sealed class SelectionDataLoader : DataLoaderBase diff --git a/src/GreenDonut/src/Core/Projections/SelectionDataLoaderExtensions.cs b/src/GreenDonut/src/Core/Selectors/SelectionDataLoaderExtensions.cs similarity index 98% rename from src/GreenDonut/src/Core/Projections/SelectionDataLoaderExtensions.cs rename to src/GreenDonut/src/Core/Selectors/SelectionDataLoaderExtensions.cs index 385eb713c24..f2518dacb53 100644 --- a/src/GreenDonut/src/Core/Projections/SelectionDataLoaderExtensions.cs +++ b/src/GreenDonut/src/Core/Selectors/SelectionDataLoaderExtensions.cs @@ -2,15 +2,15 @@ using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Reflection; -using static GreenDonut.Projections.ExpressionHelpers; +using static GreenDonut.ExpressionHelpers; -namespace GreenDonut.Projections; +namespace GreenDonut.Selectors; /// /// Data loader extensions for projections. /// #if NET8_0_OR_GREATER -[Experimental(Experiments.Projections)] +[Experimental(Experiments.Selectors)] #endif public static class SelectionDataLoaderExtensions { diff --git a/src/HotChocolate/Core/src/Execution/Experiments.cs b/src/HotChocolate/Core/src/Execution/Experiments.cs index 73217e4d5da..cf9f4f64f53 100644 --- a/src/HotChocolate/Core/src/Execution/Experiments.cs +++ b/src/HotChocolate/Core/src/Execution/Experiments.cs @@ -2,5 +2,6 @@ namespace HotChocolate.Execution; internal static class Experiments { - public const string Projections = "GD0001"; + public const string Selectors = "GD0001"; + public const string Predicates = "GD0002"; } diff --git a/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs b/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs index 2b0a63bb5c1..6ef63a532dc 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs @@ -7,13 +7,13 @@ using HotChocolate.Pagination; // ReSharper disable once CheckNamespace -namespace GreenDonut.Projections; +namespace GreenDonut.Selectors; /// /// Provides extension methods for projection on DataLoader. /// #if NET8_0_OR_GREATER -[Experimental(Experiments.Projections)] +[Experimental(Experiments.Selectors)] #endif public static class HotChocolateExecutionDataLoaderExtensions { diff --git a/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionSelectionExtensions.cs b/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionSelectionExtensions.cs index d548a0c0b4f..95f39a30ff9 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionSelectionExtensions.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionSelectionExtensions.cs @@ -5,7 +5,7 @@ using System.Linq.Expressions; using System.Text; using System.Runtime.CompilerServices; -using GreenDonut.Projections; +using GreenDonut.Selectors; using HotChocolate.Execution.Projections; using HotChocolate.Types; using HotChocolate.Types.Descriptors.Definitions; @@ -34,7 +34,7 @@ public static class HotChocolateExecutionSelectionExtensions /// Returns a selector expression that can be used for data projections. /// #if NET8_0_OR_GREATER - [Experimental(Experiments.Projections)] + [Experimental(Experiments.Selectors)] #endif public static Expression> AsSelector( this ISelection selection) @@ -94,7 +94,7 @@ private static Expression> GetOrCreateExpression( (_builder, selection)); #if NET8_0_OR_GREATER - [Experimental(Experiments.Projections)] + [Experimental(Experiments.Selectors)] #endif private static Expression> GetOrCreateExpression( ISelection selection, diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderInfo.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderInfo.cs index ebc066f20cf..a7b0ca2e071 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderInfo.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderInfo.cs @@ -181,6 +181,18 @@ private static ImmutableArray CreateParameters(IMethodS continue; } + // check for well-known state + if (IsPredicateBuilder(parameter)) + { + builder.Add( + new DataLoaderParameterInfo( + $"p{i}", + parameter, + DataLoaderParameterKind.PredicateBuilder, + WellKnownTypes.PredicateBuilder)); + continue; + } + if (IsPagingArguments(parameter)) { builder.Add( @@ -229,6 +241,12 @@ private static bool IsSelectorBuilder(IParameterSymbol parameter) return string.Equals(typeName, WellKnownTypes.SelectorBuilder, StringComparison.Ordinal); } + private static bool IsPredicateBuilder(IParameterSymbol parameter) + { + var typeName = parameter.Type.ToDisplayString(); + return string.Equals(typeName, WellKnownTypes.PredicateBuilder, StringComparison.Ordinal); + } + private static bool IsPagingArguments(IParameterSymbol parameter) { var typeName = parameter.Type.ToDisplayString(); diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderParameterKind.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderParameterKind.cs index 15d34d66acc..6481bdbb9e3 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderParameterKind.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderParameterKind.cs @@ -7,5 +7,6 @@ public enum DataLoaderParameterKind ContextData = 2, CancellationToken = 3, SelectorBuilder = 4, - PagingArguments = 5 + PagingArguments = 5, + PredicateBuilder = 6 } diff --git a/src/HotChocolate/Core/src/Types.Analyzers/WellKnownTypes.cs b/src/HotChocolate/Core/src/Types.Analyzers/WellKnownTypes.cs index 9595a0df611..4a224bc6bb5 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/WellKnownTypes.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/WellKnownTypes.cs @@ -67,6 +67,7 @@ public static class WellKnownTypes public const string KeyValuePair = "System.Collections.Generic.KeyValuePair"; public const string EnumerableExtensions = "System.Linq.Enumerable"; public const string SelectorBuilder = "GreenDonut.Projections.ISelectorBuilder"; + public const string PredicateBuilder = "GreenDonut.Predicates.IPredicateBuilder"; public const string PagingArguments = "HotChocolate.Pagination.PagingArguments"; public static HashSet TypeClass { get; } = diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs index 672993a2907..9c676b7897b 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs @@ -2,7 +2,7 @@ using System.Linq.Expressions; using CookieCrumble; using GreenDonut; -using GreenDonut.Projections; +using GreenDonut.Selectors; using HotChocolate.Execution.Processing; using HotChocolate.Execution.TestContext; using HotChocolate.Types; diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/Types/DataLoaders.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/Types/DataLoaders.cs index c76eb982919..ecb4875e251 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/Types/DataLoaders.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/Types/DataLoaders.cs @@ -1,7 +1,6 @@ using GreenDonut; -using GreenDonut.Projections; +using GreenDonut.Selectors; using HotChocolate.Pagination; -using Microsoft.Extensions.DependencyInjection; namespace HotChocolate.Types; diff --git a/src/HotChocolate/Data/src/Data/Filters/Context/FilterContext.cs b/src/HotChocolate/Data/src/Data/Filters/Context/FilterContext.cs index bfb5d620379..701c6dcb482 100644 --- a/src/HotChocolate/Data/src/Data/Filters/Context/FilterContext.cs +++ b/src/HotChocolate/Data/src/Data/Filters/Context/FilterContext.cs @@ -1,3 +1,5 @@ +using System.Linq.Expressions; +using HotChocolate.Data.Filters.Expressions; using HotChocolate.Language; using HotChocolate.Resolvers; using HotChocolate.Types; @@ -52,6 +54,19 @@ public void Handled(bool isHandled) public IDictionary? ToDictionary() => Serialize(_value) as IDictionary; + public Expression>? AsPredicate() + { + var localContextData = _context.LocalContextData; + + if (localContextData.TryGetValue(ContextAsPredicateKey, out var asPredicateObj) && + asPredicateObj is AsPredicate asPredicate) + { + return asPredicate(_context, false); + } + + return null; + } + private object? Serialize(IFilterValueNode? value) { switch (value) diff --git a/src/HotChocolate/Data/src/Data/Filters/Context/IFilterContext.cs b/src/HotChocolate/Data/src/Data/Filters/Context/IFilterContext.cs index 48a87418202..1a490620bb4 100644 --- a/src/HotChocolate/Data/src/Data/Filters/Context/IFilterContext.cs +++ b/src/HotChocolate/Data/src/Data/Filters/Context/IFilterContext.cs @@ -1,3 +1,5 @@ +using System.Linq.Expressions; + namespace HotChocolate.Data.Filters; /// @@ -22,4 +24,9 @@ public interface IFilterContext : IFilterInfo /// Serializes the input object to a dictionary /// IDictionary? ToDictionary(); + + /// + /// Creates a predicate expression for the filter context. + /// + Expression>? AsPredicate(); } diff --git a/src/HotChocolate/Data/src/Data/Filters/Expressions/QueryableFilterProvider.cs b/src/HotChocolate/Data/src/Data/Filters/Expressions/QueryableFilterProvider.cs index 8219f159eef..b9a19db3a1c 100644 --- a/src/HotChocolate/Data/src/Data/Filters/Expressions/QueryableFilterProvider.cs +++ b/src/HotChocolate/Data/src/Data/Filters/Expressions/QueryableFilterProvider.cs @@ -13,6 +13,10 @@ namespace HotChocolate.Data.Filters.Expressions; [return: NotNullIfNotNull("input")] public delegate object? ApplyFiltering(IResolverContext context, object? input); +public delegate Expression>? AsPredicate( + IResolverContext context, + bool isInMemory); + /// /// Visit the value node and returns the populated /// @@ -46,6 +50,12 @@ public class QueryableFilterProvider : FilterProvider /// public static readonly string ContextApplyFilteringKey = nameof(ApplyFiltering); + /// + /// The key for on + /// that defines if the filter should be applied as a predicate + /// + public static readonly string ContextAsPredicateKey = nameof(AsPredicate); + /// /// The key for on /// that defines if filtering is already applied and should be skipped @@ -69,7 +79,9 @@ public QueryableFilterProvider() { } /// Configures the provider public QueryableFilterProvider( Action> configure) - : base(configure) { } + : base(configure) + { + } /// /// The visitor that is used to visit the input @@ -79,7 +91,9 @@ public QueryableFilterProvider( /// public override IQueryBuilder CreateBuilder(string argumentName) - => new QueryableQueryBuilder(CreateApplicator(argumentName)); + => new QueryableQueryBuilder( + CreateApplicator(argumentName), + (ctx, inMemory) => AsPredicate(ctx, argumentName, inMemory)); /// public override void ConfigureField( @@ -169,61 +183,82 @@ protected virtual bool IsInMemoryQuery(object? input) private ApplyFiltering CreateApplicator(string argumentName) => (context, input) => { - // next we get the filter argument. If the filter argument is already on the context - // we use this. This enabled overriding the context with LocalContextData - var argument = context.Selection.Field.Arguments[argumentName]; - var filter = context.GetLocalStateOrDefault(ContextValueNodeKey) ?? - context.ArgumentLiteral(argumentName); - - // if no filter is defined we can stop here and yield back control. - var skipFiltering = context.GetLocalStateOrDefault(SkipFilteringKey); - - // ensure filtering is only applied once - context.SetLocalState(SkipFilteringKey, true); + var inMemory = IsInMemoryQuery(input); + var predicate = AsPredicate(context, argumentName, inMemory); - if (filter.IsNull() || skipFiltering) + if (predicate is not null) { - return input; + input = ApplyToResult(input, predicate); } - if (argument.Type is IFilterInputType filterInput && - context.Selection.Field.ContextData.TryGetValue(ContextVisitFilterArgumentKey, out var executorObj) && - executorObj is VisitFilterArgument executor) - { - var inMemory = IsInMemoryQuery(input); + return input; + }; - var visitorContext = executor(filter, filterInput, inMemory); + private Expression>? AsPredicate( + IResolverContext context, + string argumentName, + bool isInMemory) + { + // next we get the filter argument. If the filter argument is already on the context + // we use this. This enabled overriding the context with LocalContextData + var argument = context.Selection.Field.Arguments[argumentName]; + var filter = context.GetLocalStateOrDefault(ContextValueNodeKey) ?? + context.ArgumentLiteral(argumentName); - // compile expression tree - if (visitorContext.Errors.Count == 0) + // if no filter is defined we can stop here and yield back control. + var skipFiltering = context.GetLocalStateOrDefault(SkipFilteringKey); + + // ensure filtering is only applied once + context.SetLocalState(SkipFilteringKey, true); + + if (filter.IsNull() || skipFiltering) + { + return null; + } + + if (argument.Type is IFilterInputType filterInput && + context.Selection.Field.ContextData.TryGetValue(ContextVisitFilterArgumentKey, + out var executorObj) && + executorObj is VisitFilterArgument executor) + { + var visitorContext = executor(filter, filterInput, isInMemory); + + // compile expression tree + if (visitorContext.Errors.Count == 0) + { + // if we have an empty filter object it might be that there is no lambda that needs to be applied. + // this depends on the provider implementation. + if (visitorContext.TryCreateLambda(out Expression>? where)) { - // if we have an empty filter object it might be that there is no lambda that needs to be applied. - // this depends on the provider implementation. - if (visitorContext.TryCreateLambda(out Expression>? where)) - { - input = ApplyToResult(input, where); - } + return where; } - else - { - var exceptions = new List(visitorContext.Errors.Count); - - foreach (var error in visitorContext.Errors) - { - exceptions.Add(new GraphQLException(error)); - } + } + else + { + var exceptions = new List(visitorContext.Errors.Count); - throw new AggregateException(exceptions); + foreach (var error in visitorContext.Errors) + { + exceptions.Add(new GraphQLException(error)); } + + throw new AggregateException(exceptions); } + } - return input; - }; + return null; + } - private sealed class QueryableQueryBuilder(ApplyFiltering applicator) : IQueryBuilder + private sealed class QueryableQueryBuilder( + ApplyFiltering applicator, + AsPredicate asPredicate) + : IQueryBuilder { public void Prepare(IMiddlewareContext context) - => context.SetLocalState(ContextApplyFilteringKey, applicator); + { + context.SetLocalState(ContextApplyFilteringKey, applicator); + context.SetLocalState(ContextAsPredicateKey, asPredicate); + } public void Apply(IMiddlewareContext context) => context.Result = applicator(context, context.Result); diff --git a/src/HotChocolate/Data/src/Data/Filters/Extensions/HotChocolateExecutionPredicateDataLoaderExtensions.cs b/src/HotChocolate/Data/src/Data/Filters/Extensions/HotChocolateExecutionPredicateDataLoaderExtensions.cs new file mode 100644 index 00000000000..d991dc0a314 --- /dev/null +++ b/src/HotChocolate/Data/src/Data/Filters/Extensions/HotChocolateExecutionPredicateDataLoaderExtensions.cs @@ -0,0 +1,68 @@ +using System.Diagnostics.CodeAnalysis; +using HotChocolate.Data.Filters; +using HotChocolate.Execution; +using HotChocolate.Pagination; + +// ReSharper disable once CheckNamespace +namespace GreenDonut.Predicates; + +/// +/// Provides extension methods for projection on DataLoader. +/// +[Experimental(Experiments.Predicates)] +public static class HotChocolateExecutionPredicateDataLoaderExtensions +{ + /// + /// Applies a filter context to the data loader. + /// + /// + /// The data loader. + /// + /// + /// The filter context that shall be applied to the data loader. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns a new data loader that applies the selection. + /// + public static IDataLoader Where( + this IDataLoader dataLoader, + IFilterContext context) + where TKey : notnull + { + var expression = context.AsPredicate(); + return dataLoader.Where(expression); + } + + /// + /// Applies a filter context to the data loader. + /// + /// + /// The data loader. + /// + /// + /// The filter context that shall be applied to the data loader. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns a new data loader that applies the selection. + /// + public static IPagingDataLoader> Where( + this IPagingDataLoader> dataLoader, + IFilterContext context) + where TKey : notnull + { + var expression = context.AsPredicate(); + return dataLoader.Where(expression); + } +} diff --git a/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj b/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj index d185e44b091..51eb3e22767 100644 --- a/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj +++ b/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj @@ -11,6 +11,7 @@ + diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/DataLoaderTests.cs b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/DataLoaderTests.cs new file mode 100644 index 00000000000..ee39eb6b7a5 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/DataLoaderTests.cs @@ -0,0 +1,279 @@ +using System.Linq.Expressions; +using CookieCrumble; +using GreenDonut; +using GreenDonut.Predicates; +using HotChocolate.Execution; +using HotChocolate.Data.Filters; +using HotChocolate.Data.TestContext; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace HotChocolate.Data.Predicates; + +public sealed class DataLoaderTests +{ + [Fact] + public async Task Filter_With_Expression() + { + // Arrange + var queries = new List(); + var context = new CatalogContext(); + await context.SeedAsync(); + + // Act + var result = await new ServiceCollection() + .AddScoped(_ => queries) + .AddTransient(_ => context) + .AddGraphQL() + .AddQueryType() + .AddFiltering() + .AddPagingArguments() + .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) + .ExecuteRequestAsync( + """ + { + filterExpression(id: 1) { + name + } + } + """); + + Snapshot.Create() + .AddSql(queries) + .AddResult(result) + .MatchMarkdownSnapshot(); + } + + [Fact] + public async Task Filter_With_Multi_Expression() + { + // Arrange + var queries = new List(); + var context = new CatalogContext(); + await context.SeedAsync(); + + // Act + var result = await new ServiceCollection() + .AddScoped(_ => queries) + .AddTransient(_ => context) + .AddGraphQL() + .AddQueryType() + .AddFiltering() + .AddPagingArguments() + .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) + .ExecuteRequestAsync( + """ + { + multiFilterExpression(id: 1) { + name + } + } + """); + + Snapshot.Create() + .AddSql(queries) + .AddResult(result) + .MatchMarkdownSnapshot(); + } + + [Fact] + public async Task Filter_With_Filtering() + { + // Arrange + var queries = new List(); + var context = new CatalogContext(); + await context.SeedAsync(); + + // Act + var result = await new ServiceCollection() + .AddScoped(_ => queries) + .AddTransient(_ => context) + .AddGraphQL() + .AddFiltering() + .AddQueryType() + .AddPagingArguments() + .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) + .ExecuteRequestAsync( + """ + { + filterContext(brandId: 1, where: { name: { startsWith: "Product" } }) { + name + } + } + """); + + Snapshot.Create() + .AddSql(queries) + .AddResult(result) + .MatchMarkdownSnapshot(); + } + + [Fact] + public async Task Filter_With_Expression_Null() + { + // Arrange + var queries = new List(); + var context = new CatalogContext(); + await context.SeedAsync(); + + // Act + var result = await new ServiceCollection() + .AddScoped(_ => queries) + .AddTransient(_ => context) + .AddGraphQL() + .AddQueryType() + .AddFiltering() + .AddPagingArguments() + .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) + .ExecuteRequestAsync( + """ + { + brandByIdFilterNull(id: 1) { + name + } + } + """); + + Snapshot.Create() + .AddSql(queries) + .AddResult(result) + .MatchMarkdownSnapshot(); + } + + public class Query + { + public async Task FilterExpression( + int id, + BrandByIdDataLoader brandById, + CancellationToken cancellationToken) + => await brandById + .Where(x => x.Name.StartsWith("Brand")) + .LoadAsync(id, cancellationToken); + + public async Task MultiFilterExpression( + int id, + BrandByIdDataLoader brandById, + CancellationToken cancellationToken) + => await brandById + .Where(x => x.Name.StartsWith("Brand")) + .Where(x => x.Name.EndsWith("0")) + .LoadAsync(id, cancellationToken); + + [UseFiltering] + public async Task FilterContext( + int brandId, + IFilterContext context, + ProductsByBrandIdDataLoader productsByBrandId, + CancellationToken cancellationToken) + => await productsByBrandId + .Where(context) + .LoadAsync(brandId, cancellationToken); + + public async Task GetBrandByIdFilterNullAsync( + int id, + BrandByIdDataLoader brandById, + CancellationToken cancellationToken) + => await brandById.Where(default(Expression>)) + .LoadAsync(id, cancellationToken); + } + + public class BrandByIdDataLoader : StatefulBatchDataLoader + { + private readonly IServiceProvider _services; + private readonly List _queries; + + public BrandByIdDataLoader(IServiceProvider services, + List queries, + IBatchScheduler batchScheduler, + DataLoaderOptions options) : base(batchScheduler, options) + { + _services = services; + _queries = queries; + + PromiseCacheObserver + .Create( + p => + { + if (p.Brand is not null) + { + return new KeyValuePair(p.Brand.Id, p.Brand); + } + + return null; + }, + this) + .Accept(this); + } + + protected override async Task> LoadBatchAsync( + IReadOnlyList keys, + DataLoaderFetchContext context, + CancellationToken cancellationToken) + { + var catalogContext = _services.GetRequiredService(); + + var query = catalogContext.Brands + .Where(t => keys.Contains(t.Id)) + .Where(context.GetPredicate()); + + lock (_queries) + { + _queries.Add(query.ToQueryString()); + } + + var x = await query.ToDictionaryAsync(t => t.Id, cancellationToken); + + return x; + } + } + + public class ProductsByBrandIdDataLoader : StatefulBatchDataLoader + { + private readonly IServiceProvider _services; + private readonly List _queries; + + public ProductsByBrandIdDataLoader(IServiceProvider services, + List queries, + IBatchScheduler batchScheduler, + DataLoaderOptions options) : base(batchScheduler, options) + { + _services = services; + _queries = queries; + } + + protected override async Task> LoadBatchAsync( + IReadOnlyList keys, + DataLoaderFetchContext context, + CancellationToken cancellationToken) + { + var catalogContext = _services.GetRequiredService(); + + var query = catalogContext.Products + .Where(t => keys.Contains(t.BrandId)) + .Where(context.GetPredicate()); + + lock (_queries) + { + _queries.Add(query.ToQueryString()); + } + + var x = await query.ToListAsync(cancellationToken); + return x.GroupBy(t => t.Id).ToDictionary(t => t.Key, t => t.ToArray()); + } + } +} + +file static class Extensions +{ + public static Snapshot AddSql(this Snapshot snapshot, List queries) + { + snapshot.Add(string.Join("\n", queries), "SQL"); + return snapshot; + } + + public static Snapshot AddResult(this Snapshot snapshot, IExecutionResult result) + { + snapshot.Add(result, "Result"); + return snapshot; + } +} diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/HotChocolate.Data.Filters.SqlServer.Tests.csproj b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/HotChocolate.Data.Filters.SqlServer.Tests.csproj index ca998fc1428..5ddfb18c7f1 100644 --- a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/HotChocolate.Data.Filters.SqlServer.Tests.csproj +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/HotChocolate.Data.Filters.SqlServer.Tests.csproj @@ -3,6 +3,7 @@ HotChocolate.Data.Filters.SqlServer.Tests HotChocolate.Data.Filters + $(NoWarn);GD0002 diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/Brand.cs b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/Brand.cs new file mode 100644 index 00000000000..1232425d0aa --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/Brand.cs @@ -0,0 +1,30 @@ +// ReSharper disable CollectionNeverUpdated.Global + +using System.ComponentModel.DataAnnotations; + +namespace HotChocolate.Data.TestContext; + +public class Brand +{ + public int Id { get; set; } + + [Required] + public string Name { get; set; } = default!; + + [MaxLength(100)] + public string? DisplayName { get; set; } + + public ICollection Products { get; } = new List(); + + public BrandDetails Details { get; set; } = default!; +} + +public class BrandDetails +{ + public Country Country { get; set; } = default!; +} + +public class Country +{ + public string Name { get; set; } = default!; +} diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/CatalogContext.cs b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/CatalogContext.cs new file mode 100644 index 00000000000..0d0d675dddb --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/CatalogContext.cs @@ -0,0 +1,62 @@ +using HotChocolate.Data.TestContext.EntityConfigurations; +using Microsoft.EntityFrameworkCore; + + +namespace HotChocolate.Data.TestContext; + +public class CatalogContext : DbContext +{ + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + var randomDbName = Guid.NewGuid().ToString("N"); + optionsBuilder.UseSqlite($"Data Source={randomDbName}.db"); + } + + public DbSet Products { get; set; } = default!; + + public DbSet ProductTypes { get; set; } = default!; + + public DbSet Brands { get; set; } = default!; + + protected override void OnModelCreating(ModelBuilder builder) + { + builder.ApplyConfiguration(new BrandEntityTypeConfiguration()); + builder.ApplyConfiguration(new ProductTypeEntityTypeConfiguration()); + builder.ApplyConfiguration(new ProductEntityTypeConfiguration()); + + // Add the outbox table to this context + // builder.UseIntegrationEventLogs(); + } + + public async Task SeedAsync() + { + await Database.EnsureCreatedAsync(); + + var type = new ProductType { Name = "T-Shirt", }; + ProductTypes.Add(type); + + for (var i = 0; i < 100; i++) + { + var brand = new Brand + { + Name = "Brand" + i, + DisplayName = i % 2 == 0 ? "BrandDisplay" + i : null, + Details = new() { Country = new() { Name = "Country" + i } } + }; + Brands.Add(brand); + + for (var j = 0; j < 100; j++) + { + var product = new Product + { + Name = $"Product {i}-{j}", + Type = type, + Brand = brand, + }; + Products.Add(product); + } + } + + await SaveChangesAsync(); + } +} diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/EntityConfigurations/BrandEntityTypeConfiguration.cs b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/EntityConfigurations/BrandEntityTypeConfiguration.cs new file mode 100644 index 00000000000..98b125c3489 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/EntityConfigurations/BrandEntityTypeConfiguration.cs @@ -0,0 +1,20 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace HotChocolate.Data.TestContext.EntityConfigurations; + +internal sealed class BrandEntityTypeConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder + .ToTable("Brands"); + + builder + .Property(cb => cb.Name) + .HasMaxLength(100); + + builder.OwnsOne(x => x.Details, + bd => bd.OwnsOne(x => x.Country, c => c.Property(x => x.Name).HasMaxLength(100))); + } +} diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/EntityConfigurations/ProductEntityTypeConfiguration.cs b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/EntityConfigurations/ProductEntityTypeConfiguration.cs new file mode 100644 index 00000000000..ee0e00231f4 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/EntityConfigurations/ProductEntityTypeConfiguration.cs @@ -0,0 +1,42 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace HotChocolate.Data.TestContext.EntityConfigurations; + +internal sealed class ProductEntityTypeConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder + .ToTable("Products"); + + builder + .Property(ci => ci.Name) + .HasMaxLength(50); + + builder + .Property(ci => ci.Description) + .HasMaxLength(2048); + + builder + .Property(ci => ci.ImageFileName) + .HasMaxLength(256); + + /* + builder + .Property(ci => ci.Embedding) + .HasColumnType("vector(1536)"); + */ + + builder + .HasOne(ci => ci.Brand) + .WithMany(ci => ci.Products); + + builder + .HasOne(ci => ci.Type) + .WithMany(ci => ci.Products); + + builder + .HasIndex(ci => ci.Name); + } +} diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/EntityConfigurations/ProductTypeEntityTypeConfiguration.cs b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/EntityConfigurations/ProductTypeEntityTypeConfiguration.cs new file mode 100644 index 00000000000..6851933e960 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/EntityConfigurations/ProductTypeEntityTypeConfiguration.cs @@ -0,0 +1,17 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace HotChocolate.Data.TestContext.EntityConfigurations; + +internal sealed class ProductTypeEntityTypeConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder + .ToTable("ProductTypes"); + + builder + .Property(cb => cb.Name) + .HasMaxLength(100); + } +} diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/Product.cs b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/Product.cs new file mode 100644 index 00000000000..169b250599e --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/Product.cs @@ -0,0 +1,102 @@ +// ReSharper disable CollectionNeverUpdated.Global + +using System.ComponentModel.DataAnnotations; + +namespace HotChocolate.Data.TestContext; + +public class Product +{ + public int Id { get; set; } + + [Required] + public string Name { get; set; } = default!; + + public string? Description { get; set; } + + public decimal Price { get; set; } + + public string? ImageFileName { get; set; } + + public int TypeId { get; set; } + + public ProductType? Type { get; set; } + + public int BrandId { get; set; } + + public Brand? Brand { get; set; } + + // Quantity in stock + public int AvailableStock { get; set; } + + // Available stock at which we should reorder + public int RestockThreshold { get; set; } + + // Maximum number of units that can be in-stock at any time (due to physicial/logistical constraints in warehouses) + public int MaxStockThreshold { get; set; } + + /// Optional embedding for the catalog item's description. + // [JsonIgnore] + // public Vector Embedding { get; set; } + + /// + /// True if item is on reorder + /// + public bool OnReorder { get; set; } + + /// + /// Decrements the quantity of a particular item in inventory and ensures the restockThreshold hasn't + /// been breached. If so, a RestockRequest is generated in CheckThreshold. + /// + /// If there is sufficient stock of an item, then the integer returned at the end of this call should be the same as quantityDesired. + /// In the event that there is not sufficient stock available, the method will remove whatever stock is available and return that quantity to the client. + /// In this case, it is the responsibility of the client to determine if the amount that is returned is the same as quantityDesired. + /// It is invalid to pass in a negative number. + /// + /// + /// int: Returns the number actually removed from stock. + /// + public int RemoveStock(int quantityDesired) + { + if (AvailableStock == 0) + { + // throw new CatalogDomainException($"Empty stock, product item {Name} is sold out"); + } + + if (quantityDesired <= 0) + { + // throw new CatalogDomainException($"Item units desired should be greater than zero"); + } + + var removed = Math.Min(quantityDesired, AvailableStock); + + AvailableStock -= removed; + + return removed; + } + + /// + /// Increments the quantity of a particular item in inventory. + /// + /// int: Returns the quantity that has been added to stock + /// + public int AddStock(int quantity) + { + var original = AvailableStock; + + // The quantity that the client is trying to add to stock is greater than what can be physically accommodated in the Warehouse + if ((AvailableStock + quantity) > MaxStockThreshold) + { + // For now, this method only adds new units up maximum stock threshold. In an expanded version of this application, we + //could include tracking for the remaining units and store information about overstock elsewhere. + AvailableStock += (MaxStockThreshold - AvailableStock); + } + else + { + AvailableStock += quantity; + } + + OnReorder = false; + + return AvailableStock - original; + } +} diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/ProductImage.cs b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/ProductImage.cs new file mode 100644 index 00000000000..8fceab06941 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/ProductImage.cs @@ -0,0 +1,3 @@ +namespace HotChocolate.Data.TestContext; + +public sealed record ProductImage(string Name, Func OpenStream); diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/ProductType.cs b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/ProductType.cs new file mode 100644 index 00000000000..9e777ccdbd2 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/TestContext/ProductType.cs @@ -0,0 +1,14 @@ +// ReSharper disable CollectionNeverUpdated.Global + +using System.ComponentModel.DataAnnotations; + +namespace HotChocolate.Data.TestContext; + +public class ProductType +{ + public int Id { get; set; } + + [Required] public string Name { get; set; } = default!; + + public ICollection Products { get; } = new List(); +} diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Expression.md b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Expression.md new file mode 100644 index 00000000000..7c8fe6476d6 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Expression.md @@ -0,0 +1,27 @@ +# Filter_With_Expression + +## SQL + +```text +.param set @__keys_0 '[1]' + +SELECT "b"."Id", "b"."DisplayName", "b"."Name", "b"."Details_Country_Name" +FROM "Brands" AS "b" +WHERE "b"."Id" IN ( + SELECT "k"."value" + FROM json_each(@__keys_0) AS "k" +) AND "b"."Name" LIKE 'Brand%' +``` + +## Result + +```json +{ + "data": { + "filterExpression": { + "name": "Brand0" + } + } +} +``` + diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Expression_Null.md b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Expression_Null.md new file mode 100644 index 00000000000..42e4487769c --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Expression_Null.md @@ -0,0 +1,27 @@ +# Filter_With_Expression_Null + +## SQL + +```text +.param set @__keys_0 '[1]' + +SELECT "b"."Id", "b"."DisplayName", "b"."Name", "b"."Details_Country_Name" +FROM "Brands" AS "b" +WHERE "b"."Id" IN ( + SELECT "k"."value" + FROM json_each(@__keys_0) AS "k" +) +``` + +## Result + +```json +{ + "data": { + "brandByIdFilterNull": { + "name": "Brand0" + } + } +} +``` + diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Filtering.md b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Filtering.md new file mode 100644 index 00000000000..17e80aa06c1 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Filtering.md @@ -0,0 +1,29 @@ +# Filter_With_Filtering + +## SQL + +```text +.param set @__keys_0 '[1]' + +SELECT "p"."Id", "p"."AvailableStock", "p"."BrandId", "p"."Description", "p"."ImageFileName", "p"."MaxStockThreshold", "p"."Name", "p"."OnReorder", "p"."Price", "p"."RestockThreshold", "p"."TypeId" +FROM "Products" AS "p" +WHERE "p"."BrandId" IN ( + SELECT "k"."value" + FROM json_each(@__keys_0) AS "k" +) +``` + +## Result + +```json +{ + "data": { + "filterContext": [ + { + "name": "Product 0-0" + } + ] + } +} +``` + diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Multi_Expression.md b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Multi_Expression.md new file mode 100644 index 00000000000..cb7b09fd484 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Multi_Expression.md @@ -0,0 +1,27 @@ +# Filter_With_Multi_Expression + +## SQL + +```text +.param set @__keys_0 '[1]' + +SELECT "b"."Id", "b"."DisplayName", "b"."Name", "b"."Details_Country_Name" +FROM "Brands" AS "b" +WHERE "b"."Id" IN ( + SELECT "k"."value" + FROM json_each(@__keys_0) AS "k" +) AND "b"."Name" LIKE 'Brand%' AND "b"."Name" LIKE '%0' +``` + +## Result + +```json +{ + "data": { + "multiFilterExpression": { + "name": "Brand0" + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderPredicateExtensions.cs b/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderPredicateExtensions.cs new file mode 100644 index 00000000000..f4c8421769b --- /dev/null +++ b/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderPredicateExtensions.cs @@ -0,0 +1,75 @@ +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using HotChocolate.Pagination; + +// ReSharper disable once CheckNamespace +namespace GreenDonut.Predicates; + +/// +/// Provides extension methods to pass a pagination context to a DataLoader. +/// +public static class HotChocolatePaginationBatchingDataLoaderPredicateExtensions +{ + /// + /// Adds a predicate as state to the DataLoader. + /// + /// + /// The DataLoader. + /// + /// + /// The predicate that shall be added as state to the DataLoader. + /// + /// + /// The key type of the DataLoader. + /// + /// + /// The value type of the DataLoader. + /// + /// + /// Returns the DataLoader with the added projection. + /// + /// + /// Throws if the is null. + /// + [Experimental(Experiments.Predicates)] + public static IPagingDataLoader> Where( + this IPagingDataLoader> dataLoader, + Expression>? predicate) + where TKey : notnull + { + if (dataLoader is null) + { + throw new ArgumentNullException(nameof(dataLoader)); + } + + if (predicate is null) + { + return dataLoader; + } + + if (dataLoader.ContextData.TryGetValue(typeof(IPredicateBuilder).FullName!, out var value)) + { + var context = (DefaultPredicateBuilder)value!; + context.Add(predicate); + return dataLoader; + } + + var branchKey = predicate.ToString(); + return (IPagingDataLoader>)dataLoader.Branch( + branchKey, + CreateBranch, + predicate); + + static IDataLoader CreateBranch( + string key, + IDataLoader> dataLoader, + Expression> predicate) + { + var branch = new PagingDataLoader>(dataLoader, key); + var context = new DefaultPredicateBuilder(); + branch.ContextData = branch.ContextData.SetItem(typeof(IPredicateBuilder).FullName!, context); + context.Add(predicate); + return branch; + } + } +} diff --git a/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs b/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderSelectorExtensions.cs similarity index 96% rename from src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs rename to src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderSelectorExtensions.cs index bbb33613241..31ae7ace89a 100644 --- a/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs +++ b/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderSelectorExtensions.cs @@ -2,15 +2,15 @@ using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Runtime.CompilerServices; -using GreenDonut.Projections; using HotChocolate.Pagination; -namespace GreenDonut.Projections; +// ReSharper disable once CheckNamespace +namespace GreenDonut.Selectors; /// /// Provides extension methods to pass a pagination context to a DataLoader. /// -public static class HotChocolatePaginationBatchingDataLoaderExtensions +public static class HotChocolatePaginationBatchingDataLoaderSelectorExtensions { /// /// Branches a DataLoader with the provided . @@ -85,7 +85,7 @@ static IDataLoader CreatePagingDataLoader( /// Throws if the is null. /// #if NET8_0_OR_GREATER - [Experimental(Experiments.Projections)] + [Experimental(Experiments.Selectors)] #endif public static IPagingDataLoader> Select( this IPagingDataLoader> dataLoader, @@ -124,7 +124,7 @@ static IDataLoader CreateBranch( return branch; } } - + private static string CreateBranchKey( PagingArguments pagingArguments) { @@ -238,7 +238,7 @@ private static int EstimateIntLength(int? value) } // if the number is negative we need one more digit for the sign - var length = (value < 0) ? 1 : 0; + var length = value < 0 ? 1 : 0; // we add the number of digits the number has to the length of the number. length += (int)Math.Floor(Math.Log10(Math.Abs(value.Value)) + 1); diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/InterfaceIntegrationTests.cs b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/InterfaceIntegrationTests.cs index 4c5e69206e2..e8d52383b02 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/InterfaceIntegrationTests.cs +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/InterfaceIntegrationTests.cs @@ -1,7 +1,7 @@ using System.Collections.Immutable; using CookieCrumble; using GreenDonut; -using GreenDonut.Projections; +using GreenDonut.Selectors; using HotChocolate.Data.TestContext; using HotChocolate.Execution; using HotChocolate.Execution.Processing; diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs index a8f9653bb1f..1837ef53dd4 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs @@ -2,7 +2,7 @@ using HotChocolate.Data.TestContext; using CookieCrumble; using GreenDonut; -using GreenDonut.Projections; +using GreenDonut.Selectors; using HotChocolate.Execution; using HotChocolate.Execution.Processing; using HotChocolate.Types; From 5149561612a3c26b06e9ce0496d559fd7a9bb54c Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Mon, 14 Oct 2024 23:22:09 +0200 Subject: [PATCH 067/154] Fixed Compile Issues --- .../src/Core/DataLoaderFetchContext.cs | 1 + src/GreenDonut/src/Core/IDataLoader.cs | 2 +- .../Predicates/DefaultPredicateBuilder.cs | 4 ++++ .../src/Core/Predicates/IPredicateBuilder.cs | 2 ++ .../PredicateDataLoaderExtensions.cs | 4 ++++ ...eExecutionPredicateDataLoaderExtensions.cs | 2 ++ .../DataLoaderTests.cs | 22 +++++++++++++++++ ...colate.Data.Filters.SqlServer.Tests.csproj | 2 ++ ...aderTests.Filter_With_Expression_NET6_0.md | 22 +++++++++++++++++ ...aderTests.Filter_With_Expression_NET7_0.md | 22 +++++++++++++++++ ...ests.Filter_With_Expression_Null_NET6_0.md | 22 +++++++++++++++++ ...oaderTests.Filter_With_Filtering_NET6_0.md | 24 +++++++++++++++++++ ...sts.Filter_With_Multi_Expression_NET6_0.md | 22 +++++++++++++++++ ...sts.Filter_With_Multi_Expression_NET7_0.md | 22 +++++++++++++++++ ...onBatchingDataLoaderPredicateExtensions.cs | 2 ++ 15 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Expression_NET6_0.md create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Expression_NET7_0.md create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Expression_Null_NET6_0.md create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Filtering_NET6_0.md create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Multi_Expression_NET6_0.md create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Multi_Expression_NET7_0.md diff --git a/src/GreenDonut/src/Core/DataLoaderFetchContext.cs b/src/GreenDonut/src/Core/DataLoaderFetchContext.cs index e2d8a1621ec..cc250fe428b 100644 --- a/src/GreenDonut/src/Core/DataLoaderFetchContext.cs +++ b/src/GreenDonut/src/Core/DataLoaderFetchContext.cs @@ -4,6 +4,7 @@ #endif #if NET6_0_OR_GREATER using GreenDonut.Selectors; +using GreenDonut.Predicates; #endif namespace GreenDonut; diff --git a/src/GreenDonut/src/Core/IDataLoader.cs b/src/GreenDonut/src/Core/IDataLoader.cs index 4bf0ddc2343..4cd5538b00a 100644 --- a/src/GreenDonut/src/Core/IDataLoader.cs +++ b/src/GreenDonut/src/Core/IDataLoader.cs @@ -1,6 +1,6 @@ using System.Collections.Immutable; #if NET8_0_OR_GREATER -using GreenDonut.Projections; +using GreenDonut.Selectors; #endif namespace GreenDonut; diff --git a/src/GreenDonut/src/Core/Predicates/DefaultPredicateBuilder.cs b/src/GreenDonut/src/Core/Predicates/DefaultPredicateBuilder.cs index bc80389c66b..bc10b8d212b 100644 --- a/src/GreenDonut/src/Core/Predicates/DefaultPredicateBuilder.cs +++ b/src/GreenDonut/src/Core/Predicates/DefaultPredicateBuilder.cs @@ -1,9 +1,12 @@ +#if NET6_0_OR_GREATER using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; namespace GreenDonut.Predicates; +#if NET8_0_OR_GREATER [Experimental(Experiments.Predicates)] +#endif internal sealed class DefaultPredicateBuilder : IPredicateBuilder { private List? _predicates; @@ -49,3 +52,4 @@ public void Add(Expression> selector) return expression; } } +#endif diff --git a/src/GreenDonut/src/Core/Predicates/IPredicateBuilder.cs b/src/GreenDonut/src/Core/Predicates/IPredicateBuilder.cs index 545ba2c9655..8997eba7812 100644 --- a/src/GreenDonut/src/Core/Predicates/IPredicateBuilder.cs +++ b/src/GreenDonut/src/Core/Predicates/IPredicateBuilder.cs @@ -7,7 +7,9 @@ namespace GreenDonut.Predicates; /// The predicate builder helps you create a combined predicate expression /// by adding multiple expressions together. /// +#if NET8_0_OR_GREATER [Experimental(Experiments.Predicates)] +#endif public interface IPredicateBuilder { /// diff --git a/src/GreenDonut/src/Core/Predicates/PredicateDataLoaderExtensions.cs b/src/GreenDonut/src/Core/Predicates/PredicateDataLoaderExtensions.cs index 53999442221..63b13ce4306 100644 --- a/src/GreenDonut/src/Core/Predicates/PredicateDataLoaderExtensions.cs +++ b/src/GreenDonut/src/Core/Predicates/PredicateDataLoaderExtensions.cs @@ -1,3 +1,4 @@ +#if NET6_0_OR_GREATER using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; @@ -6,7 +7,9 @@ namespace GreenDonut.Predicates; /// /// Data loader extensions for predicates. /// +#if NET8_0_OR_GREATER [Experimental(Experiments.Predicates)] +#endif public static class PredicateDataLoaderExtensions { /// @@ -113,3 +116,4 @@ public static IQueryable Where( return query; } } +#endif diff --git a/src/HotChocolate/Data/src/Data/Filters/Extensions/HotChocolateExecutionPredicateDataLoaderExtensions.cs b/src/HotChocolate/Data/src/Data/Filters/Extensions/HotChocolateExecutionPredicateDataLoaderExtensions.cs index d991dc0a314..cdd683e4829 100644 --- a/src/HotChocolate/Data/src/Data/Filters/Extensions/HotChocolateExecutionPredicateDataLoaderExtensions.cs +++ b/src/HotChocolate/Data/src/Data/Filters/Extensions/HotChocolateExecutionPredicateDataLoaderExtensions.cs @@ -9,7 +9,9 @@ namespace GreenDonut.Predicates; /// /// Provides extension methods for projection on DataLoader. /// +#if NET8_0_OR_GREATER [Experimental(Experiments.Predicates)] +#endif public static class HotChocolateExecutionPredicateDataLoaderExtensions { /// diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/DataLoaderTests.cs b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/DataLoaderTests.cs index ee39eb6b7a5..d9b1eec9917 100644 --- a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/DataLoaderTests.cs +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/DataLoaderTests.cs @@ -1,3 +1,4 @@ +#if NET6_0_OR_GREATER using System.Linq.Expressions; using CookieCrumble; using GreenDonut; @@ -38,7 +39,13 @@ public async Task Filter_With_Expression() } """); +#if NET8_0_OR_GREATER Snapshot.Create() +#elif NET7_0 + Snapshot.Create("NET7_0") +#else + Snapshot.Create("NET6_0") +#endif .AddSql(queries) .AddResult(result) .MatchMarkdownSnapshot(); @@ -70,7 +77,13 @@ public async Task Filter_With_Multi_Expression() } """); +#if NET8_0_OR_GREATER Snapshot.Create() +#elif NET7_0 + Snapshot.Create("NET7_0") +#else + Snapshot.Create("NET6_0") +#endif .AddSql(queries) .AddResult(result) .MatchMarkdownSnapshot(); @@ -102,7 +115,11 @@ public async Task Filter_With_Filtering() } """); +#if NET8_0_OR_GREATER Snapshot.Create() +#else + Snapshot.Create("NET6_0") +#endif .AddSql(queries) .AddResult(result) .MatchMarkdownSnapshot(); @@ -134,7 +151,11 @@ public async Task Filter_With_Expression_Null() } """); +#if NET8_0_OR_GREATER Snapshot.Create() +#else + Snapshot.Create("NET6_0") +#endif .AddSql(queries) .AddResult(result) .MatchMarkdownSnapshot(); @@ -277,3 +298,4 @@ public static Snapshot AddResult(this Snapshot snapshot, IExecutionResult result return snapshot; } } +#endif diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/HotChocolate.Data.Filters.SqlServer.Tests.csproj b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/HotChocolate.Data.Filters.SqlServer.Tests.csproj index 5ddfb18c7f1..fea78c5972b 100644 --- a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/HotChocolate.Data.Filters.SqlServer.Tests.csproj +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/HotChocolate.Data.Filters.SqlServer.Tests.csproj @@ -7,7 +7,9 @@ + + diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Expression_NET6_0.md b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Expression_NET6_0.md new file mode 100644 index 00000000000..80f6240b4fb --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Expression_NET6_0.md @@ -0,0 +1,22 @@ +# Filter_With_Expression + +## SQL + +```text +SELECT "b"."Id", "b"."DisplayName", "b"."Name", "b"."Details_Country_Name" +FROM "Brands" AS "b" +WHERE ("b"."Id" = 1) AND ("b"."Name" LIKE 'Brand%') +``` + +## Result + +```json +{ + "data": { + "filterExpression": { + "name": "Brand0" + } + } +} +``` + diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Expression_NET7_0.md b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Expression_NET7_0.md new file mode 100644 index 00000000000..d4869caba92 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Expression_NET7_0.md @@ -0,0 +1,22 @@ +# Filter_With_Expression + +## SQL + +```text +SELECT "b"."Id", "b"."DisplayName", "b"."Name", "b"."Details_Country_Name" +FROM "Brands" AS "b" +WHERE "b"."Id" = 1 AND ("b"."Name" LIKE 'Brand%') +``` + +## Result + +```json +{ + "data": { + "filterExpression": { + "name": "Brand0" + } + } +} +``` + diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Expression_Null_NET6_0.md b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Expression_Null_NET6_0.md new file mode 100644 index 00000000000..fb2a968ab82 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Expression_Null_NET6_0.md @@ -0,0 +1,22 @@ +# Filter_With_Expression_Null + +## SQL + +```text +SELECT "b"."Id", "b"."DisplayName", "b"."Name", "b"."Details_Country_Name" +FROM "Brands" AS "b" +WHERE "b"."Id" = 1 +``` + +## Result + +```json +{ + "data": { + "brandByIdFilterNull": { + "name": "Brand0" + } + } +} +``` + diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Filtering_NET6_0.md b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Filtering_NET6_0.md new file mode 100644 index 00000000000..578f1fb85fd --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Filtering_NET6_0.md @@ -0,0 +1,24 @@ +# Filter_With_Filtering + +## SQL + +```text +SELECT "p"."Id", "p"."AvailableStock", "p"."BrandId", "p"."Description", "p"."ImageFileName", "p"."MaxStockThreshold", "p"."Name", "p"."OnReorder", "p"."Price", "p"."RestockThreshold", "p"."TypeId" +FROM "Products" AS "p" +WHERE "p"."BrandId" = 1 +``` + +## Result + +```json +{ + "data": { + "filterContext": [ + { + "name": "Product 0-0" + } + ] + } +} +``` + diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Multi_Expression_NET6_0.md b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Multi_Expression_NET6_0.md new file mode 100644 index 00000000000..1cc09978ba7 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Multi_Expression_NET6_0.md @@ -0,0 +1,22 @@ +# Filter_With_Multi_Expression + +## SQL + +```text +SELECT "b"."Id", "b"."DisplayName", "b"."Name", "b"."Details_Country_Name" +FROM "Brands" AS "b" +WHERE ("b"."Id" = 1) AND (("b"."Name" LIKE 'Brand%') AND ("b"."Name" LIKE '%0')) +``` + +## Result + +```json +{ + "data": { + "multiFilterExpression": { + "name": "Brand0" + } + } +} +``` + diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Multi_Expression_NET7_0.md b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Multi_Expression_NET7_0.md new file mode 100644 index 00000000000..bcc30d49da2 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Multi_Expression_NET7_0.md @@ -0,0 +1,22 @@ +# Filter_With_Multi_Expression + +## SQL + +```text +SELECT "b"."Id", "b"."DisplayName", "b"."Name", "b"."Details_Country_Name" +FROM "Brands" AS "b" +WHERE "b"."Id" = 1 AND ("b"."Name" LIKE 'Brand%') AND ("b"."Name" LIKE '%0') +``` + +## Result + +```json +{ + "data": { + "multiFilterExpression": { + "name": "Brand0" + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderPredicateExtensions.cs b/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderPredicateExtensions.cs index f4c8421769b..1cd6c8202df 100644 --- a/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderPredicateExtensions.cs +++ b/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderPredicateExtensions.cs @@ -31,7 +31,9 @@ public static class HotChocolatePaginationBatchingDataLoaderPredicateExtensions /// /// Throws if the is null. /// +#if NET8_0_OR_GREATER [Experimental(Experiments.Predicates)] +#endif public static IPagingDataLoader> Where( this IPagingDataLoader> dataLoader, Expression>? predicate) From b27fa585dcdf9813923504f3309d56fe1804d3e1 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Tue, 15 Oct 2024 01:07:30 +0200 Subject: [PATCH 068/154] Added event hook for document not found in storage. (#7596) --- .../AggregateExecutionDiagnosticEvents.cs | 10 ++++++++++ .../ExecutionDiagnosticEventListener.cs | 7 +++++++ .../Instrumentation/IExecutionDiagnosticEvents.cs | 13 +++++++++++++ .../NoopExecutionDiagnosticEvents.cs | 4 ++++ .../PersistedOperationNotFoundMiddleware.cs | 11 ++++++----- 5 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/HotChocolate/Core/src/Execution/Instrumentation/AggregateExecutionDiagnosticEvents.cs b/src/HotChocolate/Core/src/Execution/Instrumentation/AggregateExecutionDiagnosticEvents.cs index b4317dffb0a..68527cc920b 100644 --- a/src/HotChocolate/Core/src/Execution/Instrumentation/AggregateExecutionDiagnosticEvents.cs +++ b/src/HotChocolate/Core/src/Execution/Instrumentation/AggregateExecutionDiagnosticEvents.cs @@ -308,6 +308,16 @@ public void RetrievedDocumentFromStorage(IRequestContext context) } } + public void DocumentNotFoundInStorage( + IRequestContext context, + OperationDocumentId documentId) + { + for (var i = 0; i < _listeners.Length; i++) + { + _listeners[i].DocumentNotFoundInStorage(context, documentId); + } + } + public void AddedOperationToCache(IRequestContext context) { for (var i = 0; i < _listeners.Length; i++) diff --git a/src/HotChocolate/Core/src/Execution/Instrumentation/ExecutionDiagnosticEventListener.cs b/src/HotChocolate/Core/src/Execution/Instrumentation/ExecutionDiagnosticEventListener.cs index 2e2955445f1..d022eb52daa 100644 --- a/src/HotChocolate/Core/src/Execution/Instrumentation/ExecutionDiagnosticEventListener.cs +++ b/src/HotChocolate/Core/src/Execution/Instrumentation/ExecutionDiagnosticEventListener.cs @@ -164,6 +164,13 @@ public virtual void RetrievedDocumentFromStorage(IRequestContext context) { } + /// + public virtual void DocumentNotFoundInStorage( + IRequestContext context, + OperationDocumentId documentId) + { + } + /// public virtual void AddedOperationToCache(IRequestContext context) { diff --git a/src/HotChocolate/Core/src/Execution/Instrumentation/IExecutionDiagnosticEvents.cs b/src/HotChocolate/Core/src/Execution/Instrumentation/IExecutionDiagnosticEvents.cs index 2b6ffb6eb43..67feace4d6e 100644 --- a/src/HotChocolate/Core/src/Execution/Instrumentation/IExecutionDiagnosticEvents.cs +++ b/src/HotChocolate/Core/src/Execution/Instrumentation/IExecutionDiagnosticEvents.cs @@ -352,6 +352,19 @@ public interface IExecutionDiagnosticEvents /// void RetrievedDocumentFromStorage(IRequestContext context); + /// + /// Called when the document for a persisted operation could not be found in the + /// operation document storage. + /// + /// + /// The request context encapsulates all GraphQL-specific information + /// about an individual GraphQL request. + /// + /// + /// The document id that was not found in the storage. + /// + void DocumentNotFoundInStorage(IRequestContext context, OperationDocumentId documentId); + /// /// A compiled operation was added to the operation cache. /// diff --git a/src/HotChocolate/Core/src/Execution/Instrumentation/NoopExecutionDiagnosticEvents.cs b/src/HotChocolate/Core/src/Execution/Instrumentation/NoopExecutionDiagnosticEvents.cs index 33625aab74e..ff5a934b758 100644 --- a/src/HotChocolate/Core/src/Execution/Instrumentation/NoopExecutionDiagnosticEvents.cs +++ b/src/HotChocolate/Core/src/Execution/Instrumentation/NoopExecutionDiagnosticEvents.cs @@ -101,6 +101,10 @@ public void RetrievedDocumentFromStorage(IRequestContext context) { } + public void DocumentNotFoundInStorage(IRequestContext context, OperationDocumentId documentId) + { + } + public void AddedOperationToCache(IRequestContext context) { } diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/PersistedOperationNotFoundMiddleware.cs b/src/HotChocolate/Core/src/Execution/Pipeline/PersistedOperationNotFoundMiddleware.cs index 3603cab501e..75cf7533324 100644 --- a/src/HotChocolate/Core/src/Execution/Pipeline/PersistedOperationNotFoundMiddleware.cs +++ b/src/HotChocolate/Core/src/Execution/Pipeline/PersistedOperationNotFoundMiddleware.cs @@ -27,15 +27,16 @@ public ValueTask InvokeAsync(IRequestContext context) return _next(context); } + // we know that the key is not null since otherwise the request would have + // failed already since no operation is specified. var requestedKey = - context.Request.DocumentId ?? + (context.Request.DocumentId ?? context.DocumentId ?? context.DocumentHash ?? - context.Request.DocumentHash; + context.Request.DocumentHash)!.Value; - // we know that the key is not null since otherwise the request would have - // failed already since no operation is specified. - var error = ErrorHelper.PersistedOperationNotFound(requestedKey!.Value); + _diagnosticEvents.DocumentNotFoundInStorage(context, requestedKey); + var error = ErrorHelper.PersistedOperationNotFound(requestedKey); _diagnosticEvents.RequestError(context, new GraphQLException(error)); context.Result = OperationResultBuilder.CreateError(error, _statusCode); From 4356b88bf9f9fbb9511c4150efeb8f55e4363b62 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Tue, 15 Oct 2024 02:21:39 +0200 Subject: [PATCH 069/154] Updated Nitro to 20.0.2 (#7597) --- src/HotChocolate/Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HotChocolate/Directory.Build.props b/src/HotChocolate/Directory.Build.props index 04d61431827..8766174fd84 100644 --- a/src/HotChocolate/Directory.Build.props +++ b/src/HotChocolate/Directory.Build.props @@ -4,6 +4,6 @@ hotchocolate-signet.png - 19.0.1 + 20.0.2 From 4f109b139b631bb13edd02e1d2349f8604be92a6 Mon Sep 17 00:00:00 2001 From: Glen Date: Wed, 16 Oct 2024 07:42:03 +0200 Subject: [PATCH 070/154] Set NuGetAuditMode to "direct" (#7606) --- src/Directory.Build.props | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 159157aea65..0fffd3a8dc9 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -7,6 +7,7 @@ true enable enable + direct From 935cbea7feb0dde70d68333256db78fa75c8de21 Mon Sep 17 00:00:00 2001 From: Glen Date: Wed, 16 Oct 2024 07:42:38 +0200 Subject: [PATCH 071/154] Updated insecure packages (#7607) --- src/Directory.Packages.props | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index b9a57951277..a96023bc781 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -75,7 +75,7 @@ - + @@ -86,7 +86,7 @@ - + @@ -123,7 +123,7 @@ - + @@ -140,7 +140,7 @@ - + @@ -150,7 +150,7 @@ - + From 671dfd0a9930a6aad3d9d41ffe9dff7dedd6ed1b Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 16 Oct 2024 08:23:56 +0200 Subject: [PATCH 072/154] Fixed issue with defer path when defers are nested. (#7605) --- .../Processing/DeferredExecutionTask.cs | 5 +- .../src/Execution/Processing/PathHelper.cs | 27 +++++++-- .../DeferAndStreamTestSchema.cs | 23 +++++++- .../Core/test/Execution.Tests/DeferTests.cs | 56 +++++++++++++++---- ...Is_Passed_To_DeferContext_Single_Defer.md} | 5 ++ ...Is_Passed_To_DeferContext_Stacked_Defer.md | 12 ++++ ..._Passed_To_DeferContext_Stacked_Defer.snap | 7 --- ..._Passed_To_DeferContext_Stacked_Defer_2.md | 14 +++++ ...nap => DeferTests.FragmentSpread_Defer.md} | 5 ++ ...s.FragmentSpread_Defer_If_Set_To_false.md} | 4 ++ ...ntSpread_Defer_If_Variable_Set_To_false.md | 11 ++++ ...s.FragmentSpread_Defer_Label_Set_To_abc.md | 12 ++++ ...DeferTests.FragmentSpread_Defer_Nested.md} | 5 ++ ...nap => DeferTests.InlineFragment_Defer.md} | 5 ++ .../DeferTests.InlineFragment_Defer.snap | 7 --- ...s.InlineFragment_Defer_If_Set_To_false.md} | 4 ++ ....InlineFragment_Defer_If_Set_To_false.snap | 7 --- ...Fragment_Defer_If_Variable_Set_To_false.md | 11 ++++ ...agment_Defer_If_Variable_Set_To_false.snap | 7 --- ...s.InlineFragment_Defer_Label_Set_To_abc.md | 12 ++++ ...InlineFragment_Defer_Label_Set_To_abc.snap | 7 --- ...DeferTests.InlineFragment_Defer_Nested.md} | 5 ++ 22 files changed, 194 insertions(+), 57 deletions(-) rename src/HotChocolate/Core/test/Execution.Tests/__snapshots__/{DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer.snap => DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer.md} (50%) create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Stacked_Defer.md delete mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Stacked_Defer.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Stacked_Defer_2.md rename src/HotChocolate/Core/test/Execution.Tests/__snapshots__/{DeferTests.FragmentSpread_Defer_If_Variable_Set_To_false.snap => DeferTests.FragmentSpread_Defer.md} (65%) rename src/HotChocolate/Core/test/Execution.Tests/__snapshots__/{DeferTests.FragmentSpread_Defer.snap => DeferTests.FragmentSpread_Defer_If_Set_To_false.md} (57%) create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_If_Variable_Set_To_false.md create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_Label_Set_To_abc.md rename src/HotChocolate/Core/test/Execution.Tests/__snapshots__/{DeferTests.FragmentSpread_Defer_Nested.snap => DeferTests.FragmentSpread_Defer_Nested.md} (67%) rename src/HotChocolate/Core/test/Execution.Tests/__snapshots__/{DeferTests.FragmentSpread_Defer_Label_Set_To_abc.snap => DeferTests.InlineFragment_Defer.md} (65%) delete mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer.snap rename src/HotChocolate/Core/test/Execution.Tests/__snapshots__/{DeferTests.FragmentSpread_Defer_If_Set_To_false.snap => DeferTests.InlineFragment_Defer_If_Set_To_false.md} (57%) delete mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_If_Set_To_false.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_If_Variable_Set_To_false.md delete mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_If_Variable_Set_To_false.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_Label_Set_To_abc.md delete mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_Label_Set_To_abc.snap rename src/HotChocolate/Core/test/Execution.Tests/__snapshots__/{DeferTests.InlineFragment_Defer_Nested.snap => DeferTests.InlineFragment_Defer_Nested.md} (67%) diff --git a/src/HotChocolate/Core/src/Execution/Processing/DeferredExecutionTask.cs b/src/HotChocolate/Core/src/Execution/Processing/DeferredExecutionTask.cs index 93094f752d0..747c21d4c77 100644 --- a/src/HotChocolate/Core/src/Execution/Processing/DeferredExecutionTask.cs +++ b/src/HotChocolate/Core/src/Execution/Processing/DeferredExecutionTask.cs @@ -59,8 +59,9 @@ public void Begin(OperationContextOwner operationContextOwner, uint resultId, ui Task.Factory.StartNew( () => { - ExecutionContext.Run(capturedContext, (state) => - ExecuteAsync(operationContextOwner, resultId, parentResultId, patchId), + ExecutionContext.Run( + capturedContext, + _ => ExecuteAsync(operationContextOwner, resultId, parentResultId, patchId), null); }, default, diff --git a/src/HotChocolate/Core/src/Execution/Processing/PathHelper.cs b/src/HotChocolate/Core/src/Execution/Processing/PathHelper.cs index b15e3afcb15..79d20a25f83 100644 --- a/src/HotChocolate/Core/src/Execution/Processing/PathHelper.cs +++ b/src/HotChocolate/Core/src/Execution/Processing/PathHelper.cs @@ -8,7 +8,19 @@ internal static class PathHelper private const int _initialPathLength = 64; public static Path CreatePathFromContext(ObjectResult parent) - => parent.Parent is null ? Path.Root : CreatePath(parent); + { + if (parent.Parent is null) + { + if (parent.PatchPath is null) + { + return Path.Root; + } + + return parent.PatchPath; + } + + return CreatePath(parent); + } public static Path CreatePathFromContext(ISelection selection, ResultData parent, int index) => parent switch @@ -37,8 +49,9 @@ private static Path CreatePath(ResultData parent, object segmentValue) { var segments = ArrayPool.Shared.Rent(_initialPathLength); segments[0] = segmentValue; - var length = Build(segments, parent); - var path = CreatePath(parent.PatchPath, segments, length); + var current = parent; + var length = Build(segments, ref current); + var path = CreatePath(current.PatchPath, segments, length); ArrayPool.Shared.Return(segments); return path; } @@ -46,8 +59,9 @@ private static Path CreatePath(ResultData parent, object segmentValue) private static Path CreatePath(ResultData parent) { var segments = ArrayPool.Shared.Rent(_initialPathLength); - var length = Build(segments, parent, 0); - var path = CreatePath(parent.PatchPath, segments, length); + var current = parent; + var length = Build(segments, ref current, 0); + var path = CreatePath(current.PatchPath, segments, length); ArrayPool.Shared.Return(segments); return path; } @@ -73,7 +87,7 @@ private static Path CreatePath(Path? patchPath, object[] segments, int length) return path; } - private static int Build(object[] segments, ResultData parent, int start = 1) + private static int Build(object[] segments, ref ResultData parent, int start = 1) { var segment = start; var current = parent; @@ -117,6 +131,7 @@ private static int Build(object[] segments, ResultData parent, int start = 1) } } + parent = current; return segment; } } diff --git a/src/HotChocolate/Core/test/Execution.Tests/DeferAndStreamTestSchema.cs b/src/HotChocolate/Core/test/Execution.Tests/DeferAndStreamTestSchema.cs index ff978ec370b..bb45d6e9ae3 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/DeferAndStreamTestSchema.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/DeferAndStreamTestSchema.cs @@ -64,7 +64,8 @@ public async Task Wait(int m, CancellationToken ct) public async Task EnsureState() { - await Task.Delay(150); + var random = new Random(); + await Task.Delay(random.Next(500, 1000)); return new Stateful(); } } @@ -92,7 +93,25 @@ public class Stateful { public async Task GetState([GlobalState] string requestState) { - await Task.Delay(250); + var random = new Random(); + await Task.Delay(random.Next(1000, 5000)); + return requestState; + } + + public async Task GetMore() + { + var random = new Random(); + await Task.Delay(random.Next(1000, 5000)); + return new MoreState(); + } + } + + public class MoreState + { + public async Task GetStuff([GlobalState] string requestState) + { + var random = new Random(); + await Task.Delay(random.Next(1000, 5000)); return requestState; } } diff --git a/src/HotChocolate/Core/test/Execution.Tests/DeferTests.cs b/src/HotChocolate/Core/test/Execution.Tests/DeferTests.cs index dc28740f3b0..343cb6a0a51 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/DeferTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/DeferTests.cs @@ -22,7 +22,7 @@ ... @defer { } """); - Assert.IsType(result).MatchSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); } [Fact] @@ -46,7 +46,7 @@ ... @defer { } """); - Assert.IsType(result).MatchSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); } [Fact] @@ -65,7 +65,7 @@ ... @defer(label: ""abc"") { } }"); - Assert.IsType(result).MatchSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); } [Fact] @@ -86,7 +86,7 @@ ... @defer(if: false) { } """); - Assert.IsType(result).MatchSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); } [Fact] @@ -112,7 +112,7 @@ ... @defer(if: $defer) { .SetVariableValues(new Dictionary { { "defer", false }, }) .Build()); - Assert.IsType(result).MatchSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); } [Fact] @@ -135,7 +135,7 @@ fragment Foo on Query { } """); - Assert.IsType(result).MatchSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); } [Fact] @@ -161,7 +161,7 @@ ... @defer { } """); - Assert.IsType(result).MatchSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); } [Fact] @@ -184,7 +184,7 @@ fragment Foo on Query { } """); - Assert.IsType(result).MatchSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); } [Fact] @@ -207,7 +207,7 @@ fragment Foo on Query { } """); - Assert.IsType(result).MatchSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); } [Fact] @@ -235,7 +235,7 @@ fragment Foo on Query { .SetVariableValues(new Dictionary { { "defer", false }, }) .Build()); - Assert.IsType(result).MatchSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); } [Fact] @@ -263,7 +263,39 @@ ... @defer { .SetGlobalState("requestState", "state 123") .Build()); - Assert.IsType(result).MatchSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); + } + + [Fact] + public async Task Ensure_GlobalState_Is_Passed_To_DeferContext_Stacked_Defer_2() + { + // arrange + var executor = await DeferAndStreamTestSchema.CreateAsync(); + + // act + await using var response = await executor.ExecuteAsync( + OperationRequestBuilder + .New() + .SetDocument( + """ + { + ... @defer { + e: ensureState { + ... @defer { + more { + ... @defer { + stuff + } + } + } + } + } + } + """) + .SetGlobalState("requestState", "state 123") + .Build()); + + Assert.IsType(response).MatchMarkdownSnapshot(); } [Fact] @@ -289,6 +321,6 @@ ... @defer { .SetGlobalState("requestState", "state 123") .Build()); - Assert.IsType(result).MatchSnapshot(); + Assert.IsType(result).MatchMarkdownSnapshot(); } } diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer.md similarity index 50% rename from src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer.snap rename to src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer.md index b312149a5b4..6efc6e6bfde 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer.md @@ -1,3 +1,6 @@ +# Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer + +```text { "data": { "ensureState": { @@ -5,3 +8,5 @@ } } } + +``` 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 new file mode 100644 index 00000000000..7392a71cb38 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Stacked_Defer.md @@ -0,0 +1,12 @@ +# Ensure_GlobalState_Is_Passed_To_DeferContext_Stacked_Defer + +```text +{ + "data": { + "ensureState": { + "state": "state 123" + } + } +} + +``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Stacked_Defer.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Stacked_Defer.snap deleted file mode 100644 index b312149a5b4..00000000000 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Stacked_Defer.snap +++ /dev/null @@ -1,7 +0,0 @@ -{ - "data": { - "ensureState": { - "state": "state 123" - } - } -} 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 new file mode 100644 index 00000000000..20f6822686e --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Stacked_Defer_2.md @@ -0,0 +1,14 @@ +# Ensure_GlobalState_Is_Passed_To_DeferContext_Stacked_Defer_2 + +```text +{ + "data": { + "e": { + "more": { + "stuff": "state 123" + } + } + } +} + +``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_If_Variable_Set_To_false.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer.md similarity index 65% rename from src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_If_Variable_Set_To_false.snap rename to src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer.md index e04b7f6c488..6660699b0bb 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_If_Variable_Set_To_false.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer.md @@ -1,3 +1,6 @@ +# FragmentSpread_Defer + +```text { "data": { "person": { @@ -5,3 +8,5 @@ } } } + +``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_If_Set_To_false.md similarity index 57% rename from src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer.snap rename to src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_If_Set_To_false.md index e04b7f6c488..7c63efedd52 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_If_Set_To_false.md @@ -1,3 +1,6 @@ +# FragmentSpread_Defer_If_Set_To_false + +```json { "data": { "person": { @@ -5,3 +8,4 @@ } } } +``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_If_Variable_Set_To_false.md b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_If_Variable_Set_To_false.md new file mode 100644 index 00000000000..fdb6fefb50f --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_If_Variable_Set_To_false.md @@ -0,0 +1,11 @@ +# FragmentSpread_Defer_If_Variable_Set_To_false + +```json +{ + "data": { + "person": { + "id": "UGVyc29uOjE=" + } + } +} +``` 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 new file mode 100644 index 00000000000..f506c45d484 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_Label_Set_To_abc.md @@ -0,0 +1,12 @@ +# FragmentSpread_Defer_Label_Set_To_abc + +```text +{ + "data": { + "person": { + "id": "UGVyc29uOjE=" + } + } +} + +``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_Nested.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_Nested.md similarity index 67% rename from src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_Nested.snap rename to src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_Nested.md index 4f99b3bb651..212b0c67482 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_Nested.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_Nested.md @@ -1,3 +1,6 @@ +# FragmentSpread_Defer_Nested + +```text { "data": { "person": { @@ -6,3 +9,5 @@ } } } + +``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_Label_Set_To_abc.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer.md similarity index 65% rename from src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_Label_Set_To_abc.snap rename to src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer.md index e04b7f6c488..203b8a7163f 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_Label_Set_To_abc.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer.md @@ -1,3 +1,6 @@ +# InlineFragment_Defer + +```text { "data": { "person": { @@ -5,3 +8,5 @@ } } } + +``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer.snap deleted file mode 100644 index e04b7f6c488..00000000000 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer.snap +++ /dev/null @@ -1,7 +0,0 @@ -{ - "data": { - "person": { - "id": "UGVyc29uOjE=" - } - } -} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_If_Set_To_false.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_If_Set_To_false.md similarity index 57% rename from src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_If_Set_To_false.snap rename to src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_If_Set_To_false.md index e04b7f6c488..0dc23d4ed3a 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.FragmentSpread_Defer_If_Set_To_false.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_If_Set_To_false.md @@ -1,3 +1,6 @@ +# InlineFragment_Defer_If_Set_To_false + +```json { "data": { "person": { @@ -5,3 +8,4 @@ } } } +``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_If_Set_To_false.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_If_Set_To_false.snap deleted file mode 100644 index e04b7f6c488..00000000000 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_If_Set_To_false.snap +++ /dev/null @@ -1,7 +0,0 @@ -{ - "data": { - "person": { - "id": "UGVyc29uOjE=" - } - } -} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_If_Variable_Set_To_false.md b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_If_Variable_Set_To_false.md new file mode 100644 index 00000000000..25b59b30933 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_If_Variable_Set_To_false.md @@ -0,0 +1,11 @@ +# InlineFragment_Defer_If_Variable_Set_To_false + +```json +{ + "data": { + "person": { + "id": "UGVyc29uOjE=" + } + } +} +``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_If_Variable_Set_To_false.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_If_Variable_Set_To_false.snap deleted file mode 100644 index e04b7f6c488..00000000000 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_If_Variable_Set_To_false.snap +++ /dev/null @@ -1,7 +0,0 @@ -{ - "data": { - "person": { - "id": "UGVyc29uOjE=" - } - } -} 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 new file mode 100644 index 00000000000..2539516c9f4 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_Label_Set_To_abc.md @@ -0,0 +1,12 @@ +# InlineFragment_Defer_Label_Set_To_abc + +```text +{ + "data": { + "person": { + "id": "UGVyc29uOjE=" + } + } +} + +``` diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_Label_Set_To_abc.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_Label_Set_To_abc.snap deleted file mode 100644 index e04b7f6c488..00000000000 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_Label_Set_To_abc.snap +++ /dev/null @@ -1,7 +0,0 @@ -{ - "data": { - "person": { - "id": "UGVyc29uOjE=" - } - } -} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_Nested.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_Nested.md similarity index 67% rename from src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_Nested.snap rename to src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_Nested.md index 4f99b3bb651..84c20b20a12 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_Nested.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.InlineFragment_Defer_Nested.md @@ -1,3 +1,6 @@ +# InlineFragment_Defer_Nested + +```text { "data": { "person": { @@ -6,3 +9,5 @@ } } } + +``` From 770775d1c21bee8b5c886dfd869cc3f445de31c4 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 16 Oct 2024 08:28:52 +0200 Subject: [PATCH 073/154] Fixed issue with predicates and selectors in generated DataLoader. (#7610) --- .../FileBuilders/DataLoaderFileBuilder.cs | 14 +- .../src/Types.Analyzers/WellKnownTypes.cs | 2 +- .../Types.Analyzers.Tests/DataLoaderTests.cs | 50 +++++++ ...r_With_PredicateBuilder_MatchesSnapshot.md | 125 ++++++++++++++++++ ...er_With_SelectorBuilder_MatchesSnapshot.md | 125 ++++++++++++++++++ 5 files changed, 314 insertions(+), 2 deletions(-) create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PredicateBuilder_MatchesSnapshot.md create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_SelectorBuilder_MatchesSnapshot.md diff --git a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs index 2f97e844fd3..01fd447f696 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs @@ -264,7 +264,19 @@ public void WriteDataLoaderLoadMethod( parameter.StateKey); _writer.IncreaseIndent(); _writer.WriteIndentedLine( - "?? new global::GreenDonut.Projections.DefaultSelectorBuilder();"); + "?? new global::GreenDonut.Selectors.DefaultSelectorBuilder();"); + _writer.DecreaseIndent(); + } + else if (parameter.Kind is DataLoaderParameterKind.PredicateBuilder) + { + _writer.WriteIndentedLine( + "var {0} = context.GetState<{1}>(\"{2}\")", + parameter.VariableName, + parameter.Type.ToFullyQualified(), + parameter.StateKey); + _writer.IncreaseIndent(); + _writer.WriteIndentedLine( + "?? new global::GreenDonut.Predicates.DefaultPredicateBuilder();"); _writer.DecreaseIndent(); } else if (parameter.Kind is DataLoaderParameterKind.PagingArguments) diff --git a/src/HotChocolate/Core/src/Types.Analyzers/WellKnownTypes.cs b/src/HotChocolate/Core/src/Types.Analyzers/WellKnownTypes.cs index 4a224bc6bb5..550cab59229 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/WellKnownTypes.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/WellKnownTypes.cs @@ -66,7 +66,7 @@ public static class WellKnownTypes public const string PromiseCacheObserver = "GreenDonut.PromiseCacheObserver"; public const string KeyValuePair = "System.Collections.Generic.KeyValuePair"; public const string EnumerableExtensions = "System.Linq.Enumerable"; - public const string SelectorBuilder = "GreenDonut.Projections.ISelectorBuilder"; + public const string SelectorBuilder = "GreenDonut.Selectors.ISelectorBuilder"; public const string PredicateBuilder = "GreenDonut.Predicates.IPredicateBuilder"; public const string PagingArguments = "HotChocolate.Pagination.PagingArguments"; diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/DataLoaderTests.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/DataLoaderTests.cs index 413e8156841..a654e1f217c 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/DataLoaderTests.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/DataLoaderTests.cs @@ -449,4 +449,54 @@ public static Task> GetEntityByIdAsync( } """).MatchMarkdownAsync(); } + + [Fact] + public async Task GenerateSource_BatchDataLoader_With_SelectorBuilder_MatchesSnapshot() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using HotChocolate; + using GreenDonut; + + namespace TestNamespace; + + internal static class TestClass + { + [DataLoader] + public static Task> GetEntityByIdAsync( + IReadOnlyList entityIds, + GreenDonut.Selectors.ISelectorBuilder selector, + CancellationToken cancellationToken) + => default!; + } + """).MatchMarkdownAsync(); + } + + [Fact] + public async Task GenerateSource_BatchDataLoader_With_PredicateBuilder_MatchesSnapshot() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using HotChocolate; + using GreenDonut; + + namespace TestNamespace; + + internal static class TestClass + { + [DataLoader] + public static Task> GetEntityByIdAsync( + IReadOnlyList entityIds, + GreenDonut.Predicates.IPredicateBuilder predicate, + CancellationToken cancellationToken) + => default!; + } + """).MatchMarkdownAsync(); + } } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PredicateBuilder_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PredicateBuilder_MatchesSnapshot.md new file mode 100644 index 00000000000..4f861540b39 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PredicateBuilder_MatchesSnapshot.md @@ -0,0 +1,125 @@ +# GenerateSource_BatchDataLoader_With_PredicateBuilder_MatchesSnapshot + +## GreenDonutDataLoader.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using Microsoft.Extensions.DependencyInjection; +using GreenDonut; + +namespace TestNamespace +{ + public interface IEntityByIdDataLoader + : global::GreenDonut.IDataLoader + { + } + + public sealed class EntityByIdDataLoader + : global::GreenDonut.DataLoaderBase + , IEntityByIdDataLoader + { + private readonly global::System.IServiceProvider _services; + + public EntityByIdDataLoader( + global::System.IServiceProvider services, + global::GreenDonut.IBatchScheduler batchScheduler, + global::GreenDonut.DataLoaderOptions options) + : base(batchScheduler, options) + { + _services = services ?? + throw new global::System.ArgumentNullException(nameof(services)); + } + + protected override async global::System.Threading.Tasks.ValueTask FetchAsync( + global::System.Collections.Generic.IReadOnlyList keys, + global::System.Memory> results, + global::GreenDonut.DataLoaderFetchContext context, + global::System.Threading.CancellationToken ct) + { + var p1 = context.GetState("GreenDonut.Predicates.IPredicateBuilder") + ?? new global::GreenDonut.Predicates.DefaultPredicateBuilder(); + var temp = await TestNamespace.TestClass.GetEntityByIdAsync(keys, p1, ct).ConfigureAwait(false); + CopyResults(keys, results.Span, temp); + } + + private void CopyResults( + global::System.Collections.Generic.IReadOnlyList keys, + global::System.Span> results, + global::System.Collections.Generic.IDictionary resultMap) + { + for (var i = 0; i < keys.Count; i++) + { + var key = keys[i]; + if (resultMap.TryGetValue(key, out var value)) + { + results[i] = global::GreenDonut.Result.Resolve(value); + } + else + { + results[i] = global::GreenDonut.Result.Resolve(default(string)); + } + } + } + } +} + + +``` + +## HotChocolateTypeModule.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + builder.AddDataLoader(); + return builder; + } + } +} + +``` + +## Compilation Diagnostics + +```json +[ + { + "Id": "GD0002", + "Title": "Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.", + "Severity": "Error", + "WarningLevel": 0, + "Location": ": (13,8)-(13,47)", + "HelpLinkUri": "https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS9204)", + "MessageFormat": "'{0}' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.", + "Message": "'GreenDonut.Predicates.IPredicateBuilder' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.", + "Category": "Compiler", + "CustomTags": [ + "Compiler", + "Telemetry", + "CustomObsolete" + ] + } +] +``` + diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_SelectorBuilder_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_SelectorBuilder_MatchesSnapshot.md new file mode 100644 index 00000000000..1e2b7b762a9 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_SelectorBuilder_MatchesSnapshot.md @@ -0,0 +1,125 @@ +# GenerateSource_BatchDataLoader_With_SelectorBuilder_MatchesSnapshot + +## GreenDonutDataLoader.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using Microsoft.Extensions.DependencyInjection; +using GreenDonut; + +namespace TestNamespace +{ + public interface IEntityByIdDataLoader + : global::GreenDonut.IDataLoader + { + } + + public sealed class EntityByIdDataLoader + : global::GreenDonut.DataLoaderBase + , IEntityByIdDataLoader + { + private readonly global::System.IServiceProvider _services; + + public EntityByIdDataLoader( + global::System.IServiceProvider services, + global::GreenDonut.IBatchScheduler batchScheduler, + global::GreenDonut.DataLoaderOptions options) + : base(batchScheduler, options) + { + _services = services ?? + throw new global::System.ArgumentNullException(nameof(services)); + } + + protected override async global::System.Threading.Tasks.ValueTask FetchAsync( + global::System.Collections.Generic.IReadOnlyList keys, + global::System.Memory> results, + global::GreenDonut.DataLoaderFetchContext context, + global::System.Threading.CancellationToken ct) + { + var p1 = context.GetState("GreenDonut.Selectors.ISelectorBuilder") + ?? new global::GreenDonut.Selectors.DefaultSelectorBuilder(); + var temp = await TestNamespace.TestClass.GetEntityByIdAsync(keys, p1, ct).ConfigureAwait(false); + CopyResults(keys, results.Span, temp); + } + + private void CopyResults( + global::System.Collections.Generic.IReadOnlyList keys, + global::System.Span> results, + global::System.Collections.Generic.IDictionary resultMap) + { + for (var i = 0; i < keys.Count; i++) + { + var key = keys[i]; + if (resultMap.TryGetValue(key, out var value)) + { + results[i] = global::GreenDonut.Result.Resolve(value); + } + else + { + results[i] = global::GreenDonut.Result.Resolve(default(string)); + } + } + } + } +} + + +``` + +## HotChocolateTypeModule.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + builder.AddDataLoader(); + return builder; + } + } +} + +``` + +## Compilation Diagnostics + +```json +[ + { + "Id": "GD0001", + "Title": "Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.", + "Severity": "Error", + "WarningLevel": 0, + "Location": ": (13,8)-(13,45)", + "HelpLinkUri": "https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS9204)", + "MessageFormat": "'{0}' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.", + "Message": "'GreenDonut.Selectors.ISelectorBuilder' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.", + "Category": "Compiler", + "CustomTags": [ + "Compiler", + "Telemetry", + "CustomObsolete" + ] + } +] +``` + From a1b8caae358e075e8e834bbcf76ae937e25b3b03 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 16 Oct 2024 14:40:51 +0200 Subject: [PATCH 074/154] Fixed issue with global state when using deferred execution and scoped services. (#7611) --- .../Core/src/Execution/IRequestContext.cs | 5 -- .../Pipeline/OperationExecutionMiddleware.cs | 6 +- .../Core/src/Execution/RequestContext.cs | 33 ---------- .../Core/src/Execution/RequestExecutor.cs | 12 +++- .../DeferAndStreamTestSchema.cs | 16 +++++ .../Core/test/Execution.Tests/DeferTests.cs | 63 ++++++++++++++++++- ...s_Passed_To_DeferContext_Single_Defer_2.md | 12 ++++ 7 files changed, 100 insertions(+), 47 deletions(-) create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer_2.md diff --git a/src/HotChocolate/Core/src/Execution/IRequestContext.cs b/src/HotChocolate/Core/src/Execution/IRequestContext.cs index 16197203e85..22a10c990fe 100644 --- a/src/HotChocolate/Core/src/Execution/IRequestContext.cs +++ b/src/HotChocolate/Core/src/Execution/IRequestContext.cs @@ -113,9 +113,4 @@ public interface IRequestContext : IHasContextData /// Gets or sets an unexpected execution exception. /// Exception? Exception { get; set; } - - /// - /// Clones the request context. - /// - IRequestContext Clone(); } diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/OperationExecutionMiddleware.cs b/src/HotChocolate/Core/src/Execution/Pipeline/OperationExecutionMiddleware.cs index b157416165d..1a523a06172 100644 --- a/src/HotChocolate/Core/src/Execution/Pipeline/OperationExecutionMiddleware.cs +++ b/src/HotChocolate/Core/src/Execution/Pipeline/OperationExecutionMiddleware.cs @@ -89,12 +89,8 @@ private async Task ExecuteOperationRequestAsync( { if (operation.Definition.Operation is OperationType.Subscription) { - // since the request context is pooled we need to clone the context for - // long-running executions. - var cloned = context.Clone(); - context.Result = await _subscriptionExecutor - .ExecuteAsync(cloned, () => GetQueryRootValue(cloned)) + .ExecuteAsync(context, () => GetQueryRootValue(context)) .ConfigureAwait(false); } else diff --git a/src/HotChocolate/Core/src/Execution/RequestContext.cs b/src/HotChocolate/Core/src/Execution/RequestContext.cs index 0b0c68b5823..693418ecbb8 100644 --- a/src/HotChocolate/Core/src/Execution/RequestContext.cs +++ b/src/HotChocolate/Core/src/Execution/RequestContext.cs @@ -66,38 +66,6 @@ public DocumentValidatorResult? ValidationResult public Exception? Exception { get; set; } - public IRequestContext Clone() - { - var cloned = new RequestContext( - Schema, - ExecutorVersion, - ErrorHandler, - DiagnosticEvents) - { - Request = Request, - Services = Services, - RequestAborted = RequestAborted, - DocumentId = DocumentId, - DocumentHash = DocumentHash, - IsCachedDocument = IsCachedDocument, - Document = Document, - ValidationResult = ValidationResult, - OperationId = OperationId, - Operation = Operation, - Variables = Variables, - Result = Result, - Exception = Exception, - RequestIndex = RequestIndex, - }; - - foreach (var item in _contextData) - { - cloned._contextData.TryAdd(item.Key, item.Value); - } - - return cloned; - } - public void Initialize(IOperationRequest request, IServiceProvider services) { Request = request; @@ -139,4 +107,3 @@ public void Reset() RequestIndex = default; } } - diff --git a/src/HotChocolate/Core/src/Execution/RequestExecutor.cs b/src/HotChocolate/Core/src/Execution/RequestExecutor.cs index 2e0b85db5d7..a3661bff83a 100644 --- a/src/HotChocolate/Core/src/Execution/RequestExecutor.cs +++ b/src/HotChocolate/Core/src/Execution/RequestExecutor.cs @@ -103,7 +103,7 @@ internal async Task ExecuteAsync( services.InitializeDataLoaderScope(); } - RequestContext? context = _contextPool.Get(); + var context = _contextPool.Get(); try { @@ -123,7 +123,15 @@ internal async Task ExecuteAsync( if (scope is null) { - return context.Result; + var localContext = context; + + if (context.Result.IsStreamResult()) + { + context.Result.RegisterForCleanup(() => _contextPool.Return(localContext)); + context = null; + } + + return localContext.Result; } if (context.Result.IsStreamResult()) diff --git a/src/HotChocolate/Core/test/Execution.Tests/DeferAndStreamTestSchema.cs b/src/HotChocolate/Core/test/Execution.Tests/DeferAndStreamTestSchema.cs index bb45d6e9ae3..7f38da32d28 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/DeferAndStreamTestSchema.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/DeferAndStreamTestSchema.cs @@ -21,6 +21,22 @@ public static async Task CreateAsync() .BuildRequestExecutorAsync(); } + public static IServiceProvider CreateServiceProvider() + { + return new ServiceCollection() + .AddGraphQL() + .AddQueryType() + .AddGlobalObjectIdentification() + .ModifyOptions( + o => + { + o.EnableDefer = true; + o.EnableStream = true; + }) + .Services + .BuildServiceProvider(); + } + public class Query { private readonly List _persons = diff --git a/src/HotChocolate/Core/test/Execution.Tests/DeferTests.cs b/src/HotChocolate/Core/test/Execution.Tests/DeferTests.cs index 343cb6a0a51..19b3556b7e3 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/DeferTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/DeferTests.cs @@ -1,4 +1,7 @@ using CookieCrumble; +using HotChocolate.AspNetCore; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; namespace HotChocolate.Execution; @@ -109,7 +112,11 @@ ... @defer(if: $defer) { } } """) - .SetVariableValues(new Dictionary { { "defer", false }, }) + .SetVariableValues( + new Dictionary + { + { "defer", false }, + }) .Build()); Assert.IsType(result).MatchMarkdownSnapshot(); @@ -232,7 +239,11 @@ fragment Foo on Query { } } """) - .SetVariableValues(new Dictionary { { "defer", false }, }) + .SetVariableValues( + new Dictionary + { + { "defer", false }, + }) .Build()); Assert.IsType(result).MatchMarkdownSnapshot(); @@ -301,6 +312,9 @@ ... @defer { [Fact] public async Task Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer() { + // this test ensures that the request context is not recycled until the + // a stream is fully processed when no outer DI scope exists. + // arrange var executor = await DeferAndStreamTestSchema.CreateAsync(); @@ -323,4 +337,49 @@ ... @defer { Assert.IsType(result).MatchMarkdownSnapshot(); } + + [Fact] + public async Task Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer_2() + { + // this test ensures that the request context is not recycled until the + // a stream is fully processed when an outer DI scope exists. + + // arrange + var services = DeferAndStreamTestSchema.CreateServiceProvider(); + var executor = await services.GetRequestExecutorAsync(); + await using var scope = services.CreateAsyncScope(); + + // act + var result = await executor.ExecuteAsync( + OperationRequestBuilder + .New() + .SetDocument( + """ + { + ... @defer { + ensureState { + state + } + } + } + """) + .SetGlobalState("requestState", "state 123") + .SetServices(scope.ServiceProvider) + .Build()); + + Assert.IsType(result).MatchMarkdownSnapshot(); + } + + private class StateRequestInterceptor : DefaultHttpRequestInterceptor + { + public override ValueTask OnCreateAsync( + HttpContext context, + IRequestExecutor requestExecutor, + OperationRequestBuilder requestBuilder, + CancellationToken cancellationToken) + { + requestBuilder.AddGlobalState("requestState", "bar"); + return base.OnCreateAsync(context, requestExecutor, requestBuilder, cancellationToken); + } + } } 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 new file mode 100644 index 00000000000..d5f10272be4 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/DeferTests.Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer_2.md @@ -0,0 +1,12 @@ +# Ensure_GlobalState_Is_Passed_To_DeferContext_Single_Defer_2 + +```text +{ + "data": { + "ensureState": { + "state": "state 123" + } + } +} + +``` From 21ce35e293f0e241f06aa05f5a29dde3a417ac7e Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 16 Oct 2024 15:05:58 +0200 Subject: [PATCH 075/154] Ensures that FieldResult has errors declared. (#7612) --- .../EnsureFieldResultsDeclareErrorsRule.cs | 72 ++++++++++++++ .../Validation/SchemaValidator.cs | 1 + .../CodeFirstSchemaTests.cs | 98 +++++++++++++++++++ ....FieldResult_With_Errors_Are_Valid.graphql | 22 +++++ ...aError_When_FieldResult_Has_No_Errors.snap | 1 + ...rror_When_FieldResult_Has_No_Errors_1.snap | 1 + ...rror_When_FieldResult_Has_No_Errors_2.snap | 1 + ...rror_When_FieldResult_Has_No_Errors_3.snap | 1 + 8 files changed, 197 insertions(+) create mode 100644 src/HotChocolate/Core/src/Types/Configuration/Validation/EnsureFieldResultsDeclareErrorsRule.cs create mode 100644 src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/CodeFirstSchemaTests.FieldResult_With_Errors_Are_Valid.graphql create mode 100644 src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/CodeFirstSchemaTests.Throw_SchemaError_When_FieldResult_Has_No_Errors.snap create mode 100644 src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/CodeFirstSchemaTests.Throw_SchemaError_When_FieldResult_Has_No_Errors_1.snap create mode 100644 src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/CodeFirstSchemaTests.Throw_SchemaError_When_FieldResult_Has_No_Errors_2.snap create mode 100644 src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/CodeFirstSchemaTests.Throw_SchemaError_When_FieldResult_Has_No_Errors_3.snap diff --git a/src/HotChocolate/Core/src/Types/Configuration/Validation/EnsureFieldResultsDeclareErrorsRule.cs b/src/HotChocolate/Core/src/Types/Configuration/Validation/EnsureFieldResultsDeclareErrorsRule.cs new file mode 100644 index 00000000000..e0e2ba84e24 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Configuration/Validation/EnsureFieldResultsDeclareErrorsRule.cs @@ -0,0 +1,72 @@ +using HotChocolate.Types; +using HotChocolate.Types.Descriptors; +using HotChocolate.Utilities; + +namespace HotChocolate.Configuration.Validation; + +internal sealed class EnsureFieldResultsDeclareErrorsRule : ISchemaValidationRule +{ + private const string _errorKey = "HotChocolate.Types.Errors.ErrorDefinitions"; + + public void Validate( + IDescriptorContext context, + ISchema schema, + ICollection errors) + { + var mutationType = schema.MutationType; + + foreach (var objectType in schema.Types.OfType()) + { + if (ReferenceEquals(objectType, mutationType)) + { + continue; + } + + foreach (var field in objectType.Fields.AsSpan()) + { + var member = field.ResolverMember ?? field.Member; + if (member is not null) + { + var returnType = member.GetReturnType(); + if (returnType is not null + && returnType.IsGenericType + && returnType.GenericTypeArguments.Length == 1) + { + var typeDefinition = returnType.GetGenericTypeDefinition(); + + if (typeDefinition == typeof(FieldResult<>)) + { + EnsureErrorsAreDefined(field, errors); + } + else if (typeDefinition == typeof(ValueTask<>) || typeDefinition == typeof(Task<>)) + { + var type = returnType.GenericTypeArguments[0]; + if (type.IsGenericType + && type.GenericTypeArguments.Length == 1 + && type.GetGenericTypeDefinition() == typeof(FieldResult<>)) + { + EnsureErrorsAreDefined(field, errors); + } + } + } + } + } + } + } + + private static void EnsureErrorsAreDefined( + ObjectField field, + ICollection errors) + { + if (!field.ContextData.ContainsKey(_errorKey)) + { + errors.Add( + SchemaErrorBuilder.New() + .SetMessage( + "The field `{0}` must declare errors to use a FieldResult.", + field.Coordinate) + .SetTypeSystemObject(field.DeclaringType) + .Build()); + } + } +} diff --git a/src/HotChocolate/Core/src/Types/Configuration/Validation/SchemaValidator.cs b/src/HotChocolate/Core/src/Types/Configuration/Validation/SchemaValidator.cs index aaf70a88ed5..877f7b5d731 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/Validation/SchemaValidator.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/Validation/SchemaValidator.cs @@ -12,6 +12,7 @@ internal static class SchemaValidator new DirectiveValidationRule(), new InterfaceHasAtLeastOneImplementationRule(), new IsSelectedPatternValidation(), + new EnsureFieldResultsDeclareErrorsRule() ]; public static IReadOnlyList Validate( diff --git a/src/HotChocolate/Core/test/Types.Queries.Tests/CodeFirstSchemaTests.cs b/src/HotChocolate/Core/test/Types.Queries.Tests/CodeFirstSchemaTests.cs index 030143976df..b8bb45c242d 100644 --- a/src/HotChocolate/Core/test/Types.Queries.Tests/CodeFirstSchemaTests.cs +++ b/src/HotChocolate/Core/test/Types.Queries.Tests/CodeFirstSchemaTests.cs @@ -354,6 +354,78 @@ async Task Error() => exception.Errors[0].Message.MatchSnapshot(); } + [Fact] + public async Task Throw_SchemaError_When_FieldResult_Has_No_Errors() + { + async Task Error() + => await new ServiceCollection() + .AddGraphQL() + .AddQueryConventions() + .AddQueryType() + .BuildSchemaAsync(); + + var exception = await Assert.ThrowsAsync(Error); + Assert.Single(exception.Errors); + exception.Errors[0].Message.MatchSnapshot(); + } + + [Fact] + public async Task Throw_SchemaError_When_FieldResult_Has_No_Errors_1() + { + async Task Error() + => await new ServiceCollection() + .AddGraphQL() + .AddQueryType() + .BuildSchemaAsync(); + + var exception = await Assert.ThrowsAsync(Error); + Assert.Single(exception.Errors); + exception.Errors[0].Message.MatchSnapshot(); + } + + [Fact] + public async Task Throw_SchemaError_When_FieldResult_Has_No_Errors_2() + { + async Task Error() + => await new ServiceCollection() + .AddGraphQL() + .AddQueryConventions() + .AddQueryType() + .BuildSchemaAsync(); + + var exception = await Assert.ThrowsAsync(Error); + Assert.Single(exception.Errors); + exception.Errors[0].Message.MatchSnapshot(); + } + + [Fact] + public async Task Throw_SchemaError_When_FieldResult_Has_No_Errors_3() + { + async Task Error() + => await new ServiceCollection() + .AddGraphQL() + .AddQueryConventions() + .AddQueryType() + .BuildSchemaAsync(); + + var exception = await Assert.ThrowsAsync(Error); + Assert.Single(exception.Errors); + exception.Errors[0].Message.MatchSnapshot(); + } + + [Fact] + public async Task FieldResult_With_Errors_Are_Valid() + { + var schema = + await new ServiceCollection() + .AddGraphQL() + .AddQueryConventions() + .AddQueryType() + .BuildSchemaAsync(); + + schema.MatchSnapshot(); + } + public class QueryWithFieldResultType : ObjectType { protected override void Configure(IObjectTypeDescriptor descriptor) @@ -507,4 +579,30 @@ public sealed record AddressNotFound(string Id, string Message); public sealed class UserNotFoundException : Exception; public sealed class InvalidUserIdException : Exception; + + public class InvalidQuery + { + public FieldResult Foo() => default!; + } + + public class InvalidQueryTask + { + public Task> Foo() => default!; + } + + public class InvalidQueryValueTask + { + public Task> Foo() => default!; + } + + public class ValidQueryValueTask + { + [Error] + public Task> Foo() => default!; + } + + public class Foo + { + public string Bar => default!; + } } diff --git a/src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/CodeFirstSchemaTests.FieldResult_With_Errors_Are_Valid.graphql b/src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/CodeFirstSchemaTests.FieldResult_With_Errors_Are_Valid.graphql new file mode 100644 index 00000000000..2c797e30094 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/CodeFirstSchemaTests.FieldResult_With_Errors_Are_Valid.graphql @@ -0,0 +1,22 @@ +schema { + query: ValidQueryValueTask +} + +interface Error { + message: String! +} + +type ArgumentError implements Error { + message: String! + paramName: String +} + +type Foo { + bar: String! +} + +type ValidQueryValueTask { + foo: FooResult! +} + +union FooResult = Foo | ArgumentError diff --git a/src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/CodeFirstSchemaTests.Throw_SchemaError_When_FieldResult_Has_No_Errors.snap b/src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/CodeFirstSchemaTests.Throw_SchemaError_When_FieldResult_Has_No_Errors.snap new file mode 100644 index 00000000000..f9d49f7e598 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/CodeFirstSchemaTests.Throw_SchemaError_When_FieldResult_Has_No_Errors.snap @@ -0,0 +1 @@ +The field `InvalidQuery.foo` must declare errors to use a FieldResult. diff --git a/src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/CodeFirstSchemaTests.Throw_SchemaError_When_FieldResult_Has_No_Errors_1.snap b/src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/CodeFirstSchemaTests.Throw_SchemaError_When_FieldResult_Has_No_Errors_1.snap new file mode 100644 index 00000000000..f9d49f7e598 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/CodeFirstSchemaTests.Throw_SchemaError_When_FieldResult_Has_No_Errors_1.snap @@ -0,0 +1 @@ +The field `InvalidQuery.foo` must declare errors to use a FieldResult. diff --git a/src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/CodeFirstSchemaTests.Throw_SchemaError_When_FieldResult_Has_No_Errors_2.snap b/src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/CodeFirstSchemaTests.Throw_SchemaError_When_FieldResult_Has_No_Errors_2.snap new file mode 100644 index 00000000000..540c75bb339 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/CodeFirstSchemaTests.Throw_SchemaError_When_FieldResult_Has_No_Errors_2.snap @@ -0,0 +1 @@ +The field `InvalidQueryTask.foo` must declare errors to use a FieldResult. diff --git a/src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/CodeFirstSchemaTests.Throw_SchemaError_When_FieldResult_Has_No_Errors_3.snap b/src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/CodeFirstSchemaTests.Throw_SchemaError_When_FieldResult_Has_No_Errors_3.snap new file mode 100644 index 00000000000..12048deef51 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Queries.Tests/__snapshots__/CodeFirstSchemaTests.Throw_SchemaError_When_FieldResult_Has_No_Errors_3.snap @@ -0,0 +1 @@ +The field `InvalidQueryValueTask.foo` must declare errors to use a FieldResult. From 81a40ed809f74135d5b8b5dac4fcf86b8218f566 Mon Sep 17 00:00:00 2001 From: Glen Date: Wed, 16 Oct 2024 16:45:25 +0200 Subject: [PATCH 076/154] Required keyed services to be registered instead of returning null (#7602) Co-authored-by: Michael Staib --- .../EntitiesResolverTests.cs | 12 +++-- .../Processing/MiddlewareContext.Global.cs | 2 +- .../Processing/MiddlewareContext.Pure.cs | 2 +- .../Parameters/ServiceExpressionHelper.cs | 50 ++++++++++++++----- .../ServiceParameterExpressionBuilder.cs | 14 +++++- .../src/Types/Resolvers/IResolverContext.cs | 2 +- .../QueryableCursorPagingProviderTests.cs | 2 +- .../Resolvers/ResolverCompilerTests.cs | 4 +- .../Resolvers/ResolverServiceTests.cs | 40 +++++++++++++++ ...er_Optional_KeyedService_Does_Not_Exist.md | 9 ++++ ...s.Resolver_Optional_KeyedService_Exists.md | 9 ++++ ...ojectionObjectFieldDescriptorExtensions.cs | 6 +-- 12 files changed, 125 insertions(+), 27 deletions(-) create mode 100644 src/HotChocolate/Core/test/Types.Tests/Resolvers/__snapshots__/ResolverServiceTests.Resolver_Optional_KeyedService_Does_Not_Exist.md create mode 100644 src/HotChocolate/Core/test/Types.Tests/Resolvers/__snapshots__/ResolverServiceTests.Resolver_Optional_KeyedService_Exists.md diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/EntitiesResolverTests.cs b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/EntitiesResolverTests.cs index 6ee84958ac7..9ac09ff4d52 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/EntitiesResolverTests.cs +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/EntitiesResolverTests.cs @@ -4,6 +4,7 @@ using HotChocolate.Execution; using HotChocolate.Language; using Microsoft.Extensions.DependencyInjection; +using Moq; using static HotChocolate.ApolloFederation.TestHelper; namespace HotChocolate.ApolloFederation; @@ -108,12 +109,13 @@ public async Task TestResolveViaEntityResolver_WithDataLoader() var batchScheduler = new ManualBatchScheduler(); var dataLoader = new FederatedTypeDataLoader(batchScheduler, new DataLoaderOptions()); - var context = CreateResolverContext(schema, + var serviceProviderMock = new Mock(); + serviceProviderMock.Setup(c => c.GetService(typeof(FederatedTypeDataLoader))).Returns(dataLoader); + + var context = CreateResolverContext( + schema, null, - mock => - { - mock.Setup(c => c.Service()).Returns(dataLoader); - }); + mock => mock.Setup(c => c.Services).Returns(serviceProviderMock.Object)); var representations = RepresentationsOf( nameof(FederatedType), diff --git a/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Global.cs b/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Global.cs index 3c6fb1368b1..5e8ac6585f5 100644 --- a/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Global.cs +++ b/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Global.cs @@ -171,7 +171,7 @@ public T Resolver() public T Service() where T : notnull => Services.GetRequiredService(); #if NET8_0_OR_GREATER - public T? Service(object key) where T : notnull => Services.GetKeyedService(key); + public T Service(object key) where T : notnull => parentContext.Service(key); #endif public object Service(Type service) diff --git a/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Pure.cs b/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Pure.cs index 42e63156954..7f32a21b130 100644 --- a/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Pure.cs +++ b/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Pure.cs @@ -213,7 +213,7 @@ public object Service(Type service) public T Service() where T : notnull => parentContext.Service(); #if NET8_0_OR_GREATER - public T? Service(object key) where T : notnull => parentContext.Service(key); + public T Service(object key) where T : notnull => parentContext.Service(key); #endif public T Resolver() => parentContext.Resolver(); diff --git a/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/ServiceExpressionHelper.cs b/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/ServiceExpressionHelper.cs index f91eeb02fa6..c766859b94a 100644 --- a/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/ServiceExpressionHelper.cs +++ b/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/ServiceExpressionHelper.cs @@ -10,20 +10,19 @@ namespace HotChocolate.Resolvers.Expressions.Parameters; /// internal static class ServiceExpressionHelper { - private const string _service = nameof(IResolverContext.Service); + private const string _serviceResolver = nameof(GetService); + private const string _keyedServiceResolver = nameof(GetKeyedService); + private static readonly Expression _true = Expression.Constant(true); + private static readonly Expression _false = Expression.Constant(false); private static readonly MethodInfo _getServiceMethod = - ParameterExpressionBuilderHelpers.ContextType.GetMethods().First( - method => method.Name.Equals(_service, StringComparison.Ordinal) && - method.IsGenericMethod && - method.GetParameters().Length == 0); + typeof(ServiceExpressionHelper).GetMethods().First( + method => method.Name.Equals(_serviceResolver, StringComparison.Ordinal)); #if NET8_0_OR_GREATER private static readonly MethodInfo _getKeyedServiceMethod = - ParameterExpressionBuilderHelpers.ContextType.GetMethods().First( - method => method.Name.Equals(_service, StringComparison.Ordinal) && - method.IsGenericMethod && - method.GetParameters().Length == 1); + typeof(ServiceExpressionHelper).GetMethods().First( + method => method.Name.Equals(_keyedServiceResolver, StringComparison.Ordinal)); #endif /// @@ -49,16 +48,43 @@ private static Expression BuildDefaultService(ParameterInfo parameter, Expressio { var parameterType = parameter.ParameterType; var argumentMethod = _getServiceMethod.MakeGenericMethod(parameterType); - return Expression.Call(context, argumentMethod); + var nullabilityContext = new NullabilityInfoContext(); + var nullabilityInfo = nullabilityContext.Create(parameter); + var isRequired = nullabilityInfo.ReadState == NullabilityState.NotNull; + return Expression.Call(argumentMethod, context, isRequired ? _true : _false); } #if NET8_0_OR_GREATER private static Expression BuildDefaultService(ParameterInfo parameter, Expression context, string key) { var parameterType = parameter.ParameterType; - var argumentMethod = _getKeyedServiceMethod.MakeGenericMethod(parameterType); + var argumentMethod = _getKeyedServiceMethod.MakeGenericMethod(parameterType); var keyExpression = Expression.Constant(key, typeof(object)); - return Expression.Call(context, argumentMethod, keyExpression); + var nullabilityContext = new NullabilityInfoContext(); + var nullabilityInfo = nullabilityContext.Create(parameter); + var isRequired = nullabilityInfo.ReadState == NullabilityState.NotNull; + return Expression.Call(argumentMethod, context, keyExpression, isRequired ? _true : _false); + } + + public static TService? GetService( + IResolverContext context, + bool required) + where TService : notnull + { + return required + ? context.Services.GetRequiredService() + : context.Services.GetService(); + } + + public static TService? GetKeyedService( + IResolverContext context, + object? key, + bool required) + where TService : notnull + { + return required + ? context.Services.GetRequiredKeyedService(key) + : context.Services.GetKeyedService(key); } #endif } diff --git a/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/ServiceParameterExpressionBuilder.cs b/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/ServiceParameterExpressionBuilder.cs index 22e75855cd7..ab5fae95519 100644 --- a/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/ServiceParameterExpressionBuilder.cs +++ b/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/ServiceParameterExpressionBuilder.cs @@ -52,11 +52,17 @@ public ServiceParameterBinding(ParameterInfo parameter) { var attribute = parameter.GetCustomAttribute(); Key = attribute?.Key; + + var context = new NullabilityInfoContext(); + var nullabilityInfo = context.Create(parameter); + IsRequired = nullabilityInfo.ReadState == NullabilityState.NotNull; } public string? Key { get; } #endif + public bool IsRequired { get; } + public ArgumentKind Kind => ArgumentKind.Service; public bool IsPure => true; @@ -66,11 +72,15 @@ public T Execute(IResolverContext context) where T : notnull #if NET8_0_OR_GREATER if (Key is not null) { - return context.Service(Key)!; + return IsRequired + ? context.Services.GetRequiredKeyedService(Key) + : context.Services.GetKeyedService(Key)!; } #endif - return context.Service(); + return IsRequired + ? context.Services.GetRequiredService() + : context.Services.GetService()!; } } } diff --git a/src/HotChocolate/Core/src/Types/Resolvers/IResolverContext.cs b/src/HotChocolate/Core/src/Types/Resolvers/IResolverContext.cs index 7748fd9c329..b3480d949cd 100644 --- a/src/HotChocolate/Core/src/Types/Resolvers/IResolverContext.cs +++ b/src/HotChocolate/Core/src/Types/Resolvers/IResolverContext.cs @@ -130,7 +130,7 @@ public interface IResolverContext : IHasContextData /// /// Returns the specified service. /// - T? Service(object key) where T : notnull; + T Service(object key) where T : notnull; #endif /// diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/QueryableCursorPagingProviderTests.cs b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/QueryableCursorPagingProviderTests.cs index 32c8e143e74..578a3ed94a6 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/QueryableCursorPagingProviderTests.cs +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/QueryableCursorPagingProviderTests.cs @@ -697,7 +697,7 @@ public T Service() where T : notnull throw new NotImplementedException(); } - public T? Service(object key) where T : notnull + public T Service(object key) where T : notnull { throw new NotImplementedException(); } diff --git a/src/HotChocolate/Core/test/Types.Tests/Resolvers/ResolverCompilerTests.cs b/src/HotChocolate/Core/test/Types.Tests/Resolvers/ResolverCompilerTests.cs index 485792558b4..5f3b571c866 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Resolvers/ResolverCompilerTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Resolvers/ResolverCompilerTests.cs @@ -695,9 +695,11 @@ public async Task Compile_Arguments_Service() var resolver = compiler.CompileResolve(member, type).Resolver!; // assert + var serviceProvider = new Mock(); + serviceProvider.Setup(t => t.GetService(typeof(MyService))).Returns(new MyService()); var context = new Mock(); context.Setup(t => t.Parent()).Returns(new Resolvers()); - context.Setup(t => t.Service()).Returns(new MyService()); + context.Setup(t => t.Services).Returns(serviceProvider.Object); var result = (bool)(await resolver(context.Object))!; Assert.True(result); } diff --git a/src/HotChocolate/Core/test/Types.Tests/Resolvers/ResolverServiceTests.cs b/src/HotChocolate/Core/test/Types.Tests/Resolvers/ResolverServiceTests.cs index 1ee0985b347..6df83a3e538 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Resolvers/ResolverServiceTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Resolvers/ResolverServiceTests.cs @@ -1,3 +1,5 @@ +#nullable enable + using CookieCrumble; using HotChocolate.Execution; using HotChocolate.Types; @@ -317,6 +319,38 @@ public async Task Resolver_KeyedService() } #endif + [Fact] + public async Task Resolver_Optional_KeyedService_Does_Not_Exist() + { + var executor = + await new ServiceCollection() + .AddGraphQL() + .AddQueryType() + .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) + .BuildRequestExecutorAsync(); + + var result = await executor.ExecuteAsync("{ foo }"); + + result.MatchMarkdownSnapshot(); + } + + [Fact] + public async Task Resolver_Optional_KeyedService_Exists() + { + var executor = + await new ServiceCollection() + .AddKeyedSingleton("abc", (_, _) => new KeyedService("abc")) + .AddKeyedSingleton("def", (_, _) => new KeyedService("def")) + .AddGraphQL() + .AddQueryType() + .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) + .BuildRequestExecutorAsync(); + + var result = await executor.ExecuteAsync("{ foo }"); + + result.MatchMarkdownSnapshot(); + } + public sealed class SayHelloService { public string Scope = "Resolver"; @@ -375,6 +409,12 @@ public string Foo([AbcService] KeyedService service) => service.Key; } + public class QueryOptional + { + public string Foo([AbcService] KeyedService? service) + => service?.Key ?? "No Service"; + } + public class KeyedService(string key) { public string Key => key; diff --git a/src/HotChocolate/Core/test/Types.Tests/Resolvers/__snapshots__/ResolverServiceTests.Resolver_Optional_KeyedService_Does_Not_Exist.md b/src/HotChocolate/Core/test/Types.Tests/Resolvers/__snapshots__/ResolverServiceTests.Resolver_Optional_KeyedService_Does_Not_Exist.md new file mode 100644 index 00000000000..6e0878370b9 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests/Resolvers/__snapshots__/ResolverServiceTests.Resolver_Optional_KeyedService_Does_Not_Exist.md @@ -0,0 +1,9 @@ +# Resolver_Optional_KeyedService_Does_Not_Exist + +```json +{ + "data": { + "foo": "No Service" + } +} +``` diff --git a/src/HotChocolate/Core/test/Types.Tests/Resolvers/__snapshots__/ResolverServiceTests.Resolver_Optional_KeyedService_Exists.md b/src/HotChocolate/Core/test/Types.Tests/Resolvers/__snapshots__/ResolverServiceTests.Resolver_Optional_KeyedService_Exists.md new file mode 100644 index 00000000000..e05eb0df329 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests/Resolvers/__snapshots__/ResolverServiceTests.Resolver_Optional_KeyedService_Exists.md @@ -0,0 +1,9 @@ +# Resolver_Optional_KeyedService_Exists + +```json +{ + "data": { + "foo": "abc" + } +} +``` diff --git a/src/HotChocolate/Data/src/Data/Projections/Extensions/ProjectionObjectFieldDescriptorExtensions.cs b/src/HotChocolate/Data/src/Data/Projections/Extensions/ProjectionObjectFieldDescriptorExtensions.cs index 623207920dc..55331c615e4 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Extensions/ProjectionObjectFieldDescriptorExtensions.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Extensions/ProjectionObjectFieldDescriptorExtensions.cs @@ -323,9 +323,9 @@ public TValueNode ArgumentLiteral(string name) where TValueNode : IV public T Service() where T : notnull => _context.Service(); - #if NET8_0_OR_GREATER - public T? Service(object key) where T : notnull => _context.Service(key); - #endif +#if NET8_0_OR_GREATER + public T Service(object key) where T : notnull => _context.Service(key); +#endif public T Resolver() => _context.Resolver(); From d7f67edc9e8d3694dfcff2f6e3f3308300a7e15a Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 16 Oct 2024 16:36:41 +0100 Subject: [PATCH 077/154] Fixed Service Tests --- .../Processing/MiddlewareContext.Global.cs | 2 +- .../Parameters/ServiceExpressionHelper.cs | 10 ++++++++ .../Types.Analyzers.Tests/DataLoaderTests.cs | 2 ++ ...r_With_PredicateBuilder_MatchesSnapshot.md | 23 ------------------- ...er_With_SelectorBuilder_MatchesSnapshot.md | 23 ------------------- .../Resolvers/ResolverServiceTests.cs | 2 +- 6 files changed, 14 insertions(+), 48 deletions(-) diff --git a/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Global.cs b/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Global.cs index 5e8ac6585f5..1cd5cc0e27d 100644 --- a/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Global.cs +++ b/src/HotChocolate/Core/src/Execution/Processing/MiddlewareContext.Global.cs @@ -171,7 +171,7 @@ public T Resolver() public T Service() where T : notnull => Services.GetRequiredService(); #if NET8_0_OR_GREATER - public T Service(object key) where T : notnull => parentContext.Service(key); + public T Service(object key) where T : notnull => Services.GetRequiredKeyedService(key); #endif public object Service(Type service) diff --git a/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/ServiceExpressionHelper.cs b/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/ServiceExpressionHelper.cs index c766859b94a..aaea3314752 100644 --- a/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/ServiceExpressionHelper.cs +++ b/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/ServiceExpressionHelper.cs @@ -11,7 +11,9 @@ namespace HotChocolate.Resolvers.Expressions.Parameters; internal static class ServiceExpressionHelper { private const string _serviceResolver = nameof(GetService); +#if NET8_0_OR_GREATER private const string _keyedServiceResolver = nameof(GetKeyedService); +#endif private static readonly Expression _true = Expression.Constant(true); private static readonly Expression _false = Expression.Constant(false); @@ -46,12 +48,18 @@ public static Expression Build( private static Expression BuildDefaultService(ParameterInfo parameter, Expression context) { +#if NET7_0_OR_GREATER var parameterType = parameter.ParameterType; var argumentMethod = _getServiceMethod.MakeGenericMethod(parameterType); var nullabilityContext = new NullabilityInfoContext(); var nullabilityInfo = nullabilityContext.Create(parameter); var isRequired = nullabilityInfo.ReadState == NullabilityState.NotNull; return Expression.Call(argumentMethod, context, isRequired ? _true : _false); +#else + var parameterType = parameter.ParameterType; + var argumentMethod = _getServiceMethod.MakeGenericMethod(parameterType); + return Expression.Call(argumentMethod, context, _true); +#endif } #if NET8_0_OR_GREATER @@ -65,6 +73,7 @@ private static Expression BuildDefaultService(ParameterInfo parameter, Expressio var isRequired = nullabilityInfo.ReadState == NullabilityState.NotNull; return Expression.Call(argumentMethod, context, keyExpression, isRequired ? _true : _false); } +#endif public static TService? GetService( IResolverContext context, @@ -76,6 +85,7 @@ private static Expression BuildDefaultService(ParameterInfo parameter, Expressio : context.Services.GetService(); } +#if NET8_0_OR_GREATER public static TService? GetKeyedService( IResolverContext context, object? key, diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/DataLoaderTests.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/DataLoaderTests.cs index a654e1f217c..94420076e0f 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/DataLoaderTests.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/DataLoaderTests.cs @@ -450,6 +450,7 @@ public static Task> GetEntityByIdAsync( """).MatchMarkdownAsync(); } +#if NET8_0_OR_GREATER [Fact] public async Task GenerateSource_BatchDataLoader_With_SelectorBuilder_MatchesSnapshot() { @@ -499,4 +500,5 @@ public static Task> GetEntityByIdAsync( } """).MatchMarkdownAsync(); } +#endif } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PredicateBuilder_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PredicateBuilder_MatchesSnapshot.md index 4f861540b39..5df86f6feff 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PredicateBuilder_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PredicateBuilder_MatchesSnapshot.md @@ -100,26 +100,3 @@ namespace Microsoft.Extensions.DependencyInjection ``` -## Compilation Diagnostics - -```json -[ - { - "Id": "GD0002", - "Title": "Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.", - "Severity": "Error", - "WarningLevel": 0, - "Location": ": (13,8)-(13,47)", - "HelpLinkUri": "https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS9204)", - "MessageFormat": "'{0}' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.", - "Message": "'GreenDonut.Predicates.IPredicateBuilder' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.", - "Category": "Compiler", - "CustomTags": [ - "Compiler", - "Telemetry", - "CustomObsolete" - ] - } -] -``` - diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_SelectorBuilder_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_SelectorBuilder_MatchesSnapshot.md index 1e2b7b762a9..be453e01888 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_SelectorBuilder_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_SelectorBuilder_MatchesSnapshot.md @@ -100,26 +100,3 @@ namespace Microsoft.Extensions.DependencyInjection ``` -## Compilation Diagnostics - -```json -[ - { - "Id": "GD0001", - "Title": "Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.", - "Severity": "Error", - "WarningLevel": 0, - "Location": ": (13,8)-(13,45)", - "HelpLinkUri": "https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS9204)", - "MessageFormat": "'{0}' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.", - "Message": "'GreenDonut.Selectors.ISelectorBuilder' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.", - "Category": "Compiler", - "CustomTags": [ - "Compiler", - "Telemetry", - "CustomObsolete" - ] - } -] -``` - diff --git a/src/HotChocolate/Core/test/Types.Tests/Resolvers/ResolverServiceTests.cs b/src/HotChocolate/Core/test/Types.Tests/Resolvers/ResolverServiceTests.cs index 6df83a3e538..295db1edcc1 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Resolvers/ResolverServiceTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Resolvers/ResolverServiceTests.cs @@ -317,7 +317,6 @@ public async Task Resolver_KeyedService() result.MatchMarkdownSnapshot(); } -#endif [Fact] public async Task Resolver_Optional_KeyedService_Does_Not_Exist() @@ -350,6 +349,7 @@ public async Task Resolver_Optional_KeyedService_Exists() result.MatchMarkdownSnapshot(); } +#endif public sealed class SayHelloService { From d7415e64b83b633f3ecb57370f7b4bde1ba5ea23 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Fri, 1 Nov 2024 17:46:02 +0100 Subject: [PATCH 078/154] Change RequireSlicingArgs default to false. (#7666) --- .../HttpGetSchemaMiddlewareTests.cs | 27 ++++ ...aMiddlewareTests.Download_GraphQL_SDL.snap | 4 +- ...s.Download_GraphQL_SDL_Explicit_Route.snap | 4 +- ...L_SDL_Explicit_Route_Explicit_Pattern.snap | 4 +- ...MiddlewareTests.Download_GraphQL_Schema.md | 8 +- ...oad_GraphQL_Schema_Slicing_Args_Enabled.md | 151 ++++++++++++++++++ .../src/CostAnalysis/CostTypeInterceptor.cs | 6 +- .../test/CostAnalysis.Tests/PagingTests.cs | 4 + .../SlicingArgumentsTests.cs | 3 + .../MongoDbAggregateFluentTests.cs | 8 +- .../MongoDbCursorPagingFindFluentTests.cs | 39 ++--- .../MongoDbAggregateFluentTests.cs | 12 +- 12 files changed, 229 insertions(+), 41 deletions(-) create mode 100644 src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema_Slicing_Args_Enabled.md diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/HttpGetSchemaMiddlewareTests.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/HttpGetSchemaMiddlewareTests.cs index 6c94f3ed9d7..109b170c117 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/HttpGetSchemaMiddlewareTests.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/HttpGetSchemaMiddlewareTests.cs @@ -65,6 +65,33 @@ public async Task Download_GraphQL_Schema(string path) #endif } + [Theory] + [InlineData("/graphql?sdl")] + [InlineData("/graphql/schema/")] + [InlineData("/graphql/schema.graphql")] + [InlineData("/graphql/schema")] + public async Task Download_GraphQL_Schema_Slicing_Args_Enabled(string path) + { + // arrange + var server = CreateStarWarsServer( + configureServices: sp => + sp + .RemoveAll() + .AddSingleton() + .AddGraphQL() + .ModifyPagingOptions(o => o.RequirePagingBoundaries = true)); + var url = TestServerExtensions.CreateUrl(path); + var request = new HttpRequestMessage(HttpMethod.Get, url); + + // act + var response = await server.CreateClient().SendAsync(request); + + // assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + response.MatchMarkdownSnapshot(); + } + [Theory] [InlineData("/graphql/?sdl")] [InlineData("/graphql/schema/")] diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL.snap b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL.snap index 55d348a047b..b2a1cda45f1 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL.snap +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL.snap @@ -17,7 +17,7 @@ type Droid implements Character { id: ID! name: String! appearsIn: [Episode] - friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) + friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) height(unit: Unit): Float primaryFunction: String traits: JSON @@ -45,7 +45,7 @@ type Human implements Character { id: ID! name: String! appearsIn: [Episode] - friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) + friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) otherHuman: Human height(unit: Unit): Float homePlanet: String diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL_Explicit_Route.snap b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL_Explicit_Route.snap index 55d348a047b..b2a1cda45f1 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL_Explicit_Route.snap +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL_Explicit_Route.snap @@ -17,7 +17,7 @@ type Droid implements Character { id: ID! name: String! appearsIn: [Episode] - friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) + friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) height(unit: Unit): Float primaryFunction: String traits: JSON @@ -45,7 +45,7 @@ type Human implements Character { id: ID! name: String! appearsIn: [Episode] - friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) + friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) otherHuman: Human height(unit: Unit): Float homePlanet: String diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL_Explicit_Route_Explicit_Pattern.snap b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL_Explicit_Route_Explicit_Pattern.snap index 55d348a047b..b2a1cda45f1 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL_Explicit_Route_Explicit_Pattern.snap +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL_Explicit_Route_Explicit_Pattern.snap @@ -17,7 +17,7 @@ type Droid implements Character { id: ID! name: String! appearsIn: [Episode] - friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) + friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) height(unit: Unit): Float primaryFunction: String traits: JSON @@ -45,7 +45,7 @@ type Human implements Character { id: ID! name: String! appearsIn: [Episode] - friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) + friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) otherHuman: Human height(unit: Unit): Float homePlanet: String diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema.md b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema.md index dfc6d91a00d..d6d500a0681 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema.md +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema.md @@ -2,12 +2,12 @@ ```text Headers: -ETag: "1-B2t9cwf/BRF8NYIpFDtJE4DLg8FfD5d5HdAF9KObaUc=" +ETag: "1-LzuFZZrknIre5etJZHfNRC1e/vj+qW9tAf9pYpS8bQM=" Cache-Control: public, must-revalidate, max-age=3600 Content-Type: application/graphql; charset=utf-8 Content-Disposition: attachment; filename="schema.graphql" Last-Modified: Fri, 01 Jan 2021 00:00:00 GMT -Content-Length: 7193 +Content-Length: 7261 --------------------------> Status Code: OK --------------------------> @@ -30,7 +30,7 @@ type Droid implements Character { id: ID! name: String! appearsIn: [Episode] - friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) + friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) height(unit: Unit): Float primaryFunction: String traits: JSON @@ -58,7 +58,7 @@ type Human implements Character { id: ID! name: String! appearsIn: [Episode] - friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) + friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) otherHuman: Human height(unit: Unit): Float homePlanet: String diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema_Slicing_Args_Enabled.md b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema_Slicing_Args_Enabled.md new file mode 100644 index 00000000000..a92a5395df1 --- /dev/null +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema_Slicing_Args_Enabled.md @@ -0,0 +1,151 @@ +# Download_GraphQL_Schema_Slicing_Args_Enabled + +```text +Headers: +ETag: "1-B2t9cwf/BRF8NYIpFDtJE4DLg8FfD5d5HdAF9KObaUc=" +Cache-Control: public, must-revalidate, max-age=3600 +Content-Type: application/graphql; charset=utf-8 +Content-Disposition: attachment; filename="schema.graphql" +Last-Modified: Fri, 01 Jan 2021 00:00:00 GMT +Content-Length: 7193 +--------------------------> +Status Code: OK +--------------------------> +schema { + query: Query + mutation: Mutation + subscription: Subscription +} + +interface Character { + id: ID! + name: String! + friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection + appearsIn: [Episode] + traits: JSON + height(unit: Unit): Float +} + +type Droid implements Character { + id: ID! + name: String! + appearsIn: [Episode] + friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) + height(unit: Unit): Float + primaryFunction: String + traits: JSON +} + +"A connection to a list of items." +type FriendsConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [FriendsEdge!] + "A flattened list of the nodes." + nodes: [Character] +} + +"An edge in a connection." +type FriendsEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: Character +} + +type Human implements Character { + id: ID! + name: String! + appearsIn: [Episode] + friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) + otherHuman: Human + height(unit: Unit): Float + homePlanet: String + traits: JSON +} + +type Mutation { + createReview(episode: Episode! review: ReviewInput!): Review! @cost(weight: "10") + complete(episode: Episode!): Boolean! @cost(weight: "10") +} + +"Information about pagination in a connection." +type PageInfo { + "Indicates whether more edges exist following the set defined by the clients arguments." + hasNextPage: Boolean! + "Indicates whether more edges exist prior the set defined by the clients arguments." + hasPreviousPage: Boolean! + "When paginating backwards, the cursor to continue." + startCursor: String + "When paginating forwards, the cursor to continue." + endCursor: String +} + +type Query { + hero(episode: Episode! = NEW_HOPE): Character + heroByTraits(traits: JSON!): Character + heroes(episodes: [Episode!]!): [Character!] + character(characterIds: [String!]!): [Character!]! @cost(weight: "10") + search(text: String!): [SearchResult] + human(id: String!): Human + droid(id: String!): Droid + time: Long! + evict: Boolean! + wait(m: Int!): Boolean! @cost(weight: "10") + someDeprecatedField(deprecatedArg: String! = "foo" @deprecated(reason: "use something else")): String! @deprecated(reason: "use something else") +} + +type Review { + commentary: String @cost(weight: "10") + stars: Int! +} + +type Starship { + id: ID! + name: String! + length(unit: Unit): Float! +} + +type Subscription { + onReview(episode: Episode!): Review! + onNext: String! @cost(weight: "10") + onException: String! @cost(weight: "10") + delay(delay: Int! count: Int!): String! @cost(weight: "10") +} + +union SearchResult = Starship | Human | Droid + +input ReviewInput { + stars: Int! + commentary: String +} + +enum Episode { + NEW_HOPE + EMPIRE + JEDI +} + +enum Unit { + FOOT + METERS +} + +"The purpose of the `cost` directive is to define a `weight` for GraphQL types, fields, and arguments. Static analysis can use these weights when calculating the overall cost of a query or response." +directive @cost("The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." weight: String!) on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM | INPUT_FIELD_DEFINITION + +"The `@defer` directive may be provided for fragment spreads and inline fragments to inform the executor to delay the execution of the current fragment to indicate deprioritization of the current fragment. A query with `@defer` directive will cause the request to potentially return multiple responses, where non-deferred data is delivered in the initial response and data deferred is delivered in a subsequent response. `@include` and `@skip` take precedence over `@defer`." +directive @defer("If this argument label has a value other than null, it will be passed on to the result of this defer directive. This label is intended to give client applications a way to identify to which fragment a deferred result belongs to." label: String "Deferred when true." if: Boolean) on FRAGMENT_SPREAD | INLINE_FRAGMENT + +"The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." +directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!] "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true) on FIELD_DEFINITION + +"The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." +directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! = 0 "Streamed when true." if: Boolean) on FIELD + +scalar JSON + +"The `Long` scalar type represents non-fractional signed whole 64-bit numeric values. Long can represent values between -(2^63) and 2^63 - 1." +scalar Long +``` diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostTypeInterceptor.cs b/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostTypeInterceptor.cs index 559dbbea3bd..9b179f602ba 100644 --- a/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostTypeInterceptor.cs +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostTypeInterceptor.cs @@ -72,8 +72,12 @@ public override void OnAfterCompleteName(ITypeCompletionContext completionContex // https://ibm.github.io/graphql-specs/cost-spec.html#sec-requireOneSlicingArgument // Per default, requireOneSlicingArgument is enabled, // and has to be explicitly disabled if not desired for a field. + // However, we have found that users turn the whole cost feature of because of this setting + // which leads to less overall security for the deployed GraphQL server. + // For this reason we have decided to disable slicing arguments by default. var requirePagingBoundaries = - slicingArgs.Length > 0 && (options.RequirePagingBoundaries ?? true); + slicingArgs.Length > 0 + && (options.RequirePagingBoundaries ?? false); fieldDef.AddDirective( new ListSizeDirective( diff --git a/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/PagingTests.cs b/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/PagingTests.cs index 949f3ab7e1a..2bbf4ca8415 100644 --- a/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/PagingTests.cs +++ b/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/PagingTests.cs @@ -21,6 +21,7 @@ public async Task Ensure_Paging_Defaults_Are_Applied() .AddQueryType() .AddFiltering() .AddSorting() + .ModifyPagingOptions(o => o.RequirePagingBoundaries = true) .BuildSchemaAsync(); schema.MatchSnapshot(); @@ -109,6 +110,7 @@ public async Task Require_Paging_Boundaries_By_Default_With_Connections() .AddQueryType() .AddFiltering() .AddSorting() + .ModifyPagingOptions(o => o.RequirePagingBoundaries = true) .BuildRequestExecutorAsync(); // act @@ -277,6 +279,7 @@ public async Task Require_Paging_Boundaries_Two_Boundaries_Mixed() .AddQueryType() .AddFiltering() .AddSorting() + .ModifyPagingOptions(o => o.RequirePagingBoundaries = true) .BuildRequestExecutorAsync(); // act @@ -324,6 +327,7 @@ public async Task Require_Paging_Nested_Boundaries() .AddQueryType() .AddFiltering() .AddSorting() + .ModifyPagingOptions(o => o.RequirePagingBoundaries = true) .BuildRequestExecutorAsync(); // act diff --git a/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/SlicingArgumentsTests.cs b/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/SlicingArgumentsTests.cs index ecb42312c96..2a89c84c4c4 100644 --- a/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/SlicingArgumentsTests.cs +++ b/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/SlicingArgumentsTests.cs @@ -14,6 +14,7 @@ public async Task SlicingArguments_Non_Specified() await new ServiceCollection() .AddGraphQLServer() .AddQueryType() + .ModifyPagingOptions(o => o.RequirePagingBoundaries = true) .ExecuteRequestAsync( """ { @@ -54,6 +55,7 @@ public async Task SlicingArguments_Set_To_Null() await new ServiceCollection() .AddGraphQLServer() .AddQueryType() + .ModifyPagingOptions(o => o.RequirePagingBoundaries = true) .ExecuteRequestAsync( """ { @@ -94,6 +96,7 @@ public async Task SlicingArguments_Set_First_To_Null_And_Last_To_Null() await new ServiceCollection() .AddGraphQLServer() .AddQueryType() + .ModifyPagingOptions(o => o.RequirePagingBoundaries = true) .ExecuteRequestAsync( """ { diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbAggregateFluentTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbAggregateFluentTests.cs index 37e8940ce79..697f525b540 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbAggregateFluentTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbAggregateFluentTests.cs @@ -57,10 +57,10 @@ public async Task BsonElement_Rename() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "true"), res2, "false") + await Snapshot + .Create() + .AddResult(res1, "true") + .AddResult(res2, "false") .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingFindFluentTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingFindFluentTests.cs index a0abc295c4d..5fb788549ca 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingFindFluentTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingFindFluentTests.cs @@ -32,7 +32,7 @@ public MongoDbCursorPagingFindFluentTests(MongoResource resource) public async Task Simple_StringList_Default_Items() { // arrange - var executor = await CreateSchemaAsync(); + var executor = await CreateSchemaAsync(requiresPagingBoundaries: false); // act var result = await executor.ExecuteAsync( @@ -57,9 +57,9 @@ public async Task Simple_StringList_Default_Items() }"); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), result) + await Snapshot + .Create() + .AddResult(result) .MatchAsync(); } @@ -92,9 +92,9 @@ public async Task Simple_StringList_First_2() }"); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), result) + await Snapshot + .Create() + .AddResult(result) .MatchAsync(); } @@ -172,7 +172,7 @@ await SnapshotExtensions.AddResult( public async Task Simple_StringList_Global_DefaultItem_2() { // arrange - var executor = await CreateSchemaAsync(); + var executor = await CreateSchemaAsync(requiresPagingBoundaries: false); // act var result = await executor.ExecuteAsync( @@ -197,9 +197,9 @@ public async Task Simple_StringList_Global_DefaultItem_2() }"); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), result) + await Snapshot + .Create() + .AddResult(result) .MatchAsync(); } @@ -207,7 +207,7 @@ await SnapshotExtensions.AddResult( public async Task JustTotalCount() { // arrange - var executor = await CreateSchemaAsync(); + var executor = await CreateSchemaAsync(requiresPagingBoundaries: false); // act var result = await executor.ExecuteAsync( @@ -218,9 +218,9 @@ public async Task JustTotalCount() }"); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), result) + await Snapshot + .Create() + .AddResult(result) .MatchAsync(); } @@ -239,9 +239,9 @@ public async Task TotalCount_AndFirst() }"); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), result) + await Snapshot + .Create() + .AddResult(result) .MatchAsync(); } @@ -266,7 +266,7 @@ private Func> BuildResolv return ctx => collection.AsExecutable(); } - private ValueTask CreateSchemaAsync() + private ValueTask CreateSchemaAsync(bool requiresPagingBoundaries = true) { return new ServiceCollection() .AddGraphQL() @@ -305,6 +305,7 @@ private ValueTask CreateSchemaAsync() } }) .ModifyRequestOptions(x => x.IncludeExceptionDetails = true) + .ModifyPagingOptions(o => o.RequirePagingBoundaries = requiresPagingBoundaries) .UseDefaultPipeline() .Services .BuildServiceProvider() diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbAggregateFluentTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbAggregateFluentTests.cs index bc7b7437460..57516c35654 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbAggregateFluentTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbAggregateFluentTests.cs @@ -39,9 +39,7 @@ public async Task BsonElement_Rename() var tester = CreateSchema( () => { - var collection = - _resource.CreateCollection("data_" + Guid.NewGuid().ToString("N")); - + var collection = _resource.CreateCollection("data_" + Guid.NewGuid().ToString("N")); collection.InsertMany(_fooEntities); return collection.Aggregate().AsExecutable(); }); @@ -58,10 +56,10 @@ public async Task BsonElement_Rename() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "ASC"), res2, "DESC") + await Snapshot + .Create() + .AddResult(res1, "ASC") + .AddResult(res2, "DESC") .MatchAsync(); } From 21d7769252fa887b5c6489241be0f2e3fd627984 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Thu, 17 Oct 2024 23:48:10 +0200 Subject: [PATCH 079/154] Allow control over interfaces of generated DataLoader. (#7621) --- .../Attributes/DataLoaderDefaultsAttribute.cs | 5 + .../FileBuilders/DataLoaderFileBuilder.cs | 44 ++++--- .../DataLoaderModuleFileBuilder.cs | 25 +++- .../FileBuilders/ModuleFileBuilder.cs | 41 ++++-- .../Generators/DataLoaderGenerator.cs | 10 +- .../Generators/DataLoaderModuleGenerator.cs | 4 +- .../Generators/TypeModuleSyntaxGenerator.cs | 10 +- .../Helpers/DataLoaderAttributeHelper.cs | 21 ++++ .../Types.Analyzers/Helpers/GeneratorUtils.cs | 16 ++- .../Inspectors/DataLoaderDefaultsInspector.cs | 3 +- .../Models/DataLoaderDefaultsInfo.cs | 10 +- .../Types.Analyzers.Tests/DataLoaderTests.cs | 27 ++++ ...erTests.DataLoader_With_Optional_Lookup.md | 2 +- ...hDataLoader_IDictionary_MatchesSnapshot.md | 2 +- ...eSource_BatchDataLoader_MatchesSnapshot.md | 2 +- ...aLoader_Nullable_Result_MatchesSnapshot.md | 2 +- ...chDataLoader_With_Group_MatchesSnapshot.md | 6 +- ...ith_Group_Only_On_Class_MatchesSnapshot.md | 4 +- ...th_Group_Only_On_Method_MatchesSnapshot.md | 4 +- ...h_Lookup_From_OtherType_MatchesSnapshot.md | 2 +- ...hDataLoader_With_Lookup_MatchesSnapshot.md | 2 +- ...der_With_Optional_State_MatchesSnapshot.md | 2 +- ...er_With_PagingArguments_MatchesSnapshot.md | 2 +- ...r_With_PredicateBuilder_MatchesSnapshot.md | 2 +- ...der_With_Required_State_MatchesSnapshot.md | 2 +- ...er_With_SelectorBuilder_MatchesSnapshot.md | 2 +- ...With_State_With_Default_MatchesSnapshot.md | 2 +- ...eSource_CacheDataLoader_MatchesSnapshot.md | 2 +- ...ource_GroupedDataLoader_MatchesSnapshot.md | 2 +- ...aLoaderTests.Generate_Without_Interface.md | 119 ++++++++++++++++++ 30 files changed, 313 insertions(+), 64 deletions(-) create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.Generate_Without_Interface.md diff --git a/src/GreenDonut/src/Core/Attributes/DataLoaderDefaultsAttribute.cs b/src/GreenDonut/src/Core/Attributes/DataLoaderDefaultsAttribute.cs index 92a9bbf4724..f709f0e7d02 100644 --- a/src/GreenDonut/src/Core/Attributes/DataLoaderDefaultsAttribute.cs +++ b/src/GreenDonut/src/Core/Attributes/DataLoaderDefaultsAttribute.cs @@ -20,4 +20,9 @@ public sealed class DataLoaderDefaultsAttribute : Attribute /// Specifies if module registration code for DataLoaders shall be generated. /// public bool GenerateRegistrationCode { get; set; } = true; + + /// + /// Specifies if interfaces for DataLoaders shall be generated. + /// + public bool GenerateInterfaces { get; set; } = true; } diff --git a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs index 01fd447f696..1ae101d8b83 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs @@ -84,10 +84,11 @@ public void WriteBeginDataLoaderClass( bool isPublic, DataLoaderKind kind, ITypeSymbol key, - ITypeSymbol value) + ITypeSymbol value, + bool withInterface) { _writer.WriteIndentedLine( - "{0} sealed class {1}", + "{0} sealed partial class {1}", isPublic ? "public" : "internal", @@ -99,7 +100,10 @@ kind is DataLoaderKind.Group : ": global::GreenDonut.DataLoaderBase<{0}, {1}>", key.ToFullyQualified(), value.ToFullyQualified()); - _writer.WriteIndentedLine(", {0}", interfaceName); + if (withInterface) + { + _writer.WriteIndentedLine(", {0}", interfaceName); + } _writer.DecreaseIndent(); _writer.WriteIndentedLine("{"); _writer.IncreaseIndent(); @@ -509,22 +513,34 @@ kind is DataLoaderKind.Cache public void WriteDataLoaderGroupClass( string groupClassName, - IReadOnlyList dataLoaders) + IReadOnlyList dataLoaders, + bool withInterface) { - _writer.WriteIndentedLine("public interface I{0}", groupClassName); - _writer.WriteIndentedLine("{"); - _writer.IncreaseIndent(); - - foreach (var dataLoader in dataLoaders) + if (withInterface) { - _writer.WriteIndentedLine("{0} {1} {{ get; }}", dataLoader.InterfaceName, dataLoader.Name); + _writer.WriteIndentedLine("public interface I{0}", groupClassName); + _writer.WriteIndentedLine("{"); + _writer.IncreaseIndent(); + + foreach (var dataLoader in dataLoaders) + { + _writer.WriteIndentedLine("{0} {1} {{ get; }}", dataLoader.InterfaceName, dataLoader.Name); + } + + _writer.DecreaseIndent(); + _writer.WriteIndentedLine("}"); + _writer.WriteLine(); } - _writer.DecreaseIndent(); - _writer.WriteIndentedLine("}"); - _writer.WriteLine(); + if (withInterface) + { + _writer.WriteIndentedLine("public sealed partial class {0} : I{0}", groupClassName); + } + else + { + _writer.WriteIndentedLine("public sealed partial class {0}", groupClassName); + } - _writer.WriteIndentedLine("public sealed class {0} : I{0}", groupClassName); _writer.WriteIndentedLine("{"); _writer.IncreaseIndent(); diff --git a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderModuleFileBuilder.cs b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderModuleFileBuilder.cs index 237d98ebc5e..be473b271da 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderModuleFileBuilder.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderModuleFileBuilder.cs @@ -84,13 +84,26 @@ public void WriteAddDataLoader(string dataLoaderType) dataLoaderType); } - public void WriteAddDataLoader(string dataLoaderType, string dataLoaderInterfaceType) + public void WriteAddDataLoader( + string dataLoaderType, + string dataLoaderInterfaceType, + bool withInterface) { - _writer.WriteIndentedLine( - "global::{0}.AddDataLoader(services);", - WellKnownTypes.DataLoaderServiceCollectionExtension, - dataLoaderInterfaceType, - dataLoaderType); + if (withInterface) + { + _writer.WriteIndentedLine( + "global::{0}.AddDataLoader(services);", + WellKnownTypes.DataLoaderServiceCollectionExtension, + dataLoaderInterfaceType, + dataLoaderType); + } + else + { + _writer.WriteIndentedLine( + "global::{0}.AddDataLoader(services);", + WellKnownTypes.DataLoaderServiceCollectionExtension, + dataLoaderType); + } } public void WriteAddDataLoaderGroup(string groupType, string groupInterfaceType) diff --git a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ModuleFileBuilder.cs b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ModuleFileBuilder.cs index 2c8470bb124..6dd4e21890e 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ModuleFileBuilder.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ModuleFileBuilder.cs @@ -133,9 +133,9 @@ public void WriteRegisterObjectTypeExtensionHelpers() using (_writer.IncreaseIndent()) { _writer.WriteIndentedLine( - "var hooks = (global::System.Collections.Generic.List<" + - "Action>>)" + - "descriptor.Extend().Context.ContextData[hooksKey]!;"); + "var hooks = (global::System.Collections.Generic.List<" + + "Action>>)" + + "descriptor.Extend().Context.ContextData[hooksKey]!;"); _writer.WriteIndentedLine("foreach (var configure in hooks)"); _writer.WriteIndentedLine("{"); @@ -169,8 +169,8 @@ public void WriteRegisterObjectTypeExtensionHelpers() _writer.WriteIndentedLine("}"); _writer.WriteLine(); _writer.WriteIndentedLine( - "((System.Collections.Generic.List>>)value!)" + - ".Add(initialize);"); + "((System.Collections.Generic.List>>)value!)" + + ".Add(initialize);"); } _writer.WriteIndentedLine("});"); @@ -217,9 +217,9 @@ public void WriteRegisterInterfaceTypeExtensionHelpers() using (_writer.IncreaseIndent()) { _writer.WriteIndentedLine( - "var hooks = (global::System.Collections.Generic.List<" + - "Action>>)" + - "descriptor.Extend().Context.ContextData[hooksKey]!;"); + "var hooks = (global::System.Collections.Generic.List<" + + "Action>>)" + + "descriptor.Extend().Context.ContextData[hooksKey]!;"); _writer.WriteIndentedLine("foreach (var configure in hooks)"); _writer.WriteIndentedLine("{"); @@ -253,8 +253,8 @@ public void WriteRegisterInterfaceTypeExtensionHelpers() _writer.WriteIndentedLine("}"); _writer.WriteLine(); _writer.WriteIndentedLine( - "((System.Collections.Generic.List>>)value!)" + - ".Add(initialize);"); + "((System.Collections.Generic.List>>)value!)" + + ".Add(initialize);"); } _writer.WriteIndentedLine("});"); @@ -266,8 +266,25 @@ public void WriteRegisterInterfaceTypeExtensionHelpers() public void WriteRegisterDataLoader(string typeName) => _writer.WriteIndentedLine("builder.AddDataLoader();", typeName); - public void WriteRegisterDataLoader(string typeName, string interfaceTypeName) - => _writer.WriteIndentedLine("builder.AddDataLoader();", interfaceTypeName, typeName); + public void WriteRegisterDataLoader( + string typeName, + string interfaceTypeName, + bool withInterface) + { + if (withInterface) + { + _writer.WriteIndentedLine( + "builder.AddDataLoader();", + interfaceTypeName, + typeName); + } + else + { + _writer.WriteIndentedLine( + "builder.AddDataLoader();", + typeName); + } + } public void WriteRegisterDataLoaderGroup(string typeName, string interfaceTypeName) => _writer.WriteIndentedLine( diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Generators/DataLoaderGenerator.cs b/src/HotChocolate/Core/src/Types.Analyzers/Generators/DataLoaderGenerator.cs index 8615167a22a..44611fe70e6 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Generators/DataLoaderGenerator.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Generators/DataLoaderGenerator.cs @@ -109,7 +109,7 @@ private static void WriteDataLoader( buffer ??= new(); buffer.Clear(); buffer.AddRange(dataLoaderGroups); - generator.WriteDataLoaderGroupClass(dataLoaderGroup.Key, buffer); + generator.WriteDataLoaderGroupClass(dataLoaderGroup.Key, buffer, defaults.GenerateInterfaces); } generator.WriteEndNamespace(); @@ -133,7 +133,10 @@ private static void GenerateDataLoader( var isPublic = dataLoader.IsPublic ?? defaults.IsPublic ?? true; var isInterfacePublic = dataLoader.IsInterfacePublic ?? defaults.IsInterfacePublic ?? true; - generator.WriteDataLoaderInterface(dataLoader.InterfaceName, isInterfacePublic, kind, keyType, valueType); + if (defaults.GenerateInterfaces) + { + generator.WriteDataLoaderInterface(dataLoader.InterfaceName, isInterfacePublic, kind, keyType, valueType); + } generator.WriteBeginDataLoaderClass( dataLoader.Name, @@ -141,7 +144,8 @@ private static void GenerateDataLoader( isPublic, kind, keyType, - valueType); + valueType, + defaults.GenerateInterfaces); generator.WriteDataLoaderConstructor( dataLoader.Name, kind, diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Generators/DataLoaderModuleGenerator.cs b/src/HotChocolate/Core/src/Types.Analyzers/Generators/DataLoaderModuleGenerator.cs index 2f9de065f56..96903b72814 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Generators/DataLoaderModuleGenerator.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Generators/DataLoaderModuleGenerator.cs @@ -1,5 +1,6 @@ using System.Collections.Immutable; using HotChocolate.Types.Analyzers.FileBuilders; +using HotChocolate.Types.Analyzers.Helpers; using HotChocolate.Types.Analyzers.Models; using Microsoft.CodeAnalysis; @@ -13,6 +14,7 @@ public void Generate( ImmutableArray syntaxInfos) { var module = GetDataLoaderModuleInfo(syntaxInfos); + var dataLoaderDefaults = syntaxInfos.GetDataLoaderDefaults(); if (module is null || !syntaxInfos.Any(t => t is DataLoaderInfo or RegisterDataLoaderInfo)) { @@ -43,7 +45,7 @@ public void Generate( case DataLoaderInfo dataLoader: var typeName = $"{dataLoader.Namespace}.{dataLoader.Name}"; var interfaceTypeName = $"{dataLoader.Namespace}.{dataLoader.InterfaceName}"; - generator.WriteAddDataLoader(typeName, interfaceTypeName); + generator.WriteAddDataLoader(typeName, interfaceTypeName, dataLoaderDefaults.GenerateInterfaces); if(dataLoader.Groups.Count > 0) { diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Generators/TypeModuleSyntaxGenerator.cs b/src/HotChocolate/Core/src/Types.Analyzers/Generators/TypeModuleSyntaxGenerator.cs index 411f029f456..44128fc1927 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Generators/TypeModuleSyntaxGenerator.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Generators/TypeModuleSyntaxGenerator.cs @@ -49,6 +49,7 @@ private static void WriteConfiguration( List syntaxInfos, ModuleInfo module) { + var dataLoaderDefaults = syntaxInfos.GetDataLoaderDefaults(); HashSet<(string InterfaceName, string ClassName)>? groups = null; using var generator = new ModuleFileBuilder(module.ModuleName, "Microsoft.Extensions.DependencyInjection"); @@ -109,7 +110,10 @@ private static void WriteConfiguration( var typeName = $"{dataLoader.Namespace}.{dataLoader.Name}"; var interfaceTypeName = $"{dataLoader.Namespace}.{dataLoader.InterfaceName}"; - generator.WriteRegisterDataLoader(typeName, interfaceTypeName); + generator.WriteRegisterDataLoader( + typeName, + interfaceTypeName, + dataLoaderDefaults.GenerateInterfaces); hasConfigurations = true; if(dataLoader.Groups.Count > 0) @@ -117,7 +121,9 @@ private static void WriteConfiguration( groups ??= []; foreach (var groupName in dataLoader.Groups) { - groups.Add(($"{dataLoader.Namespace}.I{groupName}", $"{dataLoader.Namespace}.{groupName}")); + groups.Add(( + $"{dataLoader.Namespace}.I{groupName}", + $"{dataLoader.Namespace}.{groupName}")); } } } diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Helpers/DataLoaderAttributeHelper.cs b/src/HotChocolate/Core/src/Types.Analyzers/Helpers/DataLoaderAttributeHelper.cs index 97b5523a85b..2c2112e216a 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Helpers/DataLoaderAttributeHelper.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Helpers/DataLoaderAttributeHelper.cs @@ -328,6 +328,27 @@ public static bool RegisterService( return true; } + public static bool GenerateInterfaces( + this SeparatedSyntaxList arguments, + GeneratorSyntaxContext context) + { + var argumentSyntax = arguments.FirstOrDefault( + t => t.NameEquals?.Name.ToFullString().Trim() == "GenerateInterfaces"); + + if (argumentSyntax is not null) + { + var valueExpression = argumentSyntax.Expression; + var value = context.SemanticModel.GetConstantValue(valueExpression).Value; + + if (value is not null) + { + return (bool)value; + } + } + + return true; + } + public static bool TryGetName( this AttributeData attribute, [NotNullWhen(true)] out string? name) diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Helpers/GeneratorUtils.cs b/src/HotChocolate/Core/src/Types.Analyzers/Helpers/GeneratorUtils.cs index 2ca69ef51c6..a8342c0c3a3 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Helpers/GeneratorUtils.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Helpers/GeneratorUtils.cs @@ -41,7 +41,21 @@ public static DataLoaderDefaultsInfo GetDataLoaderDefaults( } } - return new DataLoaderDefaultsInfo(null, null, true, true); + return new DataLoaderDefaultsInfo(null, null, true, true, true); + } + + public static DataLoaderDefaultsInfo GetDataLoaderDefaults( + this List syntaxInfos) + { + foreach (var syntaxInfo in syntaxInfos) + { + if (syntaxInfo is DataLoaderDefaultsInfo defaults) + { + return defaults; + } + } + + return new DataLoaderDefaultsInfo(null, null, true, true, true); } public static string CreateModuleName(string? assemblyName) diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Inspectors/DataLoaderDefaultsInspector.cs b/src/HotChocolate/Core/src/Types.Analyzers/Inspectors/DataLoaderDefaultsInspector.cs index e300bea0d98..a10b5d0c4a9 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Inspectors/DataLoaderDefaultsInspector.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Inspectors/DataLoaderDefaultsInspector.cs @@ -39,7 +39,8 @@ public bool TryHandle( attribList.Arguments.IsScoped(context), attribList.Arguments.IsPublic(context), attribList.Arguments.IsInterfacePublic(context), - attribList.Arguments.RegisterService(context)); + attribList.Arguments.RegisterService(context), + attribList.Arguments.GenerateInterfaces(context)); return true; } } diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderDefaultsInfo.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderDefaultsInfo.cs index 5b58bf4ecdf..b20d2b6f4a4 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderDefaultsInfo.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderDefaultsInfo.cs @@ -4,7 +4,8 @@ public sealed class DataLoaderDefaultsInfo( bool? scoped, bool? isPublic, bool? isInterfacePublic, - bool registerServices) + bool registerServices, + bool generateInterfaces) : SyntaxInfo { public bool? Scoped { get; } = scoped; @@ -15,6 +16,8 @@ public sealed class DataLoaderDefaultsInfo( public bool RegisterServices { get; } = registerServices; + public bool GenerateInterfaces { get; } = generateInterfaces; + public override bool Equals(object? obj) => obj is DataLoaderDefaultsInfo other && Equals(other); @@ -25,8 +28,9 @@ private bool Equals(DataLoaderDefaultsInfo other) => Scoped.Equals(other.Scoped) && IsPublic.Equals(other.IsPublic) && IsInterfacePublic.Equals(other.IsInterfacePublic) - && RegisterServices.Equals(other.RegisterServices); + && RegisterServices.Equals(other.RegisterServices) + && GenerateInterfaces.Equals(other.GenerateInterfaces); public override int GetHashCode() - => HashCode.Combine(Scoped, IsPublic, IsInterfacePublic, RegisterServices); + => HashCode.Combine(Scoped, IsPublic, IsInterfacePublic, RegisterServices, GenerateInterfaces); } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/DataLoaderTests.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/DataLoaderTests.cs index 94420076e0f..81ec13224eb 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/DataLoaderTests.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/DataLoaderTests.cs @@ -500,5 +500,32 @@ public static Task> GetEntityByIdAsync( } """).MatchMarkdownAsync(); } + + [Fact] + public async Task Generate_Without_Interface() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using HotChocolate; + using GreenDonut; + + [assembly: GreenDonut.DataLoaderDefaults(GenerateInterfaces = false)] + + namespace TestNamespace; + + internal static class TestClass + { + [DataLoader] + public static Task> GetEntityByIdAsync( + IReadOnlyList entityIds, + GreenDonut.Predicates.IPredicateBuilder predicate, + CancellationToken cancellationToken) + => default!; + } + """).MatchMarkdownAsync(); + } #endif } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.DataLoader_With_Optional_Lookup.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.DataLoader_With_Optional_Lookup.md index 30bf9869b61..323f2ffe7dc 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.DataLoader_With_Optional_Lookup.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.DataLoader_With_Optional_Lookup.md @@ -20,7 +20,7 @@ namespace TestNamespace { } - public sealed class EntityByIdDataLoader + public sealed partial class EntityByIdDataLoader : global::GreenDonut.DataLoaderBase , IEntityByIdDataLoader { diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_IDictionary_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_IDictionary_MatchesSnapshot.md index 13dcc39b94d..255aef1a53d 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_IDictionary_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_IDictionary_MatchesSnapshot.md @@ -20,7 +20,7 @@ namespace TestNamespace { } - public sealed class EntityByIdDataLoader + public sealed partial class EntityByIdDataLoader : global::GreenDonut.DataLoaderBase , IEntityByIdDataLoader { diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_MatchesSnapshot.md index 2802257c228..89233d86cb1 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_MatchesSnapshot.md @@ -20,7 +20,7 @@ namespace TestNamespace { } - public sealed class EntityByIdDataLoader + public sealed partial class EntityByIdDataLoader : global::GreenDonut.DataLoaderBase , IEntityByIdDataLoader { diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_Nullable_Result_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_Nullable_Result_MatchesSnapshot.md index 6395ad50e42..c0d8237b5c2 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_Nullable_Result_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_Nullable_Result_MatchesSnapshot.md @@ -20,7 +20,7 @@ namespace TestNamespace { } - public sealed class EntityByIdDataLoader + public sealed partial class EntityByIdDataLoader : global::GreenDonut.DataLoaderBase , IEntityByIdDataLoader { diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_MatchesSnapshot.md index ce52a9eb22c..94ba5bd46e8 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_MatchesSnapshot.md @@ -20,7 +20,7 @@ namespace TestNamespace { } - public sealed class EntityByIdDataLoader + public sealed partial class EntityByIdDataLoader : global::GreenDonut.DataLoaderBase , IEntityByIdDataLoader { @@ -70,7 +70,7 @@ namespace TestNamespace IEntityByIdDataLoader EntityById { get; } } - public sealed class Group1 : IGroup1 + public sealed partial class Group1 : IGroup1 { private readonly IServiceProvider _services; private IEntityByIdDataLoader? _entityById; @@ -98,7 +98,7 @@ namespace TestNamespace IEntityByIdDataLoader EntityById { get; } } - public sealed class Group2 : IGroup2 + public sealed partial class Group2 : IGroup2 { private readonly IServiceProvider _services; private IEntityByIdDataLoader? _entityById; diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_Only_On_Class_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_Only_On_Class_MatchesSnapshot.md index 7927e10cd18..0a852f8df15 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_Only_On_Class_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_Only_On_Class_MatchesSnapshot.md @@ -20,7 +20,7 @@ namespace TestNamespace { } - public sealed class EntityByIdDataLoader + public sealed partial class EntityByIdDataLoader : global::GreenDonut.DataLoaderBase , IEntityByIdDataLoader { @@ -70,7 +70,7 @@ namespace TestNamespace IEntityByIdDataLoader EntityById { get; } } - public sealed class Group1 : IGroup1 + public sealed partial class Group1 : IGroup1 { private readonly IServiceProvider _services; private IEntityByIdDataLoader? _entityById; diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_Only_On_Method_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_Only_On_Method_MatchesSnapshot.md index fe1ac6df85e..c5fc5e41f15 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_Only_On_Method_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_Only_On_Method_MatchesSnapshot.md @@ -20,7 +20,7 @@ namespace TestNamespace { } - public sealed class EntityByIdDataLoader + public sealed partial class EntityByIdDataLoader : global::GreenDonut.DataLoaderBase , IEntityByIdDataLoader { @@ -70,7 +70,7 @@ namespace TestNamespace IEntityByIdDataLoader EntityById { get; } } - public sealed class Group1 : IGroup1 + public sealed partial class Group1 : IGroup1 { private readonly IServiceProvider _services; private IEntityByIdDataLoader? _entityById; diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Lookup_From_OtherType_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Lookup_From_OtherType_MatchesSnapshot.md index 298401b14ab..a19286a6c8a 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Lookup_From_OtherType_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Lookup_From_OtherType_MatchesSnapshot.md @@ -20,7 +20,7 @@ namespace TestNamespace { } - public sealed class EntityByIdDataLoader + public sealed partial class EntityByIdDataLoader : global::GreenDonut.DataLoaderBase , IEntityByIdDataLoader { diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Lookup_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Lookup_MatchesSnapshot.md index 190e5e2d801..86d4a85a01c 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Lookup_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Lookup_MatchesSnapshot.md @@ -20,7 +20,7 @@ namespace TestNamespace { } - public sealed class EntityByIdDataLoader + public sealed partial class EntityByIdDataLoader : global::GreenDonut.DataLoaderBase , IEntityByIdDataLoader { diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Optional_State_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Optional_State_MatchesSnapshot.md index 9030cdde690..f5da3d19f20 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Optional_State_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Optional_State_MatchesSnapshot.md @@ -20,7 +20,7 @@ namespace TestNamespace { } - public sealed class EntityByIdDataLoader + public sealed partial class EntityByIdDataLoader : global::GreenDonut.DataLoaderBase , IEntityByIdDataLoader { diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PagingArguments_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PagingArguments_MatchesSnapshot.md index 6d693b13bde..cd1be20c685 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PagingArguments_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PagingArguments_MatchesSnapshot.md @@ -20,7 +20,7 @@ namespace TestNamespace { } - public sealed class EntityByIdDataLoader + public sealed partial class EntityByIdDataLoader : global::GreenDonut.DataLoaderBase , IEntityByIdDataLoader { diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PredicateBuilder_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PredicateBuilder_MatchesSnapshot.md index 5df86f6feff..a2cdbb7fc67 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PredicateBuilder_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PredicateBuilder_MatchesSnapshot.md @@ -20,7 +20,7 @@ namespace TestNamespace { } - public sealed class EntityByIdDataLoader + public sealed partial class EntityByIdDataLoader : global::GreenDonut.DataLoaderBase , IEntityByIdDataLoader { diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Required_State_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Required_State_MatchesSnapshot.md index 23c98de2f8d..60d8d89159d 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Required_State_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Required_State_MatchesSnapshot.md @@ -20,7 +20,7 @@ namespace TestNamespace { } - public sealed class EntityByIdDataLoader + public sealed partial class EntityByIdDataLoader : global::GreenDonut.DataLoaderBase , IEntityByIdDataLoader { diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_SelectorBuilder_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_SelectorBuilder_MatchesSnapshot.md index be453e01888..6266fef0a39 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_SelectorBuilder_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_SelectorBuilder_MatchesSnapshot.md @@ -20,7 +20,7 @@ namespace TestNamespace { } - public sealed class EntityByIdDataLoader + public sealed partial class EntityByIdDataLoader : global::GreenDonut.DataLoaderBase , IEntityByIdDataLoader { diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_State_With_Default_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_State_With_Default_MatchesSnapshot.md index 164241cd8fb..ccf3c4af82a 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_State_With_Default_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_State_With_Default_MatchesSnapshot.md @@ -20,7 +20,7 @@ namespace TestNamespace { } - public sealed class EntityByIdDataLoader + public sealed partial class EntityByIdDataLoader : global::GreenDonut.DataLoaderBase , IEntityByIdDataLoader { diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_CacheDataLoader_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_CacheDataLoader_MatchesSnapshot.md index 3c4a6c0f902..65d00aaf8d7 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_CacheDataLoader_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_CacheDataLoader_MatchesSnapshot.md @@ -20,7 +20,7 @@ namespace TestNamespace { } - public sealed class EntityByIdDataLoader + public sealed partial class EntityByIdDataLoader : global::GreenDonut.DataLoaderBase , IEntityByIdDataLoader { diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_GroupedDataLoader_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_GroupedDataLoader_MatchesSnapshot.md index 6e7f2981fd2..a34817e682f 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_GroupedDataLoader_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_GroupedDataLoader_MatchesSnapshot.md @@ -20,7 +20,7 @@ namespace TestNamespace { } - public sealed class EntitiesByIdDataLoader + public sealed partial class EntitiesByIdDataLoader : global::GreenDonut.DataLoaderBase , IEntitiesByIdDataLoader { diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.Generate_Without_Interface.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.Generate_Without_Interface.md new file mode 100644 index 00000000000..61a56b92933 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.Generate_Without_Interface.md @@ -0,0 +1,119 @@ +# Generate_Without_Interface + +## GreenDonutDataLoader.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using Microsoft.Extensions.DependencyInjection; +using GreenDonut; + +namespace TestNamespace +{ + public sealed partial class EntityByIdDataLoader + : global::GreenDonut.DataLoaderBase + { + private readonly global::System.IServiceProvider _services; + + public EntityByIdDataLoader( + global::System.IServiceProvider services, + global::GreenDonut.IBatchScheduler batchScheduler, + global::GreenDonut.DataLoaderOptions options) + : base(batchScheduler, options) + { + _services = services ?? + throw new global::System.ArgumentNullException(nameof(services)); + } + + protected override async global::System.Threading.Tasks.ValueTask FetchAsync( + global::System.Collections.Generic.IReadOnlyList keys, + global::System.Memory> results, + global::GreenDonut.DataLoaderFetchContext context, + global::System.Threading.CancellationToken ct) + { + var p1 = context.GetState("GreenDonut.Predicates.IPredicateBuilder") + ?? new global::GreenDonut.Predicates.DefaultPredicateBuilder(); + var temp = await TestNamespace.TestClass.GetEntityByIdAsync(keys, p1, ct).ConfigureAwait(false); + CopyResults(keys, results.Span, temp); + } + + private void CopyResults( + global::System.Collections.Generic.IReadOnlyList keys, + global::System.Span> results, + global::System.Collections.Generic.IDictionary resultMap) + { + for (var i = 0; i < keys.Count; i++) + { + var key = keys[i]; + if (resultMap.TryGetValue(key, out var value)) + { + results[i] = global::GreenDonut.Result.Resolve(value); + } + else + { + results[i] = global::GreenDonut.Result.Resolve(default(string)); + } + } + } + } +} + + +``` + +## HotChocolateTypeModule.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + builder.AddDataLoader(); + return builder; + } + } +} + +``` + +## Compilation Diagnostics + +```json +[ + { + "Id": "GD0002", + "Title": "Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.", + "Severity": "Error", + "WarningLevel": 0, + "Location": ": (15,8)-(15,47)", + "HelpLinkUri": "https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS9204)", + "MessageFormat": "'{0}' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.", + "Message": "'GreenDonut.Predicates.IPredicateBuilder' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.", + "Category": "Compiler", + "CustomTags": [ + "Compiler", + "Telemetry", + "CustomObsolete" + ] + } +] +``` + From 8ca3246492b73e340828c6f9c4d9ffce6ddbc5ad Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Tue, 22 Oct 2024 23:14:15 +0200 Subject: [PATCH 080/154] Fixed issue that is caused by an internal predicate builder. (#7640) --- .../src/Core/Predicates/DefaultPredicateBuilder.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/GreenDonut/src/Core/Predicates/DefaultPredicateBuilder.cs b/src/GreenDonut/src/Core/Predicates/DefaultPredicateBuilder.cs index bc10b8d212b..715a5cdf253 100644 --- a/src/GreenDonut/src/Core/Predicates/DefaultPredicateBuilder.cs +++ b/src/GreenDonut/src/Core/Predicates/DefaultPredicateBuilder.cs @@ -4,10 +4,13 @@ namespace GreenDonut.Predicates; +/// +/// A default implementation of the . +/// #if NET8_0_OR_GREATER [Experimental(Experiments.Predicates)] #endif -internal sealed class DefaultPredicateBuilder : IPredicateBuilder +public sealed class DefaultPredicateBuilder : IPredicateBuilder { private List? _predicates; From ef56d40df803fa8c47c7acc2cda13b6be09e78b1 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Thu, 24 Oct 2024 21:21:19 +0200 Subject: [PATCH 081/154] Fixed issue that caused compile errors on generated DataLoader code. (#7647) --- .../src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs | 2 +- .../DataLoaderTests.DataLoader_With_Optional_Lookup.md | 2 +- ...enerateSource_BatchDataLoader_IDictionary_MatchesSnapshot.md | 2 +- ...oaderTests.GenerateSource_BatchDataLoader_MatchesSnapshot.md | 2 +- ...ateSource_BatchDataLoader_Nullable_Result_MatchesSnapshot.md | 2 +- ...GenerateSource_BatchDataLoader_With_Group_MatchesSnapshot.md | 2 +- ..._BatchDataLoader_With_Group_Only_On_Class_MatchesSnapshot.md | 2 +- ...BatchDataLoader_With_Group_Only_On_Method_MatchesSnapshot.md | 2 +- ...atchDataLoader_With_Lookup_From_OtherType_MatchesSnapshot.md | 2 +- ...enerateSource_BatchDataLoader_With_Lookup_MatchesSnapshot.md | 2 +- ...ource_BatchDataLoader_With_Optional_State_MatchesSnapshot.md | 2 +- ...urce_BatchDataLoader_With_PagingArguments_MatchesSnapshot.md | 2 +- ...rce_BatchDataLoader_With_PredicateBuilder_MatchesSnapshot.md | 2 +- ...ource_BatchDataLoader_With_Required_State_MatchesSnapshot.md | 2 +- ...urce_BatchDataLoader_With_SelectorBuilder_MatchesSnapshot.md | 2 +- ...e_BatchDataLoader_With_State_With_Default_MatchesSnapshot.md | 2 +- ...oaderTests.GenerateSource_CacheDataLoader_MatchesSnapshot.md | 2 +- ...derTests.GenerateSource_GroupedDataLoader_MatchesSnapshot.md | 2 +- .../__snapshots__/DataLoaderTests.Generate_Without_Interface.md | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs index 1ae101d8b83..0ea4192ec67 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs @@ -484,7 +484,7 @@ private void WriteFetchCall( DataLoaderKind kind, ImmutableArray parameters) { - _writer.Write("await {0}.{1}(", containingType, fetchMethod.Name); + _writer.Write("await global::{0}.{1}(", containingType, fetchMethod.Name); for (var i = 0; i < parameters.Length; i++) { diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.DataLoader_With_Optional_Lookup.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.DataLoader_With_Optional_Lookup.md index 323f2ffe7dc..5855887311d 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.DataLoader_With_Optional_Lookup.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.DataLoader_With_Optional_Lookup.md @@ -42,7 +42,7 @@ namespace TestNamespace global::GreenDonut.DataLoaderFetchContext context, global::System.Threading.CancellationToken ct) { - var temp = await TestNamespace.TestClass.GetEntityByIdAsync(keys, ct).ConfigureAwait(false); + var temp = await global::TestNamespace.TestClass.GetEntityByIdAsync(keys, ct).ConfigureAwait(false); CopyResults(keys, results.Span, temp); } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_IDictionary_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_IDictionary_MatchesSnapshot.md index 255aef1a53d..52452ce4488 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_IDictionary_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_IDictionary_MatchesSnapshot.md @@ -42,7 +42,7 @@ namespace TestNamespace global::GreenDonut.DataLoaderFetchContext context, global::System.Threading.CancellationToken ct) { - var temp = await TestNamespace.TestClass.GetEntityByIdAsync(keys, ct).ConfigureAwait(false); + var temp = await global::TestNamespace.TestClass.GetEntityByIdAsync(keys, ct).ConfigureAwait(false); CopyResults(keys, results.Span, temp); } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_MatchesSnapshot.md index 89233d86cb1..ba185aa5d1e 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_MatchesSnapshot.md @@ -42,7 +42,7 @@ namespace TestNamespace global::GreenDonut.DataLoaderFetchContext context, global::System.Threading.CancellationToken ct) { - var temp = await TestNamespace.TestClass.GetEntityByIdAsync(keys, ct).ConfigureAwait(false); + var temp = await global::TestNamespace.TestClass.GetEntityByIdAsync(keys, ct).ConfigureAwait(false); CopyResults(keys, results.Span, temp); } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_Nullable_Result_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_Nullable_Result_MatchesSnapshot.md index c0d8237b5c2..e85309a09de 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_Nullable_Result_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_Nullable_Result_MatchesSnapshot.md @@ -42,7 +42,7 @@ namespace TestNamespace global::GreenDonut.DataLoaderFetchContext context, global::System.Threading.CancellationToken ct) { - var temp = await TestNamespace.TestClass.GetEntityByIdAsync(keys, ct).ConfigureAwait(false); + var temp = await global::TestNamespace.TestClass.GetEntityByIdAsync(keys, ct).ConfigureAwait(false); CopyResults(keys, results.Span, temp); } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_MatchesSnapshot.md index 94ba5bd46e8..a5ca6c94b75 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_MatchesSnapshot.md @@ -42,7 +42,7 @@ namespace TestNamespace global::GreenDonut.DataLoaderFetchContext context, global::System.Threading.CancellationToken ct) { - var temp = await TestNamespace.TestClass.GetEntityByIdAsync(keys, ct).ConfigureAwait(false); + var temp = await global::TestNamespace.TestClass.GetEntityByIdAsync(keys, ct).ConfigureAwait(false); CopyResults(keys, results.Span, temp); } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_Only_On_Class_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_Only_On_Class_MatchesSnapshot.md index 0a852f8df15..b44d11b31ed 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_Only_On_Class_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_Only_On_Class_MatchesSnapshot.md @@ -42,7 +42,7 @@ namespace TestNamespace global::GreenDonut.DataLoaderFetchContext context, global::System.Threading.CancellationToken ct) { - var temp = await TestNamespace.TestClass.GetEntityByIdAsync(keys, ct).ConfigureAwait(false); + var temp = await global::TestNamespace.TestClass.GetEntityByIdAsync(keys, ct).ConfigureAwait(false); CopyResults(keys, results.Span, temp); } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_Only_On_Method_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_Only_On_Method_MatchesSnapshot.md index c5fc5e41f15..57af9f382e4 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_Only_On_Method_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Group_Only_On_Method_MatchesSnapshot.md @@ -42,7 +42,7 @@ namespace TestNamespace global::GreenDonut.DataLoaderFetchContext context, global::System.Threading.CancellationToken ct) { - var temp = await TestNamespace.TestClass.GetEntityByIdAsync(keys, ct).ConfigureAwait(false); + var temp = await global::TestNamespace.TestClass.GetEntityByIdAsync(keys, ct).ConfigureAwait(false); CopyResults(keys, results.Span, temp); } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Lookup_From_OtherType_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Lookup_From_OtherType_MatchesSnapshot.md index a19286a6c8a..039f448f57a 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Lookup_From_OtherType_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Lookup_From_OtherType_MatchesSnapshot.md @@ -46,7 +46,7 @@ namespace TestNamespace global::GreenDonut.DataLoaderFetchContext context, global::System.Threading.CancellationToken ct) { - var temp = await TestNamespace.TestClass.GetEntityByIdAsync(keys, ct).ConfigureAwait(false); + var temp = await global::TestNamespace.TestClass.GetEntityByIdAsync(keys, ct).ConfigureAwait(false); CopyResults(keys, results.Span, temp); } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Lookup_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Lookup_MatchesSnapshot.md index 86d4a85a01c..c8b3f2e28e2 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Lookup_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Lookup_MatchesSnapshot.md @@ -46,7 +46,7 @@ namespace TestNamespace global::GreenDonut.DataLoaderFetchContext context, global::System.Threading.CancellationToken ct) { - var temp = await TestNamespace.TestClass.GetEntityByIdAsync(keys, ct).ConfigureAwait(false); + var temp = await global::TestNamespace.TestClass.GetEntityByIdAsync(keys, ct).ConfigureAwait(false); CopyResults(keys, results.Span, temp); } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Optional_State_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Optional_State_MatchesSnapshot.md index f5da3d19f20..7012928a393 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Optional_State_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Optional_State_MatchesSnapshot.md @@ -43,7 +43,7 @@ namespace TestNamespace global::System.Threading.CancellationToken ct) { var p1 = context.GetState("key"); - var temp = await TestNamespace.TestClass.GetEntityByIdAsync(keys, p1, ct).ConfigureAwait(false); + var temp = await global::TestNamespace.TestClass.GetEntityByIdAsync(keys, p1, ct).ConfigureAwait(false); CopyResults(keys, results.Span, temp); } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PagingArguments_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PagingArguments_MatchesSnapshot.md index cd1be20c685..874273d38be 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PagingArguments_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PagingArguments_MatchesSnapshot.md @@ -43,7 +43,7 @@ namespace TestNamespace global::System.Threading.CancellationToken ct) { var p1 = context.GetRequiredState("HotChocolate.Pagination.PagingArguments"); - var temp = await TestNamespace.TestClass.GetEntityByIdAsync(keys, p1, ct).ConfigureAwait(false); + var temp = await global::TestNamespace.TestClass.GetEntityByIdAsync(keys, p1, ct).ConfigureAwait(false); CopyResults(keys, results.Span, temp); } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PredicateBuilder_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PredicateBuilder_MatchesSnapshot.md index a2cdbb7fc67..540e99be34d 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PredicateBuilder_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PredicateBuilder_MatchesSnapshot.md @@ -44,7 +44,7 @@ namespace TestNamespace { var p1 = context.GetState("GreenDonut.Predicates.IPredicateBuilder") ?? new global::GreenDonut.Predicates.DefaultPredicateBuilder(); - var temp = await TestNamespace.TestClass.GetEntityByIdAsync(keys, p1, ct).ConfigureAwait(false); + var temp = await global::TestNamespace.TestClass.GetEntityByIdAsync(keys, p1, ct).ConfigureAwait(false); CopyResults(keys, results.Span, temp); } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Required_State_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Required_State_MatchesSnapshot.md index 60d8d89159d..b1116d98436 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Required_State_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_Required_State_MatchesSnapshot.md @@ -43,7 +43,7 @@ namespace TestNamespace global::System.Threading.CancellationToken ct) { var p1 = context.GetRequiredState("key"); - var temp = await TestNamespace.TestClass.GetEntityByIdAsync(keys, p1, ct).ConfigureAwait(false); + var temp = await global::TestNamespace.TestClass.GetEntityByIdAsync(keys, p1, ct).ConfigureAwait(false); CopyResults(keys, results.Span, temp); } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_SelectorBuilder_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_SelectorBuilder_MatchesSnapshot.md index 6266fef0a39..da24ba18f11 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_SelectorBuilder_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_SelectorBuilder_MatchesSnapshot.md @@ -44,7 +44,7 @@ namespace TestNamespace { var p1 = context.GetState("GreenDonut.Selectors.ISelectorBuilder") ?? new global::GreenDonut.Selectors.DefaultSelectorBuilder(); - var temp = await TestNamespace.TestClass.GetEntityByIdAsync(keys, p1, ct).ConfigureAwait(false); + var temp = await global::TestNamespace.TestClass.GetEntityByIdAsync(keys, p1, ct).ConfigureAwait(false); CopyResults(keys, results.Span, temp); } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_State_With_Default_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_State_With_Default_MatchesSnapshot.md index ccf3c4af82a..633e7aa703f 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_State_With_Default_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_State_With_Default_MatchesSnapshot.md @@ -43,7 +43,7 @@ namespace TestNamespace global::System.Threading.CancellationToken ct) { var p1 = context.GetStateOrDefault("key", "default"); - var temp = await TestNamespace.TestClass.GetEntityByIdAsync(keys, p1, ct).ConfigureAwait(false); + var temp = await global::TestNamespace.TestClass.GetEntityByIdAsync(keys, p1, ct).ConfigureAwait(false); CopyResults(keys, results.Span, temp); } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_CacheDataLoader_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_CacheDataLoader_MatchesSnapshot.md index 65d00aaf8d7..4fa9a5ac90a 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_CacheDataLoader_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_CacheDataLoader_MatchesSnapshot.md @@ -46,7 +46,7 @@ namespace TestNamespace try { var key = keys[i]; - var value = await TestNamespace.TestClass.GetEntityByIdAsync(key, ct).ConfigureAwait(false); + var value = await global::TestNamespace.TestClass.GetEntityByIdAsync(key, ct).ConfigureAwait(false); results.Span[i] = Result.Resolve(value); } catch (global::System.Exception ex) diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_GroupedDataLoader_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_GroupedDataLoader_MatchesSnapshot.md index a34817e682f..583781c044b 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_GroupedDataLoader_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_GroupedDataLoader_MatchesSnapshot.md @@ -42,7 +42,7 @@ namespace TestNamespace global::GreenDonut.DataLoaderFetchContext context, global::System.Threading.CancellationToken ct) { - var temp = await TestNamespace.TestClass.GetEntitiesByIdAsync(keys, ct).ConfigureAwait(false); + var temp = await global::TestNamespace.TestClass.GetEntitiesByIdAsync(keys, ct).ConfigureAwait(false); CopyResults(keys, results.Span, temp); } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.Generate_Without_Interface.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.Generate_Without_Interface.md index 61a56b92933..cebdeb21888 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.Generate_Without_Interface.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.Generate_Without_Interface.md @@ -38,7 +38,7 @@ namespace TestNamespace { var p1 = context.GetState("GreenDonut.Predicates.IPredicateBuilder") ?? new global::GreenDonut.Predicates.DefaultPredicateBuilder(); - var temp = await TestNamespace.TestClass.GetEntityByIdAsync(keys, p1, ct).ConfigureAwait(false); + var temp = await global::TestNamespace.TestClass.GetEntityByIdAsync(keys, p1, ct).ConfigureAwait(false); CopyResults(keys, results.Span, temp); } From 7f72752ba534be1ff63cc7222722137708b188f0 Mon Sep 17 00:00:00 2001 From: Glen Date: Wed, 30 Oct 2024 11:41:30 +0200 Subject: [PATCH 082/154] Fixed issue that closed stream when using StreamWriter in SyntaxPrinter.PrintToAsync (#7657) --- .../Utilities/SyntaxPrinter.cs | 7 ++- .../IntegrationTests.cs | 53 +++++++++++++++++++ ...ts.ExecuteAutomaticPersistedOperation.snap | 11 ++++ 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 src/HotChocolate/PersistedOperations/test/PersistedOperations.FileSystem.Tests/__snapshots__/IntegrationTests.ExecuteAutomaticPersistedOperation.snap diff --git a/src/HotChocolate/Language/src/Language.SyntaxTree/Utilities/SyntaxPrinter.cs b/src/HotChocolate/Language/src/Language.SyntaxTree/Utilities/SyntaxPrinter.cs index 9b2ef22bbce..ced58b9b247 100644 --- a/src/HotChocolate/Language/src/Language.SyntaxTree/Utilities/SyntaxPrinter.cs +++ b/src/HotChocolate/Language/src/Language.SyntaxTree/Utilities/SyntaxPrinter.cs @@ -60,11 +60,14 @@ public static async ValueTask PrintToAsync( #if NETSTANDARD2_0 using var streamWriter = new StreamWriter( stream, - new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true)); + new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true), + -1, + leaveOpen: true); #else await using var streamWriter = new StreamWriter( stream, - new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true)); + new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true), + leaveOpen: true); #endif var syntaxWriter = StringSyntaxWriter.Rent(); diff --git a/src/HotChocolate/PersistedOperations/test/PersistedOperations.FileSystem.Tests/IntegrationTests.cs b/src/HotChocolate/PersistedOperations/test/PersistedOperations.FileSystem.Tests/IntegrationTests.cs index 2b0093d7ec7..ee985f04f26 100644 --- a/src/HotChocolate/PersistedOperations/test/PersistedOperations.FileSystem.Tests/IntegrationTests.cs +++ b/src/HotChocolate/PersistedOperations/test/PersistedOperations.FileSystem.Tests/IntegrationTests.cs @@ -2,6 +2,7 @@ using HotChocolate.Types; using HotChocolate.Execution; using Snapshooter.Xunit; +using HotChocolate.Language; using IO = System.IO; namespace HotChocolate.PersistedOperations.FileSystem; @@ -83,4 +84,56 @@ public async Task ExecutePersistedOperation_NotFound() File.Delete(cachedOperation); result.MatchSnapshot(); } + + [Fact] + public async Task ExecuteAutomaticPersistedOperation() + { + // arrange + var cacheDirectory = IO.Path.GetTempPath(); + var documentId = Guid.NewGuid().ToString("N"); + const string documentHash = "hash"; + + var executor = + await new ServiceCollection() + .AddGraphQL() + .AddQueryType(c => c.Name("Query").Field("a").Resolve("b")) + .AddFileSystemOperationDocumentStorage(cacheDirectory) + .UseRequest(n => async c => + { + await n(c); + + if (c.IsPersistedDocument && c.Result is IOperationResult r) + { + c.Result = OperationResultBuilder + .FromResult(r) + .SetExtension("persistedDocument", true) + .Build(); + } + }) + .UseAutomaticPersistedOperationPipeline() + .BuildRequestExecutorAsync(); + + // act + var result = await executor.ExecuteAsync( + OperationRequest + .FromId(documentId) + .WithDocument(new OperationDocument(Utf8GraphQLParser.Parse("{ __typename }"))) + .WithDocumentHash(documentHash) + .WithExtensions(new Dictionary + { + { + "persistedQuery", + new Dictionary + { + { "version", 1 }, + { "md5Hash", documentHash } + } + } + })); + + File.Delete(IO.Path.Combine(cacheDirectory, documentHash + ".graphql")); + + // assert + result.MatchSnapshot(); + } } diff --git a/src/HotChocolate/PersistedOperations/test/PersistedOperations.FileSystem.Tests/__snapshots__/IntegrationTests.ExecuteAutomaticPersistedOperation.snap b/src/HotChocolate/PersistedOperations/test/PersistedOperations.FileSystem.Tests/__snapshots__/IntegrationTests.ExecuteAutomaticPersistedOperation.snap new file mode 100644 index 00000000000..3721786c084 --- /dev/null +++ b/src/HotChocolate/PersistedOperations/test/PersistedOperations.FileSystem.Tests/__snapshots__/IntegrationTests.ExecuteAutomaticPersistedOperation.snap @@ -0,0 +1,11 @@ +{ + "data": { + "__typename": "Query" + }, + "extensions": { + "persistedQuery": { + "md5Hash": "hash", + "persisted": true + } + } +} From 3fb46f3356218e86a14eff77e6bbb18b16310494 Mon Sep 17 00:00:00 2001 From: Glen Date: Thu, 31 Oct 2024 14:27:02 +0200 Subject: [PATCH 083/154] Added missing ParallelExecutable flag to node and nodes fields (#7661) --- .../Types/Relay/NodeFieldTypeInterceptor.cs | 4 +- .../Integration/DataLoader/DataLoaderTests.cs | 42 +++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/NodeFieldTypeInterceptor.cs b/src/HotChocolate/Core/src/Types/Types/Relay/NodeFieldTypeInterceptor.cs index 96f6b95e39d..6d88de1aa87 100644 --- a/src/HotChocolate/Core/src/Types/Types/Relay/NodeFieldTypeInterceptor.cs +++ b/src/HotChocolate/Core/src/Types/Types/Relay/NodeFieldTypeInterceptor.cs @@ -94,7 +94,7 @@ private static void CreateNodeField( }; }), }, - Flags = FieldFlags.GlobalIdNodeField + Flags = FieldFlags.ParallelExecutable | FieldFlags.GlobalIdNodeField }; // In the projection interceptor we want to change the context data that is on this field @@ -134,7 +134,7 @@ private static void CreateNodesField( }; }), }, - Flags = FieldFlags.GlobalIdNodesField + Flags = FieldFlags.ParallelExecutable | FieldFlags.GlobalIdNodesField }; // In the projection interceptor we want to change the context data that is on this field diff --git a/src/HotChocolate/Core/test/Execution.Tests/Integration/DataLoader/DataLoaderTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Integration/DataLoader/DataLoaderTests.cs index 957668c1b4d..64ef4ccfe24 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Integration/DataLoader/DataLoaderTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Integration/DataLoader/DataLoaderTests.cs @@ -59,6 +59,43 @@ await ExpectValid( await snapshot.MatchMarkdownAsync(); } + [Fact] + public async Task FetchMultipleNodesDataLoader() + { + var batchFetchCount = 0; + + await ExpectValid( + """ + { + a: node(id: "RW50aXR5OjE==") { ... on Entity { id } } + b: node(id: "RW50aXR5OjI==") { ... on Entity { id } } + } + """, + configure: b => b + .AddGraphQL() + .AddGlobalObjectIdentification() + .AddObjectType(descriptor => + { + descriptor + .ImplementsNode() + .IdField(e => e.Id) + .ResolveNode( + async (ctx, id) => await ctx.BatchDataLoader( + (keys, _) => + { + batchFetchCount++; + + return Task.FromResult>( + keys.ToDictionary(t => t, _ => new Entity { Id = id })); + }) + .LoadAsync(id)) + .Resolve(ctx => ctx.Parent().Id); + }) + .AddQueryType()); + + Assert.Equal(1, batchFetchCount); + } + [LocalFact] public async Task FetchDataLoader() { @@ -667,4 +704,9 @@ public CounterDataLoader(DataLoaderOptions options) : base(options) protected override Task LoadSingleAsync(string key, CancellationToken cancellationToken) => Task.FromResult(key + Counter); } + + public class Entity + { + public int Id { get; set; } + } } From bdfde6bf846828a84d8ccd7d1cd87c4e772604ed Mon Sep 17 00:00:00 2001 From: Tobias Tengler <45513122+tobias-tengler@users.noreply.github.com> Date: Thu, 31 Oct 2024 13:28:09 +0100 Subject: [PATCH 084/154] [Fusion] Fixed composition of spec directives (#7643) --- .../Composition/Extensions/MergeExtensions.cs | 9 +- .../Fusion/src/Composition/FusionTypes.cs | 4 + .../Pipeline/MergeTypeMiddleware.cs | 3 +- .../test/Composition.Tests/DirectiveTests.cs | 174 ++++++++++++++++++ .../Skimmed/BuiltIns/BooleanTypeDefinition.cs | 10 - .../Skimmed/src/Skimmed/BuiltIns/BuiltIns.cs | 57 +++--- .../BuiltIns/DeprecatedDirectiveDefinition.cs | 8 +- .../Skimmed/BuiltIns/FloatTypeDefinition.cs | 10 - .../src/Skimmed/BuiltIns/IDTypeDefinition.cs | 10 - .../BuiltIns/IncludeDirectiveDefinition.cs | 5 +- .../src/Skimmed/BuiltIns/IntTypeDefinition.cs | 10 - .../BuiltIns/SkipDirectiveDefinition.cs | 5 +- .../SpecifiedByDirectiveDefinition.cs | 16 ++ .../Skimmed/BuiltIns/StringTypeDefinition.cs | 10 - .../Skimmed/Serialization/SchemaFormatter.cs | 5 + .../src/Skimmed/Serialization/SchemaParser.cs | 31 +++- 16 files changed, 280 insertions(+), 87 deletions(-) delete mode 100644 src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/BooleanTypeDefinition.cs delete mode 100644 src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/FloatTypeDefinition.cs delete mode 100644 src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/IDTypeDefinition.cs delete mode 100644 src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/IntTypeDefinition.cs create mode 100644 src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/SpecifiedByDirectiveDefinition.cs delete mode 100644 src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/StringTypeDefinition.cs diff --git a/src/HotChocolate/Fusion/src/Composition/Extensions/MergeExtensions.cs b/src/HotChocolate/Fusion/src/Composition/Extensions/MergeExtensions.cs index c3d060f28d9..49503d22ec0 100644 --- a/src/HotChocolate/Fusion/src/Composition/Extensions/MergeExtensions.cs +++ b/src/HotChocolate/Fusion/src/Composition/Extensions/MergeExtensions.cs @@ -173,18 +173,15 @@ internal static void MergeDirectivesWith( foreach (var directive in source.Directives) { if (context.FusionTypes.IsFusionDirective(directive.Name) - || BuiltIns.IsBuiltInDirective(directive.Name) + // @deprecated is handled separately + || directive.Name == BuiltIns.Deprecated.Name // @tag is handled separately || directive.Name == "tag") { continue; } - if (context.FusionGraph.DirectiveDefinitions.TryGetDirective(directive.Name, out var directiveDefinition) - && directiveDefinition.IsSpecDirective) - { - continue; - } + context.FusionGraph.DirectiveDefinitions.TryGetDirective(directive.Name, out var directiveDefinition); if (!target.Directives.ContainsName(directive.Name)) { diff --git a/src/HotChocolate/Fusion/src/Composition/FusionTypes.cs b/src/HotChocolate/Fusion/src/Composition/FusionTypes.cs index 6d0ec753c34..822c62da42b 100644 --- a/src/HotChocolate/Fusion/src/Composition/FusionTypes.cs +++ b/src/HotChocolate/Fusion/src/Composition/FusionTypes.cs @@ -183,6 +183,7 @@ private DirectiveDefinition RegisterVariableDirectiveType( ScalarTypeDefinition selection) { var directiveType = new DirectiveDefinition(name); + directiveType.IsRepeatable = true; directiveType.Arguments.Add(new InputFieldDefinition(NameArg, new NonNullTypeDefinition(typeName))); directiveType.Arguments.Add(new InputFieldDefinition(SelectArg, selection)); directiveType.Arguments.Add(new InputFieldDefinition(ArgumentArg, typeName)); @@ -259,6 +260,7 @@ private DirectiveDefinition RegisterResolverDirectiveType( EnumTypeDefinition resolverKind) { var directiveType = new DirectiveDefinition(name); + directiveType.IsRepeatable = true; directiveType.Locations |= Types.DirectiveLocation.Object; directiveType.Arguments.Add(new InputFieldDefinition(SelectArg, new NonNullTypeDefinition(selectionSet))); directiveType.Arguments.Add(new InputFieldDefinition(SubgraphArg, new NonNullTypeDefinition(typeName))); @@ -296,6 +298,7 @@ private DirectiveDefinition RegisterSourceDirectiveType(string name, ScalarTypeD new InputFieldDefinition(NameArg, typeName), }, }; + directiveType.IsRepeatable = true; directiveType.Features.Set(new FusionTypeMetadata { IsFusionType = true }); _fusionGraph.DirectiveDefinitions.Add(directiveType); return directiveType; @@ -344,6 +347,7 @@ private DirectiveDefinition RegisterTransportDirectiveType( ScalarTypeDefinition uri) { var directiveType = new DirectiveDefinition(name); + directiveType.IsRepeatable = true; directiveType.Locations = Types.DirectiveLocation.FieldDefinition; directiveType.Arguments.Add(new InputFieldDefinition(SubgraphArg, new NonNullTypeDefinition(typeName))); directiveType.Arguments.Add(new InputFieldDefinition(ClientGroupArg, typeName)); diff --git a/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeTypeMiddleware.cs b/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeTypeMiddleware.cs index 6f143cbe935..311cb17de18 100644 --- a/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeTypeMiddleware.cs +++ b/src/HotChocolate/Fusion/src/Composition/Pipeline/MergeTypeMiddleware.cs @@ -118,13 +118,12 @@ private static void MergeDirectiveDefinition( if (!target.Arguments.TryGetField(sourceArgument.Name, out var targetArgument)) { context.Log.Write(LogEntryHelper.DirectiveDefinitionArgumentMismatch(new SchemaCoordinate(source.Name), source)); - return; + continue; } if (!sourceArgument.Type.Equals(targetArgument.Type, TypeComparison.Structural)) { context.Log.Write(LogEntryHelper.DirectiveDefinitionArgumentMismatch(new SchemaCoordinate(source.Name), source)); - return; } } diff --git a/src/HotChocolate/Fusion/test/Composition.Tests/DirectiveTests.cs b/src/HotChocolate/Fusion/test/Composition.Tests/DirectiveTests.cs index d873c094378..e5de954212e 100644 --- a/src/HotChocolate/Fusion/test/Composition.Tests/DirectiveTests.cs +++ b/src/HotChocolate/Fusion/test/Composition.Tests/DirectiveTests.cs @@ -1,4 +1,5 @@ using CookieCrumble; +using HotChocolate.Fusion.Composition.Features; using HotChocolate.Fusion.Metadata; using HotChocolate.Fusion.Shared; using HotChocolate.Language; @@ -358,6 +359,179 @@ directive @test(arg: String) repeatable on FIELD_DEFINITION """); } + [Fact] + public async Task Repeat_Repeatable_Directive_Applied_Multiple_Times_On_Same_Field() + { + // arrange + var subgraphA = await TestSubgraph.CreateAsync( + """ + type Query { + foo: SubType @test(arg: "A1") @test(arg: "A2") + } + + type SubType { + bar: String @test(arg: "A1") @test(arg: "A2") + } + + directive @test(arg: String) repeatable on FIELD_DEFINITION + """); + + var subgraphB = await TestSubgraph.CreateAsync( + """ + type Query { + foo: SubType @test(arg: "B1") @test(arg: "B2") + } + + type SubType { + bar: String @test(arg: "B1") @test(arg: "B2") + } + + directive @test(arg: String) repeatable on FIELD_DEFINITION + """); + + using var subgraphs = new TestSubgraphCollection(output, [subgraphA, subgraphB]); + + // act + var fusionGraph = await subgraphs.GetFusionGraphAsync(); + + // assert + GetSchemaWithoutFusion(fusionGraph).MatchInlineSnapshot( + """ + schema { + query: Query + } + + type Query { + foo: SubType @test(arg: "A1") @test(arg: "A2") @test(arg: "B1") @test(arg: "B2") + } + + type SubType { + bar: String @test(arg: "A1") @test(arg: "A2") @test(arg: "B1") @test(arg: "B2") + } + + directive @test(arg: String) repeatable on FIELD_DEFINITION + """); + } + + [Fact] + public async Task Properly_Compose_TypeSystem_Spec_Directives() + { + // arrange + var subgraphSchemaA = """ + type Query { + field: CustomScalar @deprecated(reason: "Deprecated") + field2: String + } + + scalar CustomScalar @specifiedBy(url: "https://foo.bar") + """; + + var subgraphSchemaB = """ + type Query { + field: CustomScalar @deprecated(reason: "Deprecated") + field2: String + } + + scalar CustomScalar @specifiedBy(url: "https://foo.bar") + """; + + var features = new FusionFeatureCollection(FusionFeatures.NodeField); + + var configurations = new [] { subgraphSchemaA, subgraphSchemaB } + .Select((schema, index) => + { + return new SubgraphConfiguration( + index.ToString(), + schema, + string.Empty, + new IClientConfiguration[] + { + new HttpClientConfiguration(new Uri("http://localhost:5000/graphql")), + }, + null); + }); + + // act + var fusionGraph = await new FusionGraphComposer(logFactory:_logFactory) + .ComposeAsync(configurations, features); + + // assert + GetSchemaWithoutFusion(fusionGraph).MatchInlineSnapshot( + """ + schema { + query: Query + } + + type Query { + field: CustomScalar @deprecated(reason: "Deprecated") + field2: String + } + + scalar CustomScalar @specifiedBy(url: "https:\/\/foo.bar") + """); + } + + [Fact] + public async Task Properly_Compose_TypeSystem_Spec_Directives_When_They_Are_Part_Of_Subgraph_Schema() + { + // arrange + var subgraphSchemaA = """ + type Query { + field: CustomScalar @deprecated(reason: "Deprecated") + field2: String + } + + scalar CustomScalar @specifiedBy(url: "https://foo.bar") + """; + + var subgraphSchemaB = """ + type Query { + field: CustomScalar @deprecated(reason: "Deprecated") + field2: String + } + + "The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." + directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR + + scalar CustomScalar @specifiedBy(url: "https://foo.bar") + """; + + var features = new FusionFeatureCollection(FusionFeatures.NodeField); + + var configurations = new [] { subgraphSchemaA, subgraphSchemaB } + .Select((schema, index) => + { + return new SubgraphConfiguration( + index.ToString(), + schema, + string.Empty, + new IClientConfiguration[] + { + new HttpClientConfiguration(new Uri("http://localhost:5000/graphql")), + }, + null); + }); + + // act + var fusionGraph = await new FusionGraphComposer(logFactory:_logFactory) + .ComposeAsync(configurations, features); + + // assert + GetSchemaWithoutFusion(fusionGraph).MatchInlineSnapshot( + """ + schema { + query: Query + } + + type Query { + field: CustomScalar @deprecated(reason: "Deprecated") + field2: String + } + + scalar CustomScalar @specifiedBy(url: "https:\/\/foo.bar") + """); + } + private static DocumentNode GetSchemaWithoutFusion(SchemaDefinition fusionGraph) { var fusionGraphDoc = Utf8GraphQLParser.Parse(SchemaFormatter.FormatAsString(fusionGraph)); diff --git a/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/BooleanTypeDefinition.cs b/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/BooleanTypeDefinition.cs deleted file mode 100644 index f13f689b2f6..00000000000 --- a/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/BooleanTypeDefinition.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace HotChocolate.Skimmed; - -public sealed class BooleanTypeDefinition : ScalarTypeDefinition -{ - internal BooleanTypeDefinition() - : base(BuiltIns.Boolean.Name) - { - IsSpecScalar = true; - } -} diff --git a/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/BuiltIns.cs b/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/BuiltIns.cs index dd1756dfc49..985e53f4c80 100644 --- a/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/BuiltIns.cs +++ b/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/BuiltIns.cs @@ -6,40 +6,35 @@ public static class String { public const string Name = nameof(String); - public static StringTypeDefinition Create() - => new(); + public static ScalarTypeDefinition Create() => new(Name) { IsSpecScalar = true }; } public static class Boolean { public const string Name = nameof(Boolean); - public static BooleanTypeDefinition Create() - => new(); + public static ScalarTypeDefinition Create() => new(Name) { IsSpecScalar = true }; } public static class Float { public const string Name = nameof(Float); - public static FloatTypeDefinition Create() - => new(); + public static ScalarTypeDefinition Create() => new(Name) { IsSpecScalar = true }; } public static class ID { public const string Name = nameof(ID); - public static IDTypeDefinition Create() - => new(); + public static ScalarTypeDefinition Create() => new(Name) { IsSpecScalar = true }; } public static class Int { public const string Name = nameof(Int); - public static IntTypeDefinition Create() - => new(); + public static ScalarTypeDefinition Create() => new(Name) { IsSpecScalar = true }; } public static class Include @@ -49,13 +44,13 @@ public static class Include public static IncludeDirectiveDefinition Create(SchemaDefinition schema) { - if (!schema.Types.TryGetType(Boolean.Name, out var typeDef)) + if (!schema.Types.TryGetType(Boolean.Name, out var booleanTypeDef)) { - typeDef = new BooleanTypeDefinition(); - schema.Types.Add(typeDef); + booleanTypeDef = Boolean.Create(); + schema.Types.Add(booleanTypeDef); } - return new IncludeDirectiveDefinition(typeDef); + return new IncludeDirectiveDefinition(booleanTypeDef); } } @@ -66,13 +61,13 @@ public static class Skip public static SkipDirectiveDefinition Create(SchemaDefinition schema) { - if (!schema.Types.TryGetType(Boolean.Name, out var typeDef)) + if (!schema.Types.TryGetType(Boolean.Name, out var booleanTypeDef)) { - typeDef = new BooleanTypeDefinition(); - schema.Types.Add(typeDef); + booleanTypeDef = Boolean.Create(); + schema.Types.Add(booleanTypeDef); } - return new SkipDirectiveDefinition(typeDef); + return new SkipDirectiveDefinition(booleanTypeDef); } } @@ -83,13 +78,30 @@ public static class Deprecated public static DeprecatedDirectiveDefinition Create(SchemaDefinition schema) { - if (!schema.Types.TryGetType(String.Name, out var typeDef)) + if (!schema.Types.TryGetType(String.Name, out var stringTypeDef)) { - typeDef = new StringTypeDefinition(); - schema.Types.Add(typeDef); + stringTypeDef = String.Create(); + schema.Types.Add(stringTypeDef); } - return new DeprecatedDirectiveDefinition(typeDef); + return new DeprecatedDirectiveDefinition(stringTypeDef); + } + } + + public static class SpecifiedBy + { + public const string Name = "specifiedBy"; + public const string Url = "url"; + + public static SpecifiedByDirectiveDefinition Create(SchemaDefinition schema) + { + if (!schema.Types.TryGetType(String.Name, out var stringTypeDef)) + { + stringTypeDef = String.Create(); + schema.Types.Add(stringTypeDef); + } + + return new SpecifiedByDirectiveDefinition(stringTypeDef); } } @@ -110,6 +122,7 @@ public static bool IsBuiltInDirective(string name) Include.Name => true, Skip.Name => true, Deprecated.Name => true, + SpecifiedBy.Name => true, _ => false }; } diff --git a/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/DeprecatedDirectiveDefinition.cs b/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/DeprecatedDirectiveDefinition.cs index 136b296c024..9785758c711 100644 --- a/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/DeprecatedDirectiveDefinition.cs +++ b/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/DeprecatedDirectiveDefinition.cs @@ -1,12 +1,18 @@ +using HotChocolate.Types; + namespace HotChocolate.Skimmed; public sealed class DeprecatedDirectiveDefinition : DirectiveDefinition { - internal DeprecatedDirectiveDefinition(StringTypeDefinition stringType) + internal DeprecatedDirectiveDefinition(ScalarTypeDefinition stringType) : base(BuiltIns.Deprecated.Name) { IsSpecDirective = true; Arguments.Add(new InputFieldDefinition(BuiltIns.Deprecated.Reason, stringType)); + Locations = DirectiveLocation.FieldDefinition | + DirectiveLocation.ArgumentDefinition | + DirectiveLocation.InputFieldDefinition | + DirectiveLocation.EnumValue; } public InputFieldDefinition Reason => Arguments[BuiltIns.Deprecated.Reason]; diff --git a/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/FloatTypeDefinition.cs b/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/FloatTypeDefinition.cs deleted file mode 100644 index 4fa0ff492ca..00000000000 --- a/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/FloatTypeDefinition.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace HotChocolate.Skimmed; - -public sealed class FloatTypeDefinition : ScalarTypeDefinition -{ - internal FloatTypeDefinition() - : base(BuiltIns.Float.Name) - { - IsSpecScalar = true; - } -} diff --git a/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/IDTypeDefinition.cs b/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/IDTypeDefinition.cs deleted file mode 100644 index 5525050e5e2..00000000000 --- a/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/IDTypeDefinition.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace HotChocolate.Skimmed; - -public sealed class IDTypeDefinition : ScalarTypeDefinition -{ - internal IDTypeDefinition() - : base(BuiltIns.ID.Name) - { - IsSpecScalar = true; - } -} diff --git a/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/IncludeDirectiveDefinition.cs b/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/IncludeDirectiveDefinition.cs index 597b418f769..7936bf2324d 100644 --- a/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/IncludeDirectiveDefinition.cs +++ b/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/IncludeDirectiveDefinition.cs @@ -1,12 +1,15 @@ +using HotChocolate.Types; + namespace HotChocolate.Skimmed; public sealed class IncludeDirectiveDefinition : DirectiveDefinition { - internal IncludeDirectiveDefinition(BooleanTypeDefinition booleanType) + internal IncludeDirectiveDefinition(ScalarTypeDefinition booleanType) : base(BuiltIns.Include.Name) { IsSpecDirective = true; Arguments.Add(new InputFieldDefinition(BuiltIns.Include.If, booleanType)); + Locations = DirectiveLocation.Field | DirectiveLocation.FragmentSpread | DirectiveLocation.InlineFragment; } public InputFieldDefinition If => Arguments[BuiltIns.Include.If]; diff --git a/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/IntTypeDefinition.cs b/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/IntTypeDefinition.cs deleted file mode 100644 index d795e766ddf..00000000000 --- a/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/IntTypeDefinition.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace HotChocolate.Skimmed; - -public sealed class IntTypeDefinition : ScalarTypeDefinition -{ - internal IntTypeDefinition() - : base(BuiltIns.Int.Name) - { - IsSpecScalar = true; - } -} diff --git a/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/SkipDirectiveDefinition.cs b/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/SkipDirectiveDefinition.cs index 72dbbe528aa..8e601dd856b 100644 --- a/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/SkipDirectiveDefinition.cs +++ b/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/SkipDirectiveDefinition.cs @@ -1,12 +1,15 @@ +using HotChocolate.Types; + namespace HotChocolate.Skimmed; public sealed class SkipDirectiveDefinition : DirectiveDefinition { - internal SkipDirectiveDefinition(BooleanTypeDefinition booleanType) + internal SkipDirectiveDefinition(ScalarTypeDefinition booleanType) : base(BuiltIns.Skip.Name) { IsSpecDirective = true; Arguments.Add(new InputFieldDefinition(BuiltIns.Skip.If, booleanType)); + Locations = DirectiveLocation.Field | DirectiveLocation.FragmentSpread | DirectiveLocation.InlineFragment; } public InputFieldDefinition If => Arguments[BuiltIns.Skip.If]; diff --git a/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/SpecifiedByDirectiveDefinition.cs b/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/SpecifiedByDirectiveDefinition.cs new file mode 100644 index 00000000000..0a003fac113 --- /dev/null +++ b/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/SpecifiedByDirectiveDefinition.cs @@ -0,0 +1,16 @@ +using HotChocolate.Types; + +namespace HotChocolate.Skimmed; + +public sealed class SpecifiedByDirectiveDefinition : DirectiveDefinition +{ + internal SpecifiedByDirectiveDefinition(ScalarTypeDefinition stringType) + : base(BuiltIns.SpecifiedBy.Name) + { + IsSpecDirective = true; + Arguments.Add(new InputFieldDefinition(BuiltIns.SpecifiedBy.Url, new NonNullTypeDefinition(stringType))); + Locations = DirectiveLocation.Scalar; + } + + public InputFieldDefinition Url => Arguments[BuiltIns.SpecifiedBy.Url]; +} diff --git a/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/StringTypeDefinition.cs b/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/StringTypeDefinition.cs deleted file mode 100644 index 8bd02a55343..00000000000 --- a/src/HotChocolate/Skimmed/src/Skimmed/BuiltIns/StringTypeDefinition.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace HotChocolate.Skimmed; - -public sealed class StringTypeDefinition : ScalarTypeDefinition -{ - internal StringTypeDefinition() - : base(BuiltIns.String.Name) - { - IsSpecScalar = true; - } -} diff --git a/src/HotChocolate/Skimmed/src/Skimmed/Serialization/SchemaFormatter.cs b/src/HotChocolate/Skimmed/src/Skimmed/Serialization/SchemaFormatter.cs index cc428bf0f2f..9d8462b3576 100644 --- a/src/HotChocolate/Skimmed/src/Skimmed/Serialization/SchemaFormatter.cs +++ b/src/HotChocolate/Skimmed/src/Skimmed/Serialization/SchemaFormatter.cs @@ -175,6 +175,11 @@ public override void VisitDirectiveTypes( foreach (var type in directiveTypes.OrderBy(t => t.Name)) { + if (BuiltIns.IsBuiltInDirective(type.Name)) + { + continue; + } + VisitDirectiveType(type, context); definitionNodes.Add((IDefinitionNode)context.Result!); } diff --git a/src/HotChocolate/Skimmed/src/Skimmed/Serialization/SchemaParser.cs b/src/HotChocolate/Skimmed/src/Skimmed/Serialization/SchemaParser.cs index 509f745a27c..28640e6c5eb 100644 --- a/src/HotChocolate/Skimmed/src/Skimmed/Serialization/SchemaParser.cs +++ b/src/HotChocolate/Skimmed/src/Skimmed/Serialization/SchemaParser.cs @@ -38,13 +38,18 @@ private static void DiscoverDirectives(SchemaDefinition schema, DocumentNode doc { if (definition is DirectiveDefinitionNode def) { + if (BuiltIns.IsBuiltInDirective(def.Name.Value)) + { + // If a built-in directive is redefined in the schema, we just ignore it. + continue; + } + if (schema.DirectiveDefinitions.ContainsName(def.Name.Value)) { // TODO : parsing error throw new Exception("duplicate"); } - // TODO: This is problematic schema.DirectiveDefinitions.Add(new DirectiveDefinition(def.Name.Value)); } } @@ -540,6 +545,11 @@ private static void BuildDirectiveTypes(SchemaDefinition schema, DocumentNode do { if (definition is DirectiveDefinitionNode directiveDef) { + if (BuiltIns.IsBuiltInDirective(directiveDef.Name.Value)) + { + continue; + } + BuildDirectiveType( schema, schema.DirectiveDefinitions[directiveDef.Name.Value], @@ -634,9 +644,22 @@ private static void BuildDirectiveCollection( directiveNode.Name.Value, out var directiveType)) { - directiveType = new DirectiveDefinition(directiveNode.Name.Value); - // TODO: This is problematic - directiveType.IsRepeatable = true; + if (directiveNode.Name.Value == BuiltIns.Deprecated.Name) + { + directiveType = BuiltIns.Deprecated.Create(schema); + } + else if (directiveNode.Name.Value == BuiltIns.SpecifiedBy.Name) + { + directiveType = BuiltIns.SpecifiedBy.Create(schema); + } + else + { + directiveType = new DirectiveDefinition(directiveNode.Name.Value); + // TODO: This is problematic, but currently necessary for the Fusion + // directives to work, since they don't have definitions in the source schema. + directiveType.IsRepeatable = true; + } + schema.DirectiveDefinitions.Add(directiveType); } From 5e54f2e3a6b8752f185e4ebb227f98b05b744a34 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Fri, 1 Nov 2024 17:46:02 +0100 Subject: [PATCH 085/154] Change RequireSlicingArgs default to false. (#7666) --- .../HttpGetSchemaMiddlewareTests.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/HttpGetSchemaMiddlewareTests.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/HttpGetSchemaMiddlewareTests.cs index 109b170c117..aada7f6f4ad 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/HttpGetSchemaMiddlewareTests.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/HttpGetSchemaMiddlewareTests.cs @@ -92,6 +92,33 @@ public async Task Download_GraphQL_Schema_Slicing_Args_Enabled(string path) response.MatchMarkdownSnapshot(); } + [Theory] + [InlineData("/graphql?sdl")] + [InlineData("/graphql/schema/")] + [InlineData("/graphql/schema.graphql")] + [InlineData("/graphql/schema")] + public async Task Download_GraphQL_Schema_Slicing_Args_Enabled(string path) + { + // arrange + var server = CreateStarWarsServer( + configureServices: sp => + sp + .RemoveAll() + .AddSingleton() + .AddGraphQL() + .ModifyPagingOptions(o => o.RequirePagingBoundaries = true)); + var url = TestServerExtensions.CreateUrl(path); + var request = new HttpRequestMessage(HttpMethod.Get, url); + + // act + var response = await server.CreateClient().SendAsync(request); + + // assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + response.MatchMarkdownSnapshot(); + } + [Theory] [InlineData("/graphql/?sdl")] [InlineData("/graphql/schema/")] From 97bab69455d7433518bed624cb01ecf41024b06d Mon Sep 17 00:00:00 2001 From: Glen Date: Mon, 4 Nov 2024 08:37:45 +0200 Subject: [PATCH 086/154] Fixed incorrect TotalCount when no data is returned (#7671) --- .../QueryableCursorPagingHandler.cs | 1 + .../SqlCursorCursorPagingIntegrationTests.cs | 23 +++++++++++++++++++ ...nTests.Nodes_And_TotalCount_EmptyData.snap | 8 +++++++ 3 files changed, 32 insertions(+) create mode 100644 src/HotChocolate/Data/test/Data.EntityFramework.Tests/__snapshots__/SqlCursorPagingIntegrationTests.Nodes_And_TotalCount_EmptyData.snap diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/QueryableCursorPagingHandler.cs b/src/HotChocolate/Core/src/Types.CursorPagination/QueryableCursorPagingHandler.cs index 771286c1778..3449aaa9936 100644 --- a/src/HotChocolate/Core/src/Types.CursorPagination/QueryableCursorPagingHandler.cs +++ b/src/HotChocolate/Core/src/Types.CursorPagination/QueryableCursorPagingHandler.cs @@ -67,6 +67,7 @@ public async ValueTask> QueryAsync( { var originalQuery = executable.Source; var combinedQuery = slicedQuery.Select(t => new { TotalCount = originalQuery.Count(), Item = t }); + totalCount = 0; var index = offset; await foreach (var item in executable diff --git a/src/HotChocolate/Data/test/Data.EntityFramework.Tests/SqlCursorCursorPagingIntegrationTests.cs b/src/HotChocolate/Data/test/Data.EntityFramework.Tests/SqlCursorCursorPagingIntegrationTests.cs index 82b2c8402c8..4ca71f236c3 100644 --- a/src/HotChocolate/Data/test/Data.EntityFramework.Tests/SqlCursorCursorPagingIntegrationTests.cs +++ b/src/HotChocolate/Data/test/Data.EntityFramework.Tests/SqlCursorCursorPagingIntegrationTests.cs @@ -13,6 +13,8 @@ public class SqlCursorPagingIntegrationTests : SqlLiteCursorTestBase new TestData(Guid.NewGuid(), "D"), ]; + public TestData[] EmptyData => []; + [Fact] public async Task Simple_StringList_Default_Items() { @@ -342,6 +344,27 @@ public async Task Nodes_And_TotalCount() result.MatchSnapshot(); } + [Fact] + public async Task Nodes_And_TotalCount_EmptyData() + { + // arrange + var executor = CreateSchema(EmptyData); + + // act + var result = await executor.ExecuteAsync( + """ + { + root { + nodes { foo } + totalCount + } + } + """); + + // assert + result.MatchSnapshot(); + } + [Fact] public async Task TotalCount_Should_Be_Correct() { diff --git a/src/HotChocolate/Data/test/Data.EntityFramework.Tests/__snapshots__/SqlCursorPagingIntegrationTests.Nodes_And_TotalCount_EmptyData.snap b/src/HotChocolate/Data/test/Data.EntityFramework.Tests/__snapshots__/SqlCursorPagingIntegrationTests.Nodes_And_TotalCount_EmptyData.snap new file mode 100644 index 00000000000..fa208e854a1 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.EntityFramework.Tests/__snapshots__/SqlCursorPagingIntegrationTests.Nodes_And_TotalCount_EmptyData.snap @@ -0,0 +1,8 @@ +{ + "data": { + "root": { + "nodes": [], + "totalCount": 0 + } + } +} From 2295bdd8ba16b447981501667ad0f723b189cf90 Mon Sep 17 00:00:00 2001 From: Glen Date: Mon, 4 Nov 2024 08:46:53 +0200 Subject: [PATCH 087/154] Improved Noda Time scalar type descriptions (#7665) --- .../BaseTypes/StringToStructBaseType.cs | 29 ++ .../Core/src/Types.NodaTime/DurationType.cs | 18 +- .../Core/src/Types.NodaTime/InstantType.cs | 18 +- .../src/Types.NodaTime/LocalDateTimeType.cs | 24 +- .../Core/src/Types.NodaTime/LocalDateType.cs | 18 +- .../Core/src/Types.NodaTime/LocalTimeType.cs | 20 +- .../src/Types.NodaTime/OffsetDateTimeType.cs | 22 +- .../Core/src/Types.NodaTime/OffsetDateType.cs | 18 +- .../Core/src/Types.NodaTime/OffsetTimeType.cs | 20 +- .../Core/src/Types.NodaTime/OffsetType.cs | 18 +- .../Properties/NodaTimeResources.Designer.cs | 326 +++++++++++++++--- .../Properties/NodaTimeResources.resx | 105 +++++- .../src/Types.NodaTime/ZonedDateTimeType.cs | 24 +- .../Types.NodaTime.Tests/DurationTypeTests.cs | 34 ++ .../Types.NodaTime.Tests/InstantTypeTests.cs | 32 ++ .../LocalDateTimeTypeTests.cs | 34 ++ .../LocalDateTypeTests.cs | 32 ++ .../LocalTimeTypeTests.cs | 34 ++ .../OffsetDateTimeTypeTests.cs | 34 ++ .../OffsetDateTypeTests.cs | 34 ++ .../OffsetTimeTypeTests.cs | 34 ++ .../Types.NodaTime.Tests/OffsetTypeTests.cs | 38 ++ .../ZonedDateTimeTypeTests.cs | 43 +++ 23 files changed, 945 insertions(+), 64 deletions(-) diff --git a/src/HotChocolate/Core/src/Types.NodaTime/BaseTypes/StringToStructBaseType.cs b/src/HotChocolate/Core/src/Types.NodaTime/BaseTypes/StringToStructBaseType.cs index f42e6be6deb..c514beb4648 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/BaseTypes/StringToStructBaseType.cs +++ b/src/HotChocolate/Core/src/Types.NodaTime/BaseTypes/StringToStructBaseType.cs @@ -1,5 +1,6 @@ using System.Diagnostics.CodeAnalysis; using HotChocolate.Language; +using NodaTime.Text; using static HotChocolate.Types.NodaTime.Properties.NodaTimeResources; namespace HotChocolate.Types.NodaTime; @@ -133,4 +134,32 @@ public override bool TryDeserialize(object? resultValue, out object? runtimeValu protected abstract bool TryDeserialize( string resultValue, [NotNullWhen(true)] out TRuntimeType? runtimeValue); + + protected string CreateDescription( + IPattern[] allowedPatterns, + string description, + string extendedDescription) + { + if (allowedPatterns.All(PatternMap.ContainsKey)) + { + var patternsText = + string.Join("\n", allowedPatterns.Select(p => $"- `{PatternMap[p]}`")); + var examplesText = + string.Join("\n", allowedPatterns.Select(e => $"- `{ExampleMap[e]}`")); + + return string.Format(extendedDescription, patternsText, examplesText); + } + + return description; + } + + /// + /// A map from Noda Time patterns to more universal (ISO-like) formats for display purposes. + /// + protected abstract Dictionary, string> PatternMap { get; } + + /// + /// A map from Noda Time patterns to example strings. + /// + protected abstract Dictionary, string> ExampleMap { get; } } diff --git a/src/HotChocolate/Core/src/Types.NodaTime/DurationType.cs b/src/HotChocolate/Core/src/Types.NodaTime/DurationType.cs index 2b4e261e91a..9b8408670d9 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/DurationType.cs +++ b/src/HotChocolate/Core/src/Types.NodaTime/DurationType.cs @@ -25,7 +25,11 @@ public DurationType(params IPattern[] allowedPatterns) : base("Duratio _allowedPatterns = allowedPatterns; _serializationPattern = allowedPatterns[0]; - Description = NodaTimeResources.DurationType_Description; + + Description = CreateDescription( + allowedPatterns, + NodaTimeResources.DurationType_Description, + NodaTimeResources.DurationType_Description_Extended); } /// @@ -46,4 +50,16 @@ protected override bool TryDeserialize( string resultValue, [NotNullWhen(true)] out Duration? runtimeValue) => _allowedPatterns.TryParse(resultValue, out runtimeValue); + + protected override Dictionary, string> PatternMap => new() + { + { DurationPattern.Roundtrip, "-D:hh:mm:ss.sssssssss" }, + { DurationPattern.JsonRoundtrip, "-hh:mm:ss.sssssssss" } + }; + + protected override Dictionary, string> ExampleMap => new() + { + { DurationPattern.Roundtrip, "-1:20:00:00.999999999" }, + { DurationPattern.JsonRoundtrip, "-44:00:00.999999999" } + }; } diff --git a/src/HotChocolate/Core/src/Types.NodaTime/InstantType.cs b/src/HotChocolate/Core/src/Types.NodaTime/InstantType.cs index b130fc1bb67..38711ba1a08 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/InstantType.cs +++ b/src/HotChocolate/Core/src/Types.NodaTime/InstantType.cs @@ -25,7 +25,11 @@ public InstantType(params IPattern[] allowedPatterns) : base("Instant") _allowedPatterns = allowedPatterns; _serializationPattern = allowedPatterns[0]; - Description = NodaTimeResources.InstantType_Description; + + Description = CreateDescription( + allowedPatterns, + NodaTimeResources.InstantType_Description, + NodaTimeResources.InstantType_Description_Extended); } /// @@ -46,4 +50,16 @@ protected override bool TryDeserialize( string resultValue, [NotNullWhen(true)] out Instant? runtimeValue) => _allowedPatterns.TryParse(resultValue, out runtimeValue); + + protected override Dictionary, string> PatternMap => new() + { + { InstantPattern.General, "YYYY-MM-DDThh:mm:ss±hh:mm" }, + { InstantPattern.ExtendedIso, "YYYY-MM-DDThh:mm:ss.sssssssss±hh:mm" } + }; + + protected override Dictionary, string> ExampleMap => new() + { + { InstantPattern.General, "2000-01-01T20:00:00Z" }, + { InstantPattern.ExtendedIso, "2000-01-01T20:00:00.999999999Z" } + }; } diff --git a/src/HotChocolate/Core/src/Types.NodaTime/LocalDateTimeType.cs b/src/HotChocolate/Core/src/Types.NodaTime/LocalDateTimeType.cs index a4da2049187..f47022b4999 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/LocalDateTimeType.cs +++ b/src/HotChocolate/Core/src/Types.NodaTime/LocalDateTimeType.cs @@ -25,7 +25,11 @@ public LocalDateTimeType(params IPattern[] allowedPatterns) : bas _allowedPatterns = allowedPatterns; _serializationPattern = allowedPatterns[0]; - Description = NodaTimeResources.LocalDateTimeType_Description; + + Description = CreateDescription( + _allowedPatterns, + NodaTimeResources.LocalDateTimeType_Description, + NodaTimeResources.LocalDateTimeType_Description_Extended); } /// @@ -46,4 +50,22 @@ protected override bool TryDeserialize( string resultValue, [NotNullWhen(true)] out LocalDateTime? runtimeValue) => _allowedPatterns.TryParse(resultValue, out runtimeValue); + + protected override Dictionary, string> PatternMap => new() + { + { LocalDateTimePattern.GeneralIso, "YYYY-MM-DDThh:mm:ss" }, + { LocalDateTimePattern.ExtendedIso, "YYYY-MM-DDThh:mm:ss.sssssssss" }, + { LocalDateTimePattern.BclRoundtrip, "YYYY-MM-DDThh:mm:ss.sssssss" }, + { LocalDateTimePattern.FullRoundtripWithoutCalendar, "YYYY-MM-DDThh:mm:ss.sssssssss" }, + { LocalDateTimePattern.FullRoundtrip, "YYYY-MM-DDThh:mm:ss.sssssssss (calendar)" } + }; + + protected override Dictionary, string> ExampleMap => new() + { + { LocalDateTimePattern.GeneralIso, "2000-01-01T20:00:00" }, + { LocalDateTimePattern.ExtendedIso, "2000-01-01T20:00:00.999" }, + { LocalDateTimePattern.BclRoundtrip, "2000-01-01T20:00:00.9999999" }, + { LocalDateTimePattern.FullRoundtripWithoutCalendar, "2000-01-01T20:00:00.999999999" }, + { LocalDateTimePattern.FullRoundtrip, "2000-01-01T20:00:00.999999999 (ISO)" } + }; } diff --git a/src/HotChocolate/Core/src/Types.NodaTime/LocalDateType.cs b/src/HotChocolate/Core/src/Types.NodaTime/LocalDateType.cs index c8718dc0da6..3eaf4b1f026 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/LocalDateType.cs +++ b/src/HotChocolate/Core/src/Types.NodaTime/LocalDateType.cs @@ -26,7 +26,11 @@ public LocalDateType(params IPattern[] allowedPatterns) : base("Local _allowedPatterns = allowedPatterns; _serializationPattern = allowedPatterns[0]; - Description = NodaTimeResources.LocalDateType_Description; + + Description = CreateDescription( + allowedPatterns, + NodaTimeResources.LocalDateType_Description, + NodaTimeResources.LocalDateType_Description_Extended); } /// @@ -47,4 +51,16 @@ protected override bool TryDeserialize( string resultValue, [NotNullWhen(true)] out LocalDate? runtimeValue) => _allowedPatterns.TryParse(resultValue, out runtimeValue); + + protected override Dictionary, string> PatternMap => new() + { + { LocalDatePattern.Iso, "YYYY-MM-DD" }, + { LocalDatePattern.FullRoundtrip, "YYYY-MM-DD (calendar)" } + }; + + protected override Dictionary, string> ExampleMap => new() + { + { LocalDatePattern.Iso, "2000-01-01" }, + { LocalDatePattern.FullRoundtrip, "2000-01-01 (ISO)" } + }; } diff --git a/src/HotChocolate/Core/src/Types.NodaTime/LocalTimeType.cs b/src/HotChocolate/Core/src/Types.NodaTime/LocalTimeType.cs index ec900a9fd70..7ccc51f90e7 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/LocalTimeType.cs +++ b/src/HotChocolate/Core/src/Types.NodaTime/LocalTimeType.cs @@ -26,7 +26,11 @@ public LocalTimeType(params IPattern[] allowedPatterns) : base("Local _allowedPatterns = allowedPatterns; _serializationPattern = allowedPatterns[0]; - Description = NodaTimeResources.LocalTimeType_Description; + + Description = CreateDescription( + allowedPatterns, + NodaTimeResources.LocalTimeType_Description, + NodaTimeResources.LocalTimeType_Description_Extended); } /// @@ -47,4 +51,18 @@ protected override bool TryDeserialize( string resultValue, [NotNullWhen(true)] out LocalTime? runtimeValue) => _allowedPatterns.TryParse(resultValue, out runtimeValue); + + protected override Dictionary, string> PatternMap => new() + { + { LocalTimePattern.ExtendedIso, "hh:mm:ss.sssssssss" }, + { LocalTimePattern.LongExtendedIso, "hh:mm:ss.sssssssss" }, + { LocalTimePattern.GeneralIso, "hh:mm:ss" } + }; + + protected override Dictionary, string> ExampleMap => new() + { + { LocalTimePattern.ExtendedIso, "20:00:00.999" }, + { LocalTimePattern.LongExtendedIso, "20:00:00.999999999" }, + { LocalTimePattern.GeneralIso, "20:00:00" } + }; } diff --git a/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateTimeType.cs b/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateTimeType.cs index ce747bded5a..609a93925a5 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateTimeType.cs +++ b/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateTimeType.cs @@ -26,7 +26,11 @@ public OffsetDateTimeType(params IPattern[] allowedPatterns) _allowedPatterns = allowedPatterns; _serializationPattern = _allowedPatterns[0]; - Description = NodaTimeResources.OffsetDateTimeType_Description; + + Description = CreateDescription( + allowedPatterns, + NodaTimeResources.OffsetDateTimeType_Description, + NodaTimeResources.OffsetDateTimeType_Description_Extended); } /// @@ -50,4 +54,20 @@ protected override bool TryDeserialize( string resultValue, [NotNullWhen(true)] out OffsetDateTime? runtimeValue) => _allowedPatterns.TryParse(resultValue, out runtimeValue); + + protected override Dictionary, string> PatternMap => new() + { + { OffsetDateTimePattern.GeneralIso, "YYYY-MM-DDThh:mm:ss±hh:mm" }, + { OffsetDateTimePattern.ExtendedIso, "YYYY-MM-DDThh:mm:ss.sssssssss±hh:mm" }, + { OffsetDateTimePattern.Rfc3339, "YYYY-MM-DDThh:mm:ss.sssssssss±hh:mm" }, + { OffsetDateTimePattern.FullRoundtrip, "YYYY-MM-DDThh:mm:ss.sssssssss±hh:mm (calendar)" } + }; + + protected override Dictionary, string> ExampleMap => new() + { + { OffsetDateTimePattern.GeneralIso, "2000-01-01T20:00:00Z" }, + { OffsetDateTimePattern.ExtendedIso, "2000-01-01T20:00:00.999Z" }, + { OffsetDateTimePattern.Rfc3339, "2000-01-01T20:00:00.999Z" }, + { OffsetDateTimePattern.FullRoundtrip, "2000-01-01T20:00:00.999Z (ISO)" } + }; } diff --git a/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateType.cs b/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateType.cs index 64d31c5fc1c..e34cdc2ce12 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateType.cs +++ b/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateType.cs @@ -27,7 +27,11 @@ public OffsetDateType(params IPattern[] allowedPatterns) : base("Off _allowedPatterns = allowedPatterns; _serializationPattern = allowedPatterns[0]; - Description = NodaTimeResources.OffsetDateType_Description; + + Description = CreateDescription( + allowedPatterns, + NodaTimeResources.OffsetDateType_Description, + NodaTimeResources.OffsetDateType_Description_Extended); } /// @@ -48,4 +52,16 @@ protected override bool TryDeserialize( string resultValue, [NotNullWhen(true)] out OffsetDate? runtimeValue) => _allowedPatterns.TryParse(resultValue, out runtimeValue); + + protected override Dictionary, string> PatternMap => new() + { + { OffsetDatePattern.GeneralIso, "YYYY-MM-DD±hh:mm" }, + { OffsetDatePattern.FullRoundtrip, "YYYY-MM-DD±hh:mm (calendar)" } + }; + + protected override Dictionary, string> ExampleMap => new() + { + { OffsetDatePattern.GeneralIso, "2000-01-01Z" }, + { OffsetDatePattern.FullRoundtrip, "2000-01-01Z (ISO)" } + }; } diff --git a/src/HotChocolate/Core/src/Types.NodaTime/OffsetTimeType.cs b/src/HotChocolate/Core/src/Types.NodaTime/OffsetTimeType.cs index d5d2d60f109..683726f3c55 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/OffsetTimeType.cs +++ b/src/HotChocolate/Core/src/Types.NodaTime/OffsetTimeType.cs @@ -26,7 +26,11 @@ public OffsetTimeType(params IPattern[] allowedPatterns) : base("Off _allowedPatterns = allowedPatterns; _serializationPattern = _allowedPatterns[0]; - Description = NodaTimeResources.OffsetTimeType_Description; + + Description = CreateDescription( + allowedPatterns, + NodaTimeResources.OffsetTimeType_Description, + NodaTimeResources.OffsetTimeType_Description_Extended); } /// @@ -47,4 +51,18 @@ protected override bool TryDeserialize( string resultValue, [NotNullWhen(true)] out OffsetTime? runtimeValue) => _allowedPatterns.TryParse(resultValue, out runtimeValue); + + protected override Dictionary, string> PatternMap => new() + { + { OffsetTimePattern.GeneralIso, "hh:mm:ss±hh:mm" }, + { OffsetTimePattern.ExtendedIso, "hh:mm:ss.sssssssss±hh:mm" }, + { OffsetTimePattern.Rfc3339, "hh:mm:ss.sssssssss±hh:mm" } + }; + + protected override Dictionary, string> ExampleMap => new() + { + { OffsetTimePattern.GeneralIso, "20:00:00Z" }, + { OffsetTimePattern.ExtendedIso, "20:00:00.999Z" }, + { OffsetTimePattern.Rfc3339, "20:00:00.999999999Z" } + }; } diff --git a/src/HotChocolate/Core/src/Types.NodaTime/OffsetType.cs b/src/HotChocolate/Core/src/Types.NodaTime/OffsetType.cs index e643afbea1e..26a891c553c 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/OffsetType.cs +++ b/src/HotChocolate/Core/src/Types.NodaTime/OffsetType.cs @@ -27,7 +27,11 @@ public OffsetType(params IPattern[] allowedPatterns) : base("Offset") _allowedPatterns = allowedPatterns; _serializationPattern = allowedPatterns[0]; - Description = NodaTimeResources.OffsetType_Description; + + Description = CreateDescription( + allowedPatterns, + NodaTimeResources.OffsetType_Description, + NodaTimeResources.OffsetType_Description_Extended); } /// @@ -48,4 +52,16 @@ protected override bool TryDeserialize( string resultValue, [NotNullWhen(true)] out Offset? runtimeValue) => _allowedPatterns.TryParse(resultValue, out runtimeValue); + + protected override Dictionary, string> PatternMap => new() + { + { OffsetPattern.GeneralInvariant, "±hh:mm:ss" }, + { OffsetPattern.GeneralInvariantWithZ, "Z" } + }; + + protected override Dictionary, string> ExampleMap => new() + { + { OffsetPattern.GeneralInvariant, "+02:30:00" }, + { OffsetPattern.GeneralInvariantWithZ, "Z" } + }; } diff --git a/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.Designer.cs b/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.Designer.cs index dd95e57c574..ee19faee741 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.Designer.cs +++ b/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.Designer.cs @@ -9,34 +9,48 @@ namespace HotChocolate.Types.NodaTime.Properties { using System; - - - [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [System.Diagnostics.DebuggerNonUserCodeAttribute()] - [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class NodaTimeResources { - - private static System.Resources.ResourceManager resourceMan; - - private static System.Globalization.CultureInfo resourceCulture; - - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal NodaTimeResources() { } - - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] - internal static System.Resources.ResourceManager ResourceManager { + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { get { - if (object.Equals(null, resourceMan)) { - System.Resources.ResourceManager temp = new System.Resources.ResourceManager("HotChocolate.Types.NodaTime.Properties.NodaTimeResources", typeof(NodaTimeResources).Assembly); + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HotChocolate.Types.NodaTime.Properties.NodaTimeResources", typeof(NodaTimeResources).Assembly); resourceMan = temp; } return resourceMan; } } - - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] - internal static System.Globalization.CultureInfo Culture { + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { get { return resourceCulture; } @@ -44,106 +58,318 @@ internal static System.Globalization.CultureInfo Culture { resourceCulture = value; } } - - internal static string IntToStructBaseType_ParseLiteral_UnableToDeserializeInt { + + /// + /// Looks up a localized string similar to Represents a time zone - a mapping between UTC and local time. + ///A time zone maps UTC instants to local times - or, equivalently, to the offset from UTC at any particular instant. + /// + ///Example: `Europe/Zurich`. + /// + internal static string DateTimeZoneType_Description { get { - return ResourceManager.GetString("IntToStructBaseType_ParseLiteral_UnableToDeserializeInt", resourceCulture); + return ResourceManager.GetString("DateTimeZoneType_Description", resourceCulture); } } - - internal static string StringToClassBaseType_ParseLiteral_UnableToDeserializeString { + + /// + /// Looks up a localized string similar to Represents a fixed (and calendar-independent) length of time.. + /// + internal static string DurationType_Description { get { - return ResourceManager.GetString("StringToClassBaseType_ParseLiteral_UnableToDeserializeString", resourceCulture); + return ResourceManager.GetString("DurationType_Description", resourceCulture); } } - - internal static string StringToStructBaseType_ParseLiteral_UnableToDeserializeString { + + /// + /// Looks up a localized string similar to Represents a fixed (and calendar-independent) length of time. + /// + ///Allowed patterns: + ///{0} + /// + ///Examples: + ///{1}. + /// + internal static string DurationType_Description_Extended { get { - return ResourceManager.GetString("StringToStructBaseType_ParseLiteral_UnableToDeserializeString", resourceCulture); + return ResourceManager.GetString("DurationType_Description_Extended", resourceCulture); } } - - internal static string DateTimeZoneType_Description { + + /// + /// Looks up a localized string similar to Represents an instant on the global timeline, with nanosecond resolution.. + /// + internal static string InstantType_Description { get { - return ResourceManager.GetString("DateTimeZoneType_Description", resourceCulture); + return ResourceManager.GetString("InstantType_Description", resourceCulture); } } - - internal static string DurationType_Description { + + /// + /// Looks up a localized string similar to Represents an instant on the global timeline, with nanosecond resolution. + /// + ///Allowed patterns: + ///{0} + /// + ///Examples: + ///{1}. + /// + internal static string InstantType_Description_Extended { get { - return ResourceManager.GetString("DurationType_Description", resourceCulture); + return ResourceManager.GetString("InstantType_Description_Extended", resourceCulture); } } - - internal static string InstantType_Description { + + /// + /// Looks up a localized string similar to Unable to deserialize integer to {0}. + /// + internal static string IntToStructBaseType_ParseLiteral_UnableToDeserializeInt { get { - return ResourceManager.GetString("InstantType_Description", resourceCulture); + return ResourceManager.GetString("IntToStructBaseType_ParseLiteral_UnableToDeserializeInt", resourceCulture); } } - + + /// + /// Looks up a localized string similar to Equates the days of the week with their numerical value according to ISO-8601. + /// Monday = 1, Tuesday = 2, Wednesday = 3, Thursday = 4, Friday = 5, Saturday = 6, Sunday = 7.. + /// internal static string IsoDayOfWeekType_Description { get { return ResourceManager.GetString("IsoDayOfWeekType_Description", resourceCulture); } } - + + /// + /// Looks up a localized string similar to A date and time in a particular calendar system.. + /// internal static string LocalDateTimeType_Description { get { return ResourceManager.GetString("LocalDateTimeType_Description", resourceCulture); } } - + + /// + /// Looks up a localized string similar to A date and time in a particular calendar system. + /// + ///Allowed patterns: + ///{0} + /// + ///Examples: + ///{1}. + /// + internal static string LocalDateTimeType_Description_Extended { + get { + return ResourceManager.GetString("LocalDateTimeType_Description_Extended", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to LocalDate represents a date within the calendar, with no reference to a particular time zone or time of day.. + /// internal static string LocalDateType_Description { get { return ResourceManager.GetString("LocalDateType_Description", resourceCulture); } } - + + /// + /// Looks up a localized string similar to LocalDate represents a date within the calendar, with no reference to a particular time zone or time of day. + /// + ///Allowed patterns: + ///{0} + /// + ///Examples: + ///{1}. + /// + internal static string LocalDateType_Description_Extended { + get { + return ResourceManager.GetString("LocalDateType_Description_Extended", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to LocalTime represents a time of day, with no reference to a particular calendar, time zone, or date.. + /// internal static string LocalTimeType_Description { get { return ResourceManager.GetString("LocalTimeType_Description", resourceCulture); } } - + + /// + /// Looks up a localized string similar to LocalTime represents a time of day, with no reference to a particular calendar, time zone, or date. + /// + ///Allowed patterns: + ///{0} + /// + ///Examples: + ///{1}. + /// + internal static string LocalTimeType_Description_Extended { + get { + return ResourceManager.GetString("LocalTimeType_Description_Extended", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The provided patterns are empty. You must provide at least one pattern for type {0}.. + /// + internal static string NodaTime_NoPatternProvided { + get { + return ResourceManager.GetString("NodaTime_NoPatternProvided", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A local date and time in a particular calendar system, combined with an offset from UTC.. + /// internal static string OffsetDateTimeType_Description { get { return ResourceManager.GetString("OffsetDateTimeType_Description", resourceCulture); } } - + + /// + /// Looks up a localized string similar to A local date and time in a particular calendar system, combined with an offset from UTC. + /// + ///Allowed patterns: + ///{0} + /// + ///Examples: + ///{1}. + /// + internal static string OffsetDateTimeType_Description_Extended { + get { + return ResourceManager.GetString("OffsetDateTimeType_Description_Extended", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A combination of a LocalDate and an Offset, to represent a date at a specific offset from UTC but without any time-of-day information.. + /// internal static string OffsetDateType_Description { get { return ResourceManager.GetString("OffsetDateType_Description", resourceCulture); } } - + + /// + /// Looks up a localized string similar to A combination of a LocalDate and an Offset, to represent a date at a specific offset from UTC but without any time-of-day information. + /// + ///Allowed patterns: + ///{0} + /// + ///Examples: + ///{1}. + /// + internal static string OffsetDateType_Description_Extended { + get { + return ResourceManager.GetString("OffsetDateType_Description_Extended", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A combination of a LocalTime and an Offset, to represent a time-of-day at a specific offset from UTC but without any date information.. + /// internal static string OffsetTimeType_Description { get { return ResourceManager.GetString("OffsetTimeType_Description", resourceCulture); } } - + + /// + /// Looks up a localized string similar to A combination of a LocalTime and an Offset, to represent a time-of-day at a specific offset from UTC but without any date information. + /// + ///Allowed patterns: + ///{0} + /// + ///Examples: + ///{1}. + /// + internal static string OffsetTimeType_Description_Extended { + get { + return ResourceManager.GetString("OffsetTimeType_Description_Extended", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to An offset from UTC in seconds. + /// A positive value means that the local time is ahead of UTC (e.g. for Europe); a negative value means that the local time is behind UTC (e.g. for America).. + /// internal static string OffsetType_Description { get { return ResourceManager.GetString("OffsetType_Description", resourceCulture); } } - + + /// + /// Looks up a localized string similar to An offset from UTC in seconds. + /// A positive value means that the local time is ahead of UTC (e.g. for Europe); a negative value means that the local time is behind UTC (e.g. for America). + /// + ///Allowed patterns: + ///{0} + /// + ///Examples: + ///{1}. + /// + internal static string OffsetType_Description_Extended { + get { + return ResourceManager.GetString("OffsetType_Description_Extended", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Represents a period of time expressed in human chronological terms: hours, days, weeks, months and so on. + /// + ///Pattern: `PnYnMnDTnHnMnS` + ///Example: `P3Y6M4DT12H30M5S`. + /// internal static string PeriodType_Description { get { return ResourceManager.GetString("PeriodType_Description", resourceCulture); } } - + + /// + /// Looks up a localized string similar to Unable to deserialize string to {0}. + /// + internal static string StringToClassBaseType_ParseLiteral_UnableToDeserializeString { + get { + return ResourceManager.GetString("StringToClassBaseType_ParseLiteral_UnableToDeserializeString", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unable to deserialize string to {0}. + /// + internal static string StringToStructBaseType_ParseLiteral_UnableToDeserializeString { + get { + return ResourceManager.GetString("StringToStructBaseType_ParseLiteral_UnableToDeserializeString", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A LocalDateTime in a specific time zone and with a particular offset to distinguish between otherwise-ambiguous instants. + ///A ZonedDateTime is global, in that it maps to a single Instant.. + /// internal static string ZonedDateTimeType_Description { get { return ResourceManager.GetString("ZonedDateTimeType_Description", resourceCulture); } } - - internal static string NodaTime_NoPatternProvided { + + /// + /// Looks up a localized string similar to A LocalDateTime in a specific time zone and with a particular offset to distinguish between otherwise-ambiguous instants. + ///A ZonedDateTime is global, in that it maps to a single Instant. + /// + ///Allowed patterns: + ///{0} + /// + ///Examples: + ///{1}. + /// + internal static string ZonedDateTimeType_Description_Extended { get { - return ResourceManager.GetString("NodaTime_NoPatternProvided", resourceCulture); + return ResourceManager.GetString("ZonedDateTimeType_Description_Extended", resourceCulture); } } } diff --git a/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.resx b/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.resx index 5cb744599e9..a197bf71bfb 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.resx +++ b/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.resx @@ -29,14 +29,34 @@ Represents a time zone - a mapping between UTC and local time. -A time zone maps UTC instants to local times - or, equivalently, to the offset from UTC at any particular instant. +A time zone maps UTC instants to local times - or, equivalently, to the offset from UTC at any particular instant. + +Example: `Europe/Zurich` Represents a fixed (and calendar-independent) length of time. + + Represents a fixed (and calendar-independent) length of time. + +Allowed patterns: +{0} + +Examples: +{1} + Represents an instant on the global timeline, with nanosecond resolution. + + Represents an instant on the global timeline, with nanosecond resolution. + +Allowed patterns: +{0} + +Examples: +{1} + Equates the days of the week with their numerical value according to ISO-8601. Monday = 1, Tuesday = 2, Wednesday = 3, Thursday = 4, Friday = 5, Saturday = 6, Sunday = 7. @@ -44,31 +64,108 @@ A time zone maps UTC instants to local times - or, equivalently, to the offset f A date and time in a particular calendar system. + + A date and time in a particular calendar system. + +Allowed patterns: +{0} + +Examples: +{1} + - LocalDate is an immutable struct representing a date within the calendar, with no reference to a particular time zone or time of day. + LocalDate represents a date within the calendar, with no reference to a particular time zone or time of day. + + + LocalDate represents a date within the calendar, with no reference to a particular time zone or time of day. + +Allowed patterns: +{0} + +Examples: +{1} - LocalTime is an immutable struct representing a time of day, with no reference to a particular calendar, time zone or date. + LocalTime represents a time of day, with no reference to a particular calendar, time zone, or date. + + + LocalTime represents a time of day, with no reference to a particular calendar, time zone, or date. + +Allowed patterns: +{0} + +Examples: +{1} A local date and time in a particular calendar system, combined with an offset from UTC. + + A local date and time in a particular calendar system, combined with an offset from UTC. + +Allowed patterns: +{0} + +Examples: +{1} + A combination of a LocalDate and an Offset, to represent a date at a specific offset from UTC but without any time-of-day information. + + A combination of a LocalDate and an Offset, to represent a date at a specific offset from UTC but without any time-of-day information. + +Allowed patterns: +{0} + +Examples: +{1} + A combination of a LocalTime and an Offset, to represent a time-of-day at a specific offset from UTC but without any date information. + + A combination of a LocalTime and an Offset, to represent a time-of-day at a specific offset from UTC but without any date information. + +Allowed patterns: +{0} + +Examples: +{1} + An offset from UTC in seconds. A positive value means that the local time is ahead of UTC (e.g. for Europe); a negative value means that the local time is behind UTC (e.g. for America). + + + An offset from UTC in seconds. + A positive value means that the local time is ahead of UTC (e.g. for Europe); a negative value means that the local time is behind UTC (e.g. for America). + +Allowed patterns: +{0} + +Examples: +{1} - Represents a period of time expressed in human chronological terms: hours, days, weeks, months and so on. + Represents a period of time expressed in human chronological terms: hours, days, weeks, months and so on. + +Pattern: `PnYnMnDTnHnMnS` +Example: `P3Y6M4DT12H30M5S` A LocalDateTime in a specific time zone and with a particular offset to distinguish between otherwise-ambiguous instants. A ZonedDateTime is global, in that it maps to a single Instant. + + + A LocalDateTime in a specific time zone and with a particular offset to distinguish between otherwise-ambiguous instants. +A ZonedDateTime is global, in that it maps to a single Instant. + +Allowed patterns: +{0} + +Examples: +{1} The provided patterns are empty. You must provide at least one pattern for type {0}. diff --git a/src/HotChocolate/Core/src/Types.NodaTime/ZonedDateTimeType.cs b/src/HotChocolate/Core/src/Types.NodaTime/ZonedDateTimeType.cs index b590839b787..c1bf412966d 100644 --- a/src/HotChocolate/Core/src/Types.NodaTime/ZonedDateTimeType.cs +++ b/src/HotChocolate/Core/src/Types.NodaTime/ZonedDateTimeType.cs @@ -32,7 +32,11 @@ public ZonedDateTimeType(params IPattern[] allowedPatterns) _allowedPatterns = allowedPatterns; _serializationPattern = allowedPatterns[0]; - Description = NodaTimeResources.ZonedDateTimeType_Description; + + Description = CreateDescription( + allowedPatterns, + NodaTimeResources.ZonedDateTimeType_Description, + NodaTimeResources.ZonedDateTimeType_Description_Extended); } /// @@ -53,4 +57,22 @@ protected override bool TryDeserialize( string resultValue, [NotNullWhen(true)] out ZonedDateTime? runtimeValue) => _allowedPatterns.TryParse(resultValue, out runtimeValue); + + protected override Dictionary, string> PatternMap => new() + { + { ZonedDateTimePattern.GeneralFormatOnlyIso, "YYYY-MM-DDThh:mm:ss z (±hh:mm)" }, + { ZonedDateTimePattern.ExtendedFormatOnlyIso, "YYYY-MM-DDThh:mm:ss.sssssssss z (±hh:mm)" } + }; + + protected override Dictionary, string> ExampleMap => new() + { + { + ZonedDateTimePattern.GeneralFormatOnlyIso, + "2000-01-01T20:00:00 Europe/Zurich (+01)" + }, + { + ZonedDateTimePattern.ExtendedFormatOnlyIso, + "2000-01-01T20:00:00.999999999 Europe/Zurich (+01)" + } + }; } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/DurationTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/DurationTypeTests.cs index 47dd1ed8aee..fba12ca83ff 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/DurationTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/DurationTypeTests.cs @@ -1,5 +1,8 @@ +using System.Globalization; +using CookieCrumble; using HotChocolate.Execution; using NodaTime; +using NodaTime.Text; namespace HotChocolate.Types.NodaTime.Tests { @@ -222,4 +225,35 @@ public void PatternEmpty_ThrowSchemaException() Assert.Throws(Call); } } + + [Fact] + public void DurationType_DescriptionKnownPatterns_MatchesSnapshot() + { + var durationType = new DurationType( + DurationPattern.Roundtrip, + DurationPattern.JsonRoundtrip); + + durationType.Description.MatchInlineSnapshot( + """ + Represents a fixed (and calendar-independent) length of time. + + Allowed patterns: + - `-D:hh:mm:ss.sssssssss` + - `-hh:mm:ss.sssssssss` + + Examples: + - `-1:20:00:00.999999999` + - `-44:00:00.999999999` + """); + } + + [Fact] + public void DurationType_DescriptionUnknownPatterns_MatchesSnapshot() + { + var durationType = new DurationType( + DurationPattern.Create("mm", CultureInfo.InvariantCulture)); + + durationType.Description.MatchInlineSnapshot( + "Represents a fixed (and calendar-independent) length of time."); + } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/InstantTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/InstantTypeTests.cs index 6ae7f20a3f6..c3085bf7222 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/InstantTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/InstantTypeTests.cs @@ -1,5 +1,8 @@ +using System.Globalization; +using CookieCrumble; using HotChocolate.Execution; using NodaTime; +using NodaTime.Text; namespace HotChocolate.Types.NodaTime.Tests { @@ -102,4 +105,33 @@ public void PatternEmpty_ThrowSchemaException() Assert.Throws(Call); } } + + [Fact] + public void InstantType_DescriptionKnownPatterns_MatchesSnapshot() + { + var instantType = new InstantType(InstantPattern.General, InstantPattern.ExtendedIso); + + instantType.Description.MatchInlineSnapshot( + """ + Represents an instant on the global timeline, with nanosecond resolution. + + Allowed patterns: + - `YYYY-MM-DDThh:mm:ss±hh:mm` + - `YYYY-MM-DDThh:mm:ss.sssssssss±hh:mm` + + Examples: + - `2000-01-01T20:00:00Z` + - `2000-01-01T20:00:00.999999999Z` + """); + } + + [Fact] + public void InstantType_DescriptionUnknownPatterns_MatchesSnapshot() + { + var instantType = new InstantType( + InstantPattern.Create("MM", CultureInfo.InvariantCulture)); + + instantType.Description.MatchInlineSnapshot( + "Represents an instant on the global timeline, with nanosecond resolution."); + } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTimeTypeTests.cs index f7b21dfed0f..611eda2d32d 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTimeTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTimeTypeTests.cs @@ -1,5 +1,8 @@ +using System.Globalization; +using CookieCrumble; using HotChocolate.Execution; using NodaTime; +using NodaTime.Text; namespace HotChocolate.Types.NodaTime.Tests { @@ -106,4 +109,35 @@ public void PatternEmpty_ThrowSchemaException() Assert.Throws(Call); } } + + [Fact] + public void LocalDateTimeType_DescriptionKnownPatterns_MatchesSnapshot() + { + var localDateTimeType = new LocalDateTimeType( + LocalDateTimePattern.ExtendedIso, + LocalDateTimePattern.FullRoundtrip); + + localDateTimeType.Description.MatchInlineSnapshot( + """ + A date and time in a particular calendar system. + + Allowed patterns: + - `YYYY-MM-DDThh:mm:ss.sssssssss` + - `YYYY-MM-DDThh:mm:ss.sssssssss (calendar)` + + Examples: + - `2000-01-01T20:00:00.999` + - `2000-01-01T20:00:00.999999999 (ISO)` + """); + } + + [Fact] + public void LocalDateTimeType_DescriptionUnknownPatterns_MatchesSnapshot() + { + var localDateTimeType = new LocalDateTimeType( + LocalDateTimePattern.Create("MM", CultureInfo.InvariantCulture)); + + localDateTimeType.Description.MatchInlineSnapshot( + "A date and time in a particular calendar system."); + } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTypeTests.cs index 34b4ff8681d..2657f36a3f3 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTypeTests.cs @@ -1,5 +1,8 @@ +using System.Globalization; +using CookieCrumble; using HotChocolate.Execution; using NodaTime; +using NodaTime.Text; namespace HotChocolate.Types.NodaTime.Tests { @@ -98,4 +101,33 @@ public void PatternEmpty_ThrowSchemaException() Assert.Throws(Call); } } + + [Fact] + public void LocalDateType_DescriptionKnownPatterns_MatchesSnapshot() + { + var localDateType = new LocalDateType(LocalDatePattern.Iso, LocalDatePattern.FullRoundtrip); + + localDateType.Description.MatchInlineSnapshot( + """ + LocalDate represents a date within the calendar, with no reference to a particular time zone or time of day. + + Allowed patterns: + - `YYYY-MM-DD` + - `YYYY-MM-DD (calendar)` + + Examples: + - `2000-01-01` + - `2000-01-01 (ISO)` + """); + } + + [Fact] + public void LocalDateType_DescriptionUnknownPatterns_MatchesSnapshot() + { + var localDateType = new LocalDateType( + LocalDatePattern.Create("MM", CultureInfo.InvariantCulture)); + + localDateType.Description.MatchInlineSnapshot( + "LocalDate represents a date within the calendar, with no reference to a particular time zone or time of day."); + } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalTimeTypeTests.cs index 0769c0bc698..ee6f6b774fa 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalTimeTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalTimeTypeTests.cs @@ -1,5 +1,8 @@ +using System.Globalization; +using CookieCrumble; using HotChocolate.Execution; using NodaTime; +using NodaTime.Text; namespace HotChocolate.Types.NodaTime.Tests { @@ -127,4 +130,35 @@ public void PatternEmptyThrowSchemaException() Assert.Throws(Call); } } + + [Fact] + public void LocalTimeType_DescriptionKnownPatterns_MatchesSnapshot() + { + var localTimeType = new LocalTimeType( + LocalTimePattern.GeneralIso, + LocalTimePattern.ExtendedIso); + + localTimeType.Description.MatchInlineSnapshot( + """ + LocalTime represents a time of day, with no reference to a particular calendar, time zone, or date. + + Allowed patterns: + - `hh:mm:ss` + - `hh:mm:ss.sssssssss` + + Examples: + - `20:00:00` + - `20:00:00.999` + """); + } + + [Fact] + public void LocalTimeType_DescriptionUnknownPatterns_MatchesSnapshot() + { + var localTimeType = new LocalTimeType( + LocalTimePattern.Create("mm", CultureInfo.InvariantCulture)); + + localTimeType.Description.MatchInlineSnapshot( + "LocalTime represents a time of day, with no reference to a particular calendar, time zone, or date."); + } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeTests.cs index cec25c1337f..a372e0ecd21 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeTests.cs @@ -1,5 +1,8 @@ +using System.Globalization; +using CookieCrumble; using HotChocolate.Execution; using NodaTime; +using NodaTime.Text; namespace HotChocolate.Types.NodaTime.Tests { @@ -157,4 +160,35 @@ public void PatternEmptyThrowSchemaException() Assert.Throws(Call); } } + + [Fact] + public void OffsetDateTimeType_DescriptionKnownPatterns_MatchesSnapshot() + { + var offsetDateTimeType = new OffsetDateTimeType( + OffsetDateTimePattern.ExtendedIso, + OffsetDateTimePattern.FullRoundtrip); + + offsetDateTimeType.Description.MatchInlineSnapshot( + """ + A local date and time in a particular calendar system, combined with an offset from UTC. + + Allowed patterns: + - `YYYY-MM-DDThh:mm:ss.sssssssss±hh:mm` + - `YYYY-MM-DDThh:mm:ss.sssssssss±hh:mm (calendar)` + + Examples: + - `2000-01-01T20:00:00.999Z` + - `2000-01-01T20:00:00.999Z (ISO)` + """); + } + + [Fact] + public void OffsetDateTimeType_DescriptionUnknownPatterns_MatchesSnapshot() + { + var offsetDateTimeType = new OffsetDateTimeType( + OffsetDateTimePattern.Create("MM", CultureInfo.InvariantCulture, new OffsetDateTime())); + + offsetDateTimeType.Description.MatchInlineSnapshot( + "A local date and time in a particular calendar system, combined with an offset from UTC."); + } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTypeTests.cs index b73be0a39e4..193ed353e05 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTypeTests.cs @@ -1,5 +1,8 @@ +using System.Globalization; +using CookieCrumble; using HotChocolate.Execution; using NodaTime; +using NodaTime.Text; namespace HotChocolate.Types.NodaTime.Tests { @@ -124,4 +127,35 @@ public void PatternEmptyThrowSchemaException() Assert.Throws(Call); } } + + [Fact] + public void OffsetDateType_DescriptionKnownPatterns_MatchesSnapshot() + { + var offsetDateType = new OffsetDateType( + OffsetDatePattern.GeneralIso, + OffsetDatePattern.FullRoundtrip); + + offsetDateType.Description.MatchInlineSnapshot( + """ + A combination of a LocalDate and an Offset, to represent a date at a specific offset from UTC but without any time-of-day information. + + Allowed patterns: + - `YYYY-MM-DD±hh:mm` + - `YYYY-MM-DD±hh:mm (calendar)` + + Examples: + - `2000-01-01Z` + - `2000-01-01Z (ISO)` + """); + } + + [Fact] + public void OffsetDateType_DescriptionUnknownPatterns_MatchesSnapshot() + { + var offsetDateType = new OffsetDateType( + OffsetDatePattern.Create("MM", CultureInfo.InvariantCulture, new OffsetDate())); + + offsetDateType.Description.MatchInlineSnapshot( + "A combination of a LocalDate and an Offset, to represent a date at a specific offset from UTC but without any time-of-day information."); + } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTimeTypeTests.cs index d452cbf43a2..b49caf6ea47 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTimeTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTimeTypeTests.cs @@ -1,5 +1,8 @@ +using System.Globalization; +using CookieCrumble; using HotChocolate.Execution; using NodaTime; +using NodaTime.Text; namespace HotChocolate.Types.NodaTime.Tests { @@ -129,4 +132,35 @@ public void PatternEmptyThrowSchemaException() Assert.Throws(Call); } } + + [Fact] + public void OffsetTimeType_DescriptionKnownPatterns_MatchesSnapshot() + { + var offsetTimeType = new OffsetTimeType( + OffsetTimePattern.GeneralIso, + OffsetTimePattern.ExtendedIso); + + offsetTimeType.Description.MatchInlineSnapshot( + """ + A combination of a LocalTime and an Offset, to represent a time-of-day at a specific offset from UTC but without any date information. + + Allowed patterns: + - `hh:mm:ss±hh:mm` + - `hh:mm:ss.sssssssss±hh:mm` + + Examples: + - `20:00:00Z` + - `20:00:00.999Z` + """); + } + + [Fact] + public void OffsetTimeType_DescriptionUnknownPatterns_MatchesSnapshot() + { + var offsetTimeType = new OffsetTimeType( + OffsetTimePattern.Create("mm", CultureInfo.InvariantCulture, new OffsetTime())); + + offsetTimeType.Description.MatchInlineSnapshot( + "A combination of a LocalTime and an Offset, to represent a time-of-day at a specific offset from UTC but without any date information."); + } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTypeTests.cs index 544bee65695..fef1d43d205 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTypeTests.cs @@ -1,5 +1,8 @@ +using System.Globalization; +using CookieCrumble; using HotChocolate.Execution; using NodaTime; +using NodaTime.Text; namespace HotChocolate.Types.NodaTime.Tests { @@ -135,4 +138,39 @@ public void PatternEmptyThrowSchemaException() Assert.Throws(Call); } } + + [Fact] + public void OffsetType_DescriptionKnownPatterns_MatchesSnapshot() + { + var offsetType = new OffsetType( + OffsetPattern.GeneralInvariant, + OffsetPattern.GeneralInvariantWithZ); + + offsetType.Description.MatchInlineSnapshot( + """ + An offset from UTC in seconds. + A positive value means that the local time is ahead of UTC (e.g. for Europe); a negative value means that the local time is behind UTC (e.g. for America). + + Allowed patterns: + - `±hh:mm:ss` + - `Z` + + Examples: + - `+02:30:00` + - `Z` + """); + } + + [Fact] + public void OffsetType_DescriptionUnknownPatterns_MatchesSnapshot() + { + var offsetType = new OffsetType( + OffsetPattern.Create("mm", CultureInfo.InvariantCulture)); + + offsetType.Description.MatchInlineSnapshot( + """ + An offset from UTC in seconds. + A positive value means that the local time is ahead of UTC (e.g. for Europe); a negative value means that the local time is behind UTC (e.g. for America). + """); + } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/ZonedDateTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/ZonedDateTimeTypeTests.cs index f63000d63fd..4ed8ddb89fb 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/ZonedDateTimeTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/ZonedDateTimeTypeTests.cs @@ -1,5 +1,8 @@ +using System.Globalization; +using CookieCrumble; using HotChocolate.Execution; using NodaTime; +using NodaTime.Text; namespace HotChocolate.Types.NodaTime.Tests { @@ -157,4 +160,44 @@ public void PatternEmpty_ThrowSchemaException() Assert.Throws(Call); } } + + [Fact] + public void ZonedDateTimeType_DescriptionKnownPatterns_MatchesSnapshot() + { + var zonedDateTimeType = new ZonedDateTimeType( + ZonedDateTimePattern.GeneralFormatOnlyIso, + ZonedDateTimePattern.ExtendedFormatOnlyIso); + + zonedDateTimeType.Description.MatchInlineSnapshot( + """ + A LocalDateTime in a specific time zone and with a particular offset to distinguish between otherwise-ambiguous instants. + A ZonedDateTime is global, in that it maps to a single Instant. + + Allowed patterns: + - `YYYY-MM-DDThh:mm:ss z (±hh:mm)` + - `YYYY-MM-DDThh:mm:ss.sssssssss z (±hh:mm)` + + Examples: + - `2000-01-01T20:00:00 Europe/Zurich (+01)` + - `2000-01-01T20:00:00.999999999 Europe/Zurich (+01)` + """); + } + + [Fact] + public void ZonedDateTimeType_DescriptionUnknownPatterns_MatchesSnapshot() + { + var zonedDateTimeType = new ZonedDateTimeType( + ZonedDateTimePattern.Create( + "MM", + CultureInfo.InvariantCulture, + null, + null, + new ZonedDateTime())); + + zonedDateTimeType.Description.MatchInlineSnapshot( + """ + A LocalDateTime in a specific time zone and with a particular offset to distinguish between otherwise-ambiguous instants. + A ZonedDateTime is global, in that it maps to a single Instant. + """); + } } From ed0459f970736faa3e6d7ba57567e4dfb3c2e7eb Mon Sep 17 00:00:00 2001 From: Glen Date: Mon, 4 Nov 2024 10:16:12 +0200 Subject: [PATCH 088/154] Fixed incorrect TotalCount for MongoDB (#7669) --- .../src/Types.CursorPagination/CursorPagingHandler.cs | 8 +++++++- .../ICursorPaginationQueryExecutor.cs | 4 ++++ .../QueryableCursorPagingHandler.cs | 2 +- .../MongoDb/src/Data/Paging/MongoDbCursorPagingHandler.cs | 3 ++- .../MongoDbCursorPagingAggregateFluentTests.cs | 3 +++ .../MongoDbCursorPagingFindFluentTests.cs | 3 +++ ...sorPagingAggregateFluentTests.TotalCount_AndFirst.snap | 5 +++++ ...DbCursorPagingFindFluentTests.TotalCount_AndFirst.snap | 5 +++++ .../Raven/src/Data/Pagination/RavenCursorPagingHandler.cs | 3 ++- .../RavenAsyncDocumentQueryTests.cs | 3 +++ .../Data.Raven.Paging.Tests/RavenDocumentQueryTests.cs | 3 +++ ...syncDocumentQueryTests.Cursor_TotalCount_AndFirst.snap | 5 +++++ .../RavenQueryableTests.Cursor_TotalCount_AndFirst.snap | 5 +++++ 13 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/CursorPagingHandler.cs b/src/HotChocolate/Core/src/Types.CursorPagination/CursorPagingHandler.cs index e5710e33486..59f1983421f 100644 --- a/src/HotChocolate/Core/src/Types.CursorPagination/CursorPagingHandler.cs +++ b/src/HotChocolate/Core/src/Types.CursorPagination/CursorPagingHandler.cs @@ -179,7 +179,13 @@ protected async ValueTask> SliceAsync( return new Connection(ConnectionPageInfo.Empty, totalCount ?? -1); } - var data = await executor.QueryAsync(slicedQuery, offset, countRequired, cancellationToken).ConfigureAwait(false); + var data = await executor.QueryAsync( + slicedQuery, + originalQuery, + offset, + countRequired, + cancellationToken).ConfigureAwait(false); + var moreItemsReturnedThanRequested = data.Edges.Length > length; var isSequenceFromStart = offset == 0; var edges = data.Edges; diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/ICursorPaginationQueryExecutor.cs b/src/HotChocolate/Core/src/Types.CursorPagination/ICursorPaginationQueryExecutor.cs index bb807f3e0c5..2f679923e27 100644 --- a/src/HotChocolate/Core/src/Types.CursorPagination/ICursorPaginationQueryExecutor.cs +++ b/src/HotChocolate/Core/src/Types.CursorPagination/ICursorPaginationQueryExecutor.cs @@ -38,6 +38,9 @@ ValueTask CountAsync( /// /// The sliced query that should be executed. /// + /// + /// The original query. + /// /// /// Teh offset for the index edge. /// @@ -52,6 +55,7 @@ ValueTask CountAsync( /// ValueTask> QueryAsync( TQuery slicedQuery, + TQuery originalQuery, int offset, bool includeTotalCount, CancellationToken cancellationToken); diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/QueryableCursorPagingHandler.cs b/src/HotChocolate/Core/src/Types.CursorPagination/QueryableCursorPagingHandler.cs index 3449aaa9936..f0404a05908 100644 --- a/src/HotChocolate/Core/src/Types.CursorPagination/QueryableCursorPagingHandler.cs +++ b/src/HotChocolate/Core/src/Types.CursorPagination/QueryableCursorPagingHandler.cs @@ -56,6 +56,7 @@ public ValueTask CountAsync( public async ValueTask> QueryAsync( IQueryable slicedQuery, + IQueryable originalQuery, int offset, bool includeTotalCount, CancellationToken cancellationToken) @@ -65,7 +66,6 @@ public async ValueTask> QueryAsync( if (includeTotalCount) { - var originalQuery = executable.Source; var combinedQuery = slicedQuery.Select(t => new { TotalCount = originalQuery.Count(), Item = t }); totalCount = 0; diff --git a/src/HotChocolate/MongoDb/src/Data/Paging/MongoDbCursorPagingHandler.cs b/src/HotChocolate/MongoDb/src/Data/Paging/MongoDbCursorPagingHandler.cs index 32b77763035..fbff49acbab 100644 --- a/src/HotChocolate/MongoDb/src/Data/Paging/MongoDbCursorPagingHandler.cs +++ b/src/HotChocolate/MongoDb/src/Data/Paging/MongoDbCursorPagingHandler.cs @@ -57,6 +57,7 @@ public async ValueTask CountAsync( public async ValueTask> QueryAsync( IMongoPagingContainer slicedQuery, + IMongoPagingContainer originalQuery, int offset, bool includeTotalCount, CancellationToken cancellationToken) @@ -64,7 +65,7 @@ public async ValueTask> QueryAsync( if (includeTotalCount) { var itemsTask = slicedQuery.QueryAsync(offset, cancellationToken); - var countTask = slicedQuery.CountAsync(cancellationToken); + var countTask = originalQuery.CountAsync(cancellationToken); await Task.WhenAll(itemsTask, countTask).ConfigureAwait(false); diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingAggregateFluentTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingAggregateFluentTests.cs index 6b25fdd69b0..98c00f72276 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingAggregateFluentTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingAggregateFluentTests.cs @@ -200,6 +200,9 @@ public async Task TotalCount_AndFirst() var result = await executor.ExecuteAsync( @"{ foos(first:1) { + nodes { + bar + } totalCount } }"); diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingFindFluentTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingFindFluentTests.cs index 5fb788549ca..9bd287cae54 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingFindFluentTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingFindFluentTests.cs @@ -234,6 +234,9 @@ public async Task TotalCount_AndFirst() var result = await executor.ExecuteAsync( @"{ foos(first:1) { + nodes { + bar + } totalCount } }"); diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/__snapshots__/MongoDbCursorPagingAggregateFluentTests.TotalCount_AndFirst.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/__snapshots__/MongoDbCursorPagingAggregateFluentTests.TotalCount_AndFirst.snap index 47fb6854f77..4ccbcdc7c99 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/__snapshots__/MongoDbCursorPagingAggregateFluentTests.TotalCount_AndFirst.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/__snapshots__/MongoDbCursorPagingAggregateFluentTests.TotalCount_AndFirst.snap @@ -1,6 +1,11 @@ { "data": { "foos": { + "nodes": [ + { + "bar": "a" + } + ], "totalCount": 5 } } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/__snapshots__/MongoDbCursorPagingFindFluentTests.TotalCount_AndFirst.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/__snapshots__/MongoDbCursorPagingFindFluentTests.TotalCount_AndFirst.snap index 47fb6854f77..4ccbcdc7c99 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/__snapshots__/MongoDbCursorPagingFindFluentTests.TotalCount_AndFirst.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/__snapshots__/MongoDbCursorPagingFindFluentTests.TotalCount_AndFirst.snap @@ -1,6 +1,11 @@ { "data": { "foos": { + "nodes": [ + { + "bar": "a" + } + ], "totalCount": 5 } } diff --git a/src/HotChocolate/Raven/src/Data/Pagination/RavenCursorPagingHandler.cs b/src/HotChocolate/Raven/src/Data/Pagination/RavenCursorPagingHandler.cs index 728a8e5a2e5..e04086401f3 100644 --- a/src/HotChocolate/Raven/src/Data/Pagination/RavenCursorPagingHandler.cs +++ b/src/HotChocolate/Raven/src/Data/Pagination/RavenCursorPagingHandler.cs @@ -61,6 +61,7 @@ public async ValueTask CountAsync( public async ValueTask> QueryAsync( RavenPagingContainer slicedQuery, + RavenPagingContainer originalQuery, int offset, bool includeTotalCount, CancellationToken cancellationToken) @@ -68,7 +69,7 @@ public async ValueTask> QueryAsync( if (includeTotalCount) { var itemsTask = slicedQuery.QueryAsync(offset, cancellationToken); - var countTask = slicedQuery.CountAsync(cancellationToken); + var countTask = originalQuery.CountAsync(cancellationToken); await Task.WhenAll(itemsTask, countTask).ConfigureAwait(false); diff --git a/src/HotChocolate/Raven/test/Data.Raven.Paging.Tests/RavenAsyncDocumentQueryTests.cs b/src/HotChocolate/Raven/test/Data.Raven.Paging.Tests/RavenAsyncDocumentQueryTests.cs index 21cb6e49f07..65fee609440 100644 --- a/src/HotChocolate/Raven/test/Data.Raven.Paging.Tests/RavenAsyncDocumentQueryTests.cs +++ b/src/HotChocolate/Raven/test/Data.Raven.Paging.Tests/RavenAsyncDocumentQueryTests.cs @@ -190,6 +190,9 @@ public async Task Cursor_TotalCount_AndFirst() var result = await executor.ExecuteAsync( @"{ foos(first:1) { + nodes { + bar + } totalCount } }"); diff --git a/src/HotChocolate/Raven/test/Data.Raven.Paging.Tests/RavenDocumentQueryTests.cs b/src/HotChocolate/Raven/test/Data.Raven.Paging.Tests/RavenDocumentQueryTests.cs index 374370d90a8..67eb191e376 100644 --- a/src/HotChocolate/Raven/test/Data.Raven.Paging.Tests/RavenDocumentQueryTests.cs +++ b/src/HotChocolate/Raven/test/Data.Raven.Paging.Tests/RavenDocumentQueryTests.cs @@ -190,6 +190,9 @@ public async Task Cursor_TotalCount_AndFirst() var result = await executor.ExecuteAsync( @"{ foos(first:1) { + nodes { + bar + } totalCount } }"); diff --git a/src/HotChocolate/Raven/test/Data.Raven.Paging.Tests/__snapshots__/RavenAsyncDocumentQueryTests.Cursor_TotalCount_AndFirst.snap b/src/HotChocolate/Raven/test/Data.Raven.Paging.Tests/__snapshots__/RavenAsyncDocumentQueryTests.Cursor_TotalCount_AndFirst.snap index 47fb6854f77..4ccbcdc7c99 100644 --- a/src/HotChocolate/Raven/test/Data.Raven.Paging.Tests/__snapshots__/RavenAsyncDocumentQueryTests.Cursor_TotalCount_AndFirst.snap +++ b/src/HotChocolate/Raven/test/Data.Raven.Paging.Tests/__snapshots__/RavenAsyncDocumentQueryTests.Cursor_TotalCount_AndFirst.snap @@ -1,6 +1,11 @@ { "data": { "foos": { + "nodes": [ + { + "bar": "a" + } + ], "totalCount": 5 } } diff --git a/src/HotChocolate/Raven/test/Data.Raven.Paging.Tests/__snapshots__/RavenQueryableTests.Cursor_TotalCount_AndFirst.snap b/src/HotChocolate/Raven/test/Data.Raven.Paging.Tests/__snapshots__/RavenQueryableTests.Cursor_TotalCount_AndFirst.snap index 47fb6854f77..4ccbcdc7c99 100644 --- a/src/HotChocolate/Raven/test/Data.Raven.Paging.Tests/__snapshots__/RavenQueryableTests.Cursor_TotalCount_AndFirst.snap +++ b/src/HotChocolate/Raven/test/Data.Raven.Paging.Tests/__snapshots__/RavenQueryableTests.Cursor_TotalCount_AndFirst.snap @@ -1,6 +1,11 @@ { "data": { "foos": { + "nodes": [ + { + "bar": "a" + } + ], "totalCount": 5 } } From e946882a6c4e89e09b647233231a1e22815c8a59 Mon Sep 17 00:00:00 2001 From: Glen Date: Mon, 4 Nov 2024 10:49:47 +0200 Subject: [PATCH 089/154] Fixed incorrect ResolverParameterKind for GetScopedState/SetLocalState (#7670) --- .../Models/ResolverParameter.cs | 4 +- .../Types.Analyzers.Tests/ResolverTests.cs | 145 ++++++++++++++ ...WithGlobalStateArgument_MatchesSnapshot.md | 183 ++++++++++++++++++ ...alStateSetStateArgument_MatchesSnapshot.md | 183 ++++++++++++++++++ ...rWithLocalStateArgument_MatchesSnapshot.md | 183 ++++++++++++++++++ ...alStateSetStateArgument_MatchesSnapshot.md | 183 ++++++++++++++++++ ...WithScopedStateArgument_MatchesSnapshot.md | 183 ++++++++++++++++++ ...edStateSetStateArgument_MatchesSnapshot.md | 183 ++++++++++++++++++ 8 files changed, 1245 insertions(+), 2 deletions(-) create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/ResolverTests.cs create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithGlobalStateArgument_MatchesSnapshot.md create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithGlobalStateSetStateArgument_MatchesSnapshot.md create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithLocalStateArgument_MatchesSnapshot.md create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithLocalStateSetStateArgument_MatchesSnapshot.md create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithScopedStateArgument_MatchesSnapshot.md create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithScopedStateSetStateArgument_MatchesSnapshot.md diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/ResolverParameter.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/ResolverParameter.cs index fb106ae68fd..007016cef5f 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/ResolverParameter.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/ResolverParameter.cs @@ -116,13 +116,13 @@ private static ResolverParameterKind GetParameterKind( { return parameter.IsSetState() ? ResolverParameterKind.SetScopedState - : ResolverParameterKind.GetGlobalState; + : ResolverParameterKind.GetScopedState; } if (parameter.IsLocalState(out key)) { return parameter.IsSetState() - ? ResolverParameterKind.SetGlobalState + ? ResolverParameterKind.SetLocalState : ResolverParameterKind.GetLocalState; } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/ResolverTests.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/ResolverTests.cs new file mode 100644 index 00000000000..b5d475335f1 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/ResolverTests.cs @@ -0,0 +1,145 @@ +namespace HotChocolate.Types; + +public class ResolverTests +{ + [Fact] + public async Task GenerateSource_ResolverWithLocalStateArgument_MatchesSnapshot() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using HotChocolate; + using HotChocolate.Types; + + namespace TestNamespace; + + [ObjectType] + internal static partial class TestType + { + public static int GetTest([LocalState("Test")] int test) + { + return test; + } + } + + internal class Test; + """).MatchMarkdownAsync(); + } + + [Fact] + public async Task GenerateSource_ResolverWithScopedStateArgument_MatchesSnapshot() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using HotChocolate; + using HotChocolate.Types; + + namespace TestNamespace; + + [ObjectType] + internal static partial class TestType + { + public static int GetTest([ScopedState("Test")] int test) + { + return test; + } + } + + internal class Test; + """).MatchMarkdownAsync(); + } + + [Fact] + public async Task GenerateSource_ResolverWithGlobalStateArgument_MatchesSnapshot() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using HotChocolate; + using HotChocolate.Types; + + namespace TestNamespace; + + [ObjectType] + internal static partial class TestType + { + public static int GetTest([GlobalState("Test")] int test) + { + return test; + } + } + + internal class Test; + """).MatchMarkdownAsync(); + } + + [Fact] + public async Task GenerateSource_ResolverWithLocalStateSetStateArgument_MatchesSnapshot() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using HotChocolate; + using HotChocolate.Types; + + namespace TestNamespace; + + [ObjectType] + internal static partial class TestType + { + public static int GetTest([LocalState] SetState test) + { + test(1); + return 1; + } + } + + internal class Test; + """).MatchMarkdownAsync(); + } + + [Fact] + public async Task GenerateSource_ResolverWithScopedStateSetStateArgument_MatchesSnapshot() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using HotChocolate; + using HotChocolate.Types; + + namespace TestNamespace; + + [ObjectType] + internal static partial class TestType + { + public static int GetTest([ScopedState] SetState test) + { + test(1); + return 1; + } + } + + internal class Test; + """).MatchMarkdownAsync(); + } + + [Fact] + public async Task GenerateSource_ResolverWithGlobalStateSetStateArgument_MatchesSnapshot() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using HotChocolate; + using HotChocolate.Types; + + namespace TestNamespace; + + [ObjectType] + internal static partial class TestType + { + public static int GetTest([GlobalState] SetState test) + { + test(1); + return 1; + } + } + + internal class Test; + """).MatchMarkdownAsync(); + } +} diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithGlobalStateArgument_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithGlobalStateArgument_MatchesSnapshot.md new file mode 100644 index 00000000000..b9030b00531 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithGlobalStateArgument_MatchesSnapshot.md @@ -0,0 +1,183 @@ +# GenerateSource_ResolverWithGlobalStateArgument_MatchesSnapshot + +## HotChocolateResolvers.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using HotChocolate.Internal; + +namespace TestNamespace +{ + internal static class TestTypeResolvers + { + private static bool _bindingsInitialized; + private readonly static global::HotChocolate.Internal.IParameterBinding[] _args_TestType_GetTest = new global::HotChocolate.Internal.IParameterBinding[1]; + + public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver) + { + if (_bindingsInitialized) + { + return; + } + _bindingsInitialized = true; + + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var type = typeof(global::TestNamespace.TestType); + global::System.Reflection.MethodInfo resolver = default!; + global::System.Reflection.ParameterInfo[] parameters = default!; + + resolver = type.GetMethod( + "GetTest", + bindingFlags, + new global::System.Type[] + { + typeof(int) + })!; + parameters = resolver.GetParameters(); + _args_TestType_GetTest[0] = bindingResolver.GetBinding(parameters[0]); + } + + public static HotChocolate.Resolvers.FieldResolverDelegates TestType_GetTest() + { + if(!_bindingsInitialized) + { + throw new global::System.InvalidOperationException("The bindings must be initialized before the resolvers can be created."); + } + return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: TestType_GetTest_Resolver); + } + + private static global::System.Object? TestType_GetTest_Resolver(global::HotChocolate.Resolvers.IResolverContext context) + { + var args0 = context.GetGlobalState("Test"); + var result = global::TestNamespace.TestType.GetTest(args0); + return result; + } + } +} + + +``` + +## HotChocolateTypeModule.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + AddObjectTypeExtension_8734371(builder, global::TestNamespace.TestType.Initialize); + return builder; + } + + private static void AddObjectTypeExtension_8734371( + global::HotChocolate.Execution.Configuration.IRequestExecutorBuilder builder, + Action> initialize) + { + builder.ConfigureSchema(sb => + { + string typeName = typeof(T).FullName!; + string typeKey = $"8734371_Type_ObjectType<{typeName}>"; + string hooksKey = $"8734371_Hooks_ObjectType<{typeName}>"; + + if (!sb.ContextData.ContainsKey(typeKey)) + { + sb.AddObjectType( + descriptor => + { + var hooks = (global::System.Collections.Generic.List>>)descriptor.Extend().Context.ContextData[hooksKey]!; + foreach (var configure in hooks) + { + configure(descriptor); + }; + }); + sb.ContextData.Add(typeKey, null); + } + + if (!sb.ContextData.TryGetValue(hooksKey, out var value)) + { + value = new System.Collections.Generic.List>>(); + sb.ContextData.Add(hooksKey, value); + } + + ((System.Collections.Generic.List>>)value!).Add(initialize); + }); + } + } +} + +``` + +## HotChocolateTypes.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace TestNamespace +{ +public static partial class TestType +{ + internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor descriptor) + { + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var thisType = typeof(global::TestNamespace.TestType); + var bindingResolver = descriptor.Extend().Context.ParameterBindingResolver; + global::TestNamespace.TestTypeResolvers.InitializeBindings(bindingResolver); + + descriptor + .Field(thisType.GetMember("GetTest", bindingFlags)[0]) + .ExtendWith(c => + { + c.Definition.SetSourceGeneratorFlags(); + c.Definition.Resolvers = global::TestNamespace.TestTypeResolvers.TestType_GetTest(); + }); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); +} +} + + +``` + diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithGlobalStateSetStateArgument_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithGlobalStateSetStateArgument_MatchesSnapshot.md new file mode 100644 index 00000000000..b5d71594ca7 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithGlobalStateSetStateArgument_MatchesSnapshot.md @@ -0,0 +1,183 @@ +# GenerateSource_ResolverWithGlobalStateSetStateArgument_MatchesSnapshot + +## HotChocolateResolvers.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using HotChocolate.Internal; + +namespace TestNamespace +{ + internal static class TestTypeResolvers + { + private static bool _bindingsInitialized; + private readonly static global::HotChocolate.Internal.IParameterBinding[] _args_TestType_GetTest = new global::HotChocolate.Internal.IParameterBinding[1]; + + public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver) + { + if (_bindingsInitialized) + { + return; + } + _bindingsInitialized = true; + + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var type = typeof(global::TestNamespace.TestType); + global::System.Reflection.MethodInfo resolver = default!; + global::System.Reflection.ParameterInfo[] parameters = default!; + + resolver = type.GetMethod( + "GetTest", + bindingFlags, + new global::System.Type[] + { + typeof(global::HotChocolate.SetState) + })!; + parameters = resolver.GetParameters(); + _args_TestType_GetTest[0] = bindingResolver.GetBinding(parameters[0]); + } + + public static HotChocolate.Resolvers.FieldResolverDelegates TestType_GetTest() + { + if(!_bindingsInitialized) + { + throw new global::System.InvalidOperationException("The bindings must be initialized before the resolvers can be created."); + } + return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: TestType_GetTest_Resolver); + } + + private static global::System.Object? TestType_GetTest_Resolver(global::HotChocolate.Resolvers.IResolverContext context) + { + var args0 = new HotChocolate.SetState(value => context.SetGlobalState("test", value)); + var result = global::TestNamespace.TestType.GetTest(args0); + return result; + } + } +} + + +``` + +## HotChocolateTypeModule.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + AddObjectTypeExtension_8734371(builder, global::TestNamespace.TestType.Initialize); + return builder; + } + + private static void AddObjectTypeExtension_8734371( + global::HotChocolate.Execution.Configuration.IRequestExecutorBuilder builder, + Action> initialize) + { + builder.ConfigureSchema(sb => + { + string typeName = typeof(T).FullName!; + string typeKey = $"8734371_Type_ObjectType<{typeName}>"; + string hooksKey = $"8734371_Hooks_ObjectType<{typeName}>"; + + if (!sb.ContextData.ContainsKey(typeKey)) + { + sb.AddObjectType( + descriptor => + { + var hooks = (global::System.Collections.Generic.List>>)descriptor.Extend().Context.ContextData[hooksKey]!; + foreach (var configure in hooks) + { + configure(descriptor); + }; + }); + sb.ContextData.Add(typeKey, null); + } + + if (!sb.ContextData.TryGetValue(hooksKey, out var value)) + { + value = new System.Collections.Generic.List>>(); + sb.ContextData.Add(hooksKey, value); + } + + ((System.Collections.Generic.List>>)value!).Add(initialize); + }); + } + } +} + +``` + +## HotChocolateTypes.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace TestNamespace +{ +public static partial class TestType +{ + internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor descriptor) + { + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var thisType = typeof(global::TestNamespace.TestType); + var bindingResolver = descriptor.Extend().Context.ParameterBindingResolver; + global::TestNamespace.TestTypeResolvers.InitializeBindings(bindingResolver); + + descriptor + .Field(thisType.GetMember("GetTest", bindingFlags)[0]) + .ExtendWith(c => + { + c.Definition.SetSourceGeneratorFlags(); + c.Definition.Resolvers = global::TestNamespace.TestTypeResolvers.TestType_GetTest(); + }); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); +} +} + + +``` + diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithLocalStateArgument_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithLocalStateArgument_MatchesSnapshot.md new file mode 100644 index 00000000000..0e1dcdb1396 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithLocalStateArgument_MatchesSnapshot.md @@ -0,0 +1,183 @@ +# GenerateSource_ResolverWithLocalStateArgument_MatchesSnapshot + +## HotChocolateResolvers.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using HotChocolate.Internal; + +namespace TestNamespace +{ + internal static class TestTypeResolvers + { + private static bool _bindingsInitialized; + private readonly static global::HotChocolate.Internal.IParameterBinding[] _args_TestType_GetTest = new global::HotChocolate.Internal.IParameterBinding[1]; + + public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver) + { + if (_bindingsInitialized) + { + return; + } + _bindingsInitialized = true; + + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var type = typeof(global::TestNamespace.TestType); + global::System.Reflection.MethodInfo resolver = default!; + global::System.Reflection.ParameterInfo[] parameters = default!; + + resolver = type.GetMethod( + "GetTest", + bindingFlags, + new global::System.Type[] + { + typeof(int) + })!; + parameters = resolver.GetParameters(); + _args_TestType_GetTest[0] = bindingResolver.GetBinding(parameters[0]); + } + + public static HotChocolate.Resolvers.FieldResolverDelegates TestType_GetTest() + { + if(!_bindingsInitialized) + { + throw new global::System.InvalidOperationException("The bindings must be initialized before the resolvers can be created."); + } + return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: TestType_GetTest_Resolver); + } + + private static global::System.Object? TestType_GetTest_Resolver(global::HotChocolate.Resolvers.IResolverContext context) + { + var args0 = context.GetLocalState("Test"); + var result = global::TestNamespace.TestType.GetTest(args0); + return result; + } + } +} + + +``` + +## HotChocolateTypeModule.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + AddObjectTypeExtension_8734371(builder, global::TestNamespace.TestType.Initialize); + return builder; + } + + private static void AddObjectTypeExtension_8734371( + global::HotChocolate.Execution.Configuration.IRequestExecutorBuilder builder, + Action> initialize) + { + builder.ConfigureSchema(sb => + { + string typeName = typeof(T).FullName!; + string typeKey = $"8734371_Type_ObjectType<{typeName}>"; + string hooksKey = $"8734371_Hooks_ObjectType<{typeName}>"; + + if (!sb.ContextData.ContainsKey(typeKey)) + { + sb.AddObjectType( + descriptor => + { + var hooks = (global::System.Collections.Generic.List>>)descriptor.Extend().Context.ContextData[hooksKey]!; + foreach (var configure in hooks) + { + configure(descriptor); + }; + }); + sb.ContextData.Add(typeKey, null); + } + + if (!sb.ContextData.TryGetValue(hooksKey, out var value)) + { + value = new System.Collections.Generic.List>>(); + sb.ContextData.Add(hooksKey, value); + } + + ((System.Collections.Generic.List>>)value!).Add(initialize); + }); + } + } +} + +``` + +## HotChocolateTypes.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace TestNamespace +{ +public static partial class TestType +{ + internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor descriptor) + { + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var thisType = typeof(global::TestNamespace.TestType); + var bindingResolver = descriptor.Extend().Context.ParameterBindingResolver; + global::TestNamespace.TestTypeResolvers.InitializeBindings(bindingResolver); + + descriptor + .Field(thisType.GetMember("GetTest", bindingFlags)[0]) + .ExtendWith(c => + { + c.Definition.SetSourceGeneratorFlags(); + c.Definition.Resolvers = global::TestNamespace.TestTypeResolvers.TestType_GetTest(); + }); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); +} +} + + +``` + diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithLocalStateSetStateArgument_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithLocalStateSetStateArgument_MatchesSnapshot.md new file mode 100644 index 00000000000..57951d45d81 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithLocalStateSetStateArgument_MatchesSnapshot.md @@ -0,0 +1,183 @@ +# GenerateSource_ResolverWithLocalStateSetStateArgument_MatchesSnapshot + +## HotChocolateResolvers.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using HotChocolate.Internal; + +namespace TestNamespace +{ + internal static class TestTypeResolvers + { + private static bool _bindingsInitialized; + private readonly static global::HotChocolate.Internal.IParameterBinding[] _args_TestType_GetTest = new global::HotChocolate.Internal.IParameterBinding[1]; + + public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver) + { + if (_bindingsInitialized) + { + return; + } + _bindingsInitialized = true; + + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var type = typeof(global::TestNamespace.TestType); + global::System.Reflection.MethodInfo resolver = default!; + global::System.Reflection.ParameterInfo[] parameters = default!; + + resolver = type.GetMethod( + "GetTest", + bindingFlags, + new global::System.Type[] + { + typeof(global::HotChocolate.SetState) + })!; + parameters = resolver.GetParameters(); + _args_TestType_GetTest[0] = bindingResolver.GetBinding(parameters[0]); + } + + public static HotChocolate.Resolvers.FieldResolverDelegates TestType_GetTest() + { + if(!_bindingsInitialized) + { + throw new global::System.InvalidOperationException("The bindings must be initialized before the resolvers can be created."); + } + return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: TestType_GetTest_Resolver); + } + + private static global::System.Object? TestType_GetTest_Resolver(global::HotChocolate.Resolvers.IResolverContext context) + { + var args0 = new HotChocolate.SetState(value => context.SetLocalState("test", value)); + var result = global::TestNamespace.TestType.GetTest(args0); + return result; + } + } +} + + +``` + +## HotChocolateTypeModule.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + AddObjectTypeExtension_8734371(builder, global::TestNamespace.TestType.Initialize); + return builder; + } + + private static void AddObjectTypeExtension_8734371( + global::HotChocolate.Execution.Configuration.IRequestExecutorBuilder builder, + Action> initialize) + { + builder.ConfigureSchema(sb => + { + string typeName = typeof(T).FullName!; + string typeKey = $"8734371_Type_ObjectType<{typeName}>"; + string hooksKey = $"8734371_Hooks_ObjectType<{typeName}>"; + + if (!sb.ContextData.ContainsKey(typeKey)) + { + sb.AddObjectType( + descriptor => + { + var hooks = (global::System.Collections.Generic.List>>)descriptor.Extend().Context.ContextData[hooksKey]!; + foreach (var configure in hooks) + { + configure(descriptor); + }; + }); + sb.ContextData.Add(typeKey, null); + } + + if (!sb.ContextData.TryGetValue(hooksKey, out var value)) + { + value = new System.Collections.Generic.List>>(); + sb.ContextData.Add(hooksKey, value); + } + + ((System.Collections.Generic.List>>)value!).Add(initialize); + }); + } + } +} + +``` + +## HotChocolateTypes.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace TestNamespace +{ +public static partial class TestType +{ + internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor descriptor) + { + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var thisType = typeof(global::TestNamespace.TestType); + var bindingResolver = descriptor.Extend().Context.ParameterBindingResolver; + global::TestNamespace.TestTypeResolvers.InitializeBindings(bindingResolver); + + descriptor + .Field(thisType.GetMember("GetTest", bindingFlags)[0]) + .ExtendWith(c => + { + c.Definition.SetSourceGeneratorFlags(); + c.Definition.Resolvers = global::TestNamespace.TestTypeResolvers.TestType_GetTest(); + }); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); +} +} + + +``` + diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithScopedStateArgument_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithScopedStateArgument_MatchesSnapshot.md new file mode 100644 index 00000000000..aa2f8e21468 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithScopedStateArgument_MatchesSnapshot.md @@ -0,0 +1,183 @@ +# GenerateSource_ResolverWithScopedStateArgument_MatchesSnapshot + +## HotChocolateResolvers.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using HotChocolate.Internal; + +namespace TestNamespace +{ + internal static class TestTypeResolvers + { + private static bool _bindingsInitialized; + private readonly static global::HotChocolate.Internal.IParameterBinding[] _args_TestType_GetTest = new global::HotChocolate.Internal.IParameterBinding[1]; + + public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver) + { + if (_bindingsInitialized) + { + return; + } + _bindingsInitialized = true; + + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var type = typeof(global::TestNamespace.TestType); + global::System.Reflection.MethodInfo resolver = default!; + global::System.Reflection.ParameterInfo[] parameters = default!; + + resolver = type.GetMethod( + "GetTest", + bindingFlags, + new global::System.Type[] + { + typeof(int) + })!; + parameters = resolver.GetParameters(); + _args_TestType_GetTest[0] = bindingResolver.GetBinding(parameters[0]); + } + + public static HotChocolate.Resolvers.FieldResolverDelegates TestType_GetTest() + { + if(!_bindingsInitialized) + { + throw new global::System.InvalidOperationException("The bindings must be initialized before the resolvers can be created."); + } + return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: TestType_GetTest_Resolver); + } + + private static global::System.Object? TestType_GetTest_Resolver(global::HotChocolate.Resolvers.IResolverContext context) + { + var args0 = context.GetScopedState("Test"); + var result = global::TestNamespace.TestType.GetTest(args0); + return result; + } + } +} + + +``` + +## HotChocolateTypeModule.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + AddObjectTypeExtension_8734371(builder, global::TestNamespace.TestType.Initialize); + return builder; + } + + private static void AddObjectTypeExtension_8734371( + global::HotChocolate.Execution.Configuration.IRequestExecutorBuilder builder, + Action> initialize) + { + builder.ConfigureSchema(sb => + { + string typeName = typeof(T).FullName!; + string typeKey = $"8734371_Type_ObjectType<{typeName}>"; + string hooksKey = $"8734371_Hooks_ObjectType<{typeName}>"; + + if (!sb.ContextData.ContainsKey(typeKey)) + { + sb.AddObjectType( + descriptor => + { + var hooks = (global::System.Collections.Generic.List>>)descriptor.Extend().Context.ContextData[hooksKey]!; + foreach (var configure in hooks) + { + configure(descriptor); + }; + }); + sb.ContextData.Add(typeKey, null); + } + + if (!sb.ContextData.TryGetValue(hooksKey, out var value)) + { + value = new System.Collections.Generic.List>>(); + sb.ContextData.Add(hooksKey, value); + } + + ((System.Collections.Generic.List>>)value!).Add(initialize); + }); + } + } +} + +``` + +## HotChocolateTypes.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace TestNamespace +{ +public static partial class TestType +{ + internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor descriptor) + { + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var thisType = typeof(global::TestNamespace.TestType); + var bindingResolver = descriptor.Extend().Context.ParameterBindingResolver; + global::TestNamespace.TestTypeResolvers.InitializeBindings(bindingResolver); + + descriptor + .Field(thisType.GetMember("GetTest", bindingFlags)[0]) + .ExtendWith(c => + { + c.Definition.SetSourceGeneratorFlags(); + c.Definition.Resolvers = global::TestNamespace.TestTypeResolvers.TestType_GetTest(); + }); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); +} +} + + +``` + diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithScopedStateSetStateArgument_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithScopedStateSetStateArgument_MatchesSnapshot.md new file mode 100644 index 00000000000..137b7b827b3 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithScopedStateSetStateArgument_MatchesSnapshot.md @@ -0,0 +1,183 @@ +# GenerateSource_ResolverWithScopedStateSetStateArgument_MatchesSnapshot + +## HotChocolateResolvers.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using HotChocolate.Internal; + +namespace TestNamespace +{ + internal static class TestTypeResolvers + { + private static bool _bindingsInitialized; + private readonly static global::HotChocolate.Internal.IParameterBinding[] _args_TestType_GetTest = new global::HotChocolate.Internal.IParameterBinding[1]; + + public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver) + { + if (_bindingsInitialized) + { + return; + } + _bindingsInitialized = true; + + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var type = typeof(global::TestNamespace.TestType); + global::System.Reflection.MethodInfo resolver = default!; + global::System.Reflection.ParameterInfo[] parameters = default!; + + resolver = type.GetMethod( + "GetTest", + bindingFlags, + new global::System.Type[] + { + typeof(global::HotChocolate.SetState) + })!; + parameters = resolver.GetParameters(); + _args_TestType_GetTest[0] = bindingResolver.GetBinding(parameters[0]); + } + + public static HotChocolate.Resolvers.FieldResolverDelegates TestType_GetTest() + { + if(!_bindingsInitialized) + { + throw new global::System.InvalidOperationException("The bindings must be initialized before the resolvers can be created."); + } + return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: TestType_GetTest_Resolver); + } + + private static global::System.Object? TestType_GetTest_Resolver(global::HotChocolate.Resolvers.IResolverContext context) + { + var args0 = new HotChocolate.SetState(value => context.SetScopedState("test", value)); + var result = global::TestNamespace.TestType.GetTest(args0); + return result; + } + } +} + + +``` + +## HotChocolateTypeModule.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + AddObjectTypeExtension_8734371(builder, global::TestNamespace.TestType.Initialize); + return builder; + } + + private static void AddObjectTypeExtension_8734371( + global::HotChocolate.Execution.Configuration.IRequestExecutorBuilder builder, + Action> initialize) + { + builder.ConfigureSchema(sb => + { + string typeName = typeof(T).FullName!; + string typeKey = $"8734371_Type_ObjectType<{typeName}>"; + string hooksKey = $"8734371_Hooks_ObjectType<{typeName}>"; + + if (!sb.ContextData.ContainsKey(typeKey)) + { + sb.AddObjectType( + descriptor => + { + var hooks = (global::System.Collections.Generic.List>>)descriptor.Extend().Context.ContextData[hooksKey]!; + foreach (var configure in hooks) + { + configure(descriptor); + }; + }); + sb.ContextData.Add(typeKey, null); + } + + if (!sb.ContextData.TryGetValue(hooksKey, out var value)) + { + value = new System.Collections.Generic.List>>(); + sb.ContextData.Add(hooksKey, value); + } + + ((System.Collections.Generic.List>>)value!).Add(initialize); + }); + } + } +} + +``` + +## HotChocolateTypes.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace TestNamespace +{ +public static partial class TestType +{ + internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor descriptor) + { + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var thisType = typeof(global::TestNamespace.TestType); + var bindingResolver = descriptor.Extend().Context.ParameterBindingResolver; + global::TestNamespace.TestTypeResolvers.InitializeBindings(bindingResolver); + + descriptor + .Field(thisType.GetMember("GetTest", bindingFlags)[0]) + .ExtendWith(c => + { + c.Definition.SetSourceGeneratorFlags(); + c.Definition.Resolvers = global::TestNamespace.TestTypeResolvers.TestType_GetTest(); + }); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); +} +} + + +``` + From 03e5fb4c8798f0512d9142d9e4ecd14edb1fad23 Mon Sep 17 00:00:00 2001 From: Glen Date: Mon, 4 Nov 2024 10:50:46 +0200 Subject: [PATCH 090/154] Passed the correct argument to SetSlicedQuery in CursorPagingHandler (#7667) --- .../Core/src/Types.CursorPagination/CursorPagingHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/CursorPagingHandler.cs b/src/HotChocolate/Core/src/Types.CursorPagination/CursorPagingHandler.cs index 59f1983421f..b3873ca4000 100644 --- a/src/HotChocolate/Core/src/Types.CursorPagination/CursorPagingHandler.cs +++ b/src/HotChocolate/Core/src/Types.CursorPagination/CursorPagingHandler.cs @@ -166,7 +166,7 @@ protected async ValueTask> SliceAsync( // we store the original query and the sliced query in the // context for later use by customizations. context.SetOriginalQuery(originalQuery); - context.SetSlicedQuery(originalQuery); + context.SetSlicedQuery(slicedQuery); // if no edges are required we will return a connection without edges. if (!edgesRequired) From b30f0da36ebdb1520590d8ea93ec02ef7a3f522e Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Tue, 5 Nov 2024 18:16:56 +0100 Subject: [PATCH 091/154] Fixed compile issues after cherry picking fixes. --- .../HttpGetSchemaMiddlewareTests.cs | 31 +-- ...raphQL_Schema_Slicing_Args_Enabled_NET6.md | 145 ++++++++++++++ .../OptimizedNodeIdSerializer.cs | 2 +- .../Integration/DataLoader/DataLoaderTests.cs | 4 +- .../Types.Analyzers.Tests/ResolverTests.cs | 12 +- ...aLoaderTests.Generate_Without_Interface.md | 23 --- .../Types.NodaTime.Tests/DurationTypeTests.cs | 181 +++++++++++------- .../Types.NodaTime.Tests/InstantTypeTests.cs | 87 +++++---- .../LocalDateTimeTypeTests.cs | 67 ++++--- .../LocalDateTypeTests.cs | 90 +++++---- .../LocalTimeTypeTests.cs | 74 ++++--- .../OffsetDateTimeTypeTests.cs | 74 ++++--- .../OffsetDateTypeTests.cs | 116 ++++++----- .../OffsetTimeTypeTests.cs | 116 ++++++----- .../Types.NodaTime.Tests/OffsetTypeTests.cs | 133 +++++++------ .../ZonedDateTimeTypeTests.cs | 81 ++++---- ...ersistedOperations.FileSystem.Tests.csproj | 1 + .../IntegrationTests.cs | 2 +- 18 files changed, 744 insertions(+), 495 deletions(-) create mode 100644 src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema_Slicing_Args_Enabled_NET6.md diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/HttpGetSchemaMiddlewareTests.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/HttpGetSchemaMiddlewareTests.cs index aada7f6f4ad..4ed5eb6245a 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/HttpGetSchemaMiddlewareTests.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/HttpGetSchemaMiddlewareTests.cs @@ -89,34 +89,11 @@ public async Task Download_GraphQL_Schema_Slicing_Args_Enabled(string path) // assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); +#if NET7_0_OR_GREATER response.MatchMarkdownSnapshot(); - } - - [Theory] - [InlineData("/graphql?sdl")] - [InlineData("/graphql/schema/")] - [InlineData("/graphql/schema.graphql")] - [InlineData("/graphql/schema")] - public async Task Download_GraphQL_Schema_Slicing_Args_Enabled(string path) - { - // arrange - var server = CreateStarWarsServer( - configureServices: sp => - sp - .RemoveAll() - .AddSingleton() - .AddGraphQL() - .ModifyPagingOptions(o => o.RequirePagingBoundaries = true)); - var url = TestServerExtensions.CreateUrl(path); - var request = new HttpRequestMessage(HttpMethod.Get, url); - - // act - var response = await server.CreateClient().SendAsync(request); - - // assert - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - response.MatchMarkdownSnapshot(); +#else + response.MatchMarkdownSnapshot("NET6"); +#endif } [Theory] diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema_Slicing_Args_Enabled_NET6.md b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema_Slicing_Args_Enabled_NET6.md new file mode 100644 index 00000000000..8959905adf8 --- /dev/null +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema_Slicing_Args_Enabled_NET6.md @@ -0,0 +1,145 @@ +# Download_GraphQL_Schema_Slicing_Args_Enabled + +```text +Headers: +ETag: "1-kBEjhe2t+jfqbeZRxnezu0WDQFYAc0qzjLF1RlHs428=" +Cache-Control: public, must-revalidate, max-age=3600 +Content-Type: application/graphql; charset=utf-8 +Content-Disposition: attachment; filename="schema.graphql" +Last-Modified: Fri, 01 Jan 2021 00:00:00 GMT +Content-Length: 5070 +--------------------------> +Status Code: OK +--------------------------> +schema { + query: Query + mutation: Mutation + subscription: Subscription +} + +interface Character { + id: ID! + name: String! + friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection + appearsIn: [Episode] + traits: JSON + height(unit: Unit): Float +} + +type Droid implements Character { + id: ID! + name: String! + appearsIn: [Episode] + friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection + height(unit: Unit): Float + primaryFunction: String + traits: JSON +} + +"A connection to a list of items." +type FriendsConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [FriendsEdge!] + "A flattened list of the nodes." + nodes: [Character] +} + +"An edge in a connection." +type FriendsEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: Character +} + +type Human implements Character { + id: ID! + name: String! + appearsIn: [Episode] + friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection + otherHuman: Human + height(unit: Unit): Float + homePlanet: String + traits: JSON +} + +type Mutation { + createReview(episode: Episode! review: ReviewInput!): Review! + complete(episode: Episode!): Boolean! +} + +"Information about pagination in a connection." +type PageInfo { + "Indicates whether more edges exist following the set defined by the clients arguments." + hasNextPage: Boolean! + "Indicates whether more edges exist prior the set defined by the clients arguments." + hasPreviousPage: Boolean! + "When paginating backwards, the cursor to continue." + startCursor: String + "When paginating forwards, the cursor to continue." + endCursor: String +} + +type Query { + hero(episode: Episode! = NEW_HOPE): Character + heroByTraits(traits: JSON!): Character + heroes(episodes: [Episode!]!): [Character!] + character(characterIds: [String!]!): [Character!]! + search(text: String!): [SearchResult] + human(id: String!): Human + droid(id: String!): Droid + time: Long! + evict: Boolean! + wait(m: Int!): Boolean! + someDeprecatedField(deprecatedArg: String! = "foo" @deprecated(reason: "use something else")): String! @deprecated(reason: "use something else") +} + +type Review { + commentary: String + stars: Int! +} + +type Starship { + id: ID! + name: String! + length(unit: Unit): Float! +} + +type Subscription { + onReview(episode: Episode!): Review! + onNext: String! + onException: String! + delay(delay: Int! count: Int!): String! +} + +union SearchResult = Starship | Human | Droid + +input ReviewInput { + stars: Int! + commentary: String +} + +enum Episode { + NEW_HOPE + EMPIRE + JEDI +} + +enum Unit { + FOOT + METERS +} + +"The `@defer` directive may be provided for fragment spreads and inline fragments to inform the executor to delay the execution of the current fragment to indicate deprioritization of the current fragment. A query with `@defer` directive will cause the request to potentially return multiple responses, where non-deferred data is delivered in the initial response and data deferred is delivered in a subsequent response. `@include` and `@skip` take precedence over `@defer`." +directive @defer("If this argument label has a value other than null, it will be passed on to the result of this defer directive. This label is intended to give client applications a way to identify to which fragment a deferred result belongs to." label: String "Deferred when true." if: Boolean) on FRAGMENT_SPREAD | INLINE_FRAGMENT + +"The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." +directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! = 0 "Streamed when true." if: Boolean) on FIELD + +scalar JSON + +"The `Long` scalar type represents non-fractional signed whole 64-bit numeric values. Long can represent values between -(2^63) and 2^63 - 1." +scalar Long +``` diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/Serialization/OptimizedNodeIdSerializer.cs b/src/HotChocolate/Core/src/Types/Types/Relay/Serialization/OptimizedNodeIdSerializer.cs index b2ffa737f85..6eb58166633 100644 --- a/src/HotChocolate/Core/src/Types/Types/Relay/Serialization/OptimizedNodeIdSerializer.cs +++ b/src/HotChocolate/Core/src/Types/Types/Relay/Serialization/OptimizedNodeIdSerializer.cs @@ -118,7 +118,7 @@ public unsafe NodeId Parse(string formattedId, INodeIdRuntimeTypeLookup runtimeT } } - Base64.DecodeFromUtf8InPlace(span, out var written); + var status = Base64.DecodeFromUtf8InPlace(span, out var written); span = span.Slice(0, written); var delimiterIndex = FindDelimiterIndex(span); diff --git a/src/HotChocolate/Core/test/Execution.Tests/Integration/DataLoader/DataLoaderTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Integration/DataLoader/DataLoaderTests.cs index 64ef4ccfe24..79c48dad239 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Integration/DataLoader/DataLoaderTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Integration/DataLoader/DataLoaderTests.cs @@ -67,8 +67,8 @@ public async Task FetchMultipleNodesDataLoader() await ExpectValid( """ { - a: node(id: "RW50aXR5OjE==") { ... on Entity { id } } - b: node(id: "RW50aXR5OjI==") { ... on Entity { id } } + a: node(id: "RW50aXR5OjE=") { ... on Entity { id } } + b: node(id: "RW50aXR5OjI=") { ... on Entity { id } } } """, configure: b => b diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/ResolverTests.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/ResolverTests.cs index b5d475335f1..09fe7c8d33b 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/ResolverTests.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/ResolverTests.cs @@ -21,7 +21,7 @@ public static int GetTest([LocalState("Test")] int test) } } - internal class Test; + internal class Test { } """).MatchMarkdownAsync(); } @@ -44,7 +44,7 @@ public static int GetTest([ScopedState("Test")] int test) } } - internal class Test; + internal class Test { } """).MatchMarkdownAsync(); } @@ -67,7 +67,7 @@ public static int GetTest([GlobalState("Test")] int test) } } - internal class Test; + internal class Test { } """).MatchMarkdownAsync(); } @@ -91,7 +91,7 @@ public static int GetTest([LocalState] SetState test) } } - internal class Test; + internal class Test { } """).MatchMarkdownAsync(); } @@ -115,7 +115,7 @@ public static int GetTest([ScopedState] SetState test) } } - internal class Test; + internal class Test { } """).MatchMarkdownAsync(); } @@ -139,7 +139,7 @@ public static int GetTest([GlobalState] SetState test) } } - internal class Test; + internal class Test { } """).MatchMarkdownAsync(); } } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.Generate_Without_Interface.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.Generate_Without_Interface.md index cebdeb21888..bc544f391ca 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.Generate_Without_Interface.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.Generate_Without_Interface.md @@ -94,26 +94,3 @@ namespace Microsoft.Extensions.DependencyInjection ``` -## Compilation Diagnostics - -```json -[ - { - "Id": "GD0002", - "Title": "Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.", - "Severity": "Error", - "WarningLevel": 0, - "Location": ": (15,8)-(15,47)", - "HelpLinkUri": "https://msdn.microsoft.com/query/roslyn.query?appId=roslyn&k=k(CS9204)", - "MessageFormat": "'{0}' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.", - "Message": "'GreenDonut.Predicates.IPredicateBuilder' is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.", - "Category": "Compiler", - "CustomTags": [ - "Compiler", - "Telemetry", - "CustomObsolete" - ] - } -] -``` - diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/DurationTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/DurationTypeTests.cs index fba12ca83ff..be388c8a146 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/DurationTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/DurationTypeTests.cs @@ -14,14 +14,19 @@ public class Query { public Duration PositiveWithDecimals => Duration.FromTimeSpan(new TimeSpan(123, 7, 53, 10, 19)); + public Duration NegativeWithDecimals => -Duration.FromTimeSpan(new TimeSpan(123, 7, 53, 10, 19)); + public Duration PositiveWithoutDecimals => Duration.FromTimeSpan(new TimeSpan(123, 7, 53, 10)); + public Duration PositiveWithoutSeconds => Duration.FromTimeSpan(new TimeSpan(123, 7, 53, 0)); + public Duration PositiveWithoutMinutes => Duration.FromTimeSpan(new TimeSpan(123, 7, 0, 0)); + public Duration PositiveWithRoundtrip => Duration.FromTimeSpan(new TimeSpan(123, 26, 0, 70)); } @@ -86,10 +91,15 @@ public void QueryReturnsSerializedDataWithRoundtrip() public void MutationParsesInputWithDecimals() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation($arg: Duration!) { test(arg: $arg) }") - .SetVariableValues(new Dictionary { {"arg", "09:22:01:00.019" }, }) - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation($arg: Duration!) { test(arg: $arg) }") + .SetVariableValues( + new Dictionary + { + { "arg", "09:22:01:00.019" }, + }) + .Build()); Assert.Equal("9:22:11:00.019", result.ExpectOperationResult().Data!["test"]); } @@ -97,10 +107,15 @@ public void MutationParsesInputWithDecimals() public void MutationParsesInputWithoutDecimals() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation($arg: Duration!) { test(arg: $arg) }") - .SetVariableValues(new Dictionary { {"arg", "09:22:01:00" }, }) - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation($arg: Duration!) { test(arg: $arg) }") + .SetVariableValues( + new Dictionary + { + { "arg", "09:22:01:00" }, + }) + .Build()); Assert.Equal("9:22:11:00", result.ExpectOperationResult().Data!["test"]); } @@ -108,10 +123,15 @@ public void MutationParsesInputWithoutDecimals() public void MutationParsesInputWithoutLeadingZero() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation($arg: Duration!) { test(arg: $arg) }") - .SetVariableValues(new Dictionary { {"arg", "9:22:01:00" }, }) - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation($arg: Duration!) { test(arg: $arg) }") + .SetVariableValues( + new Dictionary + { + { "arg", "9:22:01:00" }, + }) + .Build()); Assert.Equal("9:22:11:00", result.ExpectOperationResult().Data!["test"]); } @@ -119,10 +139,15 @@ public void MutationParsesInputWithoutLeadingZero() public void MutationParsesInputWithNegativeValue() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation($arg: Duration!) { test(arg: $arg) }") - .SetVariableValues(new Dictionary { {"arg", "-9:22:01:00" }, }) - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation($arg: Duration!) { test(arg: $arg) }") + .SetVariableValues( + new Dictionary + { + { "arg", "-9:22:01:00" }, + }) + .Build()); Assert.Equal("-9:21:51:00", result.ExpectOperationResult().Data!["test"]); } @@ -130,10 +155,15 @@ public void MutationParsesInputWithNegativeValue() public void MutationDoesntParseInputWithPlusSign() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation($arg: Duration!) { test(arg: $arg) }") - .SetVariableValues(new Dictionary { {"arg", "+09:22:01:00" }, }) - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation($arg: Duration!) { test(arg: $arg) }") + .SetVariableValues( + new Dictionary + { + { "arg", "+09:22:01:00" }, + }) + .Build()); Assert.Null(result.ExpectOperationResult().Data); Assert.Single(result.ExpectOperationResult().Errors!); } @@ -142,10 +172,15 @@ public void MutationDoesntParseInputWithPlusSign() public void MutationDoesntParseInputWithOverflownHours() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation($arg: Duration!) { test(arg: $arg) }") - .SetVariableValues(new Dictionary { {"arg", "9:26:01:00" }, }) - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation($arg: Duration!) { test(arg: $arg) }") + .SetVariableValues( + new Dictionary + { + { "arg", "9:26:01:00" }, + }) + .Build()); Assert.Null(result.ExpectOperationResult().Data); Assert.Single(result.ExpectOperationResult().Errors!); } @@ -154,9 +189,10 @@ public void MutationDoesntParseInputWithOverflownHours() public void MutationParsesLiteralWithDecimals() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation { test(arg: \"09:22:01:00.019\") }") - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation { test(arg: \"09:22:01:00.019\") }") + .Build()); Assert.Equal("9:22:11:00.019", result.ExpectOperationResult().Data!["test"]); } @@ -165,9 +201,10 @@ public void MutationParsesLiteralWithDecimals() public void MutationParsesLiteralWithoutDecimals() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation { test(arg: \"09:22:01:00\") }") - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation { test(arg: \"09:22:01:00\") }") + .Build()); Assert.Equal("9:22:11:00", result.ExpectOperationResult().Data!["test"]); } @@ -176,9 +213,10 @@ public void MutationParsesLiteralWithoutDecimals() public void MutationParsesLiteralWithoutLeadingZero() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation { test(arg: \"09:22:01:00\") }") - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation { test(arg: \"09:22:01:00\") }") + .Build()); Assert.Equal("9:22:11:00", result.ExpectOperationResult().Data!["test"]); } @@ -187,9 +225,10 @@ public void MutationParsesLiteralWithoutLeadingZero() public void MutationParsesLiteralWithNegativeValue() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation { test(arg: \"-9:22:01:00\") }") - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation { test(arg: \"-9:22:01:00\") }") + .Build()); Assert.Equal("-9:21:51:00", result.ExpectOperationResult().Data!["test"]); } @@ -198,9 +237,10 @@ public void MutationParsesLiteralWithNegativeValue() public void MutationDoesntParseLiteralWithPlusSign() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation { test(arg: \"+09:22:01:00\") }") - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation { test(arg: \"+09:22:01:00\") }") + .Build()); Assert.Null(result.ExpectOperationResult().Data); Assert.Single(result.ExpectOperationResult().Errors!); @@ -210,9 +250,10 @@ public void MutationDoesntParseLiteralWithPlusSign() public void MutationDoesntParseLiteralWithOverflownHours() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation { test(arg: \"9:26:01:00\") }") - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation { test(arg: \"9:26:01:00\") }") + .Build()); Assert.Null(result.ExpectOperationResult().Data); Assert.Single(result.ExpectOperationResult().Errors!); @@ -224,36 +265,36 @@ public void PatternEmpty_ThrowSchemaException() static object Call() => new DurationType([]); Assert.Throws(Call); } - } - [Fact] - public void DurationType_DescriptionKnownPatterns_MatchesSnapshot() - { - var durationType = new DurationType( - DurationPattern.Roundtrip, - DurationPattern.JsonRoundtrip); - - durationType.Description.MatchInlineSnapshot( - """ - Represents a fixed (and calendar-independent) length of time. - - Allowed patterns: - - `-D:hh:mm:ss.sssssssss` - - `-hh:mm:ss.sssssssss` - - Examples: - - `-1:20:00:00.999999999` - - `-44:00:00.999999999` - """); - } + [Fact] + public void DurationType_DescriptionKnownPatterns_MatchesSnapshot() + { + var durationType = new DurationType( + DurationPattern.Roundtrip, + DurationPattern.JsonRoundtrip); + + durationType.Description.MatchInlineSnapshot( + """ + Represents a fixed (and calendar-independent) length of time. + + Allowed patterns: + - `-D:hh:mm:ss.sssssssss` + - `-hh:mm:ss.sssssssss` + + Examples: + - `-1:20:00:00.999999999` + - `-44:00:00.999999999` + """); + } - [Fact] - public void DurationType_DescriptionUnknownPatterns_MatchesSnapshot() - { - var durationType = new DurationType( - DurationPattern.Create("mm", CultureInfo.InvariantCulture)); + [Fact] + public void DurationType_DescriptionUnknownPatterns_MatchesSnapshot() + { + var durationType = new DurationType( + DurationPattern.Create("mm", CultureInfo.InvariantCulture)); - durationType.Description.MatchInlineSnapshot( - "Represents a fixed (and calendar-independent) length of time."); + durationType.Description.MatchInlineSnapshot( + "Represents a fixed (and calendar-independent) length of time."); + } } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/InstantTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/InstantTypeTests.cs index c3085bf7222..623366bdb60 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/InstantTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/InstantTypeTests.cs @@ -46,10 +46,15 @@ public void QueryReturnsUtc() public void ParsesVariable() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation($arg: Instant!) { test(arg: $arg) }") - .SetVariableValues(new Dictionary { {"arg", "2020-02-21T17:42:59.000001234Z" }, }) - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation($arg: Instant!) { test(arg: $arg) }") + .SetVariableValues( + new Dictionary + { + { "arg", "2020-02-21T17:42:59.000001234Z" }, + }) + .Build()); Assert.Equal( "2020-02-21T17:52:59.000001234Z", @@ -60,10 +65,15 @@ public void ParsesVariable() public void DoesntParseAnIncorrectVariable() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation($arg: Instant!) { test(arg: $arg) }") - .SetVariableValues(new Dictionary { {"arg", "2020-02-20T17:42:59" }, }) - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation($arg: Instant!) { test(arg: $arg) }") + .SetVariableValues( + new Dictionary + { + { "arg", "2020-02-20T17:42:59" }, + }) + .Build()); Assert.Null(result.ExpectOperationResult().Data); Assert.Single(result.ExpectOperationResult().Errors!); @@ -73,9 +83,10 @@ public void DoesntParseAnIncorrectVariable() public void ParsesLiteral() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation { test(arg: \"2020-02-20T17:42:59.000001234Z\") }") - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation { test(arg: \"2020-02-20T17:42:59.000001234Z\") }") + .Build()); Assert.Equal( "2020-02-20T17:52:59.000001234Z", @@ -86,9 +97,10 @@ public void ParsesLiteral() public void DoesntParseIncorrectLiteral() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation { test(arg: \"2020-02-20T17:42:59\") }") - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation { test(arg: \"2020-02-20T17:42:59\") }") + .Build()); Assert.Null(result.ExpectOperationResult().Data); Assert.Single(result.ExpectOperationResult().Errors!); @@ -104,34 +116,35 @@ public void PatternEmpty_ThrowSchemaException() static object Call() => new InstantType([]); Assert.Throws(Call); } - } - [Fact] - public void InstantType_DescriptionKnownPatterns_MatchesSnapshot() - { - var instantType = new InstantType(InstantPattern.General, InstantPattern.ExtendedIso); - instantType.Description.MatchInlineSnapshot( - """ - Represents an instant on the global timeline, with nanosecond resolution. + [Fact] + public void InstantType_DescriptionKnownPatterns_MatchesSnapshot() + { + var instantType = new InstantType(InstantPattern.General, InstantPattern.ExtendedIso); - Allowed patterns: - - `YYYY-MM-DDThh:mm:ss±hh:mm` - - `YYYY-MM-DDThh:mm:ss.sssssssss±hh:mm` + instantType.Description.MatchInlineSnapshot( + """ + Represents an instant on the global timeline, with nanosecond resolution. - Examples: - - `2000-01-01T20:00:00Z` - - `2000-01-01T20:00:00.999999999Z` - """); - } + Allowed patterns: + - `YYYY-MM-DDThh:mm:ss±hh:mm` + - `YYYY-MM-DDThh:mm:ss.sssssssss±hh:mm` - [Fact] - public void InstantType_DescriptionUnknownPatterns_MatchesSnapshot() - { - var instantType = new InstantType( - InstantPattern.Create("MM", CultureInfo.InvariantCulture)); + Examples: + - `2000-01-01T20:00:00Z` + - `2000-01-01T20:00:00.999999999Z` + """); + } - instantType.Description.MatchInlineSnapshot( - "Represents an instant on the global timeline, with nanosecond resolution."); + [Fact] + public void InstantType_DescriptionUnknownPatterns_MatchesSnapshot() + { + var instantType = new InstantType( + InstantPattern.Create("MM", CultureInfo.InvariantCulture)); + + instantType.Description.MatchInlineSnapshot( + "Represents an instant on the global timeline, with nanosecond resolution."); + } } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTimeTypeTests.cs index 611eda2d32d..c8fc98addaa 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTimeTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTimeTypeTests.cs @@ -52,7 +52,10 @@ public void ParsesVariable() OperationRequestBuilder.New() .SetDocument("mutation($arg: LocalDateTime!) { test(arg: $arg) }") .SetVariableValues( - new Dictionary { { "arg", "2020-02-21T17:42:59.000001234" }, }) + new Dictionary + { + { "arg", "2020-02-21T17:42:59.000001234" }, + }) .Build()); Assert.Equal("2020-02-21T17:52:59.000001234", result.ExpectOperationResult().Data!["test"]); @@ -66,7 +69,10 @@ public void DoesntParseAnIncorrectVariable() OperationRequestBuilder.New() .SetDocument("mutation($arg: LocalDateTime!) { test(arg: $arg) }") .SetVariableValues( - new Dictionary { { "arg", "2020-02-20T17:42:59.000001234Z" }, }) + new Dictionary + { + { "arg", "2020-02-20T17:42:59.000001234Z" }, + }) .Build()); Assert.Null(result.ExpectOperationResult().Data); @@ -108,36 +114,37 @@ public void PatternEmpty_ThrowSchemaException() static object Call() => new LocalDateTimeType([]); Assert.Throws(Call); } - } - [Fact] - public void LocalDateTimeType_DescriptionKnownPatterns_MatchesSnapshot() - { - var localDateTimeType = new LocalDateTimeType( - LocalDateTimePattern.ExtendedIso, - LocalDateTimePattern.FullRoundtrip); - - localDateTimeType.Description.MatchInlineSnapshot( - """ - A date and time in a particular calendar system. - - Allowed patterns: - - `YYYY-MM-DDThh:mm:ss.sssssssss` - - `YYYY-MM-DDThh:mm:ss.sssssssss (calendar)` - - Examples: - - `2000-01-01T20:00:00.999` - - `2000-01-01T20:00:00.999999999 (ISO)` - """); - } - [Fact] - public void LocalDateTimeType_DescriptionUnknownPatterns_MatchesSnapshot() - { - var localDateTimeType = new LocalDateTimeType( - LocalDateTimePattern.Create("MM", CultureInfo.InvariantCulture)); + [Fact] + public void LocalDateTimeType_DescriptionKnownPatterns_MatchesSnapshot() + { + var localDateTimeType = new LocalDateTimeType( + LocalDateTimePattern.ExtendedIso, + LocalDateTimePattern.FullRoundtrip); + + localDateTimeType.Description.MatchInlineSnapshot( + """ + A date and time in a particular calendar system. + + Allowed patterns: + - `YYYY-MM-DDThh:mm:ss.sssssssss` + - `YYYY-MM-DDThh:mm:ss.sssssssss (calendar)` + + Examples: + - `2000-01-01T20:00:00.999` + - `2000-01-01T20:00:00.999999999 (ISO)` + """); + } - localDateTimeType.Description.MatchInlineSnapshot( - "A date and time in a particular calendar system."); + [Fact] + public void LocalDateTimeType_DescriptionUnknownPatterns_MatchesSnapshot() + { + var localDateTimeType = new LocalDateTimeType( + LocalDateTimePattern.Create("MM", CultureInfo.InvariantCulture)); + + localDateTimeType.Description.MatchInlineSnapshot( + "A date and time in a particular calendar system."); + } } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTypeTests.cs index 2657f36a3f3..82488662077 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTypeTests.cs @@ -13,8 +13,8 @@ public static class Schema public class Query { public LocalDate One => LocalDate.FromDateTime( - new DateTime(2020, 02, 20, 17, 42, 59)) - .WithCalendar(CalendarSystem.HebrewCivil); + new DateTime(2020, 02, 20, 17, 42, 59)) + .WithCalendar(CalendarSystem.HebrewCivil); } public class Mutation @@ -46,10 +46,15 @@ public void QueryReturns() public void ParsesVariable() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation($arg: LocalDate!) { test(arg: $arg) }") - .SetVariableValues(new Dictionary { {"arg", "2020-02-21" }, }) - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation($arg: LocalDate!) { test(arg: $arg) }") + .SetVariableValues( + new Dictionary + { + { "arg", "2020-02-21" }, + }) + .Build()); Assert.Equal("2020-02-24", result.ExpectOperationResult().Data!["test"]); } @@ -58,10 +63,15 @@ public void ParsesVariable() public void DoesntParseAnIncorrectVariable() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation($arg: LocalDate!) { test(arg: $arg) }") - .SetVariableValues(new Dictionary { {"arg", "2020-02-20T17:42:59" }, }) - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation($arg: LocalDate!) { test(arg: $arg) }") + .SetVariableValues( + new Dictionary + { + { "arg", "2020-02-20T17:42:59" }, + }) + .Build()); Assert.Null(result.ExpectOperationResult().Data); Assert.Single(result.ExpectOperationResult().Errors!); @@ -71,9 +81,10 @@ public void DoesntParseAnIncorrectVariable() public void ParsesLiteral() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation { test(arg: \"2020-02-20\") }") - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation { test(arg: \"2020-02-20\") }") + .Build()); Assert.Equal("2020-02-23", result.ExpectOperationResult().Data!["test"]); } @@ -82,9 +93,10 @@ public void ParsesLiteral() public void DoesntParseIncorrectLiteral() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation { test(arg: \"2020-02-20T17:42:59\") }") - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation { test(arg: \"2020-02-20T17:42:59\") }") + .Build()); Assert.Null(result.ExpectOperationResult().Data); Assert.Single(result.ExpectOperationResult().Errors!); @@ -100,34 +112,34 @@ public void PatternEmpty_ThrowSchemaException() static object Call() => new LocalDateType([]); Assert.Throws(Call); } - } - [Fact] - public void LocalDateType_DescriptionKnownPatterns_MatchesSnapshot() - { - var localDateType = new LocalDateType(LocalDatePattern.Iso, LocalDatePattern.FullRoundtrip); + [Fact] + public void LocalDateType_DescriptionKnownPatterns_MatchesSnapshot() + { + var localDateType = new LocalDateType(LocalDatePattern.Iso, LocalDatePattern.FullRoundtrip); - localDateType.Description.MatchInlineSnapshot( - """ - LocalDate represents a date within the calendar, with no reference to a particular time zone or time of day. + localDateType.Description.MatchInlineSnapshot( + """ + LocalDate represents a date within the calendar, with no reference to a particular time zone or time of day. - Allowed patterns: - - `YYYY-MM-DD` - - `YYYY-MM-DD (calendar)` + Allowed patterns: + - `YYYY-MM-DD` + - `YYYY-MM-DD (calendar)` - Examples: - - `2000-01-01` - - `2000-01-01 (ISO)` - """); - } + Examples: + - `2000-01-01` + - `2000-01-01 (ISO)` + """); + } - [Fact] - public void LocalDateType_DescriptionUnknownPatterns_MatchesSnapshot() - { - var localDateType = new LocalDateType( - LocalDatePattern.Create("MM", CultureInfo.InvariantCulture)); + [Fact] + public void LocalDateType_DescriptionUnknownPatterns_MatchesSnapshot() + { + var localDateType = new LocalDateType( + LocalDatePattern.Create("MM", CultureInfo.InvariantCulture)); - localDateType.Description.MatchInlineSnapshot( - "LocalDate represents a date within the calendar, with no reference to a particular time zone or time of day."); + localDateType.Description.MatchInlineSnapshot( + "LocalDate represents a date within the calendar, with no reference to a particular time zone or time of day."); + } } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalTimeTypeTests.cs index ee6f6b774fa..185d68747bb 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalTimeTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalTimeTypeTests.cs @@ -49,7 +49,11 @@ public void ParsesVariable() .Execute( OperationRequestBuilder.New() .SetDocument("mutation($arg: LocalTime!) { test(arg: $arg) }") - .SetVariableValues(new Dictionary { { "arg", "12:42:13.031011234" }, }) + .SetVariableValues( + new Dictionary + { + { "arg", "12:42:13.031011234" }, + }) .Build()); Assert.Equal("12:52:13.031011234", result.ExpectOperationResult().Data!["test"]); @@ -62,7 +66,11 @@ public void ParsesVariableWithoutTicks() .Execute( OperationRequestBuilder.New() .SetDocument("mutation($arg: LocalTime!) { test(arg: $arg) }") - .SetVariableValues(new Dictionary { { "arg", "12:42:13" }, }) + .SetVariableValues( + new Dictionary + { + { "arg", "12:42:13" }, + }) .Build()); Assert.Equal("12:52:13", result.ExpectOperationResult().Data!["test"]); @@ -75,7 +83,11 @@ public void DoesntParseAnIncorrectVariable() .Execute( OperationRequestBuilder.New() .SetDocument("mutation($arg: LocalTime!) { test(arg: $arg) }") - .SetVariableValues(new Dictionary { { "arg", "12:42" }, }) + .SetVariableValues( + new Dictionary + { + { "arg", "12:42" }, + }) .Build()); Assert.Null(result.ExpectOperationResult().Data); @@ -129,36 +141,36 @@ public void PatternEmptyThrowSchemaException() static object Call() => new LocalTimeType([]); Assert.Throws(Call); } - } - [Fact] - public void LocalTimeType_DescriptionKnownPatterns_MatchesSnapshot() - { - var localTimeType = new LocalTimeType( - LocalTimePattern.GeneralIso, - LocalTimePattern.ExtendedIso); - - localTimeType.Description.MatchInlineSnapshot( - """ - LocalTime represents a time of day, with no reference to a particular calendar, time zone, or date. - - Allowed patterns: - - `hh:mm:ss` - - `hh:mm:ss.sssssssss` - - Examples: - - `20:00:00` - - `20:00:00.999` - """); - } + [Fact] + public void LocalTimeType_DescriptionKnownPatterns_MatchesSnapshot() + { + var localTimeType = new LocalTimeType( + LocalTimePattern.GeneralIso, + LocalTimePattern.ExtendedIso); + + localTimeType.Description.MatchInlineSnapshot( + """ + LocalTime represents a time of day, with no reference to a particular calendar, time zone, or date. + + Allowed patterns: + - `hh:mm:ss` + - `hh:mm:ss.sssssssss` + + Examples: + - `20:00:00` + - `20:00:00.999` + """); + } - [Fact] - public void LocalTimeType_DescriptionUnknownPatterns_MatchesSnapshot() - { - var localTimeType = new LocalTimeType( - LocalTimePattern.Create("mm", CultureInfo.InvariantCulture)); + [Fact] + public void LocalTimeType_DescriptionUnknownPatterns_MatchesSnapshot() + { + var localTimeType = new LocalTimeType( + LocalTimePattern.Create("mm", CultureInfo.InvariantCulture)); - localTimeType.Description.MatchInlineSnapshot( - "LocalTime represents a time of day, with no reference to a particular calendar, time zone, or date."); + localTimeType.Description.MatchInlineSnapshot( + "LocalTime represents a time of day, with no reference to a particular calendar, time zone, or date."); + } } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeTests.cs index a372e0ecd21..c5d6bdc4e95 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeTests.cs @@ -79,7 +79,11 @@ public void ParsesVariable() .Execute( OperationRequestBuilder.New() .SetDocument("mutation($arg: OffsetDateTime!) { test(arg: $arg) }") - .SetVariableValues(new Dictionary { { "arg", "2020-12-31T18:30:13+02" }, }) + .SetVariableValues( + new Dictionary + { + { "arg", "2020-12-31T18:30:13+02" }, + }) .Build()); Assert.Equal("2020-12-31T18:40:13+02", result.ExpectOperationResult().Data!["test"]); @@ -92,7 +96,11 @@ public void ParsesVariableWithMinutes() .Execute( OperationRequestBuilder.New() .SetDocument("mutation($arg: OffsetDateTime!) { test(arg: $arg) }") - .SetVariableValues(new Dictionary { { "arg", "2020-12-31T18:30:13+02:35" }, }) + .SetVariableValues( + new Dictionary + { + { "arg", "2020-12-31T18:30:13+02:35" }, + }) .Build()); Assert.Equal("2020-12-31T18:40:13+02:35", result.ExpectOperationResult().Data!["test"]); @@ -105,7 +113,11 @@ public void DoesntParseAnIncorrectVariable() .Execute( OperationRequestBuilder.New() .SetDocument("mutation($arg: OffsetDateTime!) { test(arg: $arg) }") - .SetVariableValues(new Dictionary { { "arg", "2020-12-31T18:30:13" }, }) + .SetVariableValues( + new Dictionary + { + { "arg", "2020-12-31T18:30:13" }, + }) .Build()); Assert.Null(result.ExpectOperationResult().Data); @@ -159,36 +171,36 @@ public void PatternEmptyThrowSchemaException() static object Call() => new OffsetDateTimeType([]); Assert.Throws(Call); } - } - [Fact] - public void OffsetDateTimeType_DescriptionKnownPatterns_MatchesSnapshot() - { - var offsetDateTimeType = new OffsetDateTimeType( - OffsetDateTimePattern.ExtendedIso, - OffsetDateTimePattern.FullRoundtrip); - - offsetDateTimeType.Description.MatchInlineSnapshot( - """ - A local date and time in a particular calendar system, combined with an offset from UTC. - - Allowed patterns: - - `YYYY-MM-DDThh:mm:ss.sssssssss±hh:mm` - - `YYYY-MM-DDThh:mm:ss.sssssssss±hh:mm (calendar)` - - Examples: - - `2000-01-01T20:00:00.999Z` - - `2000-01-01T20:00:00.999Z (ISO)` - """); - } + [Fact] + public void OffsetDateTimeType_DescriptionKnownPatterns_MatchesSnapshot() + { + var offsetDateTimeType = new OffsetDateTimeType( + OffsetDateTimePattern.ExtendedIso, + OffsetDateTimePattern.FullRoundtrip); + + offsetDateTimeType.Description.MatchInlineSnapshot( + """ + A local date and time in a particular calendar system, combined with an offset from UTC. + + Allowed patterns: + - `YYYY-MM-DDThh:mm:ss.sssssssss±hh:mm` + - `YYYY-MM-DDThh:mm:ss.sssssssss±hh:mm (calendar)` + + Examples: + - `2000-01-01T20:00:00.999Z` + - `2000-01-01T20:00:00.999Z (ISO)` + """); + } - [Fact] - public void OffsetDateTimeType_DescriptionUnknownPatterns_MatchesSnapshot() - { - var offsetDateTimeType = new OffsetDateTimeType( - OffsetDateTimePattern.Create("MM", CultureInfo.InvariantCulture, new OffsetDateTime())); + [Fact] + public void OffsetDateTimeType_DescriptionUnknownPatterns_MatchesSnapshot() + { + var offsetDateTimeType = new OffsetDateTimeType( + OffsetDateTimePattern.Create("MM", CultureInfo.InvariantCulture, new OffsetDateTime())); - offsetDateTimeType.Description.MatchInlineSnapshot( - "A local date and time in a particular calendar system, combined with an offset from UTC."); + offsetDateTimeType.Description.MatchInlineSnapshot( + "A local date and time in a particular calendar system, combined with an offset from UTC."); + } } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTypeTests.cs index 193ed353e05..36b77fb6828 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTypeTests.cs @@ -55,10 +55,15 @@ public void QueryReturnsWithMinutes() public void ParsesVariable() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation($arg: OffsetDate!) { test(arg: $arg) }") - .SetVariableValues(new Dictionary { {"arg", "2020-12-31+02" }, }) - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation($arg: OffsetDate!) { test(arg: $arg) }") + .SetVariableValues( + new Dictionary + { + { "arg", "2020-12-31+02" }, + }) + .Build()); Assert.Equal("2020-12-31+02", result.ExpectOperationResult().Data!["test"]); } @@ -66,10 +71,15 @@ public void ParsesVariable() public void ParsesVariableWithMinutes() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation($arg: OffsetDate!) { test(arg: $arg) }") - .SetVariableValues(new Dictionary { {"arg", "2020-12-31+02:35" }, }) - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation($arg: OffsetDate!) { test(arg: $arg) }") + .SetVariableValues( + new Dictionary + { + { "arg", "2020-12-31+02:35" }, + }) + .Build()); Assert.Equal("2020-12-31+02:35", result.ExpectOperationResult().Data!["test"]); } @@ -77,10 +87,15 @@ public void ParsesVariableWithMinutes() public void DoesntParseAnIncorrectVariable() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation($arg: OffsetDate!) { test(arg: $arg) }") - .SetVariableValues(new Dictionary { {"arg", "2020-12-31" }, }) - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation($arg: OffsetDate!) { test(arg: $arg) }") + .SetVariableValues( + new Dictionary + { + { "arg", "2020-12-31" }, + }) + .Build()); Assert.Null(result.ExpectOperationResult().Data); Assert.Single(result.ExpectOperationResult().Errors!); } @@ -89,9 +104,10 @@ public void DoesntParseAnIncorrectVariable() public void ParsesLiteral() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation { test(arg: \"2020-12-31+02\") }") - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation { test(arg: \"2020-12-31+02\") }") + .Build()); Assert.Equal("2020-12-31+02", result.ExpectOperationResult().Data!["test"]); } @@ -99,9 +115,10 @@ public void ParsesLiteral() public void ParsesLiteralWithMinutes() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation { test(arg: \"2020-12-31+02:35\") }") - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation { test(arg: \"2020-12-31+02:35\") }") + .Build()); Assert.Equal("2020-12-31+02:35", result.ExpectOperationResult().Data!["test"]); } @@ -109,9 +126,10 @@ public void ParsesLiteralWithMinutes() public void DoesntParseIncorrectLiteral() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation { test(arg: \"2020-12-31\") }") - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation { test(arg: \"2020-12-31\") }") + .Build()); Assert.Null(result.ExpectOperationResult().Data); Assert.Single(result.ExpectOperationResult().Errors!); Assert.Null(result.ExpectOperationResult().Errors![0].Code); @@ -126,36 +144,36 @@ public void PatternEmptyThrowSchemaException() static object Call() => new OffsetDateType([]); Assert.Throws(Call); } - } - [Fact] - public void OffsetDateType_DescriptionKnownPatterns_MatchesSnapshot() - { - var offsetDateType = new OffsetDateType( - OffsetDatePattern.GeneralIso, - OffsetDatePattern.FullRoundtrip); - - offsetDateType.Description.MatchInlineSnapshot( - """ - A combination of a LocalDate and an Offset, to represent a date at a specific offset from UTC but without any time-of-day information. - - Allowed patterns: - - `YYYY-MM-DD±hh:mm` - - `YYYY-MM-DD±hh:mm (calendar)` - - Examples: - - `2000-01-01Z` - - `2000-01-01Z (ISO)` - """); - } + [Fact] + public void OffsetDateType_DescriptionKnownPatterns_MatchesSnapshot() + { + var offsetDateType = new OffsetDateType( + OffsetDatePattern.GeneralIso, + OffsetDatePattern.FullRoundtrip); + + offsetDateType.Description.MatchInlineSnapshot( + """ + A combination of a LocalDate and an Offset, to represent a date at a specific offset from UTC but without any time-of-day information. + + Allowed patterns: + - `YYYY-MM-DD±hh:mm` + - `YYYY-MM-DD±hh:mm (calendar)` + + Examples: + - `2000-01-01Z` + - `2000-01-01Z (ISO)` + """); + } - [Fact] - public void OffsetDateType_DescriptionUnknownPatterns_MatchesSnapshot() - { - var offsetDateType = new OffsetDateType( - OffsetDatePattern.Create("MM", CultureInfo.InvariantCulture, new OffsetDate())); + [Fact] + public void OffsetDateType_DescriptionUnknownPatterns_MatchesSnapshot() + { + var offsetDateType = new OffsetDateType( + OffsetDatePattern.Create("MM", CultureInfo.InvariantCulture, new OffsetDate())); - offsetDateType.Description.MatchInlineSnapshot( - "A combination of a LocalDate and an Offset, to represent a date at a specific offset from UTC but without any time-of-day information."); + offsetDateType.Description.MatchInlineSnapshot( + "A combination of a LocalDate and an Offset, to represent a date at a specific offset from UTC but without any time-of-day information."); + } } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTimeTypeTests.cs index b49caf6ea47..23d9d6d98b7 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTimeTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTimeTypeTests.cs @@ -59,10 +59,15 @@ public void QueryReturnsWithMinutes() public void ParsesVariable() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation($arg: OffsetTime!) { test(arg: $arg) }") - .SetVariableValues(new Dictionary { {"arg", "18:30:13+02" }, }) - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation($arg: OffsetTime!) { test(arg: $arg) }") + .SetVariableValues( + new Dictionary + { + { "arg", "18:30:13+02" }, + }) + .Build()); Assert.Equal("18:30:13+02", result.ExpectOperationResult().Data!["test"]); } @@ -70,10 +75,15 @@ public void ParsesVariable() public void ParsesVariableWithMinutes() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation($arg: OffsetTime!) { test(arg: $arg) }") - .SetVariableValues(new Dictionary { {"arg", "18:30:13+02:35" }, }) - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation($arg: OffsetTime!) { test(arg: $arg) }") + .SetVariableValues( + new Dictionary + { + { "arg", "18:30:13+02:35" }, + }) + .Build()); Assert.Equal("18:30:13+02:35", result.ExpectOperationResult().Data!["test"]); } @@ -81,10 +91,15 @@ public void ParsesVariableWithMinutes() public void DoesntParseAnIncorrectVariable() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation($arg: OffsetTime!) { test(arg: $arg) }") - .SetVariableValues(new Dictionary { {"arg", "18:30:13" }, }) - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation($arg: OffsetTime!) { test(arg: $arg) }") + .SetVariableValues( + new Dictionary + { + { "arg", "18:30:13" }, + }) + .Build()); Assert.Null(result.ExpectOperationResult().Data); Assert.Single(result.ExpectOperationResult().Errors!); } @@ -93,9 +108,10 @@ public void DoesntParseAnIncorrectVariable() public void ParsesLiteral() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation { test(arg: \"18:30:13+02\") }") - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation { test(arg: \"18:30:13+02\") }") + .Build()); Assert.Equal("18:30:13+02", result.ExpectOperationResult().Data!["test"]); } @@ -103,9 +119,10 @@ public void ParsesLiteral() public void ParsesLiteralWithMinutes() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation { test(arg: \"18:30:13+02:35\") }") - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation { test(arg: \"18:30:13+02:35\") }") + .Build()); Assert.Equal("18:30:13+02:35", result.ExpectOperationResult().Data!["test"]); } @@ -113,9 +130,10 @@ public void ParsesLiteralWithMinutes() public void DoesntParseIncorrectLiteral() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation { test(arg: \"18:30:13\") }") - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation { test(arg: \"18:30:13\") }") + .Build()); Assert.Null(result.ExpectOperationResult().Data); Assert.Single(result.ExpectOperationResult().Errors!); @@ -131,36 +149,36 @@ public void PatternEmptyThrowSchemaException() static object Call() => new OffsetTimeType([]); Assert.Throws(Call); } - } - [Fact] - public void OffsetTimeType_DescriptionKnownPatterns_MatchesSnapshot() - { - var offsetTimeType = new OffsetTimeType( - OffsetTimePattern.GeneralIso, - OffsetTimePattern.ExtendedIso); - - offsetTimeType.Description.MatchInlineSnapshot( - """ - A combination of a LocalTime and an Offset, to represent a time-of-day at a specific offset from UTC but without any date information. - - Allowed patterns: - - `hh:mm:ss±hh:mm` - - `hh:mm:ss.sssssssss±hh:mm` - - Examples: - - `20:00:00Z` - - `20:00:00.999Z` - """); - } + [Fact] + public void OffsetTimeType_DescriptionKnownPatterns_MatchesSnapshot() + { + var offsetTimeType = new OffsetTimeType( + OffsetTimePattern.GeneralIso, + OffsetTimePattern.ExtendedIso); + + offsetTimeType.Description.MatchInlineSnapshot( + """ + A combination of a LocalTime and an Offset, to represent a time-of-day at a specific offset from UTC but without any date information. + + Allowed patterns: + - `hh:mm:ss±hh:mm` + - `hh:mm:ss.sssssssss±hh:mm` + + Examples: + - `20:00:00Z` + - `20:00:00.999Z` + """); + } - [Fact] - public void OffsetTimeType_DescriptionUnknownPatterns_MatchesSnapshot() - { - var offsetTimeType = new OffsetTimeType( - OffsetTimePattern.Create("mm", CultureInfo.InvariantCulture, new OffsetTime())); + [Fact] + public void OffsetTimeType_DescriptionUnknownPatterns_MatchesSnapshot() + { + var offsetTimeType = new OffsetTimeType( + OffsetTimePattern.Create("mm", CultureInfo.InvariantCulture, new OffsetTime())); - offsetTimeType.Description.MatchInlineSnapshot( - "A combination of a LocalTime and an Offset, to represent a time-of-day at a specific offset from UTC but without any date information."); + offsetTimeType.Description.MatchInlineSnapshot( + "A combination of a LocalTime and an Offset, to represent a time-of-day at a specific offset from UTC but without any date information."); + } } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTypeTests.cs index fef1d43d205..2d04c39cd4e 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTypeTests.cs @@ -56,10 +56,15 @@ public void QueryReturnsWithZ() public void ParsesVariable() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation($arg: Offset!) { test(arg: $arg) }") - .SetVariableValues(new Dictionary { {"arg", "+02" }, }) - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation($arg: Offset!) { test(arg: $arg) }") + .SetVariableValues( + new Dictionary + { + { "arg", "+02" }, + }) + .Build()); Assert.Equal("+03:05", result.ExpectOperationResult().Data!["test"]); } @@ -67,10 +72,15 @@ public void ParsesVariable() public void ParsesVariableWithMinutes() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation($arg: Offset!) { test(arg: $arg) }") - .SetVariableValues(new Dictionary { {"arg", "+02:35" }, }) - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation($arg: Offset!) { test(arg: $arg) }") + .SetVariableValues( + new Dictionary + { + { "arg", "+02:35" }, + }) + .Build()); Assert.Equal("+03:40", result.ExpectOperationResult().Data!["test"]); } @@ -78,10 +88,15 @@ public void ParsesVariableWithMinutes() public void DoesntParseAnIncorrectVariable() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation($arg: Offset!) { test(arg: $arg) }") - .SetVariableValues(new Dictionary { {"arg", "18:30:13+02" }, }) - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation($arg: Offset!) { test(arg: $arg) }") + .SetVariableValues( + new Dictionary + { + { "arg", "18:30:13+02" }, + }) + .Build()); Assert.Null(result.ExpectOperationResult().Data); Assert.Single(result.ExpectOperationResult().Errors!); } @@ -90,9 +105,10 @@ public void DoesntParseAnIncorrectVariable() public void ParsesLiteral() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation { test(arg: \"+02\") }") - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation { test(arg: \"+02\") }") + .Build()); Assert.Equal("+03:05", result.ExpectOperationResult().Data!["test"]); } @@ -100,9 +116,10 @@ public void ParsesLiteral() public void ParsesLiteralWithMinutes() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation { test(arg: \"+02:35\") }") - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation { test(arg: \"+02:35\") }") + .Build()); Assert.Equal("+03:40", result.ExpectOperationResult().Data!["test"]); } @@ -110,9 +127,10 @@ public void ParsesLiteralWithMinutes() public void ParsesLiteralWithZ() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation { test(arg: \"Z\") }") - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation { test(arg: \"Z\") }") + .Build()); Assert.Equal("+01:05", result.ExpectOperationResult().Data!["test"]); } @@ -120,9 +138,10 @@ public void ParsesLiteralWithZ() public void DoesntParseIncorrectLiteral() { var result = _testExecutor - .Execute(OperationRequestBuilder.New() - .SetDocument("mutation { test(arg: \"18:30:13+02\") }") - .Build()); + .Execute( + OperationRequestBuilder.New() + .SetDocument("mutation { test(arg: \"18:30:13+02\") }") + .Build()); Assert.Null(result.ExpectOperationResult().Data); Assert.Single(result.ExpectOperationResult().Errors!); Assert.Null(result.ExpectOperationResult().Errors![0].Code); @@ -137,40 +156,40 @@ public void PatternEmptyThrowSchemaException() static object Call() => new OffsetType([]); Assert.Throws(Call); } - } - [Fact] - public void OffsetType_DescriptionKnownPatterns_MatchesSnapshot() - { - var offsetType = new OffsetType( - OffsetPattern.GeneralInvariant, - OffsetPattern.GeneralInvariantWithZ); - - offsetType.Description.MatchInlineSnapshot( - """ - An offset from UTC in seconds. - A positive value means that the local time is ahead of UTC (e.g. for Europe); a negative value means that the local time is behind UTC (e.g. for America). - - Allowed patterns: - - `±hh:mm:ss` - - `Z` - - Examples: - - `+02:30:00` - - `Z` - """); - } + [Fact] + public void OffsetType_DescriptionKnownPatterns_MatchesSnapshot() + { + var offsetType = new OffsetType( + OffsetPattern.GeneralInvariant, + OffsetPattern.GeneralInvariantWithZ); + + offsetType.Description.MatchInlineSnapshot( + """ + An offset from UTC in seconds. + A positive value means that the local time is ahead of UTC (e.g. for Europe); a negative value means that the local time is behind UTC (e.g. for America). + + Allowed patterns: + - `±hh:mm:ss` + - `Z` + + Examples: + - `+02:30:00` + - `Z` + """); + } - [Fact] - public void OffsetType_DescriptionUnknownPatterns_MatchesSnapshot() - { - var offsetType = new OffsetType( - OffsetPattern.Create("mm", CultureInfo.InvariantCulture)); - - offsetType.Description.MatchInlineSnapshot( - """ - An offset from UTC in seconds. - A positive value means that the local time is ahead of UTC (e.g. for Europe); a negative value means that the local time is behind UTC (e.g. for America). - """); + [Fact] + public void OffsetType_DescriptionUnknownPatterns_MatchesSnapshot() + { + var offsetType = new OffsetType( + OffsetPattern.Create("mm", CultureInfo.InvariantCulture)); + + offsetType.Description.MatchInlineSnapshot( + """ + An offset from UTC in seconds. + A positive value means that the local time is ahead of UTC (e.g. for Europe); a negative value means that the local time is behind UTC (e.g. for America). + """); + } } } diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/ZonedDateTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/ZonedDateTimeTypeTests.cs index 4ed8ddb89fb..9d9d682030a 100644 --- a/src/HotChocolate/Core/test/Types.NodaTime.Tests/ZonedDateTimeTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/ZonedDateTimeTypeTests.cs @@ -86,10 +86,7 @@ public void ParsesVariableWithUtc() OperationRequestBuilder.New() .SetDocument("mutation($arg: ZonedDateTime!) { test(arg: $arg) }") .SetVariableValues( - new Dictionary - { - { "arg", "2020-12-31T19:30:13 UTC +00" } - }) + new Dictionary { { "arg", "2020-12-31T19:30:13 UTC +00" } }) .Build()); Assert.Equal( "2020-12-31T19:40:13 UTC +00", @@ -159,45 +156,45 @@ public void PatternEmpty_ThrowSchemaException() static object Call() => new ZonedDateTimeType([]); Assert.Throws(Call); } - } - [Fact] - public void ZonedDateTimeType_DescriptionKnownPatterns_MatchesSnapshot() - { - var zonedDateTimeType = new ZonedDateTimeType( - ZonedDateTimePattern.GeneralFormatOnlyIso, - ZonedDateTimePattern.ExtendedFormatOnlyIso); - - zonedDateTimeType.Description.MatchInlineSnapshot( - """ - A LocalDateTime in a specific time zone and with a particular offset to distinguish between otherwise-ambiguous instants. - A ZonedDateTime is global, in that it maps to a single Instant. - - Allowed patterns: - - `YYYY-MM-DDThh:mm:ss z (±hh:mm)` - - `YYYY-MM-DDThh:mm:ss.sssssssss z (±hh:mm)` - - Examples: - - `2000-01-01T20:00:00 Europe/Zurich (+01)` - - `2000-01-01T20:00:00.999999999 Europe/Zurich (+01)` - """); - } + [Fact] + public void ZonedDateTimeType_DescriptionKnownPatterns_MatchesSnapshot() + { + var zonedDateTimeType = new ZonedDateTimeType( + ZonedDateTimePattern.GeneralFormatOnlyIso, + ZonedDateTimePattern.ExtendedFormatOnlyIso); + + zonedDateTimeType.Description.MatchInlineSnapshot( + """ + A LocalDateTime in a specific time zone and with a particular offset to distinguish between otherwise-ambiguous instants. + A ZonedDateTime is global, in that it maps to a single Instant. + + Allowed patterns: + - `YYYY-MM-DDThh:mm:ss z (±hh:mm)` + - `YYYY-MM-DDThh:mm:ss.sssssssss z (±hh:mm)` + + Examples: + - `2000-01-01T20:00:00 Europe/Zurich (+01)` + - `2000-01-01T20:00:00.999999999 Europe/Zurich (+01)` + """); + } - [Fact] - public void ZonedDateTimeType_DescriptionUnknownPatterns_MatchesSnapshot() - { - var zonedDateTimeType = new ZonedDateTimeType( - ZonedDateTimePattern.Create( - "MM", - CultureInfo.InvariantCulture, - null, - null, - new ZonedDateTime())); - - zonedDateTimeType.Description.MatchInlineSnapshot( - """ - A LocalDateTime in a specific time zone and with a particular offset to distinguish between otherwise-ambiguous instants. - A ZonedDateTime is global, in that it maps to a single Instant. - """); + [Fact] + public void ZonedDateTimeType_DescriptionUnknownPatterns_MatchesSnapshot() + { + var zonedDateTimeType = new ZonedDateTimeType( + ZonedDateTimePattern.Create( + "MM", + CultureInfo.InvariantCulture, + null, + null, + new ZonedDateTime())); + + zonedDateTimeType.Description.MatchInlineSnapshot( + """ + A LocalDateTime in a specific time zone and with a particular offset to distinguish between otherwise-ambiguous instants. + A ZonedDateTime is global, in that it maps to a single Instant. + """); + } } } diff --git a/src/HotChocolate/PersistedOperations/test/PersistedOperations.FileSystem.Tests/HotChocolate.PersistedOperations.FileSystem.Tests.csproj b/src/HotChocolate/PersistedOperations/test/PersistedOperations.FileSystem.Tests/HotChocolate.PersistedOperations.FileSystem.Tests.csproj index 3ed195f80bd..0bcc48249db 100644 --- a/src/HotChocolate/PersistedOperations/test/PersistedOperations.FileSystem.Tests/HotChocolate.PersistedOperations.FileSystem.Tests.csproj +++ b/src/HotChocolate/PersistedOperations/test/PersistedOperations.FileSystem.Tests/HotChocolate.PersistedOperations.FileSystem.Tests.csproj @@ -6,6 +6,7 @@ + diff --git a/src/HotChocolate/PersistedOperations/test/PersistedOperations.FileSystem.Tests/IntegrationTests.cs b/src/HotChocolate/PersistedOperations/test/PersistedOperations.FileSystem.Tests/IntegrationTests.cs index ee985f04f26..018a3392f89 100644 --- a/src/HotChocolate/PersistedOperations/test/PersistedOperations.FileSystem.Tests/IntegrationTests.cs +++ b/src/HotChocolate/PersistedOperations/test/PersistedOperations.FileSystem.Tests/IntegrationTests.cs @@ -1,7 +1,7 @@ +using CookieCrumble; using Microsoft.Extensions.DependencyInjection; using HotChocolate.Types; using HotChocolate.Execution; -using Snapshooter.Xunit; using HotChocolate.Language; using IO = System.IO; From 57dbe979e2e4313b994308a1fbbc5f523cfc3921 Mon Sep 17 00:00:00 2001 From: Glen Date: Wed, 6 Nov 2024 13:22:05 +0200 Subject: [PATCH 092/154] Used pure resolver for errors field in custom mutation payloads (#7691) --- .../MutationConventionTypeInterceptor.cs | 4 ++-- .../AnnotationBasedMutations.cs | 24 +++++++++++++++++++ ...utation_Override_Payload_WithError.graphql | 23 ++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 src/HotChocolate/Core/test/Types.Mutations.Tests/__snapshots__/AnnotationBasedMutations.SimpleMutation_Override_Payload_WithError.graphql diff --git a/src/HotChocolate/Core/src/Types.Mutations/MutationConventionTypeInterceptor.cs b/src/HotChocolate/Core/src/Types.Mutations/MutationConventionTypeInterceptor.cs index 45a6b426813..8dd76a0d80f 100644 --- a/src/HotChocolate/Core/src/Types.Mutations/MutationConventionTypeInterceptor.cs +++ b/src/HotChocolate/Core/src/Types.Mutations/MutationConventionTypeInterceptor.cs @@ -395,10 +395,10 @@ private void TryApplyPayloadConvention( new ObjectFieldDefinition( options.PayloadErrorsFieldName, type: errorListTypeRef, - resolver: ctx => + pureResolver: ctx => { ctx.ScopedContextData.TryGetValue(Errors, out var errors); - return new ValueTask(errors); + return errors; })); // collect error factories for middleware diff --git a/src/HotChocolate/Core/test/Types.Mutations.Tests/AnnotationBasedMutations.cs b/src/HotChocolate/Core/test/Types.Mutations.Tests/AnnotationBasedMutations.cs index b0c457a595f..033b3f93383 100644 --- a/src/HotChocolate/Core/test/Types.Mutations.Tests/AnnotationBasedMutations.cs +++ b/src/HotChocolate/Core/test/Types.Mutations.Tests/AnnotationBasedMutations.cs @@ -436,6 +436,21 @@ public async Task SimpleMutation_Override_Payload() schema.MatchSnapshot(); } + [Fact] + public async Task SimpleMutation_Override_Payload_WithError() + { + var schema = + await new ServiceCollection() + .AddGraphQL() + .AddCostAnalyzer() + .AddMutationType() + .AddMutationConventions(true) + .ModifyOptions(o => o.StrictValidation = false) + .BuildSchemaAsync(); + + schema.MatchSnapshot(); + } + [Fact] public async Task SimpleMutation_Override_Input() { @@ -1356,6 +1371,15 @@ public DoSomethingPayload DoSomething(string something) } } + public class SimpleMutationPayloadOverrideWithError + { + [Error(typeof(CustomException))] + public DoSomethingPayload DoSomething() + { + return new DoSomethingPayload(); + } + } + public class DoSomethingPayload { public string MyResult1 { get; set; } = default!; diff --git a/src/HotChocolate/Core/test/Types.Mutations.Tests/__snapshots__/AnnotationBasedMutations.SimpleMutation_Override_Payload_WithError.graphql b/src/HotChocolate/Core/test/Types.Mutations.Tests/__snapshots__/AnnotationBasedMutations.SimpleMutation_Override_Payload_WithError.graphql new file mode 100644 index 00000000000..7ac4d4a2028 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Mutations.Tests/__snapshots__/AnnotationBasedMutations.SimpleMutation_Override_Payload_WithError.graphql @@ -0,0 +1,23 @@ +schema { + mutation: SimpleMutationPayloadOverrideWithError +} + +interface Error { + message: String! +} + +type CustomError implements Error { + message: String! +} + +type DoSomethingPayload { + myResult1: String + myResult2: String + errors: [DoSomethingError!] +} + +type SimpleMutationPayloadOverrideWithError { + doSomething: DoSomethingPayload! +} + +union DoSomethingError = CustomError From dd989b41bbfec129ea5c842712bfd6d8a54571e4 Mon Sep 17 00:00:00 2001 From: Glen Date: Wed, 6 Nov 2024 13:40:41 +0200 Subject: [PATCH 093/154] Updated MongoDB Driver to 3.0.0 (#7684) --- src/Directory.Packages.props | 4 +- .../MongoDbAggregateFluentTests.cs | 2 + .../MongoDbCollectionTests.cs | 2 + .../MongoDbFilterCombinatorTests.cs | 2 + .../MongoDbFilterVisitorBooleanTests.cs | 3 ++ .../MongoDbFilterVisitorComparableTests.cs | 3 ++ .../MongoDbFilterVisitorDateOnlyTests.cs | 37 +------------------ .../MongoDbFilterVisitorEnumTests.cs | 3 ++ .../MongoDbFilterVisitorListTests.cs | 4 ++ .../MongoDbFilterVisitorObjectIdTests.cs | 2 + .../MongoDbFilterVisitorObjectTests.cs | 5 +++ .../MongoDbFilterVisitorStringTests.cs | 3 ++ .../MongoDbFilterVisitorTimeOnlyTests.cs | 36 +----------------- .../MongoDbFindFluentTests.cs | 3 ++ ...Create_ShortEqual_Expression_DateTime.snap | 4 +- ...ortEqual_Expression_DateTime_Nullable.snap | 4 +- ...Tests.Create_DateOnlyEqual_Expression.snap | 4 +- ...ts.Create_DateOnlyNotEqual_Expression.snap | 4 +- ...eate_NullableDateOnlyEqual_Expression.snap | 4 +- ...e_NullableDateOnlyNotEqual_Expression.snap | 4 +- ...Tests.Create_ObjectIdEqual_Expression.snap | 4 +- ...bjectIdGreaterThanOrEquals_Expression.snap | 6 +-- ...Create_ObjectIdGreaterThan_Expression.snap | 6 +-- ...tIdTests.Create_ObjectIdIn_Expression.snap | 2 +- ..._ObjectIdLowerThanOrEquals_Expression.snap | 6 +-- ...s.Create_ObjectIdLowerThan_Expression.snap | 6 +-- ...ts.Create_ObjectIdNotEqual_Expression.snap | 4 +- ...ctIdNotGreaterThanOrEquals_Expression.snap | 6 +-- ...ate_ObjectIdNotGreaterThan_Expression.snap | 6 +-- ...Tests.Create_ObjectIdNotIn_Expression.snap | 2 +- ...jectIdNotLowerThanOrEquals_Expression.snap | 6 +-- ...reate_ObjectIdNotLowerThan_Expression.snap | 6 +-- ...eate_ObjectIdNullableEqual_Expression.snap | 4 +- ...ullableGreaterThanOrEquals_Expression.snap | 6 +-- ...bjectIdNullableGreaterThan_Expression.snap | 6 +-- ....Create_ObjectIdNullableIn_Expression.snap | 6 +-- ...dNullableLowerThanOrEquals_Expression.snap | 6 +-- ..._ObjectIdNullableLowerThan_Expression.snap | 6 +-- ...e_ObjectIdNullableNotEqual_Expression.snap | 4 +- ...ableNotGreaterThanOrEquals_Expression.snap | 6 +-- ...ctIdNullableNotGreaterThan_Expression.snap | 6 +-- ...eate_ObjectIdNullableNotIn_Expression.snap | 6 +-- ...llableNotLowerThanOrEquals_Expression.snap | 6 +-- ...jectIdNullableNotLowerThan_Expression.snap | 6 +-- ...ate_NullableStringContains_Expression.snap | 4 +- ...ate_NullableStringEndsWith_Expression.snap | 4 +- ...e_NullableStringNoContains_Expression.snap | 4 +- ..._NullableStringNotEndsWith_Expression.snap | 4 +- ...ullableStringNotStartsWith_Expression.snap | 4 +- ...e_NullableStringStartsWith_Expression.snap | 4 +- ...ests.Create_StringContains_Expression.snap | 4 +- ...ests.Create_StringEndsWith_Expression.snap | 4 +- ...ts.Create_StringNoContains_Expression.snap | 4 +- ...s.Create_StringNotEndsWith_Expression.snap | 4 +- ...Create_StringNotStartsWith_Expression.snap | 4 +- ...ts.Create_StringStartsWith_Expression.snap | 4 +- ...eate_NullableTimeOnlyEqual_Expression.snap | 4 +- ...e_NullableTimeOnlyNotEqual_Expression.snap | 4 +- ...Tests.Create_TimeOnlyEqual_Expression.snap | 4 +- ...ts.Create_TimeOnlyNotEqual_Expression.snap | 4 +- ...ndFluentTests.FindFluent_CombineQuery.snap | 4 +- ...MongoDbCursorPagingAggregateFluentTests.cs | 2 + .../MongoDbCursorPagingFindFluentTests.cs | 2 + .../MongoDbOffsetPagingAggregateTests.cs | 2 + .../MongoDbOffsetPagingFindFluentTests.cs | 2 + .../MongoDbProjectionObjectsTests.cs | 8 ++++ ...ongoDbProjectionVisitorIsProjectedTests.cs | 3 ++ .../MongoDbProjectionVisitorPagingTests.cs | 4 ++ .../MongoDbProjectionVisitorScalarTests.cs | 2 + .../MongoDbAggregateFluentTests.cs | 2 + .../MongoDbCollectionTests.cs | 2 + .../MongoDbFindFluentTests.cs | 3 ++ .../MongoDbSortVisitorBooleanTests.cs | 3 ++ .../MongoDbSortVisitorComparableTests.cs | 3 ++ .../MongoDbSortVisitorEnumTests.cs | 3 ++ .../MongoDbSortVisitorObjectTests.cs | 5 +++ .../MongoDbSortVisitorStringTests.cs | 3 ++ ...ndFluentTests.FindFluent_CombineQuery.snap | 4 +- ...hould_MatchSnapshot_When_BsonDocument.snap | 2 +- 79 files changed, 200 insertions(+), 184 deletions(-) diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index a96023bc781..d52fb6f5b92 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -35,7 +35,7 @@ - + @@ -50,7 +50,7 @@ - + diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbAggregateFluentTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbAggregateFluentTests.cs index 697f525b540..16268791557 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbAggregateFluentTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbAggregateFluentTests.cs @@ -103,6 +103,7 @@ await Snapshot public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); [BsonElement("renameTest")] @@ -112,6 +113,7 @@ public class Foo public class Bar { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public DateTimeOffset Baz { get; set; } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbCollectionTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbCollectionTests.cs index 6e788ce7885..7209ce41a58 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbCollectionTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbCollectionTests.cs @@ -103,6 +103,7 @@ await SnapshotExtensions.AddResult( public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); [BsonElement("renameTest")] @@ -112,6 +113,7 @@ public class Foo public class Bar { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public DateTimeOffset Baz { get; set; } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterCombinatorTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterCombinatorTests.cs index 6c6071113f1..d71b9d492b7 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterCombinatorTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterCombinatorTests.cs @@ -1,6 +1,7 @@ using CookieCrumble; using HotChocolate.Data.Filters; using HotChocolate.Execution; +using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using Squadron; @@ -42,6 +43,7 @@ await Snapshot.Create() public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public bool Bar { get; set; } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorBooleanTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorBooleanTests.cs index 0b0ca78fd1c..dddb7bd7b28 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorBooleanTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorBooleanTests.cs @@ -1,6 +1,7 @@ using CookieCrumble; using HotChocolate.Data.Filters; using HotChocolate.Execution; +using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using Squadron; @@ -147,6 +148,7 @@ await SnapshotExtensions.AddResult( public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public bool Bar { get; set; } @@ -155,6 +157,7 @@ public class Foo public class FooNullable { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public bool? Bar { get; set; } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorComparableTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorComparableTests.cs index 70dcb25f8e8..b3534a3a70c 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorComparableTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorComparableTests.cs @@ -2,6 +2,7 @@ using HotChocolate.Data.Filters; using HotChocolate.Execution; using HotChocolate.Types; +using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using Squadron; @@ -1011,6 +1012,7 @@ public void Create_Implicit_Operation_Normalized() public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public short BarShort { get; set; } @@ -1031,6 +1033,7 @@ public class Foo public class FooNullable { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public short? BarShort { get; set; } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorDateOnlyTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorDateOnlyTests.cs index 0b2364be017..c443f978d21 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorDateOnlyTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorDateOnlyTests.cs @@ -29,9 +29,6 @@ public class MongoDbFilterVisitorDateOnlyTests public MongoDbFilterVisitorDateOnlyTests(MongoResource resource) { Init(resource); - - // NOTE: At the time of coding, MongoDB C# Driver doesn't natively support DateOnly - BsonSerializer.RegisterSerializationProvider(new LocalDateOnlySerializationProvider()); } [Fact] @@ -153,6 +150,7 @@ await SnapshotExtensions.AddResult( public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public DateOnly Bar { get; set; } @@ -161,6 +159,7 @@ public class Foo public class FooNullable { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public DateOnly? Bar { get; set; } @@ -173,36 +172,4 @@ public class FooFilterType : FilterInputType public class FooNullableFilterType : FilterInputType { } - - internal class LocalDateOnlySerializationProvider : IBsonSerializationProvider - { - public IBsonSerializer? GetSerializer(Type type) - { - return type == typeof(DateOnly) ? new DateOnlySerializer() : null; - } - } - - internal class DateOnlySerializer : StructSerializerBase - { - private static readonly TimeOnly _zeroTimeComponent = new(); - - public override void Serialize( - BsonSerializationContext context, - BsonSerializationArgs args, - DateOnly value) - { - var dateTime = value.ToDateTime(_zeroTimeComponent, DateTimeKind.Utc); - var ticks = BsonUtils.ToMillisecondsSinceEpoch(dateTime); - context.Writer.WriteDateTime(ticks); - } - - public override DateOnly Deserialize( - BsonDeserializationContext context, - BsonDeserializationArgs args) - { - var ticks = context.Reader.ReadDateTime(); - var dateTime = BsonUtils.ToDateTimeFromMillisecondsSinceEpoch(ticks); - return new DateOnly(dateTime.Year, dateTime.Month, dateTime.Day); - } - } } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorEnumTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorEnumTests.cs index 29e457e2ffd..0fa21fc7d7b 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorEnumTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorEnumTests.cs @@ -1,6 +1,7 @@ using CookieCrumble; using HotChocolate.Data.Filters; using HotChocolate.Execution; +using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using Squadron; @@ -283,6 +284,7 @@ await SnapshotExtensions.AddResult( public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public FooEnum BarEnum { get; set; } @@ -291,6 +293,7 @@ public class Foo public class FooNullable { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public FooEnum? BarEnum { get; set; } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorListTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorListTests.cs index 4d1008f03a2..9750337b1bc 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorListTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorListTests.cs @@ -1,6 +1,7 @@ using CookieCrumble; using HotChocolate.Data.Filters; using HotChocolate.Execution; +using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using Squadron; @@ -411,6 +412,7 @@ await SnapshotExtensions.AddResult( public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public IEnumerable? FooNested { get; set; } @@ -419,6 +421,7 @@ public class Foo public class FooSimple { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public IEnumerable? Bar { get; set; } @@ -427,6 +430,7 @@ public class FooSimple public class FooNested { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public string? Bar { get; set; } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorObjectIdTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorObjectIdTests.cs index 9d6e4ad4184..2bdfe349d13 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorObjectIdTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorObjectIdTests.cs @@ -1002,6 +1002,7 @@ await SnapshotExtensions.AddResult( public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public ObjectId ObjectId { get; set; } @@ -1010,6 +1011,7 @@ public class Foo public class FooNullable { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public ObjectId? ObjectId { get; set; } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorObjectTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorObjectTests.cs index 434a0582b9d..1b3cf122d1e 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorObjectTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorObjectTests.cs @@ -1,6 +1,7 @@ using CookieCrumble; using HotChocolate.Data.Filters; using HotChocolate.Execution; +using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using Squadron; @@ -634,6 +635,7 @@ await SnapshotExtensions.AddResult( public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public short BarShort { get; set; } @@ -650,6 +652,7 @@ public class Foo public class FooNullable { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public short? BarShort { get; set; } @@ -666,6 +669,7 @@ public class FooNullable public class Bar { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public Foo Foo { get; set; } = null!; @@ -674,6 +678,7 @@ public class Bar public class BarNullable { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public FooNullable? Foo { get; set; } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorStringTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorStringTests.cs index c9ed3d2267c..39ed41b2b23 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorStringTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorStringTests.cs @@ -1,6 +1,7 @@ using CookieCrumble; using HotChocolate.Data.Filters; using HotChocolate.Execution; +using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using Squadron; @@ -655,6 +656,7 @@ await SnapshotExtensions.AddResult( public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public string Bar { get; set; } = null!; @@ -663,6 +665,7 @@ public class Foo public class FooNullable { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public string? Bar { get; set; } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorTimeOnlyTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorTimeOnlyTests.cs index 1bf0efa01e9..6b3e2a3f9b3 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorTimeOnlyTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorTimeOnlyTests.cs @@ -29,9 +29,6 @@ public class MongoDbFilterVisitorTimeOnlyTests public MongoDbFilterVisitorTimeOnlyTests(MongoResource resource) { Init(resource); - - // NOTE: At the time of coding, MongoDB C# Driver doesn't natively support TimeOnly - BsonSerializer.RegisterSerializationProvider(new LocalTimeOnlySerializationProvider()); } [Fact] @@ -149,6 +146,7 @@ await SnapshotExtensions.AddResult( public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public TimeOnly Bar { get; set; } @@ -157,6 +155,7 @@ public class Foo public class FooNullable { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public TimeOnly? Bar { get; set; } @@ -171,35 +170,4 @@ public class FooNullableFilterType : FilterInputType { } - - internal class LocalTimeOnlySerializationProvider : IBsonSerializationProvider - { - public IBsonSerializer? GetSerializer(Type type) - { - return type == typeof(TimeOnly) ? new TimeOnlySerializer() : null; - } - } - - internal class TimeOnlySerializer : StructSerializerBase - { - public override void Serialize( - BsonSerializationContext context, - BsonSerializationArgs args, - TimeOnly value) - { - var dateTime = default(DateTime).Add(value.ToTimeSpan()); - dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc); - var ticks = BsonUtils.ToMillisecondsSinceEpoch(dateTime); - context.Writer.WriteDateTime(ticks); - } - - public override TimeOnly Deserialize( - BsonDeserializationContext context, - BsonDeserializationArgs args) - { - var ticks = context.Reader.ReadDateTime(); - var dateTime = BsonUtils.ToDateTimeFromMillisecondsSinceEpoch(ticks); - return new TimeOnly(dateTime.Hour, dateTime.Minute, dateTime.Second); - } - } } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFindFluentTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFindFluentTests.cs index 5102d2f1228..de9787e0ded 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFindFluentTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFindFluentTests.cs @@ -142,6 +142,7 @@ await SnapshotExtensions.AddResult( public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); [BsonElement("renameTest")] @@ -151,6 +152,7 @@ public class Foo public class Bar { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public DateTimeOffset Baz { get; set; } @@ -159,6 +161,7 @@ public class Bar public class Baz { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public DateTimeOffset Bar { get; set; } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorComparableTests.Create_ShortEqual_Expression_DateTime.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorComparableTests.Create_ShortEqual_Expression_DateTime.snap index 6c4e920de46..03648b1060b 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorComparableTests.Create_ShortEqual_Expression_DateTime.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorComparableTests.Create_ShortEqual_Expression_DateTime.snap @@ -13,7 +13,7 @@ 12 Query: --------------- -find({ "BarDateTime" : { "$eq" : ISODate("2000-01-12T00:00:00Z") } }) +find({ "BarDateTime" : { "$eq" : { "$date" : "2000-01-12T00:00:00Z" } } }) --------------- 13 Result: @@ -31,7 +31,7 @@ find({ "BarDateTime" : { "$eq" : ISODate("2000-01-12T00:00:00Z") } }) 13 Query: --------------- -find({ "BarDateTime" : { "$eq" : ISODate("2000-01-12T00:00:00Z") } }) +find({ "BarDateTime" : { "$eq" : { "$date" : "2000-01-12T00:00:00Z" } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorComparableTests.Create_ShortEqual_Expression_DateTime_Nullable.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorComparableTests.Create_ShortEqual_Expression_DateTime_Nullable.snap index 3faa7791412..9cc210a004c 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorComparableTests.Create_ShortEqual_Expression_DateTime_Nullable.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorComparableTests.Create_ShortEqual_Expression_DateTime_Nullable.snap @@ -13,7 +13,7 @@ 12 Query: --------------- -find({ "BarDateTime" : { "$eq" : ISODate("2000-01-12T00:00:00Z") } }) +find({ "BarDateTime" : { "$eq" : { "$date" : "2000-01-12T00:00:00Z" } } }) --------------- 13 Result: @@ -31,7 +31,7 @@ find({ "BarDateTime" : { "$eq" : ISODate("2000-01-12T00:00:00Z") } }) 13 Query: --------------- -find({ "BarDateTime" : { "$eq" : ISODate("2000-01-12T00:00:00Z") } }) +find({ "BarDateTime" : { "$eq" : { "$date" : "2000-01-12T00:00:00Z" } } }) --------------- null Result: diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorDateOnlyTests.Create_DateOnlyEqual_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorDateOnlyTests.Create_DateOnlyEqual_Expression.snap index 6ef407df0aa..ddfc050008e 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorDateOnlyTests.Create_DateOnlyEqual_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorDateOnlyTests.Create_DateOnlyEqual_Expression.snap @@ -13,7 +13,7 @@ 2022-01-16 Query: --------------- -find({ "Bar" : { "$eq" : ISODate("2022-01-16T00:00:00Z") } }) +find({ "Bar" : { "$eq" : { "$date" : "2022-01-16T00:00:00Z" } } }) --------------- 2022-01-15 Result: @@ -31,5 +31,5 @@ find({ "Bar" : { "$eq" : ISODate("2022-01-16T00:00:00Z") } }) 2022-01-15 Query: --------------- -find({ "Bar" : { "$eq" : ISODate("2022-01-15T00:00:00Z") } }) +find({ "Bar" : { "$eq" : { "$date" : "2022-01-15T00:00:00Z" } } }) --------------- diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorDateOnlyTests.Create_DateOnlyNotEqual_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorDateOnlyTests.Create_DateOnlyNotEqual_Expression.snap index b6ae0d0ae80..56a1df2653a 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorDateOnlyTests.Create_DateOnlyNotEqual_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorDateOnlyTests.Create_DateOnlyNotEqual_Expression.snap @@ -13,7 +13,7 @@ 2022-01-16 Query: --------------- -find({ "Bar" : { "$ne" : ISODate("2022-01-15T00:00:00Z") } }) +find({ "Bar" : { "$ne" : { "$date" : "2022-01-15T00:00:00Z" } } }) --------------- 2022-01-15 Result: @@ -31,5 +31,5 @@ find({ "Bar" : { "$ne" : ISODate("2022-01-15T00:00:00Z") } }) 2022-01-15 Query: --------------- -find({ "Bar" : { "$ne" : ISODate("2022-01-16T00:00:00Z") } }) +find({ "Bar" : { "$ne" : { "$date" : "2022-01-16T00:00:00Z" } } }) --------------- diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorDateOnlyTests.Create_NullableDateOnlyEqual_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorDateOnlyTests.Create_NullableDateOnlyEqual_Expression.snap index aec0fa366ce..e03b502c55c 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorDateOnlyTests.Create_NullableDateOnlyEqual_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorDateOnlyTests.Create_NullableDateOnlyEqual_Expression.snap @@ -13,7 +13,7 @@ 2022-01-16 Query: --------------- -find({ "Bar" : { "$eq" : ISODate("2022-01-16T00:00:00Z") } }) +find({ "Bar" : { "$eq" : { "$date" : "2022-01-16T00:00:00Z" } } }) --------------- 2022-01-15 Result: @@ -31,7 +31,7 @@ find({ "Bar" : { "$eq" : ISODate("2022-01-16T00:00:00Z") } }) 2022-01-15 Query: --------------- -find({ "Bar" : { "$eq" : ISODate("2022-01-15T00:00:00Z") } }) +find({ "Bar" : { "$eq" : { "$date" : "2022-01-15T00:00:00Z" } } }) --------------- null Result: diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorDateOnlyTests.Create_NullableDateOnlyNotEqual_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorDateOnlyTests.Create_NullableDateOnlyNotEqual_Expression.snap index a5c0a40b564..9b01ad1c1e6 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorDateOnlyTests.Create_NullableDateOnlyNotEqual_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorDateOnlyTests.Create_NullableDateOnlyNotEqual_Expression.snap @@ -16,7 +16,7 @@ 2022-01-16 Query: --------------- -find({ "Bar" : { "$ne" : ISODate("2022-01-15T00:00:00Z") } }) +find({ "Bar" : { "$ne" : { "$date" : "2022-01-15T00:00:00Z" } } }) --------------- 2022-01-15 Result: @@ -37,7 +37,7 @@ find({ "Bar" : { "$ne" : ISODate("2022-01-15T00:00:00Z") } }) 2022-01-15 Query: --------------- -find({ "Bar" : { "$ne" : ISODate("2022-01-16T00:00:00Z") } }) +find({ "Bar" : { "$ne" : { "$date" : "2022-01-16T00:00:00Z" } } }) --------------- null Result: diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdEqual_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdEqual_Expression.snap index 153aea92bab..fda2c155676 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdEqual_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdEqual_Expression.snap @@ -13,7 +13,7 @@ 6124e80f3f5fc839830c1f69 Query: --------------- -find({ "ObjectId" : { "$eq" : ObjectId("6124e80f3f5fc839830c1f69") } }) +find({ "ObjectId" : { "$eq" : { "$oid" : "6124e80f3f5fc839830c1f69" } } }) --------------- 6124e80f3f5fc839830c1f6a Result: @@ -31,7 +31,7 @@ find({ "ObjectId" : { "$eq" : ObjectId("6124e80f3f5fc839830c1f69") } }) 6124e80f3f5fc839830c1f6a Query: --------------- -find({ "ObjectId" : { "$eq" : ObjectId("6124e80f3f5fc839830c1f6a") } }) +find({ "ObjectId" : { "$eq" : { "$oid" : "6124e80f3f5fc839830c1f6a" } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdGreaterThanOrEquals_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdGreaterThanOrEquals_Expression.snap index 36885bc080d..ac7fa43e507 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdGreaterThanOrEquals_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdGreaterThanOrEquals_Expression.snap @@ -19,7 +19,7 @@ 6124e80f3f5fc839830c1f69 Query: --------------- -find({ "ObjectId" : { "$gte" : ObjectId("6124e80f3f5fc839830c1f69") } }) +find({ "ObjectId" : { "$gte" : { "$oid" : "6124e80f3f5fc839830c1f69" } } }) --------------- 6124e80f3f5fc839830c1f6a Result: @@ -40,7 +40,7 @@ find({ "ObjectId" : { "$gte" : ObjectId("6124e80f3f5fc839830c1f69") } }) 6124e80f3f5fc839830c1f6a Query: --------------- -find({ "ObjectId" : { "$gte" : ObjectId("6124e80f3f5fc839830c1f6a") } }) +find({ "ObjectId" : { "$gte" : { "$oid" : "6124e80f3f5fc839830c1f6a" } } }) --------------- 6124e80f3f5fc839830c1f6b Result: @@ -58,7 +58,7 @@ find({ "ObjectId" : { "$gte" : ObjectId("6124e80f3f5fc839830c1f6a") } }) 6124e80f3f5fc839830c1f6b Query: --------------- -find({ "ObjectId" : { "$gte" : ObjectId("6124e80f3f5fc839830c1f6b") } }) +find({ "ObjectId" : { "$gte" : { "$oid" : "6124e80f3f5fc839830c1f6b" } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdGreaterThan_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdGreaterThan_Expression.snap index f8835555742..c5c9347fbdf 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdGreaterThan_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdGreaterThan_Expression.snap @@ -16,7 +16,7 @@ 6124e80f3f5fc839830c1f69 Query: --------------- -find({ "ObjectId" : { "$gt" : ObjectId("6124e80f3f5fc839830c1f69") } }) +find({ "ObjectId" : { "$gt" : { "$oid" : "6124e80f3f5fc839830c1f69" } } }) --------------- 6124e80f3f5fc839830c1f6a Result: @@ -34,7 +34,7 @@ find({ "ObjectId" : { "$gt" : ObjectId("6124e80f3f5fc839830c1f69") } }) 6124e80f3f5fc839830c1f6a Query: --------------- -find({ "ObjectId" : { "$gt" : ObjectId("6124e80f3f5fc839830c1f6a") } }) +find({ "ObjectId" : { "$gt" : { "$oid" : "6124e80f3f5fc839830c1f6a" } } }) --------------- 6124e80f3f5fc839830c1f6b Result: @@ -48,7 +48,7 @@ find({ "ObjectId" : { "$gt" : ObjectId("6124e80f3f5fc839830c1f6a") } }) 6124e80f3f5fc839830c1f6b Query: --------------- -find({ "ObjectId" : { "$gt" : ObjectId("6124e80f3f5fc839830c1f6b") } }) +find({ "ObjectId" : { "$gt" : { "$oid" : "6124e80f3f5fc839830c1f6b" } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdIn_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdIn_Expression.snap index 652c5971554..3acaffb5974 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdIn_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdIn_Expression.snap @@ -16,7 +16,7 @@ 6124e80f3f5fc839830c1f69and6124e80f3f5fc839830c1f6a Query: --------------- -find({ "ObjectId" : { "$in" : [ObjectId("6124e80f3f5fc839830c1f69"), ObjectId("6124e80f3f5fc839830c1f6a")] } }) +find({ "ObjectId" : { "$in" : [{ "$oid" : "6124e80f3f5fc839830c1f69" }, { "$oid" : "6124e80f3f5fc839830c1f6a" }] } }) --------------- band6124e80f3f5fc839830c1f6b diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdLowerThanOrEquals_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdLowerThanOrEquals_Expression.snap index 7c98fe9d46b..77fe3ab8e0c 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdLowerThanOrEquals_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdLowerThanOrEquals_Expression.snap @@ -13,7 +13,7 @@ 6124e80f3f5fc839830c1f69 Query: --------------- -find({ "ObjectId" : { "$lte" : ObjectId("6124e80f3f5fc839830c1f69") } }) +find({ "ObjectId" : { "$lte" : { "$oid" : "6124e80f3f5fc839830c1f69" } } }) --------------- 6124e80f3f5fc839830c1f6a Result: @@ -34,7 +34,7 @@ find({ "ObjectId" : { "$lte" : ObjectId("6124e80f3f5fc839830c1f69") } }) 6124e80f3f5fc839830c1f6a Query: --------------- -find({ "ObjectId" : { "$lte" : ObjectId("6124e80f3f5fc839830c1f6a") } }) +find({ "ObjectId" : { "$lte" : { "$oid" : "6124e80f3f5fc839830c1f6a" } } }) --------------- 6124e80f3f5fc839830c1f6b Result: @@ -58,7 +58,7 @@ find({ "ObjectId" : { "$lte" : ObjectId("6124e80f3f5fc839830c1f6a") } }) 6124e80f3f5fc839830c1f6b Query: --------------- -find({ "ObjectId" : { "$lte" : ObjectId("6124e80f3f5fc839830c1f6b") } }) +find({ "ObjectId" : { "$lte" : { "$oid" : "6124e80f3f5fc839830c1f6b" } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdLowerThan_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdLowerThan_Expression.snap index 933cf8a7e37..c42247eaaeb 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdLowerThan_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdLowerThan_Expression.snap @@ -9,7 +9,7 @@ 6124e80f3f5fc839830c1f69 Query: --------------- -find({ "ObjectId" : { "$lt" : ObjectId("6124e80f3f5fc839830c1f69") } }) +find({ "ObjectId" : { "$lt" : { "$oid" : "6124e80f3f5fc839830c1f69" } } }) --------------- 6124e80f3f5fc839830c1f6a Result: @@ -27,7 +27,7 @@ find({ "ObjectId" : { "$lt" : ObjectId("6124e80f3f5fc839830c1f69") } }) 6124e80f3f5fc839830c1f6a Query: --------------- -find({ "ObjectId" : { "$lt" : ObjectId("6124e80f3f5fc839830c1f6a") } }) +find({ "ObjectId" : { "$lt" : { "$oid" : "6124e80f3f5fc839830c1f6a" } } }) --------------- 6124e80f3f5fc839830c1f6b Result: @@ -48,7 +48,7 @@ find({ "ObjectId" : { "$lt" : ObjectId("6124e80f3f5fc839830c1f6a") } }) 6124e80f3f5fc839830c1f6b Query: --------------- -find({ "ObjectId" : { "$lt" : ObjectId("6124e80f3f5fc839830c1f6b") } }) +find({ "ObjectId" : { "$lt" : { "$oid" : "6124e80f3f5fc839830c1f6b" } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNotEqual_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNotEqual_Expression.snap index 19aa0bab58a..97056f91d1e 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNotEqual_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNotEqual_Expression.snap @@ -16,7 +16,7 @@ 6124e80f3f5fc839830c1f69 Query: --------------- -find({ "ObjectId" : { "$ne" : ObjectId("6124e80f3f5fc839830c1f69") } }) +find({ "ObjectId" : { "$ne" : { "$oid" : "6124e80f3f5fc839830c1f69" } } }) --------------- 6124e80f3f5fc839830c1f6a Result: @@ -37,7 +37,7 @@ find({ "ObjectId" : { "$ne" : ObjectId("6124e80f3f5fc839830c1f69") } }) 6124e80f3f5fc839830c1f6a Query: --------------- -find({ "ObjectId" : { "$ne" : ObjectId("6124e80f3f5fc839830c1f6a") } }) +find({ "ObjectId" : { "$ne" : { "$oid" : "6124e80f3f5fc839830c1f6a" } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNotGreaterThanOrEquals_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNotGreaterThanOrEquals_Expression.snap index 892dd1bb087..23fc2d5722c 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNotGreaterThanOrEquals_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNotGreaterThanOrEquals_Expression.snap @@ -9,7 +9,7 @@ 6124e80f3f5fc839830c1f69 Query: --------------- -find({ "ObjectId" : { "$not" : { "$gte" : ObjectId("6124e80f3f5fc839830c1f69") } } }) +find({ "ObjectId" : { "$not" : { "$gte" : { "$oid" : "6124e80f3f5fc839830c1f69" } } } }) --------------- 6124e80f3f5fc839830c1f6a Result: @@ -27,7 +27,7 @@ find({ "ObjectId" : { "$not" : { "$gte" : ObjectId("6124e80f3f5fc839830c1f69") } 6124e80f3f5fc839830c1f6a Query: --------------- -find({ "ObjectId" : { "$not" : { "$gte" : ObjectId("6124e80f3f5fc839830c1f6a") } } }) +find({ "ObjectId" : { "$not" : { "$gte" : { "$oid" : "6124e80f3f5fc839830c1f6a" } } } }) --------------- 6124e80f3f5fc839830c1f6b Result: @@ -48,7 +48,7 @@ find({ "ObjectId" : { "$not" : { "$gte" : ObjectId("6124e80f3f5fc839830c1f6a") } 6124e80f3f5fc839830c1f6b Query: --------------- -find({ "ObjectId" : { "$not" : { "$gte" : ObjectId("6124e80f3f5fc839830c1f6b") } } }) +find({ "ObjectId" : { "$not" : { "$gte" : { "$oid" : "6124e80f3f5fc839830c1f6b" } } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNotGreaterThan_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNotGreaterThan_Expression.snap index 5874f7cad92..c6f0601c816 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNotGreaterThan_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNotGreaterThan_Expression.snap @@ -13,7 +13,7 @@ 6124e80f3f5fc839830c1f69 Query: --------------- -find({ "ObjectId" : { "$not" : { "$gt" : ObjectId("6124e80f3f5fc839830c1f69") } } }) +find({ "ObjectId" : { "$not" : { "$gt" : { "$oid" : "6124e80f3f5fc839830c1f69" } } } }) --------------- 6124e80f3f5fc839830c1f6a Result: @@ -34,7 +34,7 @@ find({ "ObjectId" : { "$not" : { "$gt" : ObjectId("6124e80f3f5fc839830c1f69") } 6124e80f3f5fc839830c1f6a Query: --------------- -find({ "ObjectId" : { "$not" : { "$gt" : ObjectId("6124e80f3f5fc839830c1f6a") } } }) +find({ "ObjectId" : { "$not" : { "$gt" : { "$oid" : "6124e80f3f5fc839830c1f6a" } } } }) --------------- 6124e80f3f5fc839830c1f6b Result: @@ -58,7 +58,7 @@ find({ "ObjectId" : { "$not" : { "$gt" : ObjectId("6124e80f3f5fc839830c1f6a") } 6124e80f3f5fc839830c1f6b Query: --------------- -find({ "ObjectId" : { "$not" : { "$gt" : ObjectId("6124e80f3f5fc839830c1f6b") } } }) +find({ "ObjectId" : { "$not" : { "$gt" : { "$oid" : "6124e80f3f5fc839830c1f6b" } } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNotIn_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNotIn_Expression.snap index 2ff148e26be..9d92039a16a 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNotIn_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNotIn_Expression.snap @@ -13,7 +13,7 @@ 6124e80f3f5fc839830c1f69and6124e80f3f5fc839830c1f6a Query: --------------- -find({ "ObjectId" : { "$nin" : [ObjectId("6124e80f3f5fc839830c1f69"), ObjectId("6124e80f3f5fc839830c1f6a")] } }) +find({ "ObjectId" : { "$nin" : [{ "$oid" : "6124e80f3f5fc839830c1f69" }, { "$oid" : "6124e80f3f5fc839830c1f6a" }] } }) --------------- band6124e80f3f5fc839830c1f6b diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNotLowerThanOrEquals_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNotLowerThanOrEquals_Expression.snap index 65acaeabce5..eca158a25ac 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNotLowerThanOrEquals_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNotLowerThanOrEquals_Expression.snap @@ -16,7 +16,7 @@ 6124e80f3f5fc839830c1f69 Query: --------------- -find({ "ObjectId" : { "$not" : { "$lte" : ObjectId("6124e80f3f5fc839830c1f69") } } }) +find({ "ObjectId" : { "$not" : { "$lte" : { "$oid" : "6124e80f3f5fc839830c1f69" } } } }) --------------- 6124e80f3f5fc839830c1f6a Result: @@ -34,7 +34,7 @@ find({ "ObjectId" : { "$not" : { "$lte" : ObjectId("6124e80f3f5fc839830c1f69") } 6124e80f3f5fc839830c1f6a Query: --------------- -find({ "ObjectId" : { "$not" : { "$lte" : ObjectId("6124e80f3f5fc839830c1f6a") } } }) +find({ "ObjectId" : { "$not" : { "$lte" : { "$oid" : "6124e80f3f5fc839830c1f6a" } } } }) --------------- 6124e80f3f5fc839830c1f6b Result: @@ -48,7 +48,7 @@ find({ "ObjectId" : { "$not" : { "$lte" : ObjectId("6124e80f3f5fc839830c1f6a") } 6124e80f3f5fc839830c1f6b Query: --------------- -find({ "ObjectId" : { "$not" : { "$lte" : ObjectId("6124e80f3f5fc839830c1f6b") } } }) +find({ "ObjectId" : { "$not" : { "$lte" : { "$oid" : "6124e80f3f5fc839830c1f6b" } } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNotLowerThan_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNotLowerThan_Expression.snap index 94bcf9cfa27..eb8901a3e62 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNotLowerThan_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNotLowerThan_Expression.snap @@ -19,7 +19,7 @@ 6124e80f3f5fc839830c1f69 Query: --------------- -find({ "ObjectId" : { "$not" : { "$lt" : ObjectId("6124e80f3f5fc839830c1f69") } } }) +find({ "ObjectId" : { "$not" : { "$lt" : { "$oid" : "6124e80f3f5fc839830c1f69" } } } }) --------------- 6124e80f3f5fc839830c1f6a Result: @@ -40,7 +40,7 @@ find({ "ObjectId" : { "$not" : { "$lt" : ObjectId("6124e80f3f5fc839830c1f69") } 6124e80f3f5fc839830c1f6a Query: --------------- -find({ "ObjectId" : { "$not" : { "$lt" : ObjectId("6124e80f3f5fc839830c1f6a") } } }) +find({ "ObjectId" : { "$not" : { "$lt" : { "$oid" : "6124e80f3f5fc839830c1f6a" } } } }) --------------- 6124e80f3f5fc839830c1f6b Result: @@ -58,7 +58,7 @@ find({ "ObjectId" : { "$not" : { "$lt" : ObjectId("6124e80f3f5fc839830c1f6a") } 6124e80f3f5fc839830c1f6b Query: --------------- -find({ "ObjectId" : { "$not" : { "$lt" : ObjectId("6124e80f3f5fc839830c1f6b") } } }) +find({ "ObjectId" : { "$not" : { "$lt" : { "$oid" : "6124e80f3f5fc839830c1f6b" } } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableEqual_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableEqual_Expression.snap index 8dd23e85688..edddb5db0f8 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableEqual_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableEqual_Expression.snap @@ -13,7 +13,7 @@ a Result: a Query: --------------- -find({ "ObjectId" : { "$eq" : ObjectId("6124e80f3f5fc839830c1f69") } }) +find({ "ObjectId" : { "$eq" : { "$oid" : "6124e80f3f5fc839830c1f69" } } }) --------------- 6124e80f3f5fc839830c1f6a Result: @@ -31,7 +31,7 @@ find({ "ObjectId" : { "$eq" : ObjectId("6124e80f3f5fc839830c1f69") } }) 6124e80f3f5fc839830c1f6a Query: --------------- -find({ "ObjectId" : { "$eq" : ObjectId("6124e80f3f5fc839830c1f6a") } }) +find({ "ObjectId" : { "$eq" : { "$oid" : "6124e80f3f5fc839830c1f6a" } } }) --------------- null Result: diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableGreaterThanOrEquals_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableGreaterThanOrEquals_Expression.snap index 469e7b5ef2e..f8b7ccb0cae 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableGreaterThanOrEquals_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableGreaterThanOrEquals_Expression.snap @@ -19,7 +19,7 @@ a Result: a Query: --------------- -find({ "ObjectId" : { "$gte" : ObjectId("6124e80f3f5fc839830c1f69") } }) +find({ "ObjectId" : { "$gte" : { "$oid" : "6124e80f3f5fc839830c1f69" } } }) --------------- 6124e80f3f5fc839830c1f6a Result: @@ -40,7 +40,7 @@ find({ "ObjectId" : { "$gte" : ObjectId("6124e80f3f5fc839830c1f69") } }) 6124e80f3f5fc839830c1f6a Query: --------------- -find({ "ObjectId" : { "$gte" : ObjectId("6124e80f3f5fc839830c1f6a") } }) +find({ "ObjectId" : { "$gte" : { "$oid" : "6124e80f3f5fc839830c1f6a" } } }) --------------- 6124e80f3f5fc839830c1f6b Result: @@ -58,7 +58,7 @@ find({ "ObjectId" : { "$gte" : ObjectId("6124e80f3f5fc839830c1f6a") } }) 6124e80f3f5fc839830c1f6b Query: --------------- -find({ "ObjectId" : { "$gte" : ObjectId("6124e80f3f5fc839830c1f6b") } }) +find({ "ObjectId" : { "$gte" : { "$oid" : "6124e80f3f5fc839830c1f6b" } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableGreaterThan_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableGreaterThan_Expression.snap index 9b0192a8503..4680a3a0295 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableGreaterThan_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableGreaterThan_Expression.snap @@ -16,7 +16,7 @@ a Result: a Query: --------------- -find({ "ObjectId" : { "$gt" : ObjectId("6124e80f3f5fc839830c1f69") } }) +find({ "ObjectId" : { "$gt" : { "$oid" : "6124e80f3f5fc839830c1f69" } } }) --------------- 6124e80f3f5fc839830c1f6a Result: @@ -34,7 +34,7 @@ find({ "ObjectId" : { "$gt" : ObjectId("6124e80f3f5fc839830c1f69") } }) 6124e80f3f5fc839830c1f6a Query: --------------- -find({ "ObjectId" : { "$gt" : ObjectId("6124e80f3f5fc839830c1f6a") } }) +find({ "ObjectId" : { "$gt" : { "$oid" : "6124e80f3f5fc839830c1f6a" } } }) --------------- 6124e80f3f5fc839830c1f6b Result: @@ -48,7 +48,7 @@ find({ "ObjectId" : { "$gt" : ObjectId("6124e80f3f5fc839830c1f6a") } }) 6124e80f3f5fc839830c1f6b Query: --------------- -find({ "ObjectId" : { "$gt" : ObjectId("6124e80f3f5fc839830c1f6b") } }) +find({ "ObjectId" : { "$gt" : { "$oid" : "6124e80f3f5fc839830c1f6b" } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableIn_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableIn_Expression.snap index 3796b97002d..212b70eeebb 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableIn_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableIn_Expression.snap @@ -16,7 +16,7 @@ 6124e80f3f5fc839830c1f69and6124e80f3f5fc839830c1f6a Query: --------------- -find({ "ObjectId" : { "$in" : [ObjectId("6124e80f3f5fc839830c1f69"), ObjectId("6124e80f3f5fc839830c1f6a")] } }) +find({ "ObjectId" : { "$in" : [{ "$oid" : "6124e80f3f5fc839830c1f69" }, { "$oid" : "6124e80f3f5fc839830c1f6a" }] } }) --------------- band6124e80f3f5fc839830c1f6b Result: @@ -37,7 +37,7 @@ band6124e80f3f5fc839830c1f6b Result: band6124e80f3f5fc839830c1f6b Query: --------------- -find({ "ObjectId" : { "$in" : [ObjectId("6124e80f3f5fc839830c1f6a"), ObjectId("6124e80f3f5fc839830c1f6b")] } }) +find({ "ObjectId" : { "$in" : [{ "$oid" : "6124e80f3f5fc839830c1f6a" }, { "$oid" : "6124e80f3f5fc839830c1f6b" }] } }) --------------- bandNull Result: @@ -58,5 +58,5 @@ bandNull Result: bandNull Query: --------------- -find({ "ObjectId" : { "$in" : [ObjectId("6124e80f3f5fc839830c1f6a"), null] } }) +find({ "ObjectId" : { "$in" : [{ "$oid" : "6124e80f3f5fc839830c1f6a" }, null] } }) --------------- diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableLowerThanOrEquals_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableLowerThanOrEquals_Expression.snap index a47cfd337bd..f83ee701c0b 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableLowerThanOrEquals_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableLowerThanOrEquals_Expression.snap @@ -13,7 +13,7 @@ a Result: a Query: --------------- -find({ "ObjectId" : { "$lte" : ObjectId("6124e80f3f5fc839830c1f69") } }) +find({ "ObjectId" : { "$lte" : { "$oid" : "6124e80f3f5fc839830c1f69" } } }) --------------- 6124e80f3f5fc839830c1f6a Result: @@ -34,7 +34,7 @@ find({ "ObjectId" : { "$lte" : ObjectId("6124e80f3f5fc839830c1f69") } }) 6124e80f3f5fc839830c1f6a Query: --------------- -find({ "ObjectId" : { "$lte" : ObjectId("6124e80f3f5fc839830c1f6a") } }) +find({ "ObjectId" : { "$lte" : { "$oid" : "6124e80f3f5fc839830c1f6a" } } }) --------------- 6124e80f3f5fc839830c1f6b Result: @@ -58,7 +58,7 @@ find({ "ObjectId" : { "$lte" : ObjectId("6124e80f3f5fc839830c1f6a") } }) 6124e80f3f5fc839830c1f6b Query: --------------- -find({ "ObjectId" : { "$lte" : ObjectId("6124e80f3f5fc839830c1f6b") } }) +find({ "ObjectId" : { "$lte" : { "$oid" : "6124e80f3f5fc839830c1f6b" } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableLowerThan_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableLowerThan_Expression.snap index 80a7568c27f..63462078484 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableLowerThan_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableLowerThan_Expression.snap @@ -9,7 +9,7 @@ a Result: a Query: --------------- -find({ "ObjectId" : { "$lt" : ObjectId("6124e80f3f5fc839830c1f69") } }) +find({ "ObjectId" : { "$lt" : { "$oid" : "6124e80f3f5fc839830c1f69" } } }) --------------- 6124e80f3f5fc839830c1f6a Result: @@ -27,7 +27,7 @@ find({ "ObjectId" : { "$lt" : ObjectId("6124e80f3f5fc839830c1f69") } }) 6124e80f3f5fc839830c1f6a Query: --------------- -find({ "ObjectId" : { "$lt" : ObjectId("6124e80f3f5fc839830c1f6a") } }) +find({ "ObjectId" : { "$lt" : { "$oid" : "6124e80f3f5fc839830c1f6a" } } }) --------------- 6124e80f3f5fc839830c1f6b Result: @@ -48,7 +48,7 @@ find({ "ObjectId" : { "$lt" : ObjectId("6124e80f3f5fc839830c1f6a") } }) 6124e80f3f5fc839830c1f6b Query: --------------- -find({ "ObjectId" : { "$lt" : ObjectId("6124e80f3f5fc839830c1f6b") } }) +find({ "ObjectId" : { "$lt" : { "$oid" : "6124e80f3f5fc839830c1f6b" } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableNotEqual_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableNotEqual_Expression.snap index cbd5179f59a..83e3da1bf15 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableNotEqual_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableNotEqual_Expression.snap @@ -19,7 +19,7 @@ a Result: a Query: --------------- -find({ "ObjectId" : { "$ne" : ObjectId("6124e80f3f5fc839830c1f69") } }) +find({ "ObjectId" : { "$ne" : { "$oid" : "6124e80f3f5fc839830c1f69" } } }) --------------- 6124e80f3f5fc839830c1f6a Result: @@ -43,7 +43,7 @@ find({ "ObjectId" : { "$ne" : ObjectId("6124e80f3f5fc839830c1f69") } }) 6124e80f3f5fc839830c1f6a Query: --------------- -find({ "ObjectId" : { "$ne" : ObjectId("6124e80f3f5fc839830c1f6a") } }) +find({ "ObjectId" : { "$ne" : { "$oid" : "6124e80f3f5fc839830c1f6a" } } }) --------------- null Result: diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableNotGreaterThanOrEquals_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableNotGreaterThanOrEquals_Expression.snap index 1cfa059325c..9ef0540b561 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableNotGreaterThanOrEquals_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableNotGreaterThanOrEquals_Expression.snap @@ -13,7 +13,7 @@ a Result: a Query: --------------- -find({ "ObjectId" : { "$not" : { "$gte" : ObjectId("6124e80f3f5fc839830c1f69") } } }) +find({ "ObjectId" : { "$not" : { "$gte" : { "$oid" : "6124e80f3f5fc839830c1f69" } } } }) --------------- 6124e80f3f5fc839830c1f6a Result: @@ -34,7 +34,7 @@ find({ "ObjectId" : { "$not" : { "$gte" : ObjectId("6124e80f3f5fc839830c1f69") } 6124e80f3f5fc839830c1f6a Query: --------------- -find({ "ObjectId" : { "$not" : { "$gte" : ObjectId("6124e80f3f5fc839830c1f6a") } } }) +find({ "ObjectId" : { "$not" : { "$gte" : { "$oid" : "6124e80f3f5fc839830c1f6a" } } } }) --------------- 6124e80f3f5fc839830c1f6b Result: @@ -58,7 +58,7 @@ find({ "ObjectId" : { "$not" : { "$gte" : ObjectId("6124e80f3f5fc839830c1f6a") } 6124e80f3f5fc839830c1f6b Query: --------------- -find({ "ObjectId" : { "$not" : { "$gte" : ObjectId("6124e80f3f5fc839830c1f6b") } } }) +find({ "ObjectId" : { "$not" : { "$gte" : { "$oid" : "6124e80f3f5fc839830c1f6b" } } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableNotGreaterThan_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableNotGreaterThan_Expression.snap index 90a64e9fdc5..1e86c80379e 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableNotGreaterThan_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableNotGreaterThan_Expression.snap @@ -16,7 +16,7 @@ a Result: a Query: --------------- -find({ "ObjectId" : { "$not" : { "$gt" : ObjectId("6124e80f3f5fc839830c1f69") } } }) +find({ "ObjectId" : { "$not" : { "$gt" : { "$oid" : "6124e80f3f5fc839830c1f69" } } } }) --------------- 6124e80f3f5fc839830c1f6a Result: @@ -40,7 +40,7 @@ find({ "ObjectId" : { "$not" : { "$gt" : ObjectId("6124e80f3f5fc839830c1f69") } 6124e80f3f5fc839830c1f6a Query: --------------- -find({ "ObjectId" : { "$not" : { "$gt" : ObjectId("6124e80f3f5fc839830c1f6a") } } }) +find({ "ObjectId" : { "$not" : { "$gt" : { "$oid" : "6124e80f3f5fc839830c1f6a" } } } }) --------------- 6124e80f3f5fc839830c1f6b Result: @@ -67,7 +67,7 @@ find({ "ObjectId" : { "$not" : { "$gt" : ObjectId("6124e80f3f5fc839830c1f6a") } 6124e80f3f5fc839830c1f6b Query: --------------- -find({ "ObjectId" : { "$not" : { "$gt" : ObjectId("6124e80f3f5fc839830c1f6b") } } }) +find({ "ObjectId" : { "$not" : { "$gt" : { "$oid" : "6124e80f3f5fc839830c1f6b" } } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableNotIn_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableNotIn_Expression.snap index ec1662e48e8..bba8ad0b0a1 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableNotIn_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableNotIn_Expression.snap @@ -16,7 +16,7 @@ 6124e80f3f5fc839830c1f69and6124e80f3f5fc839830c1f6a Query: --------------- -find({ "ObjectId" : { "$nin" : [ObjectId("6124e80f3f5fc839830c1f69"), ObjectId("6124e80f3f5fc839830c1f6a")] } }) +find({ "ObjectId" : { "$nin" : [{ "$oid" : "6124e80f3f5fc839830c1f69" }, { "$oid" : "6124e80f3f5fc839830c1f6a" }] } }) --------------- band6124e80f3f5fc839830c1f6b Result: @@ -37,7 +37,7 @@ band6124e80f3f5fc839830c1f6b Result: band6124e80f3f5fc839830c1f6b Query: --------------- -find({ "ObjectId" : { "$nin" : [ObjectId("6124e80f3f5fc839830c1f6a"), ObjectId("6124e80f3f5fc839830c1f6b")] } }) +find({ "ObjectId" : { "$nin" : [{ "$oid" : "6124e80f3f5fc839830c1f6a" }, { "$oid" : "6124e80f3f5fc839830c1f6b" }] } }) --------------- bandNull Result: @@ -58,5 +58,5 @@ bandNull Result: bandNull Query: --------------- -find({ "ObjectId" : { "$nin" : [ObjectId("6124e80f3f5fc839830c1f6a"), null] } }) +find({ "ObjectId" : { "$nin" : [{ "$oid" : "6124e80f3f5fc839830c1f6a" }, null] } }) --------------- diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableNotLowerThanOrEquals_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableNotLowerThanOrEquals_Expression.snap index a13cae4d1b2..e2baf77f7ef 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableNotLowerThanOrEquals_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableNotLowerThanOrEquals_Expression.snap @@ -19,7 +19,7 @@ a Result: a Query: --------------- -find({ "ObjectId" : { "$not" : { "$lte" : ObjectId("6124e80f3f5fc839830c1f69") } } }) +find({ "ObjectId" : { "$not" : { "$lte" : { "$oid" : "6124e80f3f5fc839830c1f69" } } } }) --------------- 6124e80f3f5fc839830c1f6a Result: @@ -40,7 +40,7 @@ find({ "ObjectId" : { "$not" : { "$lte" : ObjectId("6124e80f3f5fc839830c1f69") } 6124e80f3f5fc839830c1f6a Query: --------------- -find({ "ObjectId" : { "$not" : { "$lte" : ObjectId("6124e80f3f5fc839830c1f6a") } } }) +find({ "ObjectId" : { "$not" : { "$lte" : { "$oid" : "6124e80f3f5fc839830c1f6a" } } } }) --------------- 6124e80f3f5fc839830c1f6b Result: @@ -58,7 +58,7 @@ find({ "ObjectId" : { "$not" : { "$lte" : ObjectId("6124e80f3f5fc839830c1f6a") } 6124e80f3f5fc839830c1f6b Query: --------------- -find({ "ObjectId" : { "$not" : { "$lte" : ObjectId("6124e80f3f5fc839830c1f6b") } } }) +find({ "ObjectId" : { "$not" : { "$lte" : { "$oid" : "6124e80f3f5fc839830c1f6b" } } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableNotLowerThan_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableNotLowerThan_Expression.snap index a3cd2c6cfd4..8d0eb73f8b9 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableNotLowerThan_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorObjectIdTests.Create_ObjectIdNullableNotLowerThan_Expression.snap @@ -22,7 +22,7 @@ a Result: a Query: --------------- -find({ "ObjectId" : { "$not" : { "$lt" : ObjectId("6124e80f3f5fc839830c1f69") } } }) +find({ "ObjectId" : { "$not" : { "$lt" : { "$oid" : "6124e80f3f5fc839830c1f69" } } } }) --------------- 6124e80f3f5fc839830c1f6a Result: @@ -46,7 +46,7 @@ find({ "ObjectId" : { "$not" : { "$lt" : ObjectId("6124e80f3f5fc839830c1f69") } 6124e80f3f5fc839830c1f6a Query: --------------- -find({ "ObjectId" : { "$not" : { "$lt" : ObjectId("6124e80f3f5fc839830c1f6a") } } }) +find({ "ObjectId" : { "$not" : { "$lt" : { "$oid" : "6124e80f3f5fc839830c1f6a" } } } }) --------------- 6124e80f3f5fc839830c1f6b Result: @@ -67,7 +67,7 @@ find({ "ObjectId" : { "$not" : { "$lt" : ObjectId("6124e80f3f5fc839830c1f6a") } 6124e80f3f5fc839830c1f6b Query: --------------- -find({ "ObjectId" : { "$not" : { "$lt" : ObjectId("6124e80f3f5fc839830c1f6b") } } }) +find({ "ObjectId" : { "$not" : { "$lt" : { "$oid" : "6124e80f3f5fc839830c1f6b" } } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_NullableStringContains_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_NullableStringContains_Expression.snap index 9bb53c898d6..b970c289968 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_NullableStringContains_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_NullableStringContains_Expression.snap @@ -13,7 +13,7 @@ a Result: a Query: --------------- -find({ "Bar" : { "$regex" : /a/ } }) +find({ "Bar" : { "$regex" : { "$regularExpression" : { "pattern" : "a", "options" : "" } } } }) --------------- b Result: @@ -31,7 +31,7 @@ b Result: b Query: --------------- -find({ "Bar" : { "$regex" : /b/ } }) +find({ "Bar" : { "$regex" : { "$regularExpression" : { "pattern" : "b", "options" : "" } } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_NullableStringEndsWith_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_NullableStringEndsWith_Expression.snap index 7992f412de3..50a8970cefc 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_NullableStringEndsWith_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_NullableStringEndsWith_Expression.snap @@ -13,7 +13,7 @@ atest Result: atest Query: --------------- -find({ "Bar" : { "$regex" : /atest$/ } }) +find({ "Bar" : { "$regex" : { "$regularExpression" : { "pattern" : "atest$", "options" : "" } } } }) --------------- btest Result: @@ -31,7 +31,7 @@ btest Result: btest Query: --------------- -find({ "Bar" : { "$regex" : /btest$/ } }) +find({ "Bar" : { "$regex" : { "$regularExpression" : { "pattern" : "btest$", "options" : "" } } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_NullableStringNoContains_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_NullableStringNoContains_Expression.snap index f04d5c094aa..fbe322c91e6 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_NullableStringNoContains_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_NullableStringNoContains_Expression.snap @@ -16,7 +16,7 @@ a Result: a Query: --------------- -find({ "Bar" : { "$not" : { "$regex" : /a/ } } }) +find({ "Bar" : { "$not" : { "$regex" : { "$regularExpression" : { "pattern" : "a", "options" : "" } } } } }) --------------- b Result: @@ -37,7 +37,7 @@ b Result: b Query: --------------- -find({ "Bar" : { "$not" : { "$regex" : /b/ } } }) +find({ "Bar" : { "$not" : { "$regex" : { "$regularExpression" : { "pattern" : "b", "options" : "" } } } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_NullableStringNotEndsWith_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_NullableStringNotEndsWith_Expression.snap index aab55baa2be..8530ebf4732 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_NullableStringNotEndsWith_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_NullableStringNotEndsWith_Expression.snap @@ -16,7 +16,7 @@ atest Result: atest Query: --------------- -find({ "Bar" : { "$not" : { "$regex" : /atest$/ } } }) +find({ "Bar" : { "$not" : { "$regex" : { "$regularExpression" : { "pattern" : "atest$", "options" : "" } } } } }) --------------- btest Result: @@ -37,7 +37,7 @@ btest Result: btest Query: --------------- -find({ "Bar" : { "$not" : { "$regex" : /btest$/ } } }) +find({ "Bar" : { "$not" : { "$regex" : { "$regularExpression" : { "pattern" : "btest$", "options" : "" } } } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_NullableStringNotStartsWith_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_NullableStringNotStartsWith_Expression.snap index 0ada3fa61df..d94dc0d63ee 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_NullableStringNotStartsWith_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_NullableStringNotStartsWith_Expression.snap @@ -16,7 +16,7 @@ testa Result: testa Query: --------------- -find({ "Bar" : { "$not" : { "$regex" : /^testa/ } } }) +find({ "Bar" : { "$not" : { "$regex" : { "$regularExpression" : { "pattern" : "^testa", "options" : "" } } } } }) --------------- testb Result: @@ -37,7 +37,7 @@ testb Result: testb Query: --------------- -find({ "Bar" : { "$not" : { "$regex" : /^testb/ } } }) +find({ "Bar" : { "$not" : { "$regex" : { "$regularExpression" : { "pattern" : "^testb", "options" : "" } } } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_NullableStringStartsWith_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_NullableStringStartsWith_Expression.snap index e597916de58..e3065608b4e 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_NullableStringStartsWith_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_NullableStringStartsWith_Expression.snap @@ -13,7 +13,7 @@ testa Result: testa Query: --------------- -find({ "Bar" : { "$regex" : /^testa/ } }) +find({ "Bar" : { "$regex" : { "$regularExpression" : { "pattern" : "^testa", "options" : "" } } } }) --------------- testb Result: @@ -31,7 +31,7 @@ testb Result: testb Query: --------------- -find({ "Bar" : { "$regex" : /^testb/ } }) +find({ "Bar" : { "$regex" : { "$regularExpression" : { "pattern" : "^testb", "options" : "" } } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_StringContains_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_StringContains_Expression.snap index 9bb53c898d6..b970c289968 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_StringContains_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_StringContains_Expression.snap @@ -13,7 +13,7 @@ a Result: a Query: --------------- -find({ "Bar" : { "$regex" : /a/ } }) +find({ "Bar" : { "$regex" : { "$regularExpression" : { "pattern" : "a", "options" : "" } } } }) --------------- b Result: @@ -31,7 +31,7 @@ b Result: b Query: --------------- -find({ "Bar" : { "$regex" : /b/ } }) +find({ "Bar" : { "$regex" : { "$regularExpression" : { "pattern" : "b", "options" : "" } } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_StringEndsWith_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_StringEndsWith_Expression.snap index 7992f412de3..50a8970cefc 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_StringEndsWith_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_StringEndsWith_Expression.snap @@ -13,7 +13,7 @@ atest Result: atest Query: --------------- -find({ "Bar" : { "$regex" : /atest$/ } }) +find({ "Bar" : { "$regex" : { "$regularExpression" : { "pattern" : "atest$", "options" : "" } } } }) --------------- btest Result: @@ -31,7 +31,7 @@ btest Result: btest Query: --------------- -find({ "Bar" : { "$regex" : /btest$/ } }) +find({ "Bar" : { "$regex" : { "$regularExpression" : { "pattern" : "btest$", "options" : "" } } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_StringNoContains_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_StringNoContains_Expression.snap index 0e139010acf..0fe3a7a01fe 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_StringNoContains_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_StringNoContains_Expression.snap @@ -13,7 +13,7 @@ a Result: a Query: --------------- -find({ "Bar" : { "$not" : { "$regex" : /a/ } } }) +find({ "Bar" : { "$not" : { "$regex" : { "$regularExpression" : { "pattern" : "a", "options" : "" } } } } }) --------------- b Result: @@ -31,7 +31,7 @@ b Result: b Query: --------------- -find({ "Bar" : { "$not" : { "$regex" : /b/ } } }) +find({ "Bar" : { "$not" : { "$regex" : { "$regularExpression" : { "pattern" : "b", "options" : "" } } } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_StringNotEndsWith_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_StringNotEndsWith_Expression.snap index 6826b14a8f7..c8a138b9a9b 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_StringNotEndsWith_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_StringNotEndsWith_Expression.snap @@ -13,7 +13,7 @@ atest Result: atest Query: --------------- -find({ "Bar" : { "$not" : { "$regex" : /atest$/ } } }) +find({ "Bar" : { "$not" : { "$regex" : { "$regularExpression" : { "pattern" : "atest$", "options" : "" } } } } }) --------------- btest Result: @@ -31,7 +31,7 @@ btest Result: btest Query: --------------- -find({ "Bar" : { "$not" : { "$regex" : /btest$/ } } }) +find({ "Bar" : { "$not" : { "$regex" : { "$regularExpression" : { "pattern" : "btest$", "options" : "" } } } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_StringNotStartsWith_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_StringNotStartsWith_Expression.snap index 47d192c22c1..b0350061361 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_StringNotStartsWith_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_StringNotStartsWith_Expression.snap @@ -13,7 +13,7 @@ testa Result: testa Query: --------------- -find({ "Bar" : { "$not" : { "$regex" : /^testa/ } } }) +find({ "Bar" : { "$not" : { "$regex" : { "$regularExpression" : { "pattern" : "^testa", "options" : "" } } } } }) --------------- testb Result: @@ -31,7 +31,7 @@ testb Result: testb Query: --------------- -find({ "Bar" : { "$not" : { "$regex" : /^testb/ } } }) +find({ "Bar" : { "$not" : { "$regex" : { "$regularExpression" : { "pattern" : "^testb", "options" : "" } } } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_StringStartsWith_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_StringStartsWith_Expression.snap index e597916de58..e3065608b4e 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_StringStartsWith_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorStringTests.Create_StringStartsWith_Expression.snap @@ -13,7 +13,7 @@ testa Result: testa Query: --------------- -find({ "Bar" : { "$regex" : /^testa/ } }) +find({ "Bar" : { "$regex" : { "$regularExpression" : { "pattern" : "^testa", "options" : "" } } } }) --------------- testb Result: @@ -31,7 +31,7 @@ testb Result: testb Query: --------------- -find({ "Bar" : { "$regex" : /^testb/ } }) +find({ "Bar" : { "$regex" : { "$regularExpression" : { "pattern" : "^testb", "options" : "" } } } }) --------------- null diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorTimeOnlyTests.Create_NullableTimeOnlyEqual_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorTimeOnlyTests.Create_NullableTimeOnlyEqual_Expression.snap index b00a703fcf7..2866c5a266f 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorTimeOnlyTests.Create_NullableTimeOnlyEqual_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorTimeOnlyTests.Create_NullableTimeOnlyEqual_Expression.snap @@ -13,7 +13,7 @@ 0630 Query: --------------- -find({ "Bar" : { "$eq" : ISODate("0001-01-01T06:30:00Z") } }) +find({ "Bar" : { "$eq" : 234000000000 } }) --------------- 1600 Result: @@ -31,7 +31,7 @@ find({ "Bar" : { "$eq" : ISODate("0001-01-01T06:30:00Z") } }) 1600 Query: --------------- -find({ "Bar" : { "$eq" : ISODate("0001-01-01T16:00:00Z") } }) +find({ "Bar" : { "$eq" : 576000000000 } }) --------------- null Result: diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorTimeOnlyTests.Create_NullableTimeOnlyNotEqual_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorTimeOnlyTests.Create_NullableTimeOnlyNotEqual_Expression.snap index 22b6f5a4041..ea7df939fac 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorTimeOnlyTests.Create_NullableTimeOnlyNotEqual_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorTimeOnlyTests.Create_NullableTimeOnlyNotEqual_Expression.snap @@ -16,7 +16,7 @@ 0630 Query: --------------- -find({ "Bar" : { "$ne" : ISODate("0001-01-01T06:30:00Z") } }) +find({ "Bar" : { "$ne" : 234000000000 } }) --------------- 1600 Result: @@ -37,7 +37,7 @@ find({ "Bar" : { "$ne" : ISODate("0001-01-01T06:30:00Z") } }) 1600 Query: --------------- -find({ "Bar" : { "$ne" : ISODate("0001-01-01T16:00:00Z") } }) +find({ "Bar" : { "$ne" : 576000000000 } }) --------------- null Result: diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorTimeOnlyTests.Create_TimeOnlyEqual_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorTimeOnlyTests.Create_TimeOnlyEqual_Expression.snap index ec9729325e5..96e15129a8c 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorTimeOnlyTests.Create_TimeOnlyEqual_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorTimeOnlyTests.Create_TimeOnlyEqual_Expression.snap @@ -13,7 +13,7 @@ 0630 Query: --------------- -find({ "Bar" : { "$eq" : ISODate("0001-01-01T06:30:00Z") } }) +find({ "Bar" : { "$eq" : 234000000000 } }) --------------- 1600 Result: @@ -31,5 +31,5 @@ find({ "Bar" : { "$eq" : ISODate("0001-01-01T06:30:00Z") } }) 1600 Query: --------------- -find({ "Bar" : { "$eq" : ISODate("0001-01-01T16:00:00Z") } }) +find({ "Bar" : { "$eq" : 576000000000 } }) --------------- diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorTimeOnlyTests.Create_TimeOnlyNotEqual_Expression.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorTimeOnlyTests.Create_TimeOnlyNotEqual_Expression.snap index 8aaa12dd095..9ce3f01296f 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorTimeOnlyTests.Create_TimeOnlyNotEqual_Expression.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFilterVisitorTimeOnlyTests.Create_TimeOnlyNotEqual_Expression.snap @@ -13,7 +13,7 @@ 0630 Query: --------------- -find({ "Bar" : { "$ne" : ISODate("0001-01-01T06:30:00Z") } }) +find({ "Bar" : { "$ne" : 234000000000 } }) --------------- 1600 Result: @@ -31,5 +31,5 @@ find({ "Bar" : { "$ne" : ISODate("0001-01-01T06:30:00Z") } }) 1600 Query: --------------- -find({ "Bar" : { "$ne" : ISODate("0001-01-01T16:00:00Z") } }) +find({ "Bar" : { "$ne" : 576000000000 } }) --------------- diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFindFluentTests.FindFluent_CombineQuery.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFindFluentTests.FindFluent_CombineQuery.snap index 26ea2c27b9b..9a629229477 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFindFluentTests.FindFluent_CombineQuery.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/__snapshots__/MongoDbFindFluentTests.FindFluent_CombineQuery.snap @@ -13,7 +13,7 @@ 2020-01-11 Query: --------------- -find({ "Bar" : { "$gt" : [NumberLong("630822816000000000"), 0], "$eq" : [NumberLong("637142976000000000"), 0] } }) +find({ "Bar" : { "$gt" : { "DateTime" : { "$date" : "2000-01-01T00:00:00Z" }, "Ticks" : 630822816000000000, "Offset" : 0 }, "$eq" : { "DateTime" : { "$date" : "2020-01-11T00:00:00Z" }, "Ticks" : 637142976000000000, "Offset" : 0 } } }) --------------- 2020-01-12 Result: @@ -31,5 +31,5 @@ find({ "Bar" : { "$gt" : [NumberLong("630822816000000000"), 0], "$eq" : [NumberL 2020-01-12 Query: --------------- -find({ "Bar" : { "$gt" : [NumberLong("630822816000000000"), 0], "$eq" : [NumberLong("637143840000000000"), 0] } }) +find({ "Bar" : { "$gt" : { "DateTime" : { "$date" : "2000-01-01T00:00:00Z" }, "Ticks" : 630822816000000000, "Offset" : 0 }, "$eq" : { "DateTime" : { "$date" : "2020-01-12T00:00:00Z" }, "Ticks" : 637143840000000000, "Offset" : 0 } } }) --------------- diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingAggregateFluentTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingAggregateFluentTests.cs index 98c00f72276..d3911d3e73e 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingAggregateFluentTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingAggregateFluentTests.cs @@ -5,6 +5,7 @@ using HotChocolate.Types; using HotChocolate.Types.Pagination; using Microsoft.Extensions.DependencyInjection; +using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Driver; using Squadron; @@ -217,6 +218,7 @@ await SnapshotExtensions.AddResult( public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public string Bar { get; set; } = default!; diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingFindFluentTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingFindFluentTests.cs index 9bd287cae54..0d972da2a6e 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingFindFluentTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingFindFluentTests.cs @@ -5,6 +5,7 @@ using HotChocolate.Types; using HotChocolate.Types.Pagination; using Microsoft.Extensions.DependencyInjection; +using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using Squadron; @@ -251,6 +252,7 @@ await Snapshot public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public string Bar { get; set; } = default!; diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbOffsetPagingAggregateTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbOffsetPagingAggregateTests.cs index 57637753e75..7f247837250 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbOffsetPagingAggregateTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbOffsetPagingAggregateTests.cs @@ -5,6 +5,7 @@ using HotChocolate.Types; using HotChocolate.Types.Pagination; using Microsoft.Extensions.DependencyInjection; +using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Driver; using Squadron; @@ -162,6 +163,7 @@ await SnapshotExtensions.AddResult( public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public string Bar { get; set; } = default!; diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbOffsetPagingFindFluentTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbOffsetPagingFindFluentTests.cs index 9933c6f18a3..43e5cffde10 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbOffsetPagingFindFluentTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbOffsetPagingFindFluentTests.cs @@ -5,6 +5,7 @@ using HotChocolate.Types; using HotChocolate.Types.Pagination; using Microsoft.Extensions.DependencyInjection; +using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using Squadron; @@ -161,6 +162,7 @@ await SnapshotExtensions.AddResult( public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public string Bar { get; set; } = default!; diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionObjectsTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionObjectsTests.cs index efadca85bea..9a6aee557aa 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionObjectsTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionObjectsTests.cs @@ -1,5 +1,6 @@ using CookieCrumble; using HotChocolate.Execution; +using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using Squadron; @@ -127,6 +128,7 @@ await SnapshotExtensions.AddResult( public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public short BarShort { get; set; } @@ -145,6 +147,7 @@ public class Foo public class FooDeep { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public short BarShort { get; set; } @@ -155,6 +158,7 @@ public class FooDeep public class FooNullable { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public short? BarShort { get; set; } @@ -173,6 +177,7 @@ public class FooNullable public class Bar { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public Foo Foo { get; set; } = default!; @@ -183,6 +188,7 @@ public class Bar public class BarDeep { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public FooDeep Foo { get; set; } = default!; @@ -191,6 +197,7 @@ public class BarDeep public class BarNullableDeep { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public FooDeep? Foo { get; set; } @@ -209,6 +216,7 @@ public BarNullable(int number, FooNullable? foo) } [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public FooNullable? Foo { get; set; } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorIsProjectedTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorIsProjectedTests.cs index 03bbdc6f6f1..d68495ed87f 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorIsProjectedTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorIsProjectedTests.cs @@ -1,5 +1,6 @@ using CookieCrumble; using HotChocolate.Execution; +using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using Squadron; @@ -113,6 +114,7 @@ await Snapshot public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); [IsProjected(true)] @@ -127,6 +129,7 @@ public class Foo public class Bar { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); [IsProjected(false)] diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorPagingTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorPagingTests.cs index 518ad962e2d..19e76984b9b 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorPagingTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorPagingTests.cs @@ -1,6 +1,7 @@ using CookieCrumble; using HotChocolate.Execution; using HotChocolate.Types; +using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using Squadron; @@ -415,6 +416,7 @@ await SnapshotExtensions.AddResult( public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public bool Bar { get; set; } @@ -432,6 +434,7 @@ public class Foo public class Bar { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public string? BarBaz { get; set; } @@ -442,6 +445,7 @@ public class Bar public class FooNullable { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public bool? Bar { get; set; } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorScalarTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorScalarTests.cs index a3d7925d1bd..ff3de7f24ef 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorScalarTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorScalarTests.cs @@ -1,6 +1,7 @@ using CookieCrumble; using HotChocolate.Execution; using HotChocolate.Types; +using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using Squadron; @@ -91,6 +92,7 @@ await SnapshotExtensions.AddResult( public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public bool Bar { get; set; } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbAggregateFluentTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbAggregateFluentTests.cs index 57516c35654..5323cc91fbc 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbAggregateFluentTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbAggregateFluentTests.cs @@ -104,6 +104,7 @@ await SnapshotExtensions.AddResult( public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); [BsonElement("renameTest")] @@ -113,6 +114,7 @@ public class Foo public class Bar { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public DateTimeOffset Baz { get; set; } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbCollectionTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbCollectionTests.cs index e8f8999c5bc..80b6ead76ee 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbCollectionTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbCollectionTests.cs @@ -106,6 +106,7 @@ await SnapshotExtensions.AddResult( public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); [BsonElement("renameTest")] @@ -115,6 +116,7 @@ public class Foo public class Bar { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public DateTimeOffset Baz { get; set; } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbFindFluentTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbFindFluentTests.cs index 8585e52edb3..56c0060bbd2 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbFindFluentTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbFindFluentTests.cs @@ -151,6 +151,7 @@ await SnapshotExtensions.AddResult( public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); [BsonElement("renameTest")] @@ -160,6 +161,7 @@ public class Foo public class Bar { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public DateTimeOffset Baz { get; set; } @@ -168,6 +170,7 @@ public class Bar public class Baz { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public DateTimeOffset Bar { get; set; } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorBooleanTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorBooleanTests.cs index 558f5c992c3..70ffe4e3577 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorBooleanTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorBooleanTests.cs @@ -1,6 +1,7 @@ using CookieCrumble; using HotChocolate.Data.Sorting; using HotChocolate.Execution; +using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using Squadron; @@ -82,6 +83,7 @@ await SnapshotExtensions.AddResult( public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public bool Bar { get; set; } @@ -90,6 +92,7 @@ public class Foo public class FooNullable { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public bool? Bar { get; set; } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorComparableTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorComparableTests.cs index 40554622382..f6f85e6fe14 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorComparableTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorComparableTests.cs @@ -1,6 +1,7 @@ using CookieCrumble; using HotChocolate.Data.Sorting; using HotChocolate.Execution; +using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using Squadron; @@ -84,6 +85,7 @@ await SnapshotExtensions.AddResult( public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public short BarShort { get; set; } @@ -102,6 +104,7 @@ public class Foo public class FooNullable { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public short? BarShort { get; set; } } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorEnumTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorEnumTests.cs index 6f9a346889f..2c1125df6b0 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorEnumTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorEnumTests.cs @@ -1,6 +1,7 @@ using CookieCrumble; using HotChocolate.Data.Sorting; using HotChocolate.Execution; +using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using Squadron; @@ -86,6 +87,7 @@ await SnapshotExtensions.AddResult( public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public FooEnum BarEnum { get; set; } @@ -94,6 +96,7 @@ public class Foo public class FooNullable { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public FooEnum? BarEnum { get; set; } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorObjectTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorObjectTests.cs index 2f1c857f20d..588d2f26e08 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorObjectTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorObjectTests.cs @@ -1,6 +1,7 @@ using CookieCrumble; using HotChocolate.Data.Sorting; using HotChocolate.Execution; +using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using Squadron; @@ -355,6 +356,7 @@ await SnapshotExtensions.AddResult( public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public short BarShort { get; set; } @@ -374,6 +376,7 @@ public class Foo public class FooNullable { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public short? BarShort { get; set; } @@ -393,6 +396,7 @@ public class FooNullable public class Bar { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public Foo Foo { get; set; } = null!; @@ -401,6 +405,7 @@ public class Bar public class BarNullable { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public FooNullable? Foo { get; set; } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorStringTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorStringTests.cs index 17824a203b1..61cae8242fa 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorStringTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorStringTests.cs @@ -1,6 +1,7 @@ using CookieCrumble; using HotChocolate.Data.Sorting; using HotChocolate.Execution; +using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using Squadron; @@ -82,6 +83,7 @@ await SnapshotExtensions.AddResult( public class Foo { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public string Bar { get; set; } = null!; @@ -90,6 +92,7 @@ public class Foo public class FooNullable { [BsonId] + [BsonGuidRepresentation(GuidRepresentation.Standard)] public Guid Id { get; set; } = Guid.NewGuid(); public string? Bar { get; set; } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/__snapshots__/MongoDbFindFluentTests.FindFluent_CombineQuery.snap b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/__snapshots__/MongoDbFindFluentTests.FindFluent_CombineQuery.snap index 6ea0086ac4e..cc3aaeb845d 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/__snapshots__/MongoDbFindFluentTests.FindFluent_CombineQuery.snap +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/__snapshots__/MongoDbFindFluentTests.FindFluent_CombineQuery.snap @@ -16,7 +16,7 @@ ASC Result: ASC Query: --------------- -find({ "Bar" : { "$gt" : [NumberLong("630822816000000000"), 0] } }).sort({ "Qux" : 1, "Bar" : 1 }) +find({ "Bar" : { "$gt" : { "DateTime" : { "$date" : "2000-01-01T00:00:00Z" }, "Ticks" : 630822816000000000, "Offset" : 0 } } }).sort({ "Qux" : 1, "Bar" : 1 }) --------------- DESC Result: @@ -37,5 +37,5 @@ DESC Result: DESC Query: --------------- -find({ "Bar" : { "$gt" : [NumberLong("630822816000000000"), 0] } }).sort({ "Qux" : 1, "Bar" : -1 }) +find({ "Bar" : { "$gt" : { "DateTime" : { "$date" : "2000-01-01T00:00:00Z" }, "Ticks" : 630822816000000000, "Offset" : 0 } } }).sort({ "Qux" : 1, "Bar" : -1 }) --------------- diff --git a/src/HotChocolate/MongoDb/test/Types.MongoDb/__snapshots__/BsonTypeTests.Input_Should_MatchSnapshot_When_BsonDocument.snap b/src/HotChocolate/MongoDb/test/Types.MongoDb/__snapshots__/BsonTypeTests.Input_Should_MatchSnapshot_When_BsonDocument.snap index 3bd60fb7406..c46a627111f 100644 --- a/src/HotChocolate/MongoDb/test/Types.MongoDb/__snapshots__/BsonTypeTests.Input_Should_MatchSnapshot_When_BsonDocument.snap +++ b/src/HotChocolate/MongoDb/test/Types.MongoDb/__snapshots__/BsonTypeTests.Input_Should_MatchSnapshot_When_BsonDocument.snap @@ -1 +1 @@ -{ "int32" : NumberLong(42), "int64" : NumberLong(42), "decimal" : "42.123456789123456789123456789", "double" : 42.229999999999997, "boolean" : true, "bsonArray" : [false, true], "string" : "String", "null" : null, "nested" : { "int32" : NumberLong(42), "int64" : NumberLong(42) } } +{ "int32" : 42, "int64" : 42, "decimal" : "42.123456789123456789123456789", "double" : 42.229999999999997, "boolean" : true, "bsonArray" : [false, true], "string" : "String", "null" : null, "nested" : { "int32" : 42, "int64" : 42 } } From f658ce01e52d0860298d51fef181c32c16d490d4 Mon Sep 17 00:00:00 2001 From: Glen Date: Wed, 6 Nov 2024 14:59:52 +0200 Subject: [PATCH 094/154] Replaced static calls to SnapshotExtensions.AddResult in MongoDB tests (#7689) --- .../MongoDbCollectionTests.cs | 16 +- .../MongoDbFilterCombinatorTests.cs | 3 +- .../MongoDbFilterVisitorBooleanTests.cs | 36 +-- .../MongoDbFilterVisitorDateOnlyTests.cs | 36 +-- .../MongoDbFilterVisitorEnumTests.cs | 80 +++--- .../MongoDbFilterVisitorListTests.cs | 80 +++--- .../MongoDbFilterVisitorObjectIdTests.cs | 272 +++++++++--------- .../MongoDbFilterVisitorObjectTests.cs | 138 ++++----- .../MongoDbFilterVisitorStringTests.cs | 200 ++++++------- .../MongoDbFilterVisitorTimeOnlyTests.cs | 36 +-- .../MongoDbFindFluentTests.cs | 24 +- ...MongoDbCursorPagingAggregateFluentTests.cs | 36 +-- .../MongoDbCursorPagingFindFluentTests.cs | 12 +- .../MongoDbOffsetPagingAggregateTests.cs | 30 +- .../MongoDbOffsetPagingFindFluentTests.cs | 30 +- .../MongoDbProjectionObjectsTests.cs | 6 +- ...ongoDbProjectionVisitorIsProjectedTests.cs | 18 +- .../MongoDbProjectionVisitorPagingTests.cs | 120 ++++---- .../MongoDbProjectionVisitorScalarTests.cs | 18 +- .../MongoDbAggregateFluentTests.cs | 8 +- .../MongoDbCollectionTests.cs | 16 +- .../MongoDbFindFluentTests.cs | 24 +- .../MongoDbSortVisitorBooleanTests.cs | 16 +- .../MongoDbSortVisitorComparableTests.cs | 16 +- .../MongoDbSortVisitorEnumTests.cs | 16 +- .../MongoDbSortVisitorObjectTests.cs | 64 ++--- .../MongoDbSortVisitorStringTests.cs | 16 +- 27 files changed, 684 insertions(+), 683 deletions(-) diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbCollectionTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbCollectionTests.cs index 7209ce41a58..7804c78b825 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbCollectionTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbCollectionTests.cs @@ -56,10 +56,10 @@ public async Task BsonElement_Rename() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "true"), res2, "false") + await Snapshot + .Create() + .AddResult(res1, "true") + .AddResult(res2, "false") .MatchAsync(); } @@ -93,10 +93,10 @@ public async Task Collection_Serializer() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "2020-01-11"), res2, "2020-01-12") + await Snapshot + .Create() + .AddResult(res1, "2020-01-11") + .AddResult(res2, "2020-01-12") .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterCombinatorTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterCombinatorTests.cs index d71b9d492b7..1816d423f6a 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterCombinatorTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterCombinatorTests.cs @@ -35,7 +35,8 @@ public async Task Create_Empty_Expression() .SetDocument("{ root(where: { }){ bar }}") .Build()); - await Snapshot.Create() + await Snapshot + .Create() .Add(res1) .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorBooleanTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorBooleanTests.cs index dddb7bd7b28..28106f353ed 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorBooleanTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorBooleanTests.cs @@ -48,10 +48,10 @@ public async Task Create_BooleanEqual_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "true"), res2, "false") + await Snapshot + .Create() + .AddResult(res1, "true") + .AddResult(res2, "false") .MatchAsync(); } @@ -74,10 +74,10 @@ public async Task Create_BooleanNotEqual_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "true"), res2, "false") + await Snapshot + .Create() + .AddResult(res1, "true") + .AddResult(res2, "false") .MatchAsync(); } @@ -105,11 +105,11 @@ public async Task Create_NullableBooleanEqual_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "true"), res2, "false"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "true") + .AddResult(res2, "false") + .AddResult(res3, "null") .MatchAsync(); } @@ -137,11 +137,11 @@ public async Task Create_NullableBooleanNotEqual_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "true"), res2, "false"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "true") + .AddResult(res2, "false") + .AddResult(res3, "null") .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorDateOnlyTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorDateOnlyTests.cs index c443f978d21..747b2ab9d9f 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorDateOnlyTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorDateOnlyTests.cs @@ -50,10 +50,10 @@ public async Task Create_DateOnlyEqual_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "2022-01-16"), res2, "2022-01-15") + await Snapshot + .Create() + .AddResult(res1, "2022-01-16") + .AddResult(res2, "2022-01-15") .MatchAsync(); } @@ -75,10 +75,10 @@ public async Task Create_DateOnlyNotEqual_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "2022-01-16"), res2, "2022-01-15") + await Snapshot + .Create() + .AddResult(res1, "2022-01-16") + .AddResult(res2, "2022-01-15") .MatchAsync(); } @@ -106,11 +106,11 @@ public async Task Create_NullableDateOnlyEqual_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "2022-01-16"), res2, "2022-01-15"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "2022-01-16") + .AddResult(res2, "2022-01-15") + .AddResult(res3, "null") .MatchAsync(); } @@ -139,11 +139,11 @@ public async Task Create_NullableDateOnlyNotEqual_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "2022-01-16"), res2, "2022-01-15"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "2022-01-16") + .AddResult(res2, "2022-01-15") + .AddResult(res3, "null") .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorEnumTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorEnumTests.cs index 0fa21fc7d7b..467ccf6f080 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorEnumTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorEnumTests.cs @@ -56,11 +56,11 @@ public async Task Create_EnumEqual_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "BAR"), res2, "FOO"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "BAR") + .AddResult(res2, "FOO") + .AddResult(res3, "null") .MatchAsync(); } @@ -87,11 +87,11 @@ public async Task Create_EnumNotEqual_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "BAR"), res2, "FOO"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "BAR") + .AddResult(res2, "FOO") + .AddResult(res3, "null") .MatchAsync(); } @@ -118,11 +118,11 @@ public async Task Create_EnumIn_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "BarAndFoo"), res2, "FOO"), res3, "nullAndFoo") + await Snapshot + .Create() + .AddResult(res1, "BarAndFoo") + .AddResult(res2, "FOO") + .AddResult(res3, "nullAndFoo") .MatchAsync(); } @@ -149,11 +149,11 @@ public async Task Create_EnumNotIn_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "BarAndFoo"), res2, "FOO"), res3, "nullAndFoo") + await Snapshot + .Create() + .AddResult(res1, "BarAndFoo") + .AddResult(res2, "FOO") + .AddResult(res3, "nullAndFoo") .MatchAsync(); } @@ -180,11 +180,11 @@ public async Task Create_NullableEnumEqual_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "BAR"), res2, "FOO"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "BAR") + .AddResult(res2, "FOO") + .AddResult(res3, "null") .MatchAsync(); } @@ -211,11 +211,11 @@ public async Task Create_NullableEnumNotEqual_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "BAR"), res2, "FOO"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "BAR") + .AddResult(res2, "FOO") + .AddResult(res3, "null") .MatchAsync(); } @@ -242,11 +242,11 @@ public async Task Create_NullableEnumIn_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "BarAndFoo"), res2, "FOO"), res3, "nullAndFoo") + await Snapshot + .Create() + .AddResult(res1, "BarAndFoo") + .AddResult(res2, "FOO") + .AddResult(res3, "nullAndFoo") .MatchAsync(); } @@ -273,11 +273,11 @@ public async Task Create_NullableEnumNotIn_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "BarAndFoo"), res2, "FOO"), res3, "nullAndFoo") + await Snapshot + .Create() + .AddResult(res1, "BarAndFoo") + .AddResult(res2, "FOO") + .AddResult(res3, "nullAndFoo") .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorListTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorListTests.cs index 9750337b1bc..0c88af3f807 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorListTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorListTests.cs @@ -158,11 +158,11 @@ public async Task Create_ArraySomeObjectStringEqualWithNull_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "a"), res2, "d"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "a") + .AddResult(res2, "d") + .AddResult(res3, "null") .MatchAsync(); } @@ -192,11 +192,11 @@ public async Task Create_ArrayNoneObjectStringEqual_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "a"), res2, "d"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "a") + .AddResult(res2, "d") + .AddResult(res3, "null") .MatchAsync(); } @@ -226,11 +226,11 @@ public async Task Create_ArrayAllObjectStringEqual_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "a"), res2, "d"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "a") + .AddResult(res2, "d") + .AddResult(res3, "null") .MatchAsync(); } @@ -257,11 +257,11 @@ public async Task Create_ArrayAnyObjectStringEqual_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "false"), res2, "true"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "false") + .AddResult(res2, "true") + .AddResult(res3, "null") .MatchAsync(); } @@ -302,11 +302,11 @@ public async Task Create_ArraySomeStringEqualWithNull_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "a"), res2, "d"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "a") + .AddResult(res2, "d") + .AddResult(res3, "null") .MatchAsync(); } @@ -336,11 +336,11 @@ public async Task Create_ArrayNoneStringEqual_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "a"), res2, "d"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "a") + .AddResult(res2, "d") + .AddResult(res3, "null") .MatchAsync(); } @@ -370,11 +370,11 @@ public async Task Create_ArrayAllStringEqual_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "a"), res2, "d"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "a") + .AddResult(res2, "d") + .AddResult(res3, "null") .MatchAsync(); } @@ -401,11 +401,11 @@ public async Task Create_ArrayAnyStringEqual_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "false"), res2, "true"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "false") + .AddResult(res2, "true") + .AddResult(res3, "null") .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorObjectIdTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorObjectIdTests.cs index 2bdfe349d13..556fc9316b3 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorObjectIdTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorObjectIdTests.cs @@ -57,11 +57,11 @@ public async Task Create_ObjectIdEqual_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "6124e80f3f5fc839830c1f69"), res2, "6124e80f3f5fc839830c1f6a"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "6124e80f3f5fc839830c1f69") + .AddResult(res2, "6124e80f3f5fc839830c1f6a") + .AddResult(res3, "null") .MatchAsync(); } @@ -91,11 +91,11 @@ public async Task Create_ObjectIdNotEqual_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "6124e80f3f5fc839830c1f69"), res2, "6124e80f3f5fc839830c1f6a"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "6124e80f3f5fc839830c1f69") + .AddResult(res2, "6124e80f3f5fc839830c1f6a") + .AddResult(res3, "null") .MatchAsync(); } @@ -131,12 +131,12 @@ public async Task Create_ObjectIdGreaterThan_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "6124e80f3f5fc839830c1f69"), res2, "6124e80f3f5fc839830c1f6a"), res3, "6124e80f3f5fc839830c1f6b"), res4, "null") + await Snapshot + .Create() + .AddResult(res1, "6124e80f3f5fc839830c1f69") + .AddResult(res2, "6124e80f3f5fc839830c1f6a") + .AddResult(res3, "6124e80f3f5fc839830c1f6b") + .AddResult(res4, "null") .MatchAsync(); } @@ -172,12 +172,12 @@ public async Task Create_ObjectIdNotGreaterThan_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "6124e80f3f5fc839830c1f69"), res2, "6124e80f3f5fc839830c1f6a"), res3, "6124e80f3f5fc839830c1f6b"), res4, "null") + await Snapshot + .Create() + .AddResult(res1, "6124e80f3f5fc839830c1f69") + .AddResult(res2, "6124e80f3f5fc839830c1f6a") + .AddResult(res3, "6124e80f3f5fc839830c1f6b") + .AddResult(res4, "null") .MatchAsync(); } @@ -213,12 +213,12 @@ public async Task Create_ObjectIdGreaterThanOrEquals_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "6124e80f3f5fc839830c1f69"), res2, "6124e80f3f5fc839830c1f6a"), res3, "6124e80f3f5fc839830c1f6b"), res4, "null") + await Snapshot + .Create() + .AddResult(res1, "6124e80f3f5fc839830c1f69") + .AddResult(res2, "6124e80f3f5fc839830c1f6a") + .AddResult(res3, "6124e80f3f5fc839830c1f6b") + .AddResult(res4, "null") .MatchAsync(); } @@ -254,12 +254,12 @@ public async Task Create_ObjectIdNotGreaterThanOrEquals_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "6124e80f3f5fc839830c1f69"), res2, "6124e80f3f5fc839830c1f6a"), res3, "6124e80f3f5fc839830c1f6b"), res4, "null") + await Snapshot + .Create() + .AddResult(res1, "6124e80f3f5fc839830c1f69") + .AddResult(res2, "6124e80f3f5fc839830c1f6a") + .AddResult(res3, "6124e80f3f5fc839830c1f6b") + .AddResult(res4, "null") .MatchAsync(); } @@ -291,12 +291,12 @@ public async Task Create_ObjectIdLowerThan_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "6124e80f3f5fc839830c1f69"), res2, "6124e80f3f5fc839830c1f6a"), res3, "6124e80f3f5fc839830c1f6b"), res4, "null") + await Snapshot + .Create() + .AddResult(res1, "6124e80f3f5fc839830c1f69") + .AddResult(res2, "6124e80f3f5fc839830c1f6a") + .AddResult(res3, "6124e80f3f5fc839830c1f6b") + .AddResult(res4, "null") .MatchAsync(); } @@ -332,12 +332,12 @@ public async Task Create_ObjectIdNotLowerThan_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "6124e80f3f5fc839830c1f69"), res2, "6124e80f3f5fc839830c1f6a"), res3, "6124e80f3f5fc839830c1f6b"), res4, "null") + await Snapshot + .Create() + .AddResult(res1, "6124e80f3f5fc839830c1f69") + .AddResult(res2, "6124e80f3f5fc839830c1f6a") + .AddResult(res3, "6124e80f3f5fc839830c1f6b") + .AddResult(res4, "null") .MatchAsync(); } @@ -373,12 +373,12 @@ public async Task Create_ObjectIdLowerThanOrEquals_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "6124e80f3f5fc839830c1f69"), res2, "6124e80f3f5fc839830c1f6a"), res3, "6124e80f3f5fc839830c1f6b"), res4, "null") + await Snapshot + .Create() + .AddResult(res1, "6124e80f3f5fc839830c1f69") + .AddResult(res2, "6124e80f3f5fc839830c1f6a") + .AddResult(res3, "6124e80f3f5fc839830c1f6b") + .AddResult(res4, "null") .MatchAsync(); } @@ -414,12 +414,12 @@ public async Task Create_ObjectIdNotLowerThanOrEquals_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "6124e80f3f5fc839830c1f69"), res2, "6124e80f3f5fc839830c1f6a"), res3, "6124e80f3f5fc839830c1f6b"), res4, "null") + await Snapshot + .Create() + .AddResult(res1, "6124e80f3f5fc839830c1f69") + .AddResult(res2, "6124e80f3f5fc839830c1f6a") + .AddResult(res3, "6124e80f3f5fc839830c1f6b") + .AddResult(res4, "null") .MatchAsync(); } @@ -467,11 +467,11 @@ public async Task Create_ObjectIdIn_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "6124e80f3f5fc839830c1f69and6124e80f3f5fc839830c1f6a"), res2, "band6124e80f3f5fc839830c1f6b"), res3, "nullAnd6124e80f3f5fc839830c1f6b") + await Snapshot + .Create() + .AddResult(res1, "6124e80f3f5fc839830c1f69and6124e80f3f5fc839830c1f6a") + .AddResult(res2, "band6124e80f3f5fc839830c1f6b") + .AddResult(res3, "nullAnd6124e80f3f5fc839830c1f6b") .MatchAsync(); } @@ -532,11 +532,11 @@ public async Task Create_ObjectIdNotIn_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "6124e80f3f5fc839830c1f69and6124e80f3f5fc839830c1f6a"), res2, "band6124e80f3f5fc839830c1f6b"), res3, "nullAnd6124e80f3f5fc839830c1f6b") + await Snapshot + .Create() + .AddResult(res1, "6124e80f3f5fc839830c1f69and6124e80f3f5fc839830c1f6a") + .AddResult(res2, "band6124e80f3f5fc839830c1f6b") + .AddResult(res3, "nullAnd6124e80f3f5fc839830c1f6b") .MatchAsync(); } @@ -567,11 +567,11 @@ public async Task Create_ObjectIdNullableEqual_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "a"), res2, "6124e80f3f5fc839830c1f6a"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "a") + .AddResult(res2, "6124e80f3f5fc839830c1f6a") + .AddResult(res3, "null") .MatchAsync(); } @@ -601,11 +601,11 @@ public async Task Create_ObjectIdNullableNotEqual_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "a"), res2, "6124e80f3f5fc839830c1f6a"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "a") + .AddResult(res2, "6124e80f3f5fc839830c1f6a") + .AddResult(res3, "null") .MatchAsync(); } @@ -637,12 +637,12 @@ public async Task Create_ObjectIdNullableGreaterThan_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "a"), res2, "6124e80f3f5fc839830c1f6a"), res3, "6124e80f3f5fc839830c1f6b"), res4, "null") + await Snapshot + .Create() + .AddResult(res1, "a") + .AddResult(res2, "6124e80f3f5fc839830c1f6a") + .AddResult(res3, "6124e80f3f5fc839830c1f6b") + .AddResult(res4, "null") .MatchAsync(); } @@ -678,12 +678,12 @@ public async Task Create_ObjectIdNullableNotGreaterThan_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "a"), res2, "6124e80f3f5fc839830c1f6a"), res3, "6124e80f3f5fc839830c1f6b"), res4, "null") + await Snapshot + .Create() + .AddResult(res1, "a") + .AddResult(res2, "6124e80f3f5fc839830c1f6a") + .AddResult(res3, "6124e80f3f5fc839830c1f6b") + .AddResult(res4, "null") .MatchAsync(); } @@ -719,12 +719,12 @@ public async Task Create_ObjectIdNullableGreaterThanOrEquals_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "a"), res2, "6124e80f3f5fc839830c1f6a"), res3, "6124e80f3f5fc839830c1f6b"), res4, "null") + await Snapshot + .Create() + .AddResult(res1, "a") + .AddResult(res2, "6124e80f3f5fc839830c1f6a") + .AddResult(res3, "6124e80f3f5fc839830c1f6b") + .AddResult(res4, "null") .MatchAsync(); } @@ -760,12 +760,12 @@ public async Task Create_ObjectIdNullableNotGreaterThanOrEquals_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "a"), res2, "6124e80f3f5fc839830c1f6a"), res3, "6124e80f3f5fc839830c1f6b"), res4, "null") + await Snapshot + .Create() + .AddResult(res1, "a") + .AddResult(res2, "6124e80f3f5fc839830c1f6a") + .AddResult(res3, "6124e80f3f5fc839830c1f6b") + .AddResult(res4, "null") .MatchAsync(); } @@ -797,12 +797,12 @@ public async Task Create_ObjectIdNullableLowerThan_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "a"), res2, "6124e80f3f5fc839830c1f6a"), res3, "6124e80f3f5fc839830c1f6b"), res4, "null") + await Snapshot + .Create() + .AddResult(res1, "a") + .AddResult(res2, "6124e80f3f5fc839830c1f6a") + .AddResult(res3, "6124e80f3f5fc839830c1f6b") + .AddResult(res4, "null") .MatchAsync(); } @@ -838,12 +838,12 @@ public async Task Create_ObjectIdNullableNotLowerThan_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "a"), res2, "6124e80f3f5fc839830c1f6a"), res3, "6124e80f3f5fc839830c1f6b"), res4, "null") + await Snapshot + .Create() + .AddResult(res1, "a") + .AddResult(res2, "6124e80f3f5fc839830c1f6a") + .AddResult(res3, "6124e80f3f5fc839830c1f6b") + .AddResult(res4, "null") .MatchAsync(); } @@ -880,12 +880,12 @@ public async Task Create_ObjectIdNullableLowerThanOrEquals_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "a"), res2, "6124e80f3f5fc839830c1f6a"), res3, "6124e80f3f5fc839830c1f6b"), res4, "null") + await Snapshot + .Create() + .AddResult(res1, "a") + .AddResult(res2, "6124e80f3f5fc839830c1f6a") + .AddResult(res3, "6124e80f3f5fc839830c1f6b") + .AddResult(res4, "null") .MatchAsync(); } @@ -920,12 +920,12 @@ public async Task Create_ObjectIdNullableNotLowerThanOrEquals_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "a"), res2, "6124e80f3f5fc839830c1f6a"), res3, "6124e80f3f5fc839830c1f6b"), res4, "null") + await Snapshot + .Create() + .AddResult(res1, "a") + .AddResult(res2, "6124e80f3f5fc839830c1f6a") + .AddResult(res3, "6124e80f3f5fc839830c1f6b") + .AddResult(res4, "null") .MatchAsync(); } @@ -958,11 +958,11 @@ public async Task Create_ObjectIdNullableIn_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "6124e80f3f5fc839830c1f69and6124e80f3f5fc839830c1f6a"), res2, "band6124e80f3f5fc839830c1f6b"), res3, "bandNull") + await Snapshot + .Create() + .AddResult(res1, "6124e80f3f5fc839830c1f69and6124e80f3f5fc839830c1f6a") + .AddResult(res2, "band6124e80f3f5fc839830c1f6b") + .AddResult(res3, "bandNull") .MatchAsync(); } @@ -991,11 +991,11 @@ public async Task Create_ObjectIdNullableNotIn_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "6124e80f3f5fc839830c1f69and6124e80f3f5fc839830c1f6a"), res2, "band6124e80f3f5fc839830c1f6b"), res3, "bandNull") + await Snapshot + .Create() + .AddResult(res1, "6124e80f3f5fc839830c1f69and6124e80f3f5fc839830c1f6a") + .AddResult(res2, "band6124e80f3f5fc839830c1f6b") + .AddResult(res3, "bandNull") .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorObjectTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorObjectTests.cs index 1b3cf122d1e..d811bcdba7c 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorObjectTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorObjectTests.cs @@ -146,11 +146,11 @@ public async Task Create_ObjectShortEqual_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "12"), res2, "13"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "12") + .AddResult(res2, "13") + .AddResult(res3, "null") .MatchAsync(); } @@ -183,11 +183,11 @@ public async Task Create_ObjectShortIn_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "12and13"), res2, "13and14"), res3, "nullAnd14") + await Snapshot + .Create() + .AddResult(res1, "12and13") + .AddResult(res2, "13and14") + .AddResult(res3, "nullAnd14") .MatchAsync(); } @@ -220,11 +220,11 @@ public async Task Create_ObjectNullableShortEqual_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "12"), res2, "13"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "12") + .AddResult(res2, "13") + .AddResult(res3, "null") .MatchAsync(); } @@ -257,11 +257,11 @@ public async Task Create_ObjectNullableShortIn_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "12and13"), res2, "13and14"), res3, "13andNull") + await Snapshot + .Create() + .AddResult(res1, "12and13") + .AddResult(res2, "13and14") + .AddResult(res3, "13andNull") .MatchAsync(); } @@ -287,10 +287,10 @@ public async Task Create_ObjectBooleanEqual_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "true"), res2, "false") + await Snapshot + .Create() + .AddResult(res1, "true") + .AddResult(res2, "false") .MatchAsync(); } @@ -325,11 +325,11 @@ public async Task Create_ObjectNullableBooleanEqual_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "true"), res2, "false"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "true") + .AddResult(res2, "false") + .AddResult(res3, "null") .MatchAsync(); } @@ -362,11 +362,11 @@ public async Task Create_ObjectEnumEqual_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "BAR"), res2, "FOO"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "BAR") + .AddResult(res2, "FOO") + .AddResult(res3, "null") .MatchAsync(); } @@ -399,11 +399,11 @@ public async Task Create_ObjectEnumIn_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "BarAndFoo"), res2, "FOO"), res3, "nullAndFoo") + await Snapshot + .Create() + .AddResult(res1, "BarAndFoo") + .AddResult(res2, "FOO") + .AddResult(res3, "nullAndFoo") .MatchAsync(); } @@ -436,11 +436,11 @@ public async Task Create_ObjectNullableEnumEqual_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "BAR"), res2, "FOO"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "BAR") + .AddResult(res2, "FOO") + .AddResult(res3, "null") .MatchAsync(); } @@ -473,11 +473,11 @@ public async Task Create_ObjectNullableEnumIn_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "BarAndFoo"), res2, "FOO"), res3, "nullAndFoo") + await Snapshot + .Create() + .AddResult(res1, "BarAndFoo") + .AddResult(res2, "FOO") + .AddResult(res3, "nullAndFoo") .MatchAsync(); } @@ -509,11 +509,11 @@ public async Task Create_ObjectStringEqual_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "testatest"), res2, "testbtest"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "testatest") + .AddResult(res2, "testbtest") + .AddResult(res3, "null") .MatchAsync(); } @@ -547,11 +547,11 @@ public async Task Create_ObjectStringIn_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "testatestAndtestb"), res2, "testbtestAndNull"), res3, "testatest") + await Snapshot + .Create() + .AddResult(res1, "testatestAndtestb") + .AddResult(res2, "testbtestAndNull") + .AddResult(res3, "testatest") .MatchAsync(); } @@ -587,11 +587,11 @@ public async Task Create_ArrayObjectNestedArraySomeStringEqual_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "a"), res2, "d"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "a") + .AddResult(res2, "d") + .AddResult(res3, "null") .MatchAsync(); } @@ -624,11 +624,11 @@ public async Task Create_ArrayObjectNestedArrayAnyStringEqual_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "false"), res2, "true"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "false") + .AddResult(res2, "true") + .AddResult(res3, "null") .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorStringTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorStringTests.cs index 39ed41b2b23..37307e1eea4 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorStringTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorStringTests.cs @@ -52,11 +52,11 @@ public async Task Create_StringEqual_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "testatest"), res2, "testbtest"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "testatest") + .AddResult(res2, "testbtest") + .AddResult(res3, "null") .MatchAsync(); } @@ -83,11 +83,11 @@ public async Task Create_StringNotEqual_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "testatest"), res2, "testbtest"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "testatest") + .AddResult(res2, "testbtest") + .AddResult(res3, "null") .MatchAsync(); } @@ -115,11 +115,11 @@ public async Task Create_StringIn_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "testatestAndtestb"), res2, "testbtestAndNull"), res3, "testatest") + await Snapshot + .Create() + .AddResult(res1, "testatestAndtestb") + .AddResult(res2, "testbtestAndNull") + .AddResult(res3, "testatest") .MatchAsync(); } @@ -147,11 +147,11 @@ public async Task Create_StringNotIn_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "testatestAndtestb"), res2, "testbtestAndNull"), res3, "testatest") + await Snapshot + .Create() + .AddResult(res1, "testatestAndtestb") + .AddResult(res2, "testbtestAndNull") + .AddResult(res3, "testatest") .MatchAsync(); } @@ -178,11 +178,11 @@ public async Task Create_StringContains_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "a"), res2, "b"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "a") + .AddResult(res2, "b") + .AddResult(res3, "null") .MatchAsync(); } @@ -209,11 +209,11 @@ public async Task Create_StringNoContains_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "a"), res2, "b"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "a") + .AddResult(res2, "b") + .AddResult(res3, "null") .MatchAsync(); } @@ -240,11 +240,11 @@ public async Task Create_StringStartsWith_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "testa"), res2, "testb"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "testa") + .AddResult(res2, "testb") + .AddResult(res3, "null") .MatchAsync(); } @@ -271,11 +271,11 @@ public async Task Create_StringNotStartsWith_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "testa"), res2, "testb"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "testa") + .AddResult(res2, "testb") + .AddResult(res3, "null") .MatchAsync(); } @@ -302,11 +302,11 @@ public async Task Create_StringEndsWith_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "atest"), res2, "btest"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "atest") + .AddResult(res2, "btest") + .AddResult(res3, "null") .MatchAsync(); } @@ -333,11 +333,11 @@ public async Task Create_StringNotEndsWith_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "atest"), res2, "btest"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "atest") + .AddResult(res2, "btest") + .AddResult(res3, "null") .MatchAsync(); } @@ -364,11 +364,11 @@ public async Task Create_NullableStringEqual_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "testatest"), res2, "testbtest"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "testatest") + .AddResult(res2, "testbtest") + .AddResult(res3, "null") .MatchAsync(); } @@ -395,11 +395,11 @@ public async Task Create_NullableStringNotEqual_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "testatest"), res2, "testbtest"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "testatest") + .AddResult(res2, "testbtest") + .AddResult(res3, "null") .MatchAsync(); } @@ -427,11 +427,11 @@ public async Task Create_NullableStringIn_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "testatestAndtestb"), res2, "testbtestAndNull"), res3, "testatest") + await Snapshot + .Create() + .AddResult(res1, "testatestAndtestb") + .AddResult(res2, "testbtestAndNull") + .AddResult(res3, "testatest") .MatchAsync(); } @@ -459,11 +459,11 @@ public async Task Create_NullableStringNotIn_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "testatestAndtestb"), res2, "testbtestAndNull"), res3, "testatest") + await Snapshot + .Create() + .AddResult(res1, "testatestAndtestb") + .AddResult(res2, "testbtestAndNull") + .AddResult(res3, "testatest") .MatchAsync(); } @@ -490,11 +490,11 @@ public async Task Create_NullableStringContains_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "a"), res2, "b"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "a") + .AddResult(res2, "b") + .AddResult(res3, "null") .MatchAsync(); } @@ -521,11 +521,11 @@ public async Task Create_NullableStringNoContains_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "a"), res2, "b"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "a") + .AddResult(res2, "b") + .AddResult(res3, "null") .MatchAsync(); } @@ -552,11 +552,11 @@ public async Task Create_NullableStringStartsWith_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "testa"), res2, "testb"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "testa") + .AddResult(res2, "testb") + .AddResult(res3, "null") .MatchAsync(); } @@ -583,11 +583,11 @@ public async Task Create_NullableStringNotStartsWith_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "testa"), res2, "testb"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "testa") + .AddResult(res2, "testb") + .AddResult(res3, "null") .MatchAsync(); } @@ -614,11 +614,11 @@ public async Task Create_NullableStringEndsWith_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "atest"), res2, "btest"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "atest") + .AddResult(res2, "btest") + .AddResult(res3, "null") .MatchAsync(); } @@ -645,11 +645,11 @@ public async Task Create_NullableStringNotEndsWith_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "atest"), res2, "btest"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "atest") + .AddResult(res2, "btest") + .AddResult(res3, "null") .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorTimeOnlyTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorTimeOnlyTests.cs index 6b3e2a3f9b3..e207d4abe24 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorTimeOnlyTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFilterVisitorTimeOnlyTests.cs @@ -49,10 +49,10 @@ public async Task Create_TimeOnlyEqual_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "0630"), res2, "1600") + await Snapshot + .Create() + .AddResult(res1, "0630") + .AddResult(res2, "1600") .MatchAsync(); } @@ -74,10 +74,10 @@ public async Task Create_TimeOnlyNotEqual_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "0630"), res2, "1600") + await Snapshot + .Create() + .AddResult(res1, "0630") + .AddResult(res2, "1600") .MatchAsync(); } @@ -104,11 +104,11 @@ public async Task Create_NullableTimeOnlyEqual_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "0630"), res2, "1600"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "0630") + .AddResult(res2, "1600") + .AddResult(res3, "null") .MatchAsync(); } @@ -135,11 +135,11 @@ public async Task Create_NullableTimeOnlyNotEqual_Expression() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "0630"), res2, "1600"), res3, "null") + await Snapshot + .Create() + .AddResult(res1, "0630") + .AddResult(res2, "1600") + .AddResult(res3, "null") .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFindFluentTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFindFluentTests.cs index de9787e0ded..175716312a6 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFindFluentTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Filters.Tests/MongoDbFindFluentTests.cs @@ -64,10 +64,10 @@ public async Task BsonElement_Rename() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "true"), res2, "false") + await Snapshot + .Create() + .AddResult(res1, "true") + .AddResult(res2, "false") .MatchAsync(); } @@ -100,10 +100,10 @@ public async Task FindFluent_Serializer() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "2020-01-11"), res2, "2020-01-12") + await Snapshot + .Create() + .AddResult(res1, "2020-01-11") + .AddResult(res2, "2020-01-12") .MatchAsync(); } @@ -133,10 +133,10 @@ public async Task FindFluent_CombineQuery() .Build()); // arrange - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "2020-01-11"), res2, "2020-01-12") + await Snapshot + .Create() + .AddResult(res1, "2020-01-11") + .AddResult(res2, "2020-01-12") .MatchAsync(); } public class Foo diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingAggregateFluentTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingAggregateFluentTests.cs index d3911d3e73e..834a11f7f67 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingAggregateFluentTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingAggregateFluentTests.cs @@ -59,9 +59,9 @@ public async Task Simple_StringList_Default_Items() }"); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), result) + await Snapshot + .Create() + .AddResult(result) .MatchAsync(); } @@ -94,9 +94,9 @@ public async Task Simple_StringList_First_2() }"); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), result) + await Snapshot + .Create() + .AddResult(result) .MatchAsync(); } @@ -129,9 +129,9 @@ public async Task Simple_StringList_First_2_After() }"); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), result) + await Snapshot + .Create() + .AddResult(result) .MatchAsync(); } @@ -164,9 +164,9 @@ public async Task Simple_StringList_Global_DefaultItem_2() }"); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), result) + await Snapshot + .Create() + .AddResult(result) .MatchAsync(); } @@ -185,9 +185,9 @@ public async Task JustTotalCount() }"); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), result) + await Snapshot + .Create() + .AddResult(result) .MatchAsync(); } @@ -209,9 +209,9 @@ public async Task TotalCount_AndFirst() }"); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), result) + await Snapshot + .Create() + .AddResult(result) .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingFindFluentTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingFindFluentTests.cs index 0d972da2a6e..8394c42af53 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingFindFluentTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingFindFluentTests.cs @@ -128,9 +128,9 @@ public async Task Simple_StringList_First_2_After() }"); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), result) + await Snapshot + .Create() + .AddResult(result) .MatchAsync(); } @@ -163,9 +163,9 @@ public async Task Simple_StringList_Last_1_Before() }"); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), result) + await Snapshot + .Create() + .AddResult(result) .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbOffsetPagingAggregateTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbOffsetPagingAggregateTests.cs index 7f247837250..e6549be23da 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbOffsetPagingAggregateTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbOffsetPagingAggregateTests.cs @@ -52,9 +52,9 @@ public async Task Simple_StringList_Default_Items() }"); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), result) + await Snapshot + .Create() + .AddResult(result) .MatchAsync(); } @@ -79,9 +79,9 @@ public async Task Simple_StringList_Take_2() }"); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), result) + await Snapshot + .Create() + .AddResult(result) .MatchAsync(); } @@ -106,9 +106,9 @@ public async Task Simple_StringList_Take_2_After() }"); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), result) + await Snapshot + .Create() + .AddResult(result) .MatchAsync(); } @@ -133,9 +133,9 @@ public async Task Simple_StringList_Global_DefaultItem_2() }"); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), result) + await Snapshot + .Create() + .AddResult(result) .MatchAsync(); } @@ -154,9 +154,9 @@ public async Task JustTotalCount() }"); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), result) + await Snapshot + .Create() + .AddResult(result) .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbOffsetPagingFindFluentTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbOffsetPagingFindFluentTests.cs index 43e5cffde10..d61101de519 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbOffsetPagingFindFluentTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbOffsetPagingFindFluentTests.cs @@ -51,9 +51,9 @@ public async Task Simple_StringList_Default_Items() }"); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), result) + await Snapshot + .Create() + .AddResult(result) .MatchAsync(); } @@ -78,9 +78,9 @@ public async Task Simple_StringList_Take_2() }"); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), result) + await Snapshot + .Create() + .AddResult(result) .MatchAsync(); } @@ -105,9 +105,9 @@ public async Task Simple_StringList_Take_2_After() }"); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), result) + await Snapshot + .Create() + .AddResult(result) .MatchAsync(); } @@ -132,9 +132,9 @@ public async Task Simple_StringList_Global_DefaultItem_2() }"); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), result) + await Snapshot + .Create() + .AddResult(result) .MatchAsync(); } @@ -153,9 +153,9 @@ public async Task JustTotalCount() }"); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), result) + await Snapshot + .Create() + .AddResult(result) .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionObjectsTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionObjectsTests.cs index 9a6aee557aa..6c8cc5a166f 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionObjectsTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionObjectsTests.cs @@ -119,9 +119,9 @@ public async Task Should_NotInitializeObject_When_ResultOfLeftJoinIsNull_Deep() .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorIsProjectedTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorIsProjectedTests.cs index d68495ed87f..adaa5d99bf4 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorIsProjectedTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorIsProjectedTests.cs @@ -44,9 +44,9 @@ public async Task IsProjected_Should_NotBeProjectedWhenSelected_When_FalseWithOn .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } @@ -63,9 +63,9 @@ public async Task IsProjected_Should_NotBeProjectedWhenSelected_When_FalseWithTw .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } @@ -82,9 +82,9 @@ public async Task IsProjected_Should_AlwaysBeProjectedWhenSelected_When_True() .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorPagingTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorPagingTests.cs index 19e76984b9b..0014011a5a0 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorPagingTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorPagingTests.cs @@ -42,9 +42,9 @@ public async Task Create_ProjectsTwoProperties_Nodes() .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } @@ -61,9 +61,9 @@ public async Task Create_ProjectsOneProperty_Nodes() .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } @@ -80,9 +80,9 @@ public async Task Create_ProjectsTwoProperties_Edges() .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } @@ -99,9 +99,9 @@ public async Task Create_ProjectsOneProperty_Edges() .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } @@ -118,9 +118,9 @@ public async Task Create_ProjectsTwoProperties_EdgesAndNodes() .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } @@ -137,9 +137,9 @@ public async Task Create_ProjectsOneProperty_EdgesAndNodesOverlap() .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } @@ -156,9 +156,9 @@ public async Task CreateNullable_ProjectsTwoProperties_Nodes() .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } @@ -175,9 +175,9 @@ public async Task CreateNullable_ProjectsOneProperty_Nodes() .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } @@ -194,9 +194,9 @@ public async Task CreateNullable_ProjectsTwoProperties_Edges() .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } @@ -213,9 +213,9 @@ public async Task CreateNullable_ProjectsOneProperty_Edges() .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } @@ -232,9 +232,9 @@ public async Task CreateNullable_ProjectsTwoProperties_EdgesAndNodes() .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } @@ -251,9 +251,9 @@ public async Task CreateNullable_ProjectsOneProperty_EdgesAndNodesOverlap() .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } @@ -270,9 +270,9 @@ public async Task Create_Projection_Should_Stop_When_UseProjectionEncountered() .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } @@ -289,9 +289,9 @@ public async Task CreateOffsetPaging_ProjectsTwoProperties_Items_WithArgs() .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } @@ -308,9 +308,9 @@ public async Task CreateOffsetPaging_ProjectsTwoProperties_Items() .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } @@ -327,9 +327,9 @@ public async Task CreateOffsetPaging_ProjectsOneProperty_Items() .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } @@ -348,9 +348,9 @@ public async Task CreateOffsetPagingNullable_ProjectsTwoProperties_Items() .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } @@ -369,9 +369,9 @@ public async Task CreateOffsetPagingNullable_ProjectsOneProperty_Items() .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } @@ -388,9 +388,9 @@ public async Task CreateOffsetPaging_Projection_Should_Stop_When_UseProjectionEn .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } @@ -407,9 +407,9 @@ public async Task CreateOffsetPaging_Projection_Should_Stop_When_UsePagingEncoun .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorScalarTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorScalarTests.cs index ff3de7f24ef..8179999bf53 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorScalarTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorScalarTests.cs @@ -35,9 +35,9 @@ public async Task Create_ProjectsTwoProperties_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } @@ -54,9 +54,9 @@ public async Task Create_ProjectsOneProperty_Expression() .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } @@ -83,9 +83,9 @@ public async Task Create_ProjectsOneProperty_WithResolver() .Build()); // assert - await SnapshotExtensions.AddResult( - Snapshot - .Create(), res1) + await Snapshot + .Create() + .AddResult(res1) .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbAggregateFluentTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbAggregateFluentTests.cs index 5323cc91fbc..7af4a6fe775 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbAggregateFluentTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbAggregateFluentTests.cs @@ -94,10 +94,10 @@ public async Task Collection_Configuration() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "ASC"), res2, "DESC") + await Snapshot + .Create() + .AddResult(res1, "ASC") + .AddResult(res2, "DESC") .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbCollectionTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbCollectionTests.cs index 80b6ead76ee..795a31d5078 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbCollectionTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbCollectionTests.cs @@ -58,10 +58,10 @@ public async Task BsonElement_Rename() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "ASC"), res2, "DESC") + await Snapshot + .Create() + .AddResult(res1, "ASC") + .AddResult(res2, "DESC") .MatchAsync(); } @@ -96,10 +96,10 @@ public async Task Collection_Configuration() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "ASC"), res2, "DESC") + await Snapshot + .Create() + .AddResult(res1, "ASC") + .AddResult(res2, "DESC") .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbFindFluentTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbFindFluentTests.cs index 56c0060bbd2..4a39cad6cc9 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbFindFluentTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbFindFluentTests.cs @@ -66,10 +66,10 @@ public async Task BsonElement_Rename() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "ASC"), res2, "DESC") + await Snapshot + .Create() + .AddResult(res1, "ASC") + .AddResult(res2, "DESC") .MatchAsync(); } @@ -104,10 +104,10 @@ public async Task Collection_Configuration() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "ASC"), res2, "DESC") + await Snapshot + .Create() + .AddResult(res1, "ASC") + .AddResult(res2, "DESC") .MatchAsync(); } @@ -141,10 +141,10 @@ public async Task FindFluent_CombineQuery() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "ASC"), res2, "DESC") + await Snapshot + .Create() + .AddResult(res1, "ASC") + .AddResult(res2, "DESC") .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorBooleanTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorBooleanTests.cs index 70ffe4e3577..17499e613b8 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorBooleanTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorBooleanTests.cs @@ -47,10 +47,10 @@ public async Task Create_Boolean_OrderBy() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "ASC"), res2, "DESC") + await Snapshot + .Create() + .AddResult(res1, "ASC") + .AddResult(res2, "DESC") .MatchAsync(); } @@ -73,10 +73,10 @@ public async Task Create_Boolean_OrderBy_Nullable() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "ASC"), res2, "DESC") + await Snapshot + .Create() + .AddResult(res1, "ASC") + .AddResult(res2, "DESC") .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorComparableTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorComparableTests.cs index f6f85e6fe14..302e7819aa6 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorComparableTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorComparableTests.cs @@ -49,10 +49,10 @@ public async Task Create_Short_OrderBy() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "ASC"), res2, "DESC") + await Snapshot + .Create() + .AddResult(res1, "ASC") + .AddResult(res2, "DESC") .MatchAsync(); } @@ -75,10 +75,10 @@ public async Task Create_Short_OrderBy_Nullable() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "ASC"), res2, "DESC") + await Snapshot + .Create() + .AddResult(res1, "ASC") + .AddResult(res2, "DESC") .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorEnumTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorEnumTests.cs index 2c1125df6b0..7cab6c6aefd 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorEnumTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorEnumTests.cs @@ -51,10 +51,10 @@ public async Task Create_Enum_OrderBy() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "ASC"), res2, "DESC") + await Snapshot + .Create() + .AddResult(res1, "ASC") + .AddResult(res2, "DESC") .MatchAsync(); } @@ -77,10 +77,10 @@ public async Task Create_Enum_OrderBy_Nullable() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "ASC"), res2, "DESC") + await Snapshot + .Create() + .AddResult(res1, "ASC") + .AddResult(res2, "DESC") .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorObjectTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorObjectTests.cs index 588d2f26e08..7358654a909 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorObjectTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorObjectTests.cs @@ -139,10 +139,10 @@ public async Task Create_ObjectShort_OrderBy() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "ASC"), res2, "DESC") + await Snapshot + .Create() + .AddResult(res1, "ASC") + .AddResult(res2, "DESC") .MatchAsync(); } @@ -169,10 +169,10 @@ public async Task Create_ObjectNullableShort_OrderBy() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "ASC"), res2, "13") + await Snapshot + .Create() + .AddResult(res1, "ASC") + .AddResult(res2, "13") .MatchAsync(); } @@ -198,10 +198,10 @@ public async Task Create_ObjectEnum_OrderBy() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "ASC"), res2, "DESC") + await Snapshot + .Create() + .AddResult(res1, "ASC") + .AddResult(res2, "DESC") .MatchAsync(); } @@ -228,10 +228,10 @@ public async Task Create_ObjectNullableEnum_OrderBy() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "ASC"), res2, "13") + await Snapshot + .Create() + .AddResult(res1, "ASC") + .AddResult(res2, "13") .MatchAsync(); } @@ -257,10 +257,10 @@ public async Task Create_ObjectString_OrderBy() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "ASC"), res2, "DESC") + await Snapshot + .Create() + .AddResult(res1, "ASC") + .AddResult(res2, "DESC") .MatchAsync(); } @@ -287,10 +287,10 @@ public async Task Create_ObjectNullableString_OrderBy() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "ASC"), res2, "13") + await Snapshot + .Create() + .AddResult(res1, "ASC") + .AddResult(res2, "13") .MatchAsync(); } @@ -316,10 +316,10 @@ public async Task Create_ObjectBool_OrderBy() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "ASC"), res2, "DESC") + await Snapshot + .Create() + .AddResult(res1, "ASC") + .AddResult(res2, "DESC") .MatchAsync(); } @@ -346,10 +346,10 @@ public async Task Create_ObjectNullableBool_OrderBy() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "ASC"), res2, "13") + await Snapshot + .Create() + .AddResult(res1, "ASC") + .AddResult(res2, "13") .MatchAsync(); } diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorStringTests.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorStringTests.cs index 61cae8242fa..24de41aa157 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorStringTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Sorting.Tests/MongoDbSortVisitorStringTests.cs @@ -47,10 +47,10 @@ public async Task Create_String_OrderBy() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "ASC"), res2, "DESC") + await Snapshot + .Create() + .AddResult(res1, "ASC") + .AddResult(res2, "DESC") .MatchAsync(); } @@ -73,10 +73,10 @@ public async Task Create_String_OrderBy_Nullable() .Build()); // assert - await SnapshotExtensions.AddResult( - SnapshotExtensions.AddResult( - Snapshot - .Create(), res1, "ASC"), res2, "DESC") + await Snapshot + .Create() + .AddResult(res1, "ASC") + .AddResult(res2, "DESC") .MatchAsync(); } From 013a45113f130085a8bb73e5472a66b655696a7d Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 6 Nov 2024 14:11:11 +0100 Subject: [PATCH 095/154] Fixed issue with extracting predicates from IFilterContext. (#7693) --- .../src/Data/Filters/Context/FilterContext.cs | 2 +- .../FilterContextResolverContextExtensions.cs | 3 +- .../Expressions/QueryableFilterProvider.cs | 20 +- .../Filters/Extensions/QueryableExtensions.cs | 79 +++++++ .../Data/src/Data/HotChocolate.Data.csproj | 2 +- .../Data/test/Data.Tests/IntegrationTests.cs | 211 +++++++++++++----- ...sPredicate_No_Filter_Returns_All_Data.snap | 12 + ...redicate_With_Filter_Returns_Author_1.snap | 9 + 8 files changed, 275 insertions(+), 63 deletions(-) create mode 100644 src/HotChocolate/Data/src/Data/Filters/Extensions/QueryableExtensions.cs create mode 100644 src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.AsPredicate_No_Filter_Returns_All_Data.snap create mode 100644 src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.AsPredicate_With_Filter_Returns_Author_1.snap diff --git a/src/HotChocolate/Data/src/Data/Filters/Context/FilterContext.cs b/src/HotChocolate/Data/src/Data/Filters/Context/FilterContext.cs index 701c6dcb482..473c62ae554 100644 --- a/src/HotChocolate/Data/src/Data/Filters/Context/FilterContext.cs +++ b/src/HotChocolate/Data/src/Data/Filters/Context/FilterContext.cs @@ -67,7 +67,7 @@ public void Handled(bool isHandled) return null; } - private object? Serialize(IFilterValueNode? value) + private static object? Serialize(IFilterValueNode? value) { switch (value) { diff --git a/src/HotChocolate/Data/src/Data/Filters/Context/FilterContextResolverContextExtensions.cs b/src/HotChocolate/Data/src/Data/Filters/Context/FilterContextResolverContextExtensions.cs index 9b33647a710..1a8428f53fe 100644 --- a/src/HotChocolate/Data/src/Data/Filters/Context/FilterContextResolverContextExtensions.cs +++ b/src/HotChocolate/Data/src/Data/Filters/Context/FilterContextResolverContextExtensions.cs @@ -33,8 +33,7 @@ context.LocalContextData[ContextValueNodeKey] is IValueNode node return null; } - FilterContext filterContext = - new(context, filterInput, filter, context.Service()); + var filterContext = new FilterContext(context, filterInput, filter, context.Service()); // disable the execution of filtering by default filterContext.Handled(true); diff --git a/src/HotChocolate/Data/src/Data/Filters/Expressions/QueryableFilterProvider.cs b/src/HotChocolate/Data/src/Data/Filters/Expressions/QueryableFilterProvider.cs index b9a19db3a1c..38c00d9adbe 100644 --- a/src/HotChocolate/Data/src/Data/Filters/Expressions/QueryableFilterProvider.cs +++ b/src/HotChocolate/Data/src/Data/Filters/Expressions/QueryableFilterProvider.cs @@ -184,6 +184,18 @@ private ApplyFiltering CreateApplicator(string argumentName) => (context, input) => { var inMemory = IsInMemoryQuery(input); + + // if no filter is defined we can stop here and yield back control. + var skipFiltering = context.GetLocalStateOrDefault(SkipFilteringKey); + + // ensure filtering is only applied once + context.SetLocalState(SkipFilteringKey, true); + + if (skipFiltering) + { + return input; + } + var predicate = AsPredicate(context, argumentName, inMemory); if (predicate is not null) @@ -205,13 +217,7 @@ private ApplyFiltering CreateApplicator(string argumentName) var filter = context.GetLocalStateOrDefault(ContextValueNodeKey) ?? context.ArgumentLiteral(argumentName); - // if no filter is defined we can stop here and yield back control. - var skipFiltering = context.GetLocalStateOrDefault(SkipFilteringKey); - - // ensure filtering is only applied once - context.SetLocalState(SkipFilteringKey, true); - - if (filter.IsNull() || skipFiltering) + if (filter.IsNull()) { return null; } diff --git a/src/HotChocolate/Data/src/Data/Filters/Extensions/QueryableExtensions.cs b/src/HotChocolate/Data/src/Data/Filters/Extensions/QueryableExtensions.cs new file mode 100644 index 00000000000..90598cb3628 --- /dev/null +++ b/src/HotChocolate/Data/src/Data/Filters/Extensions/QueryableExtensions.cs @@ -0,0 +1,79 @@ +using HotChocolate.Data.Filters; +using HotChocolate.Execution.Processing; + +// ReSharper disable once CheckNamespace +namespace System.Linq; + +/// +/// Provides extension methods to integrate +/// with and . +/// +public static class QueryableExtensions +{ + /// + /// Applies a selection to the queryable. + /// + /// + /// The queryable that shall be projected. + /// + /// + /// The selection that shall be applied to the queryable. + /// + /// + /// The type of the queryable. + /// + /// + /// Returns a queryable that has the selection applied. + /// + /// + /// Throws if is null or if is null. + /// + public static IQueryable Select(this IQueryable queryable, ISelection selection) + { + if (queryable is null) + { + throw new ArgumentNullException(nameof(queryable)); + } + + if (selection is null) + { + throw new ArgumentNullException(nameof(selection)); + } + + return queryable.Select(selection.AsSelector()); + } + + /// + /// Applies a filter context to the queryable. + /// + /// + /// The queryable that shall be filtered. + /// + /// + /// The filter context that shall be applied to the queryable. + /// + /// + /// The type of the queryable. + /// + /// + /// Returns a queryable that has the filter applied. + /// + /// + /// Throws if is null or if is null. + /// + public static IQueryable Where(this IQueryable queryable, IFilterContext filter) + { + if (queryable is null) + { + throw new ArgumentNullException(nameof(queryable)); + } + + if (filter is null) + { + throw new ArgumentNullException(nameof(filter)); + } + + var predicate = filter.AsPredicate(); + return predicate is null ? queryable : queryable.Where(predicate); + } +} diff --git a/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj b/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj index 51eb3e22767..4b45a288672 100644 --- a/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj +++ b/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj @@ -5,7 +5,7 @@ HotChocolate.Data HotChocolate.Data Contains ready to use extensions for data management in HotChocolate. This includes filtering, projections and sorting - HC8001;$(NoWarn) + HC8001;GD0001;$(NoWarn) diff --git a/src/HotChocolate/Data/test/Data.Tests/IntegrationTests.cs b/src/HotChocolate/Data/test/Data.Tests/IntegrationTests.cs index 464d3ed3677..ec14aa7d908 100644 --- a/src/HotChocolate/Data/test/Data.Tests/IntegrationTests.cs +++ b/src/HotChocolate/Data/test/Data.Tests/IntegrationTests.cs @@ -5,6 +5,7 @@ // ReSharper disable MoveLocalFunctionAfterJumpStatement using CookieCrumble; +using HotChocolate.Data.Filters; using HotChocolate.Execution; using HotChocolate.Types; using Microsoft.Extensions.DependencyInjection; @@ -234,10 +235,11 @@ public async Task ExecuteAsync_Should_ProjectAndPage_When_BothMiddlewaresAreAppl .AddSorting() .AddProjections() .AddQueryType() - .AddObjectType(o => - o.ImplementsNode() - .IdField(f => f.Id) - .ResolveNode(_ => default!)) + .AddObjectType( + o => + o.ImplementsNode() + .IdField(f => f.Id) + .ResolveNode(_ => default!)) .BuildRequestExecutorAsync(); // act @@ -272,10 +274,11 @@ public async Task ExecuteAsync_Should_ProjectAndPage_When_BothAreAppliedAndProvi .AddSorting() .AddProjections() .AddQueryType() - .AddObjectType(o => - o.ImplementsNode() - .IdField(f => f.Id) - .ResolveNode(_ => default!)) + .AddObjectType( + o => + o.ImplementsNode() + .IdField(f => f.Id) + .ResolveNode(_ => default!)) .BuildRequestExecutorAsync(); // act @@ -310,10 +313,11 @@ public async Task ExecuteAsync_Should_ProjectAndPage_When_EdgesFragment() .AddSorting() .AddProjections() .AddQueryType() - .AddObjectType(o => - o.ImplementsNode() - .IdField(f => f.Id) - .ResolveNode(_ => default!)) + .AddObjectType( + o => + o.ImplementsNode() + .IdField(f => f.Id) + .ResolveNode(_ => default!)) .BuildRequestExecutorAsync(); // act @@ -350,10 +354,11 @@ public async Task ExecuteAsync_Should_ProjectAndPage_When_NodesFragment() .AddSorting() .AddProjections() .AddQueryType() - .AddObjectType(o => - o.ImplementsNode() - .IdField(f => f.Id) - .ResolveNode(_ => default!)) + .AddObjectType( + o => + o.ImplementsNode() + .IdField(f => f.Id) + .ResolveNode(_ => default!)) .BuildRequestExecutorAsync(); // act @@ -388,10 +393,11 @@ public async Task ExecuteAsync_Should_ProjectAndPage_When_EdgesFragmentNested() .AddSorting() .AddProjections() .AddQueryType() - .AddObjectType(o => - o.ImplementsNode() - .IdField(f => f.Id) - .ResolveNode(_ => default!)) + .AddObjectType( + o => + o.ImplementsNode() + .IdField(f => f.Id) + .ResolveNode(_ => default!)) .BuildRequestExecutorAsync(); // act @@ -429,10 +435,11 @@ public async Task ExecuteAsync_Should_ProjectAndPage_When_NodesFragmentNested() .AddSorting() .AddProjections() .AddQueryType() - .AddObjectType(o => - o.ImplementsNode() - .IdField(f => f.Id) - .ResolveNode(_ => default!)) + .AddObjectType( + o => + o.ImplementsNode() + .IdField(f => f.Id) + .ResolveNode(_ => default!)) .BuildRequestExecutorAsync(); // act @@ -468,10 +475,11 @@ public async Task ExecuteAsync_Should_ProjectAndPage_When_NodesFragmentContainsP .AddSorting() .AddProjections() .AddQueryType() - .AddObjectType(o => - o.ImplementsNode() - .IdField(f => f.Id) - .ResolveNode(_ => default!)) + .AddObjectType( + o => + o.ImplementsNode() + .IdField(f => f.Id) + .ResolveNode(_ => default!)) .BuildRequestExecutorAsync(); // act @@ -509,10 +517,11 @@ public async Task ExecuteAsync_Should_ProjectAndPage_When_NodesFragmentContainsP .AddProjections() .AddQueryType(c => c.Name("Query")) .AddTypeExtension() - .AddObjectType(o => - o.ImplementsNode() - .IdField(f => f.Id) - .ResolveNode(_ => default!)) + .AddObjectType( + o => + o.ImplementsNode() + .IdField(f => f.Id) + .ResolveNode(_ => default!)) .BuildRequestExecutorAsync(); // act @@ -554,10 +563,11 @@ public async Task ExecuteAsync_Should_ProjectAndPage_When_AliasIsSameAsAlwaysPro .AddProjections() .AddQueryType(c => c.Name("Query")) .AddTypeExtension() - .AddObjectType(o => - o.ImplementsNode() - .IdField(f => f.Id) - .ResolveNode(_ => default!)) + .AddObjectType( + o => + o.ImplementsNode() + .IdField(f => f.Id) + .ResolveNode(_ => default!)) .BuildRequestExecutorAsync(); // act @@ -664,7 +674,10 @@ query GetBooks($title: String) { } } """, - new Dictionary { ["title"] = "BookTitle", }); + new Dictionary + { + ["title"] = "BookTitle", + }); // assert await Snapshot @@ -700,7 +713,10 @@ query GetBooks($title: String) { } } """, - new Dictionary { ["title"] = "BookTitle", }); + new Dictionary + { + ["title"] = "BookTitle", + }); // assert await Snapshot @@ -812,12 +828,67 @@ The field `DuplicateAttribute.addBook` declares the data middleware `UseFilterin """); } + [Fact] + public async Task AsPredicate_No_Filter_Returns_All_Data() + { + // arrange + var executor = await new ServiceCollection() + .AddGraphQL() + .AddFiltering() + .AddSorting() + .AddProjections() + .AddQueryType() + .BuildRequestExecutorAsync(); + + // act + var result = await executor.ExecuteAsync( + """ + { + authors { + name + } + } + """); + + // assert + result.MatchSnapshot(); + } + + [Fact] + public async Task AsPredicate_With_Filter_Returns_Author_1() + { + // arrange + var executor = await new ServiceCollection() + .AddGraphQL() + .AddFiltering() + .AddSorting() + .AddProjections() + .AddQueryType() + .BuildRequestExecutorAsync(); + + // act + var result = await executor.ExecuteAsync( + """ + { + authors(where: { name: { eq: "Author1" } }) { + name + } + } + """); + + // assert + result.MatchSnapshot(); + } + [QueryType] public static class StaticQuery { [UseOffsetPaging] public static IEnumerable GetBars() - => new[] { Bar.Create("tox"), }; + => new[] + { + Bar.Create("tox"), + }; } public class FooType : ObjectType @@ -827,15 +898,16 @@ protected override void Configure(IObjectTypeDescriptor descriptor) descriptor .Field("foos") .Type>>() - .Resolve(_ => - { - var data = new[] + .Resolve( + _ => { - Bar.Create("a"), - Bar.Create("b"), - }.AsQueryable(); - return Task.FromResult(data); - }) + var data = new[] + { + Bar.Create("a"), + Bar.Create("b"), + }.AsQueryable(); + return Task.FromResult(data); + }) .UseFiltering(); } } @@ -844,7 +916,10 @@ public class Bar { public string Qux { get; set; } = default!; - public static Bar Create(string qux) => new() { Qux = qux, }; + public static Bar Create(string qux) => new() + { + Qux = qux, + }; } public class PagingAndProjection @@ -858,7 +933,10 @@ public IQueryable GetBooks() { Id = 1, Title = "BookTitle", - Author = new Author { Name = "Author", }, + Author = new Author + { + Name = "Author", + }, }, }.AsQueryable(); } @@ -877,7 +955,10 @@ public IQueryable GetBooks() { Id = 1, Title = "BookTitle", - Author = new Author { Name = "Author", }, + Author = new Author + { + Name = "Author", + }, }, }.AsQueryable(); } @@ -958,7 +1039,10 @@ public IQueryable AddBook(Book book) new Author { Name = "Author", - Books = new List { book, }, + Books = new List + { + book, + }, }, }.AsQueryable(); } @@ -973,8 +1057,31 @@ public IQueryable AddBook(Book book) new Author { Name = "Author", - Books = new List { book, }, + Books = new List + { + book, + }, }, }.AsQueryable(); } + + public class AsPredicateQuery + { + [UseFiltering] + public IQueryable GetAuthors(IFilterContext filter) + => new[] + { + new Author + { + Name = "Author1", + Books = new List(), + }, + new Author + { + Name = "Author2", + Books = new List() + }, + }.AsQueryable() + .Where(filter); + } } diff --git a/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.AsPredicate_No_Filter_Returns_All_Data.snap b/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.AsPredicate_No_Filter_Returns_All_Data.snap new file mode 100644 index 00000000000..87c6e7027ad --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.AsPredicate_No_Filter_Returns_All_Data.snap @@ -0,0 +1,12 @@ +{ + "data": { + "authors": [ + { + "name": "Author1" + }, + { + "name": "Author2" + } + ] + } +} diff --git a/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.AsPredicate_With_Filter_Returns_Author_1.snap b/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.AsPredicate_With_Filter_Returns_Author_1.snap new file mode 100644 index 00000000000..80de323c1d5 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.AsPredicate_With_Filter_Returns_Author_1.snap @@ -0,0 +1,9 @@ +{ + "data": { + "authors": [ + { + "name": "Author1" + } + ] + } +} From d8ef0636d03a541f9282b6fa3bb2a7bb90915ac4 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 6 Nov 2024 14:49:42 +0100 Subject: [PATCH 096/154] Fixed Tests --- .../AnnotationBasedMutations.cs | 2 ++ ...rationTests.ExecutePersistedOperation.snap | 19 +++------- ...ts.ExecutePersistedOperation_NotFound.snap | 35 ++++--------------- 3 files changed, 13 insertions(+), 43 deletions(-) diff --git a/src/HotChocolate/Core/test/Types.Mutations.Tests/AnnotationBasedMutations.cs b/src/HotChocolate/Core/test/Types.Mutations.Tests/AnnotationBasedMutations.cs index 033b3f93383..2af5f0fd574 100644 --- a/src/HotChocolate/Core/test/Types.Mutations.Tests/AnnotationBasedMutations.cs +++ b/src/HotChocolate/Core/test/Types.Mutations.Tests/AnnotationBasedMutations.cs @@ -436,6 +436,7 @@ public async Task SimpleMutation_Override_Payload() schema.MatchSnapshot(); } +#if NET7_0_OR_GREATER [Fact] public async Task SimpleMutation_Override_Payload_WithError() { @@ -450,6 +451,7 @@ public async Task SimpleMutation_Override_Payload_WithError() schema.MatchSnapshot(); } +#endif [Fact] public async Task SimpleMutation_Override_Input() diff --git a/src/HotChocolate/PersistedOperations/test/PersistedOperations.FileSystem.Tests/__snapshots__/IntegrationTests.ExecutePersistedOperation.snap b/src/HotChocolate/PersistedOperations/test/PersistedOperations.FileSystem.Tests/__snapshots__/IntegrationTests.ExecutePersistedOperation.snap index 4c9157734be..20b47cc26bc 100644 --- a/src/HotChocolate/PersistedOperations/test/PersistedOperations.FileSystem.Tests/__snapshots__/IntegrationTests.ExecutePersistedOperation.snap +++ b/src/HotChocolate/PersistedOperations/test/PersistedOperations.FileSystem.Tests/__snapshots__/IntegrationTests.ExecutePersistedOperation.snap @@ -1,19 +1,8 @@ -{ - "Kind": "SingleResult", - "RequestIndex": null, - "VariableIndex": null, - "Label": null, - "Path": null, - "Data": { +{ + "data": { "__typename": "Query" }, - "Items": null, - "Errors": null, - "Extensions": { + "extensions": { "persistedDocument": true - }, - "Incremental": null, - "ContextData": null, - "HasNext": null, - "IsDataSet": true + } } diff --git a/src/HotChocolate/PersistedOperations/test/PersistedOperations.FileSystem.Tests/__snapshots__/IntegrationTests.ExecutePersistedOperation_NotFound.snap b/src/HotChocolate/PersistedOperations/test/PersistedOperations.FileSystem.Tests/__snapshots__/IntegrationTests.ExecutePersistedOperation_NotFound.snap index e17c53c9eec..bfebe739706 100644 --- a/src/HotChocolate/PersistedOperations/test/PersistedOperations.FileSystem.Tests/__snapshots__/IntegrationTests.ExecutePersistedOperation_NotFound.snap +++ b/src/HotChocolate/PersistedOperations/test/PersistedOperations.FileSystem.Tests/__snapshots__/IntegrationTests.ExecutePersistedOperation_NotFound.snap @@ -1,32 +1,11 @@ -{ - "Kind": "SingleResult", - "RequestIndex": null, - "VariableIndex": null, - "Label": null, - "Path": null, - "Data": null, - "Items": null, - "Errors": [ +{ + "errors": [ { - "Message": "The specified persisted operation key is invalid.", - "Code": "HC0020", - "Path": null, - "Locations": null, - "Extensions": { + "message": "The specified persisted operation key is invalid.", + "extensions": { "code": "HC0020", - "requestedKey": { - "IsEmpty": false, - "Value": "does_not_exist" - } - }, - "Exception": null + "requestedKey": "does_not_exist" + } } - ], - "Extensions": null, - "Incremental": null, - "ContextData": { - "HotChocolate.Execution.Transport.HttpStatusCode": 400 - }, - "HasNext": null, - "IsDataSet": false + ] } From 5c2a86a983ff4f5c99a254ec75a07ba126551bef Mon Sep 17 00:00:00 2001 From: PascalSenn Date: Sat, 16 Nov 2024 10:24:15 +0100 Subject: [PATCH 097/154] Fixed URL-safe ID serialization (#7725) --- .../Types/Relay/Serialization/OptimizedNodeIdSerializer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/Serialization/OptimizedNodeIdSerializer.cs b/src/HotChocolate/Core/src/Types/Types/Relay/Serialization/OptimizedNodeIdSerializer.cs index 6eb58166633..eebfe2e64d5 100644 --- a/src/HotChocolate/Core/src/Types/Types/Relay/Serialization/OptimizedNodeIdSerializer.cs +++ b/src/HotChocolate/Core/src/Types/Types/Relay/Serialization/OptimizedNodeIdSerializer.cs @@ -41,12 +41,12 @@ internal OptimizedNodeIdSerializer( _stringSerializerMap = boundSerializers.ToFrozenDictionary( t => t.TypeName, - t => new Serializer(t.TypeName, t.Serializer, outputNewIdFormat, _urlSafeBase64)); + t => new Serializer(t.TypeName, t.Serializer, outputNewIdFormat, urlSafeBase64)); #else _stringSerializerMap = boundSerializers.ToDictionary( t => t.TypeName, - t => new Serializer(t.TypeName, t.Serializer, outputNewIdFormat, _urlSafeBase64)); + t => new Serializer(t.TypeName, t.Serializer, outputNewIdFormat, urlSafeBase64)); #endif _serializers = allSerializers; _spanSerializerMap = new SpanSerializerMap(); From 2f074702ddabb4a630c0df831a2859a752dad22c Mon Sep 17 00:00:00 2001 From: Glen Date: Wed, 20 Nov 2024 18:50:05 +0200 Subject: [PATCH 098/154] Added .NET 9 (#7741) --- .github/workflows/ci.yml | 12 +- .github/workflows/coverage.yml | 10 +- .github/workflows/pr-labeler.yml | 4 +- global.json | 2 +- .../src/CookieCrumble/CookieCrumble.csproj | 2 +- .../src/CookieCrumble/TestEnvironment.cs | 5 + src/Directory.Build.props | 12 +- src/Directory.Packages.props | 39 +- .../Request/DefaultQueryRequestFactory.cs | 2 +- .../Core/src/Abstractions/Error.cs | 2 + .../Core/src/Abstractions/ErrorBuilder.cs | 2 + .../Core/src/Core/HotChocolate.Core.csproj | 5 + .../Execution/HotChocolate.Execution.csproj | 4 + .../Processing/Result/ObjectResult.cs | 12 +- .../Core/src/Types.Analyzers/Errors.cs | 32 +- .../HotChocolate.Types.Analyzers.csproj | 6 +- .../Core/src/Types/SchemaBuilder.Setup.cs | 2 +- .../Abstractions.Tests/ErrorBuilderTests.cs | 2 + ...colate.Subscriptions.Postgres.Tests.csproj | 2 +- .../HotChocolate.Types.Analyzers.Tests.csproj | 3 +- .../test/Types.Analyzers.Tests/TestHelper.cs | 11 +- ..._GenericBatchDataLoader_MatchesSnapshot.md | 6 +- .../Data.AutoMapper.Tests/ProjectToTests.cs | 12 + ...ToTests.Execute_ManyToOne_Deep_NET9_0.snap | 107 + ...ojectToTests.Execute_ManyToOne_NET9_0.snap | 56 + ...tToTests.Execute_OneToOne_Deep_NET9_0.snap | 73 + .../QueryableFilterVisitorComparableTests.cs | 16 + ...ableBooleanNotEqual_Expression_NET9_0.snap | 72 + ...ests.Create_ShortIn_Expression_NET9_0.snap | 83 + ...s.Create_ShortNotIn_Expression_NET9_0.snap | 77 + ...ate_ShortNullableIn_Expression_NET9_0.snap | 83 + ...ortNullableNotEqual_Expression_NET9_0.snap | 81 + ...GreaterThanOrEquals_Expression_NET9_0.snap | 111 + ...lableNotGreaterThan_Expression_NET9_0.snap | 120 + ..._ShortNullableNotIn_Expression_NET9_0.snap | 83 + ...otLowerThanOrEquals_Expression_NET9_0.snap | 111 + ...ullableNotLowerThan_Expression_NET9_0.snap | 120 + ...Tests.Create_EnumIn_Expression_NET9_0.snap | 78 + ...ts.Create_EnumNotIn_Expression_NET9_0.snap | 84 + ...eate_NullableEnumIn_Expression_NET9_0.snap | 80 + ...ullableEnumNotEqual_Expression_NET9_0.snap | 90 + ...e_NullableEnumNotIn_Expression_NET9_0.snap | 95 + ....Create_StringConcatExpression_NET9_0.snap | 56 + ...llObjectStringEqual_Expression_NET9_0.snap | 74 + ...nyObjectStringEqual_Expression_NET9_0.snap | 131 ++ ...neObjectStringEqual_Expression_NET9_0.snap | 167 ++ ...StringEqualWithNull_Expression_NET9_0.snap | 154 ++ ...ArrayAnyStringEqual_Expression_NET9_0.snap | 143 ++ ...rraySomeStringEqual_Expression_NET9_0.snap | 73 + ...Create_ObjectEnumIn_Expression_NET9_0.snap | 91 + ...bjectNullableEnumIn_Expression_NET9_0.snap | 93 + ...jectNullableShortIn_Expression_NET9_0.snap | 113 + ...reate_ObjectShortIn_Expression_NET9_0.snap | 103 + ...eate_ObjectStringIn_Expression_NET9_0.snap | 103 + ...lableStringContains_Expression_NET9_0.snap | 71 + ...lableStringEndsWith_Expression_NET9_0.snap | 71 + ...te_NullableStringIn_Expression_NET9_0.snap | 80 + ...bleStringNoContains_Expression_NET9_0.snap | 77 + ...leStringNotEndsWith_Expression_NET9_0.snap | 77 + ...lableStringNotEqual_Expression_NET9_0.snap | 72 + ...NullableStringNotIn_Expression_NET9_0.snap | 77 + ...StringNotStartsWith_Expression_NET9_0.snap | 77 + ...bleStringStartsWith_Expression_NET9_0.snap | 71 + ...eate_StringContains_Expression_NET9_0.snap | 71 + ...eate_StringEndsWith_Expression_NET9_0.snap | 71 + ...sts.Create_StringIn_Expression_NET9_0.snap | 80 + ...te_StringNoContains_Expression_NET9_0.snap | 71 + ...e_StringNotEndsWith_Expression_NET9_0.snap | 71 + ....Create_StringNotIn_Expression_NET9_0.snap | 73 + ...StringNotStartsWith_Expression_NET9_0.snap | 71 + ...te_StringStartsWith_Expression_NET9_0.snap | 71 + .../QueryableProjectionFilterTests.cs | 16 + .../QueryableProjectionHashSetTest.cs | 8 + .../QueryableProjectionSetTest.cs | 8 + .../QueryableProjectionSortedSetTest.cs | 8 + .../QueryableProjectionSortingTests.cs | 8 + ...DeepFilterObjectTwoProjections_NET9_0.snap | 42 + ...rObjectTwoProjections_Nullable_NET9_0.snap | 45 + ...ObjectDifferentLevelProjection_NET9_0.snap | 44 + ...ferentLevelProjection_Nullable_NET9_0.snap | 49 + ...DeepFilterObjectTwoProjections_NET9_0.snap | 46 + ...ObjectDifferentLevelProjection_NET9_0.snap | 48 + ...DeepFilterObjectTwoProjections_NET9_0.snap | 46 + ...ObjectDifferentLevelProjection_NET9_0.snap | 48 + ...DeepFilterObjectTwoProjections_NET9_0.snap | 46 + ...ObjectDifferentLevelProjection_NET9_0.snap | 48 + ...DeepFilterObjectTwoProjections_NET9_0.snap | 70 + ...ObjectDifferentLevelProjection_NET9_0.snap | 72 + ....Create_StringConcatExpression_NET9_0.snap | 49 + .../Data/test/Data.Tests/ExecutableTests.cs | 7 +- ...lyOneItem_When_SingleOrDefault_NET9_0.snap | 17 + src/HotChocolate/Fusion/Directory.Build.props | 2 +- ...olate.Fusion.Aspire.Analyzers.Tests.csproj | 4 +- .../FilterVisitorTestBase.cs | 10 +- .../FilteringAndPaging.cs | 2 +- .../QueryableFilterVisitorBooleanTests.cs | 8 +- .../QueryableFilterVisitorComparableTests.cs | 48 +- .../QueryableFilterVisitorEnumTests.cs | 16 +- .../QueryableFilterVisitorExecutableTests.cs | 8 +- .../QueryableFilterVisitorInterfacesTests.cs | 4 +- .../QueryableFilterVisitorListTests.cs | 8 +- .../QueryableFilterVisitorObjectTests.cs | 28 +- .../QueryableFilterVisitorStringTests.cs | 40 +- .../Data.Marten.Filters.Tests/SchemaCache.cs | 8 +- ..._ArrayAnyObjectStringEqual_Expression.snap | 2 +- ...ArrayNoneObjectStringEqual_Expression.snap | 4 +- ...eObjectStringEqualWithNull_Expression.snap | 4 +- ...tNestedArrayAnyStringEqual_Expression.snap | 2 +- ...NestedArraySomeStringEqual_Expression.snap | 4 +- .../QueryableSortVisitorBooleanTests.cs | 6 +- .../QueryableSortVisitorComparableTests.cs | 4 +- .../QueryableSortVisitorEnumTests.cs | 4 +- .../QueryableSortVisitorExecutableTests.cs | 6 +- .../QueryableSortVisitorExpressionTests.cs | 2 +- .../QueryableSortVisitorObjectTests.cs | 20 +- .../QueryableSortVisitorStringTests.cs | 4 +- .../Data.Marten.Sorting.Tests/SchemaCache.cs | 6 +- .../SortVisitorTestBase.cs | 10 +- ...sts.Create_CollectionLengthExpression.snap | 4 +- .../Relay/ObjectIdNodeIdValueSerializer.cs | 2 +- .../OpenApi/Directory.Build.props | 2 +- .../RequestExecutorBuilderExtensions.cs | 6 +- .../Extensions/StringExtensions.cs | 4 +- .../Helpers/GraphQLNamingHelper.cs | 2 +- .../PagingHelperIntegrationTests.cs | 9 - ...ngHelperTests.BatchPaging_Last_5_NET9_0.md | 18 +- ...ullable_Connections_Dont_Throw_2_NET9_0.md | 10 +- ..._Nullable_Connections_Dont_Throw_NET9_0.md | 10 +- ...agingHelperTests.GetDefaultPage2_NET9_0.md | 77 + ...PagingHelperTests.GetDefaultPage_NET9_0.md | 77 + ...erTests.GetDefaultPage_With_Deep_NET9_0.md | 169 ++ ...DefaultPage_With_Deep_SecondPage_NET9_0.md | 68 + ...faultPage_With_Nullable_Fallback_NET9_0.md | 169 ++ ...ith_Nullable_Fallback_SecondPage_NET9_0.md | 68 + ...sts.GetDefaultPage_With_Nullable_NET9_0.md | 169 ++ ...ultPage_With_Nullable_SecondPage_NET9_0.md | 68 + ...Tests.GetSecondPage_With_2_Items_NET9_0.md | 48 + ...ap_Page_To_Connection_With_Dto_2_NET9_0.md | 47 + ....Map_Page_To_Connection_With_Dto_NET9_0.md | 47 + ...elperTests.Nested_Paging_First_2_NET9_0.md | 23 +- ..._Paging_First_2_With_Projections_NET9_0.md | 23 +- ...perTests.Paging_Empty_PagingArgs_NET9_0.md | 1236 +++++++++++ ...Tests.Paging_First_5_After_Id_13_NET9_0.md | 101 + ...ests.Paging_First_5_Before_Id_96_NET9_0.md | 101 + ...PagingHelperTests.Paging_First_5_NET9_0.md | 98 + ...nPagingHelperTests.Paging_Last_5_NET9_0.md | 98 + ...grationTests.Query_Owner_Animals_NET9_0.md | 164 ++ ...ery_Owner_Animals_With_Fragments_NET9_0.md | 175 ++ .../Skimmed/Directory.Build.props | 2 +- .../ReadOnlyObjectTypeDefinitionCollection.cs | 2 +- .../DependencyInjection/ServiceFactory.cs | 54 - .../Utilities.Tests/ServiceFactoryTests.cs | 108 - .../src/Razor/StrawberryShake.Razor.csproj | 2 +- .../Analyzers/FieldCollector.cs | 2 + .../Analyzers/FragmentHelper.cs | 2 + ...nyScalarDefaultSerializationTest.Client.cs | 35 +- .../Integration/EntityIdOrDataTest.Client.cs | 165 +- .../Integration/MultiProfileTest.Client.cs | 478 +++- ...tarWarsGetFriendsDeferInListTest.Client.cs | 332 ++- .../StarWarsGetFriendsDeferredTest.Client.cs | 337 ++- .../StarWarsGetFriendsNoStoreTest.Client.cs | 128 +- .../StarWarsGetFriendsTest.Client.cs | 209 +- .../Integration/StarWarsGetHeroTest.Client.cs | 114 +- .../StarWarsGetHeroTraitsTest.Client.cs | 126 +- .../StarWarsIntrospectionTest.Client.cs | 1114 ++++++++-- ...tarWarsOnReviewSubCompletionTest.Client.cs | 107 +- ...tarWarsOnReviewSubGraphQLSSETest.Client.cs | 107 +- .../StarWarsOnReviewSubNoStoreTest.Client.cs | 104 +- ...StarWarsTypeNameOnInterfacesTest.Client.cs | 109 +- .../StarWarsTypeNameOnUnionsTest.Client.cs | 129 +- .../StarWarsUnionListTest.Client.cs | 266 ++- .../Integration/UploadScalarTest.Client.cs | 335 ++- .../UploadScalar_InMemoryTest.Client.cs | 335 ++- ...ectionGeneratorTests.Default_Combined.snap | 259 ++- ...sts.Default_DifferentTransportMethods.snap | 259 ++- ...ectionGeneratorTests.Default_InMemory.snap | 259 ++- ...onGeneratorTests.Default_MultiProfile.snap | 259 ++- ...ectionGeneratorTests.Default_Mutation.snap | 99 +- ...InjectionGeneratorTests.Default_Query.snap | 80 +- ...onGeneratorTests.Default_Subscription.snap | 88 +- ...ookClient_DataInEntity_UnionDataTypes.snap | 189 +- ...aInEntity_UnionDataTypes_With_Records.snap | 189 +- ...ookClient_DataOnly_InterfaceDataTypes.snap | 127 +- ...aOnly_InterfaceDataTypes_With_Records.snap | 127 +- ...te_BookClient_DataOnly_UnionDataTypes.snap | 107 +- ..._DataOnly_UnionDataTypes_With_Records.snap | 107 +- ...rate_ChatClient_ConnectionNotAnEntity.snap | 148 +- ...nt_ConnectionNotAnEntity_With_Records.snap | 148 +- ...lient_MapperMapsEntityOnRootCorrectly.snap | 675 +++++- ...apsEntityOnRootCorrectly_With_Records.snap | 675 +++++- ...ryGeneratorTests.Simple_ComplexEntity.snap | 93 +- ...torTests.Simple_DateTimeOffset_Entity.snap | 87 +- ...FactoryGeneratorTests.Simple_IdEntity.snap | 87 +- ...FactoryGeneratorTests.Simple_NoEntity.snap | 69 +- ...toryGeneratorTests.Simple_Uuid_Entity.snap | 87 +- ...tityOrIdGeneratorTests.InterfaceField.snap | 177 +- ...ntityOrIdGeneratorTests.InterfaceList.snap | 177 +- ...GeneratorTests.NonNullableValueTypeId.snap | 165 +- .../EntityOrIdGeneratorTests.UnionField.snap | 165 +- .../EntityOrIdGeneratorTests.UnionList.snap | 165 +- ...yOrIdGeneratorTests.UnionListInEntity.snap | 204 +- ...dGeneratorTests.UnionWithNestedObject.snap | 343 ++- ....Generate_ChatClient_InvalidNullCheck.snap | 164 +- ...ErrorGeneratorTests.Generate_NoErrors.snap | 130 +- ...pe_Fields_Are_Inspected_For_LeafTypes.snap | 176 +- ...InputGeneratorTests.KeywordCollisions.snap | 180 +- ...eneratorTests.Operation_With_Comments.snap | 145 +- ...tion_With_Comments_With_Input_Records.snap | 145 +- ...ests.Operation_With_ComplexInputTypes.snap | 69 +- ...ests.Operation_With_Complex_Arguments.snap | 145 +- ...orTests.Operation_With_FirstNonUpload.snap | 101 +- ...torTests.Operation_With_LastNonUpload.snap | 101 +- ...ratorTests.Operation_With_UploadAsArg.snap | 339 ++- ...peration_With_UploadInDeepInputObject.snap | 65 +- ...ts.Operation_With_UploadInInputObject.snap | 62 +- ...ests.Generate_StarWarsIntegrationTest.snap | 229 +- ...torTests.Interface_With_Default_Names.snap | 89 +- ...e_With_Fragment_Definition_Two_Models.snap | 277 ++- ...torTests.Operation_With_Leaf_Argument.snap | 108 +- ...torTests.Operation_With_Type_Argument.snap | 169 +- ...neratorTests.StarWarsTypeNameOnUnions.snap | 63 +- ...rWarsGeneratorTests.StarWarsUnionList.snap | 109 +- ...Tests.Subscription_With_Default_Names.snap | 102 +- ...sts.Generate_ChatClient_AllOperations.snap | 1935 ++++++++++++++++- ...NullableValueType_WithoutGlobal_Input.snap | 62 +- ...atorTests.NonNullable_ValueType_Input.snap | 62 +- ...ionGeneratorTests.Nullable_List_Input.snap | 63 +- ...neratorTests.Nullable_ValueType_Input.snap | 62 +- ...sts.Operation_With_MultipleOperations.snap | 446 +++- ...ests.Response_Name_Is_Correctly_Cased.snap | 60 +- ...onGeneratorTests.Simple_Custom_Scalar.snap | 13 +- ...esultTypeGeneratorTests.Nested_Entity.snap | 481 +++- ...eneratorTests.Operation_With_Comments.snap | 168 +- ...torTests.Operation_With_Complex_Types.snap | 168 +- ...atorTests.Operation_With_NullableData.snap | 197 +- .../ScalarGeneratorTests.Any_Scalar.snap | 68 +- .../ScalarGeneratorTests.Any_Type.snap | 69 +- ...arGeneratorTests.ByteArray_ScalarType.snap | 46 +- ...omplete_Schema_With_Uuid_And_DateTime.snap | 175 +- ...rTests.Custom_Scalar_With_RuntimeType.snap | 69 +- ...ar_With_RuntimeType_ValueType_AsInput.snap | 95 +- ....Custom_Scalar_With_SerializationType.snap | 69 +- ...ith_SerializationType_And_RuntimeType.snap | 69 +- ...ustom_Scalar_With_Unknown_RuntimeType.snap | 69 +- ...larGeneratorTests.Only_Custom_Scalars.snap | 61 +- ...rTests.Scalars_Are_Correctly_Inferred.snap | 117 +- ...arGeneratorTests.Simple_Custom_Scalar.snap | 69 +- ...rGeneratorTests.TimeSpan_Not_Detected.snap | 131 +- .../ScalarGeneratorTests.Uri_Type.snap | 66 +- .../ScalarGeneratorTests.Uuid_Type.snap | 68 +- ...aGeneratorTests.Create_DataType_Query.snap | 83 +- ...hemaGeneratorTests.Create_GetFeatById.snap | 218 +- ...emaGeneratorTests.Create_GetFeatsPage.snap | 248 ++- ...pleSearch_From_ActiveDirectory_Schema.snap | 569 ++++- ...atorTests.Create_Query_With_Skip_Take.snap | 213 +- ...orTests.Create_UpdateMembers_Mutation.snap | 164 +- ...ests.EnumWithUnderscorePrefixedValues.snap | 40 +- ...eratorTests.FieldsWithUnderlineInName.snap | 887 +++++++- ...emaGeneratorTests.Full_Extension_File.snap | 138 +- .../SchemaGeneratorTests.HasuraMutation.snap | 198 +- ...hemaGeneratorTests.IntrospectionQuery.snap | 1114 ++++++++-- ...eneratorTests.LowerCaseScalarArgument.snap | 134 +- ...GeneratorTests.MultiLineDocumentation.snap | 30 +- ...aGeneratorTests.NodeTypenameCollision.snap | 97 +- .../SchemaGeneratorTests.NonNullLists.snap | 188 +- ...chemaGeneratorTests.QueryInterference.snap | 883 +++++++- ...atorTests.Query_With_Nested_Fragments.snap | 315 ++- ...eneratorTests.Schema_With_Spec_Errors.snap | 138 +- ..._Client_With_Internal_Access_Modifier.snap | 130 +- ...ests.Generate_StarWarsIntegrationTest.snap | 232 +- ...torTests.Interface_With_Default_Names.snap | 130 +- ...e_With_Fragment_Definition_Two_Models.snap | 358 ++- ...torTests.Operation_With_Leaf_Argument.snap | 149 +- ...torTests.Operation_With_Type_Argument.snap | 172 +- ...neratorTests.StarWarsTypeNameOnUnions.snap | 129 +- ...rWarsGeneratorTests.StarWarsUnionList.snap | 173 +- ...Tests.Subscription_With_Default_Names.snap | 105 +- ...azorGeneratorTests.Query_And_Mutation.snap | 240 +- src/StrawberryShake/Directory.Build.props | 4 +- 279 files changed, 32376 insertions(+), 1789 deletions(-) create mode 100644 src/HotChocolate/Data/test/Data.AutoMapper.Tests/__snapshots__/ProjectToTests.Execute_ManyToOne_Deep_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.AutoMapper.Tests/__snapshots__/ProjectToTests.Execute_ManyToOne_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.AutoMapper.Tests/__snapshots__/ProjectToTests.Execute_OneToOne_Deep_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorBooleanTests.Create_NullableBooleanNotEqual_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortIn_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNotIn_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableIn_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableNotEqual_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableNotGreaterThanOrEquals_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableNotGreaterThan_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableNotIn_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableNotLowerThanOrEquals_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableNotLowerThan_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorEnumTests.Create_EnumIn_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorEnumTests.Create_EnumNotIn_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorEnumTests.Create_NullableEnumIn_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorEnumTests.Create_NullableEnumNotEqual_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorEnumTests.Create_NullableEnumNotIn_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorExpressionTests.Create_StringConcatExpression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArrayAllObjectStringEqual_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArrayAnyObjectStringEqual_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArrayNoneObjectStringEqual_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArraySomeObjectStringEqualWithNull_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ArrayObjectNestedArrayAnyStringEqual_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ArrayObjectNestedArraySomeStringEqual_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ObjectEnumIn_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ObjectNullableEnumIn_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ObjectNullableShortIn_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ObjectShortIn_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ObjectStringIn_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringContains_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringEndsWith_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringIn_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringNoContains_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringNotEndsWith_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringNotEqual_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringNotIn_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringNotStartsWith_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringStartsWith_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringContains_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringEndsWith_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringIn_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringNoContains_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringNotEndsWith_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringNotIn_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringNotStartsWith_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringStartsWith_Expression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionFilterTests.Create_DeepFilterObjectTwoProjections_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionFilterTests.Create_DeepFilterObjectTwoProjections_Nullable_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionFilterTests.Create_ListObjectDifferentLevelProjection_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionFilterTests.Create_ListObjectDifferentLevelProjection_Nullable_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionHashSetTests.Create_DeepFilterObjectTwoProjections_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionHashSetTests.Create_ListObjectDifferentLevelProjection_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionISetTests.Create_DeepFilterObjectTwoProjections_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionISetTests.Create_ListObjectDifferentLevelProjection_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionSortedSetTests.Create_DeepFilterObjectTwoProjections_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionSortedSetTests.Create_ListObjectDifferentLevelProjection_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionSortingTests.Create_DeepFilterObjectTwoProjections_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionSortingTests.Create_ListObjectDifferentLevelProjection_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Sorting.SqlLite.Tests/__snapshots__/QueryableSortVisitorExpressionTests.Create_StringConcatExpression_NET9_0.snap create mode 100644 src/HotChocolate/Data/test/Data.Tests/__snapshots__/ExecutableTests.ExecuteAsync_Should_OnlyOneItem_When_SingleOrDefault_NET9_0.snap create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage2_NET9_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_NET9_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_NET9_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_SecondPage_NET9_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_NET9_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_SecondPage_NET9_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_NET9_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_SecondPage_NET9_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetSecondPage_With_2_Items_NET9_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_2_NET9_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_NET9_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Empty_PagingArgs_NET9_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_After_Id_13_NET9_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_Before_Id_96_NET9_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_NET9_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Last_5_NET9_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_NET9_0.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments_NET9_0.md delete mode 100644 src/HotChocolate/Utilities/src/Utilities/DependencyInjection/ServiceFactory.cs delete mode 100644 src/HotChocolate/Utilities/test/Utilities.Tests/ServiceFactoryTests.cs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0b41eb47de5..7243095bca4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,7 @@ on: pull_request: branches: - main + - main-version-* concurrency: group: ci-new-2-${{ github.event.pull_request.number }} @@ -121,7 +122,7 @@ jobs: website/.cache/yarn key: ${{ runner.os }}-yarn-${{ hashFiles('website/yarn.lock') }} restore-keys: | - ${{ runner.os }}-yarn- + ${{ runner.os }}-yarn-${{ hashFiles('website/yarn.lock') }} - name: Install Packages run: yarn --immutable --network-timeout 100000 @@ -149,9 +150,7 @@ jobs: - name: Install .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: | - 7.x - 8.x + dotnet-version: 9.x - name: Generate Test Matrix run: dotnet run --project ./.build -- GenerateMatrix @@ -183,10 +182,11 @@ jobs: 6.x 7.x 8.x + 9.x - name: Run Build id: run-build - run: dotnet build ${{ matrix.path }} --framework net8.0 --verbosity q + run: dotnet build ${{ matrix.path }} --framework net9.0 --verbosity q timeout-minutes: 5 - name: Run tests @@ -196,7 +196,7 @@ jobs: run: > dotnet test ${{ matrix.path }} --collect:"XPlat Code Coverage;Format=opencover" - --framework net8.0 + --framework net9.0 --logger trx --no-build -- diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 77e34fa58f9..1ae90e79f27 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -4,6 +4,7 @@ on: push: branches: - main + - main-version-* paths: - 'src/**' @@ -28,9 +29,7 @@ jobs: - name: Install .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: | - 7.x - 8.x + dotnet-version: 9.x - name: Generate Test Matrix run: dotnet run --project ./.build -- GenerateMatrix @@ -61,10 +60,11 @@ jobs: 6.x 7.x 8.x + 9.x - name: Run Build id: run-build - run: dotnet build ${{ matrix.path }} --framework net8.0 --verbosity q + run: dotnet build ${{ matrix.path }} --framework net9.0 --verbosity q timeout-minutes: 5 - name: Run tests @@ -74,7 +74,7 @@ jobs: run: > dotnet test ${{ matrix.path }} --collect:"XPlat Code Coverage;Format=opencover" - --framework net8.0 + --framework net9.0 --logger trx --no-build --verbosity q diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml index 7af7d8ce11f..db241609750 100644 --- a/.github/workflows/pr-labeler.yml +++ b/.github/workflows/pr-labeler.yml @@ -1,9 +1,7 @@ name: Pull Request Labeler on: - pull_request_target: - branches: - - main + pull_request_target permissions: contents: read diff --git a/global.json b/global.json index 17c0c8e40e1..2bc13e80ad8 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "8.0.101", + "version": "9.0.100", "rollForward": "latestMinor" } } diff --git a/src/CookieCrumble/src/CookieCrumble/CookieCrumble.csproj b/src/CookieCrumble/src/CookieCrumble/CookieCrumble.csproj index cab13a0302a..6a82d9b6bfe 100644 --- a/src/CookieCrumble/src/CookieCrumble/CookieCrumble.csproj +++ b/src/CookieCrumble/src/CookieCrumble/CookieCrumble.csproj @@ -21,7 +21,7 @@ - + diff --git a/src/CookieCrumble/src/CookieCrumble/TestEnvironment.cs b/src/CookieCrumble/src/CookieCrumble/TestEnvironment.cs index 48e211fb0d1..d27065452d5 100644 --- a/src/CookieCrumble/src/CookieCrumble/TestEnvironment.cs +++ b/src/CookieCrumble/src/CookieCrumble/TestEnvironment.cs @@ -22,6 +22,11 @@ public static class TestEnvironment /// The target framework identifier. /// public const string TargetFramework = "NET8_0"; +#elif NET9_0 + /// + /// The target framework identifier. + /// + public const string TargetFramework = "NET9_0"; #endif public static bool IsLocalEnvironment() diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 0fffd3a8dc9..145c9a2297a 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -40,13 +40,13 @@ - net8.0; net7.0; net6.0; netstandard2.0 - net8.0; net7.0; net6.0 - net8.0; net7.0 - net8.0; net7.0; net6.0 - net8.0; net7.0; net6.0 + net9.0; net8.0; net7.0; net6.0; netstandard2.0 + net9.0; net8.0; net7.0; net6.0 + net9.0; net8.0; net7.0 + net9.0; net8.0; net7.0; net6.0 + net9.0; net8.0; net7.0; net6.0 net7.0; net6.0 - net8.0; net7.0; net6.0 + net9.0; net8.0; net7.0; net6.0 netstandard2.0 diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index d52fb6f5b92..a417081fb07 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -12,7 +12,8 @@ - + + @@ -21,16 +22,16 @@ - + - - - + + + @@ -48,8 +49,8 @@ - - + + @@ -66,6 +67,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore.Authorization.Opa/Request/DefaultQueryRequestFactory.cs b/src/HotChocolate/AspNetCore/src/AspNetCore.Authorization.Opa/Request/DefaultQueryRequestFactory.cs index 9466e511851..77e6b349659 100644 --- a/src/HotChocolate/AspNetCore/src/AspNetCore.Authorization.Opa/Request/DefaultQueryRequestFactory.cs +++ b/src/HotChocolate/AspNetCore/src/AspNetCore.Authorization.Opa/Request/DefaultQueryRequestFactory.cs @@ -26,7 +26,7 @@ public OpaQueryRequest CreateRequest(AuthorizationContext context, AuthorizeDire var originalRequest = new OriginalRequest( httpContext.Request.Headers, - httpContext.Request.Host.Value, + httpContext.Request.Host.Value ?? string.Empty, httpContext.Request.Method, httpContext.Request.Path.Value!, httpContext.Request.Query, diff --git a/src/HotChocolate/Core/src/Abstractions/Error.cs b/src/HotChocolate/Core/src/Abstractions/Error.cs index 7cea8b5e1da..03a9056d844 100644 --- a/src/HotChocolate/Core/src/Abstractions/Error.cs +++ b/src/HotChocolate/Core/src/Abstractions/Error.cs @@ -1,4 +1,6 @@ +#if !NET9_0_OR_GREATER using HotChocolate.Execution; +#endif using HotChocolate.Properties; namespace HotChocolate; diff --git a/src/HotChocolate/Core/src/Abstractions/ErrorBuilder.cs b/src/HotChocolate/Core/src/Abstractions/ErrorBuilder.cs index 2fd65943021..89e66e8b3b2 100644 --- a/src/HotChocolate/Core/src/Abstractions/ErrorBuilder.cs +++ b/src/HotChocolate/Core/src/Abstractions/ErrorBuilder.cs @@ -1,4 +1,6 @@ +#if !NET9_0_OR_GREATER using HotChocolate.Execution; +#endif using HotChocolate.Language; using HotChocolate.Properties; diff --git a/src/HotChocolate/Core/src/Core/HotChocolate.Core.csproj b/src/HotChocolate/Core/src/Core/HotChocolate.Core.csproj index edec303098e..66e9708a48e 100644 --- a/src/HotChocolate/Core/src/Core/HotChocolate.Core.csproj +++ b/src/HotChocolate/Core/src/Core/HotChocolate.Core.csproj @@ -20,6 +20,11 @@ + + + + + diff --git a/src/HotChocolate/Core/src/Execution/HotChocolate.Execution.csproj b/src/HotChocolate/Core/src/Execution/HotChocolate.Execution.csproj index a29ce96f31f..e6e73d3e258 100644 --- a/src/HotChocolate/Core/src/Execution/HotChocolate.Execution.csproj +++ b/src/HotChocolate/Core/src/Execution/HotChocolate.Execution.csproj @@ -39,6 +39,10 @@ + + + + diff --git a/src/HotChocolate/Core/src/Execution/Processing/Result/ObjectResult.cs b/src/HotChocolate/Core/src/Execution/Processing/Result/ObjectResult.cs index e966ee28961..a6ce385cc94 100644 --- a/src/HotChocolate/Core/src/Execution/Processing/Result/ObjectResult.cs +++ b/src/HotChocolate/Core/src/Execution/Processing/Result/ObjectResult.cs @@ -178,11 +178,11 @@ internal void Reset() { for (var i = 0; i < _capacity; i++) { - var field = _buffer[i]; + var fieldResult = _buffer[i]; - if (field.IsInitialized) + if (fieldResult.IsInitialized) { - yield return field.Name; + yield return fieldResult.Name; } } } @@ -194,11 +194,11 @@ internal void Reset() { for (var i = 0; i < _capacity; i++) { - var field = _buffer[i]; + var fieldResult = _buffer[i]; - if (field.IsInitialized) + if (fieldResult.IsInitialized) { - yield return field.Value; + yield return fieldResult.Value; } } } diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Errors.cs b/src/HotChocolate/Core/src/Types.Analyzers/Errors.cs index 696169cfb62..05d43557cd5 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Errors.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Errors.cs @@ -8,7 +8,7 @@ public static class Errors public static readonly DiagnosticDescriptor KeyParameterMissing = new( id: "HC0074", - title: "Parameter Missing.", + title: "Parameter Missing", messageFormat: SourceGenResources.DataLoader_KeyParameterMissing, category: "DataLoader", DiagnosticSeverity.Error, @@ -17,7 +17,7 @@ public static class Errors public static readonly DiagnosticDescriptor MethodAccessModifierInvalid = new( id: "HC0075", - title: "Access Modifier Invalid.", + title: "Access Modifier Invalid", messageFormat: SourceGenResources.DataLoader_InvalidAccessModifier, category: "DataLoader", DiagnosticSeverity.Error, @@ -26,8 +26,8 @@ public static class Errors public static readonly DiagnosticDescriptor ObjectTypePartialKeywordMissing = new( id: "HC0080", - title: "Partial Keyword Missing.", - messageFormat: "A split object type class needs to be a partial class.", + title: "Partial Keyword Missing", + messageFormat: "A split object type class needs to be a partial class", category: "TypeSystem", DiagnosticSeverity.Error, isEnabledByDefault: true); @@ -35,8 +35,8 @@ public static class Errors public static readonly DiagnosticDescriptor ObjectTypeStaticKeywordMissing = new( id: "HC0081", - title: "Static Keyword Missing.", - messageFormat: "A split object type class needs to be a static class.", + title: "Static Keyword Missing", + messageFormat: "A split object type class needs to be a static class", category: "TypeSystem", DiagnosticSeverity.Error, isEnabledByDefault: true); @@ -44,8 +44,8 @@ public static class Errors public static readonly DiagnosticDescriptor InterfaceTypePartialKeywordMissing = new( id: "HC0080", - title: "Partial Keyword Missing.", - messageFormat: "A split object type class needs to be a partial class.", + title: "Partial Keyword Missing", + messageFormat: "A split object type class needs to be a partial class", category: "TypeSystem", DiagnosticSeverity.Error, isEnabledByDefault: true); @@ -53,8 +53,8 @@ public static class Errors public static readonly DiagnosticDescriptor InterfaceTypeStaticKeywordMissing = new( id: "HC0081", - title: "Static Keyword Missing.", - messageFormat: "A split object type class needs to be a static class.", + title: "Static Keyword Missing", + messageFormat: "A split object type class needs to be a static class", category: "TypeSystem", DiagnosticSeverity.Error, isEnabledByDefault: true); @@ -62,8 +62,8 @@ public static class Errors public static readonly DiagnosticDescriptor TooManyNodeResolverArguments = new( id: "HC0083", - title: "Too Many Arguments.", - messageFormat: "A node resolver can only have a single field argument called `id`.", + title: "Too Many Arguments", + messageFormat: "A node resolver can only have a single field argument called `id`", category: "TypeSystem", DiagnosticSeverity.Error, isEnabledByDefault: true); @@ -71,8 +71,8 @@ public static class Errors public static readonly DiagnosticDescriptor InvalidNodeResolverArgumentName = new( id: "HC0084", - title: "Invalid Argument Name.", - messageFormat: "A node resolver can only have a single field argument called `id`.", + title: "Invalid Argument Name", + messageFormat: "A node resolver can only have a single field argument called `id`", category: "TypeSystem", DiagnosticSeverity.Error, isEnabledByDefault: true); @@ -80,8 +80,8 @@ public static class Errors public static readonly DiagnosticDescriptor DataLoaderCannotBeGeneric = new( id: "HC0085", - title: "DataLoader Cannot Be Generic.", - messageFormat: "The DataLoader source generator cannot generate generic DataLoaders.", + title: "DataLoader Cannot Be Generic", + messageFormat: "The DataLoader source generator cannot generate generic DataLoaders", category: "DataLoader", DiagnosticSeverity.Error, isEnabledByDefault: true); diff --git a/src/HotChocolate/Core/src/Types.Analyzers/HotChocolate.Types.Analyzers.csproj b/src/HotChocolate/Core/src/Types.Analyzers/HotChocolate.Types.Analyzers.csproj index 2af6186f7e6..aebc7bb71f3 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/HotChocolate.Types.Analyzers.csproj +++ b/src/HotChocolate/Core/src/Types.Analyzers/HotChocolate.Types.Analyzers.csproj @@ -16,9 +16,9 @@ - - - + + + diff --git a/src/HotChocolate/Core/src/Types/SchemaBuilder.Setup.cs b/src/HotChocolate/Core/src/Types/SchemaBuilder.Setup.cs index 21fa29f2fc5..705239fe4d3 100644 --- a/src/HotChocolate/Core/src/Types/SchemaBuilder.Setup.cs +++ b/src/HotChocolate/Core/src/Types/SchemaBuilder.Setup.cs @@ -270,7 +270,7 @@ private static void InitializeInterceptors( { if (interceptorOrType is Type type) { - var obj = ServiceFactory.CreateInstance(services, type); + var obj = ActivatorUtilities.CreateInstance(services, type); if (obj is T casted) { interceptors.Add(casted); diff --git a/src/HotChocolate/Core/test/Abstractions.Tests/ErrorBuilderTests.cs b/src/HotChocolate/Core/test/Abstractions.Tests/ErrorBuilderTests.cs index 58778ff96a2..8aead41ca37 100644 --- a/src/HotChocolate/Core/test/Abstractions.Tests/ErrorBuilderTests.cs +++ b/src/HotChocolate/Core/test/Abstractions.Tests/ErrorBuilderTests.cs @@ -1,5 +1,7 @@ using System.Collections.Immutable; +#if !NET9_0_OR_GREATER using HotChocolate.Execution; +#endif using HotChocolate.Language; namespace HotChocolate; diff --git a/src/HotChocolate/Core/test/Subscriptions.Postgres.Tests/HotChocolate.Subscriptions.Postgres.Tests.csproj b/src/HotChocolate/Core/test/Subscriptions.Postgres.Tests/HotChocolate.Subscriptions.Postgres.Tests.csproj index e395cdefd19..64a3cf2a003 100644 --- a/src/HotChocolate/Core/test/Subscriptions.Postgres.Tests/HotChocolate.Subscriptions.Postgres.Tests.csproj +++ b/src/HotChocolate/Core/test/Subscriptions.Postgres.Tests/HotChocolate.Subscriptions.Postgres.Tests.csproj @@ -3,7 +3,7 @@ HotChocolate.Subscriptions.Postgres.Tests HotChocolate.Subscriptions.Postgres - net7.0;net8.0 + net7.0;net8.0;net9.0 diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/HotChocolate.Types.Analyzers.Tests.csproj b/src/HotChocolate/Core/test/Types.Analyzers.Tests/HotChocolate.Types.Analyzers.Tests.csproj index c9706526bc5..fbc902ecaeb 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/HotChocolate.Types.Analyzers.Tests.csproj +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/HotChocolate.Types.Analyzers.Tests.csproj @@ -23,7 +23,8 @@ - + + diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs index fa2d79153ec..1ecd1bb24f7 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs @@ -16,7 +16,8 @@ namespace HotChocolate.Types; internal static partial class TestHelper { - private static HashSet _ignoreCodes = ["CS8652", "CS8632", "CS5001", "CS8019"]; + private static HashSet _ignoreCodes = + ["CS8652", "CS8632", "CS5001", "CS8019", "GD0001", "GD0002"]; public static Snapshot GetGeneratedSourceSnapshot([StringSyntax("csharp")] string sourceText) { @@ -25,6 +26,12 @@ public static Snapshot GetGeneratedSourceSnapshot([StringSyntax("csharp")] strin IEnumerable references = [ +#if NET8_0 + .. Net80.References.All, +#elif NET9_0 + .. Net90.References.All, +#endif + // HotChocolate.Types MetadataReference.CreateFromFile(typeof(ObjectTypeAttribute).Assembly.Location), @@ -42,7 +49,7 @@ public static Snapshot GetGeneratedSourceSnapshot([StringSyntax("csharp")] strin var compilation = CSharpCompilation.Create( assemblyName: "Tests", syntaxTrees: [syntaxTree], - ReferenceAssemblies.Net80.Concat(references)); + references); // Create an instance of our GraphQLServerGenerator incremental source generator. var generator = new GraphQLServerGenerator(); diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_GenericBatchDataLoader_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_GenericBatchDataLoader_MatchesSnapshot.md index d15e2b0ed88..c4e26e51868 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_GenericBatchDataLoader_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_GenericBatchDataLoader_MatchesSnapshot.md @@ -4,12 +4,12 @@ [ { "Id": "HC0085", - "Title": "DataLoader Cannot Be Generic.", + "Title": "DataLoader Cannot Be Generic", "Severity": "Error", "WarningLevel": 0, "Location": ": (11,4)-(11,17)", - "MessageFormat": "The DataLoader source generator cannot generate generic DataLoaders.", - "Message": "The DataLoader source generator cannot generate generic DataLoaders.", + "MessageFormat": "The DataLoader source generator cannot generate generic DataLoaders", + "Message": "The DataLoader source generator cannot generate generic DataLoaders", "Category": "DataLoader", "CustomTags": [] } diff --git a/src/HotChocolate/Data/test/Data.AutoMapper.Tests/ProjectToTests.cs b/src/HotChocolate/Data/test/Data.AutoMapper.Tests/ProjectToTests.cs index 850869ccae3..15722b5b641 100644 --- a/src/HotChocolate/Data/test/Data.AutoMapper.Tests/ProjectToTests.cs +++ b/src/HotChocolate/Data/test/Data.AutoMapper.Tests/ProjectToTests.cs @@ -110,7 +110,11 @@ public async Task Execute_ManyToOne() }") .Build()); +#if NET9_0 + var snapshot = new Snapshot(postFix: "NET9_0"); +#else var snapshot = new Snapshot(); +#endif snapshot.AddSqlFrom(res1); await snapshot.MatchAsync(); } @@ -147,7 +151,11 @@ query Test { }") .Build()); +#if NET9_0 + var snapshot = new Snapshot(postFix: "NET9_0"); +#else var snapshot = new Snapshot(); +#endif snapshot.AddSqlFrom(res1); await snapshot.MatchAsync(); } @@ -205,7 +213,11 @@ query Test { }") .Build()); +#if NET9_0 + var snapshot = new Snapshot(postFix: "NET9_0"); +#else var snapshot = new Snapshot(); +#endif snapshot.AddSqlFrom(res1); await snapshot.MatchAsync(); } diff --git a/src/HotChocolate/Data/test/Data.AutoMapper.Tests/__snapshots__/ProjectToTests.Execute_ManyToOne_Deep_NET9_0.snap b/src/HotChocolate/Data/test/Data.AutoMapper.Tests/__snapshots__/ProjectToTests.Execute_ManyToOne_Deep_NET9_0.snap new file mode 100644 index 00000000000..f3ab0475c64 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.AutoMapper.Tests/__snapshots__/ProjectToTests.Execute_ManyToOne_Deep_NET9_0.snap @@ -0,0 +1,107 @@ +Result: +--------------- +{ + "data": { + "posts": [ + { + "postId": 1, + "title": "titleA", + "blog": { + "url": "testa.com", + "posts": [ + { + "title": "titleA", + "blog": null + }, + { + "title": "titleB", + "blog": null + } + ] + } + }, + { + "postId": 2, + "title": "titleB", + "blog": { + "url": "testa.com", + "posts": [ + { + "title": "titleA", + "blog": null + }, + { + "title": "titleB", + "blog": null + } + ] + } + }, + { + "postId": 3, + "title": "titleC", + "blog": { + "url": "testb.com", + "posts": [ + { + "title": "titleC", + "blog": null + }, + { + "title": "titleD", + "blog": null + } + ] + } + }, + { + "postId": 4, + "title": "titleD", + "blog": { + "url": "testb.com", + "posts": [ + { + "title": "titleC", + "blog": null + }, + { + "title": "titleD", + "blog": null + } + ] + } + } + ] + } +} +--------------- + +SQL: +--------------- +SELECT "b"."BlogId" IS NULL, "p"."PostId", "b"."BlogId", "p0"."PostId", "p0"."Title", "b"."Url", "p"."Title" +FROM "Posts" AS "p" +LEFT JOIN "Blogs" AS "b" ON "p"."BlogId" = "b"."BlogId" +LEFT JOIN "Posts" AS "p0" ON "b"."BlogId" = "p0"."BlogId" +ORDER BY "p"."PostId", "b"."BlogId" +--------------- + +Expression: +--------------- +DbSet() + .Select(dtoPost => new PostDto{ + Blog = dtoPost.Blog == null ? null : new BlogDto{ + Posts = dtoPost.Blog.Posts + .Select(dtoPost => new PostDto{ + PostId = dtoPost.PostId ?? 0, + Title = dtoPost.Title + } + ) + .ToList(), + Url = dtoPost.Blog.Url + } + , + PostId = dtoPost.PostId ?? 0, + Title = dtoPost.Title + } + ) +--------------- diff --git a/src/HotChocolate/Data/test/Data.AutoMapper.Tests/__snapshots__/ProjectToTests.Execute_ManyToOne_NET9_0.snap b/src/HotChocolate/Data/test/Data.AutoMapper.Tests/__snapshots__/ProjectToTests.Execute_ManyToOne_NET9_0.snap new file mode 100644 index 00000000000..17aac4d3ea0 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.AutoMapper.Tests/__snapshots__/ProjectToTests.Execute_ManyToOne_NET9_0.snap @@ -0,0 +1,56 @@ +Result: +--------------- +{ + "data": { + "posts": [ + { + "postId": 1, + "title": "titleA", + "blog": { + "url": "testa.com" + } + }, + { + "postId": 2, + "title": "titleB", + "blog": { + "url": "testa.com" + } + }, + { + "postId": 3, + "title": "titleC", + "blog": { + "url": "testb.com" + } + }, + { + "postId": 4, + "title": "titleD", + "blog": { + "url": "testb.com" + } + } + ] + } +} +--------------- + +SQL: +--------------- +SELECT "b"."BlogId" IS NULL, "b"."Url", "p"."PostId", "p"."Title" +FROM "Posts" AS "p" +LEFT JOIN "Blogs" AS "b" ON "p"."BlogId" = "b"."BlogId" +--------------- + +Expression: +--------------- +DbSet() + .Select(dtoPost => new PostDto{ + Blog = dtoPost.Blog == null ? null : new BlogDto{ Url = dtoPost.Blog.Url } + , + PostId = dtoPost.PostId ?? 0, + Title = dtoPost.Title + } + ) +--------------- diff --git a/src/HotChocolate/Data/test/Data.AutoMapper.Tests/__snapshots__/ProjectToTests.Execute_OneToOne_Deep_NET9_0.snap b/src/HotChocolate/Data/test/Data.AutoMapper.Tests/__snapshots__/ProjectToTests.Execute_OneToOne_Deep_NET9_0.snap new file mode 100644 index 00000000000..5a7756654ed --- /dev/null +++ b/src/HotChocolate/Data/test/Data.AutoMapper.Tests/__snapshots__/ProjectToTests.Execute_OneToOne_Deep_NET9_0.snap @@ -0,0 +1,73 @@ +Result: +--------------- +{ + "data": { + "posts": [ + { + "postId": 1, + "title": "titleA", + "blog": { + "url": "testa.com", + "titleImage": { + "url": "https://testa.com/image.png" + } + } + }, + { + "postId": 2, + "title": "titleB", + "blog": { + "url": "testa.com", + "titleImage": { + "url": "https://testa.com/image.png" + } + } + }, + { + "postId": 3, + "title": "titleC", + "blog": { + "url": "testb.com", + "titleImage": { + "url": "https://testb.com/image.png" + } + } + }, + { + "postId": 4, + "title": "titleD", + "blog": { + "url": "testb.com", + "titleImage": { + "url": "https://testb.com/image.png" + } + } + } + ] + } +} +--------------- + +SQL: +--------------- +SELECT "b"."BlogId" IS NULL, "i"."ImageId" IS NULL, "i"."Url", "b"."Url", "p"."PostId", "p"."Title" +FROM "Posts" AS "p" +LEFT JOIN "Blogs" AS "b" ON "p"."BlogId" = "b"."BlogId" +LEFT JOIN "Images" AS "i" ON "b"."ImageId" = "i"."ImageId" +--------------- + +Expression: +--------------- +DbSet() + .Select(dtoPost => new PostDto{ + Blog = dtoPost.Blog == null ? null : new BlogDto{ + TitleImage = dtoPost.Blog.TitleImage == null ? null : new ImageDto{ Url = dtoPost.Blog.TitleImage.Url } + , + Url = dtoPost.Blog.Url + } + , + PostId = dtoPost.PostId ?? 0, + Title = dtoPost.Title + } + ) +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/QueryableFilterVisitorComparableTests.cs b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/QueryableFilterVisitorComparableTests.cs index 2d6e30c069e..4b10d8c7988 100644 --- a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/QueryableFilterVisitorComparableTests.cs +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/QueryableFilterVisitorComparableTests.cs @@ -570,7 +570,11 @@ public async Task Create_ShortNullableNotGreaterThan_Expression() // assert await Snapshot +#if NET9_0 + .Create(postFix: "NET9_0") +#else .Create() +#endif .AddResult(res1, "12") .AddResult(res2, "13") .AddResult(res3, "14") @@ -644,7 +648,11 @@ public async Task Create_ShortNullableNotGreaterThanOrEquals_Expression() // assert await Snapshot +#if NET9_0 + .Create(postFix: "NET9_0") +#else .Create() +#endif .AddResult(res1, "12") .AddResult(res2, "13") .AddResult(res3, "14") @@ -718,7 +726,11 @@ public async Task Create_ShortNullableNotLowerThan_Expression() // assert await Snapshot +#if NET9_0 + .Create(postFix: "NET9_0") +#else .Create() +#endif .AddResult(res1, "12") .AddResult(res2, "13") .AddResult(res3, "14") @@ -792,7 +804,11 @@ public async Task Create_ShortNullableNotLowerThanOrEquals_Expression() // assert await Snapshot +#if NET9_0 + .Create(postFix: "NET9_0") +#else .Create() +#endif .AddResult(res1, "12") .AddResult(res2, "13") .AddResult(res3, "14") diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorBooleanTests.Create_NullableBooleanNotEqual_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorBooleanTests.Create_NullableBooleanNotEqual_Expression_NET9_0.snap new file mode 100644 index 00000000000..c05143c3c25 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorBooleanTests.Create_NullableBooleanNotEqual_Expression_NET9_0.snap @@ -0,0 +1,72 @@ +true Result: +--------------- +{ + "data": { + "root": [ + { + "bar": null + }, + { + "bar": false + } + ] + } +} +--------------- + +true SQL: +--------------- +.param set @__p_0 1 + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" <> @__p_0 OR "d"."Bar" IS NULL +--------------- + +false Result: +--------------- +{ + "data": { + "root": [ + { + "bar": true + }, + { + "bar": null + } + ] + } +} +--------------- + +false SQL: +--------------- +.param set @__p_0 0 + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" <> @__p_0 OR "d"."Bar" IS NULL +--------------- + +null Result: +--------------- +{ + "data": { + "root": [ + { + "bar": true + }, + { + "bar": false + } + ] + } +} +--------------- + +null SQL: +--------------- +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" IS NOT NULL +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortIn_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortIn_Expression_NET9_0.snap new file mode 100644 index 00000000000..a22ffff9cca --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortIn_Expression_NET9_0.snap @@ -0,0 +1,83 @@ +12and13 Result: +--------------- +{ + "data": { + "root": [ + { + "barShort": 12 + }, + { + "barShort": 13 + } + ] + } +} +--------------- + +12and13 SQL: +--------------- +.param set @__p_0 '[12,13]' + +SELECT "d"."Id", "d"."BarDecimal", "d"."BarDouble", "d"."BarFloat", "d"."BarInt", "d"."BarLong", "d"."BarShort" +FROM "Data" AS "d" +WHERE "d"."BarShort" IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- + +13and14 Result: +--------------- +{ + "data": { + "root": [ + { + "barShort": 14 + }, + { + "barShort": 13 + } + ] + } +} +--------------- + +13and14 SQL: +--------------- +.param set @__p_0 '[13,14]' + +SELECT "d"."Id", "d"."BarDecimal", "d"."BarDouble", "d"."BarFloat", "d"."BarInt", "d"."BarLong", "d"."BarShort" +FROM "Data" AS "d" +WHERE "d"."BarShort" IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- + +nullAnd14 +--------------- +{ + "errors": [ + { + "message": "The provided value for filter `in` of type ShortOperationFilterInput is invalid. Null values are not supported.", + "locations": [ + { + "line": 1, + "column": 33 + } + ], + "path": [ + "root" + ], + "extensions": { + "code": "HC0026", + "expectedType": "[Short!]", + "filterType": "ShortOperationFilterInput" + } + } + ], + "data": { + "root": null + } +} +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNotIn_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNotIn_Expression_NET9_0.snap new file mode 100644 index 00000000000..6da4fd4e544 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNotIn_Expression_NET9_0.snap @@ -0,0 +1,77 @@ +12and13 Result: +--------------- +{ + "data": { + "root": [ + { + "barShort": 14 + } + ] + } +} +--------------- + +12and13 SQL: +--------------- +.param set @__p_0 '[12,13]' + +SELECT "d"."Id", "d"."BarDecimal", "d"."BarDouble", "d"."BarFloat", "d"."BarInt", "d"."BarLong", "d"."BarShort" +FROM "Data" AS "d" +WHERE "d"."BarShort" NOT IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- + +13and14 Result: +--------------- +{ + "data": { + "root": [ + { + "barShort": 12 + } + ] + } +} +--------------- + +13and14 SQL: +--------------- +.param set @__p_0 '[13,14]' + +SELECT "d"."Id", "d"."BarDecimal", "d"."BarDouble", "d"."BarFloat", "d"."BarInt", "d"."BarLong", "d"."BarShort" +FROM "Data" AS "d" +WHERE "d"."BarShort" NOT IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- + +nullAnd14 +--------------- +{ + "errors": [ + { + "message": "The provided value for filter `nin` of type ShortOperationFilterInput is invalid. Null values are not supported.", + "locations": [ + { + "line": 1, + "column": 34 + } + ], + "path": [ + "root" + ], + "extensions": { + "code": "HC0026", + "expectedType": "[Short!]", + "filterType": "ShortOperationFilterInput" + } + } + ], + "data": { + "root": null + } +} +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableIn_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableIn_Expression_NET9_0.snap new file mode 100644 index 00000000000..39ff3fefa3b --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableIn_Expression_NET9_0.snap @@ -0,0 +1,83 @@ +12and13 Result: +--------------- +{ + "data": { + "root": [ + { + "barShort": 12 + }, + { + "barShort": 13 + } + ] + } +} +--------------- + +12and13 SQL: +--------------- +.param set @__p_0 '[12,13]' + +SELECT "d"."Id", "d"."BarShort" +FROM "Data" AS "d" +WHERE "d"."BarShort" IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- + +13and14 Result: +--------------- +{ + "data": { + "root": [ + { + "barShort": 14 + }, + { + "barShort": 13 + } + ] + } +} +--------------- + +13and14 SQL: +--------------- +.param set @__p_0 '[13,14]' + +SELECT "d"."Id", "d"."BarShort" +FROM "Data" AS "d" +WHERE "d"."BarShort" IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- + +13andNull Result: +--------------- +{ + "data": { + "root": [ + { + "barShort": null + }, + { + "barShort": 13 + } + ] + } +} +--------------- + +13andNull SQL: +--------------- +.param set @__p_0_without_nulls '[13]' + +SELECT "d"."Id", "d"."BarShort" +FROM "Data" AS "d" +WHERE "d"."BarShort" IN ( + SELECT "p"."value" + FROM json_each(@__p_0_without_nulls) AS "p" +) OR "d"."BarShort" IS NULL +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableNotEqual_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableNotEqual_Expression_NET9_0.snap new file mode 100644 index 00000000000..b3b7074d1e5 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableNotEqual_Expression_NET9_0.snap @@ -0,0 +1,81 @@ +12 Result: +--------------- +{ + "data": { + "root": [ + { + "barShort": null + }, + { + "barShort": 14 + }, + { + "barShort": 13 + } + ] + } +} +--------------- + +12 SQL: +--------------- +.param set @__p_0 12 + +SELECT "d"."Id", "d"."BarShort" +FROM "Data" AS "d" +WHERE "d"."BarShort" <> @__p_0 OR "d"."BarShort" IS NULL +--------------- + +13 Result: +--------------- +{ + "data": { + "root": [ + { + "barShort": 12 + }, + { + "barShort": null + }, + { + "barShort": 14 + } + ] + } +} +--------------- + +13 SQL: +--------------- +.param set @__p_0 13 + +SELECT "d"."Id", "d"."BarShort" +FROM "Data" AS "d" +WHERE "d"."BarShort" <> @__p_0 OR "d"."BarShort" IS NULL +--------------- + +null Result: +--------------- +{ + "data": { + "root": [ + { + "barShort": 12 + }, + { + "barShort": 14 + }, + { + "barShort": 13 + } + ] + } +} +--------------- + +null SQL: +--------------- +SELECT "d"."Id", "d"."BarShort" +FROM "Data" AS "d" +WHERE "d"."BarShort" IS NOT NULL +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableNotGreaterThanOrEquals_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableNotGreaterThanOrEquals_Expression_NET9_0.snap new file mode 100644 index 00000000000..2667e6aa075 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableNotGreaterThanOrEquals_Expression_NET9_0.snap @@ -0,0 +1,111 @@ +12 Result: +--------------- +{ + "data": { + "root": [ + { + "barShort": null + } + ] + } +} +--------------- + +12 SQL: +--------------- +.param set @__p_0 12 + +SELECT "d"."Id", "d"."BarShort" +FROM "Data" AS "d" +WHERE CASE + WHEN "d"."BarShort" >= @__p_0 THEN 0 + ELSE 1 +END +--------------- + +13 Result: +--------------- +{ + "data": { + "root": [ + { + "barShort": 12 + }, + { + "barShort": null + } + ] + } +} +--------------- + +13 SQL: +--------------- +.param set @__p_0 13 + +SELECT "d"."Id", "d"."BarShort" +FROM "Data" AS "d" +WHERE CASE + WHEN "d"."BarShort" >= @__p_0 THEN 0 + ELSE 1 +END +--------------- + +14 Result: +--------------- +{ + "data": { + "root": [ + { + "barShort": 12 + }, + { + "barShort": null + }, + { + "barShort": 13 + } + ] + } +} +--------------- + +14 SQL: +--------------- +.param set @__p_0 14 + +SELECT "d"."Id", "d"."BarShort" +FROM "Data" AS "d" +WHERE CASE + WHEN "d"."BarShort" >= @__p_0 THEN 0 + ELSE 1 +END +--------------- + +null +--------------- +{ + "errors": [ + { + "message": "The provided value for filter `ngte` of type ShortOperationFilterInput is invalid. Null values are not supported.", + "locations": [ + { + "line": 1, + "column": 35 + } + ], + "path": [ + "root" + ], + "extensions": { + "code": "HC0026", + "expectedType": "Short!", + "filterType": "ShortOperationFilterInput" + } + } + ], + "data": { + "root": null + } +} +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableNotGreaterThan_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableNotGreaterThan_Expression_NET9_0.snap new file mode 100644 index 00000000000..b989d260968 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableNotGreaterThan_Expression_NET9_0.snap @@ -0,0 +1,120 @@ +12 Result: +--------------- +{ + "data": { + "root": [ + { + "barShort": 12 + }, + { + "barShort": null + } + ] + } +} +--------------- + +12 SQL: +--------------- +.param set @__p_0 12 + +SELECT "d"."Id", "d"."BarShort" +FROM "Data" AS "d" +WHERE CASE + WHEN "d"."BarShort" > @__p_0 THEN 0 + ELSE 1 +END +--------------- + +13 Result: +--------------- +{ + "data": { + "root": [ + { + "barShort": 12 + }, + { + "barShort": null + }, + { + "barShort": 13 + } + ] + } +} +--------------- + +13 SQL: +--------------- +.param set @__p_0 13 + +SELECT "d"."Id", "d"."BarShort" +FROM "Data" AS "d" +WHERE CASE + WHEN "d"."BarShort" > @__p_0 THEN 0 + ELSE 1 +END +--------------- + +14 Result: +--------------- +{ + "data": { + "root": [ + { + "barShort": 12 + }, + { + "barShort": null + }, + { + "barShort": 14 + }, + { + "barShort": 13 + } + ] + } +} +--------------- + +14 SQL: +--------------- +.param set @__p_0 14 + +SELECT "d"."Id", "d"."BarShort" +FROM "Data" AS "d" +WHERE CASE + WHEN "d"."BarShort" > @__p_0 THEN 0 + ELSE 1 +END +--------------- + +null +--------------- +{ + "errors": [ + { + "message": "The provided value for filter `ngt` of type ShortOperationFilterInput is invalid. Null values are not supported.", + "locations": [ + { + "line": 1, + "column": 34 + } + ], + "path": [ + "root" + ], + "extensions": { + "code": "HC0026", + "expectedType": "Short!", + "filterType": "ShortOperationFilterInput" + } + } + ], + "data": { + "root": null + } +} +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableNotIn_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableNotIn_Expression_NET9_0.snap new file mode 100644 index 00000000000..120bf90817f --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableNotIn_Expression_NET9_0.snap @@ -0,0 +1,83 @@ +12and13 Result: +--------------- +{ + "data": { + "root": [ + { + "barShort": null + }, + { + "barShort": 14 + } + ] + } +} +--------------- + +12and13 SQL: +--------------- +.param set @__p_0 '[12,13]' + +SELECT "d"."Id", "d"."BarShort" +FROM "Data" AS "d" +WHERE "d"."BarShort" NOT IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) OR "d"."BarShort" IS NULL +--------------- + +13and14 Result: +--------------- +{ + "data": { + "root": [ + { + "barShort": 12 + }, + { + "barShort": null + } + ] + } +} +--------------- + +13and14 SQL: +--------------- +.param set @__p_0 '[13,14]' + +SELECT "d"."Id", "d"."BarShort" +FROM "Data" AS "d" +WHERE "d"."BarShort" NOT IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) OR "d"."BarShort" IS NULL +--------------- + +13andNull Result: +--------------- +{ + "data": { + "root": [ + { + "barShort": 12 + }, + { + "barShort": 14 + } + ] + } +} +--------------- + +13andNull SQL: +--------------- +.param set @__p_0_without_nulls '[13]' + +SELECT "d"."Id", "d"."BarShort" +FROM "Data" AS "d" +WHERE "d"."BarShort" NOT IN ( + SELECT "p"."value" + FROM json_each(@__p_0_without_nulls) AS "p" +) AND "d"."BarShort" IS NOT NULL +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableNotLowerThanOrEquals_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableNotLowerThanOrEquals_Expression_NET9_0.snap new file mode 100644 index 00000000000..9c32284c141 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableNotLowerThanOrEquals_Expression_NET9_0.snap @@ -0,0 +1,111 @@ +12 Result: +--------------- +{ + "data": { + "root": [ + { + "barShort": null + }, + { + "barShort": 14 + }, + { + "barShort": 13 + } + ] + } +} +--------------- + +12 SQL: +--------------- +.param set @__p_0 12 + +SELECT "d"."Id", "d"."BarShort" +FROM "Data" AS "d" +WHERE CASE + WHEN "d"."BarShort" <= @__p_0 THEN 0 + ELSE 1 +END +--------------- + +13 Result: +--------------- +{ + "data": { + "root": [ + { + "barShort": null + }, + { + "barShort": 14 + } + ] + } +} +--------------- + +13 SQL: +--------------- +.param set @__p_0 13 + +SELECT "d"."Id", "d"."BarShort" +FROM "Data" AS "d" +WHERE CASE + WHEN "d"."BarShort" <= @__p_0 THEN 0 + ELSE 1 +END +--------------- + +14 Result: +--------------- +{ + "data": { + "root": [ + { + "barShort": null + } + ] + } +} +--------------- + +14 SQL: +--------------- +.param set @__p_0 14 + +SELECT "d"."Id", "d"."BarShort" +FROM "Data" AS "d" +WHERE CASE + WHEN "d"."BarShort" <= @__p_0 THEN 0 + ELSE 1 +END +--------------- + +null +--------------- +{ + "errors": [ + { + "message": "The provided value for filter `nlte` of type ShortOperationFilterInput is invalid. Null values are not supported.", + "locations": [ + { + "line": 1, + "column": 35 + } + ], + "path": [ + "root" + ], + "extensions": { + "code": "HC0026", + "expectedType": "Short!", + "filterType": "ShortOperationFilterInput" + } + } + ], + "data": { + "root": null + } +} +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableNotLowerThan_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableNotLowerThan_Expression_NET9_0.snap new file mode 100644 index 00000000000..4be821c0160 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorComparableTests.Create_ShortNullableNotLowerThan_Expression_NET9_0.snap @@ -0,0 +1,120 @@ +12 Result: +--------------- +{ + "data": { + "root": [ + { + "barShort": 12 + }, + { + "barShort": null + }, + { + "barShort": 14 + }, + { + "barShort": 13 + } + ] + } +} +--------------- + +12 SQL: +--------------- +.param set @__p_0 12 + +SELECT "d"."Id", "d"."BarShort" +FROM "Data" AS "d" +WHERE CASE + WHEN "d"."BarShort" < @__p_0 THEN 0 + ELSE 1 +END +--------------- + +13 Result: +--------------- +{ + "data": { + "root": [ + { + "barShort": null + }, + { + "barShort": 14 + }, + { + "barShort": 13 + } + ] + } +} +--------------- + +13 SQL: +--------------- +.param set @__p_0 13 + +SELECT "d"."Id", "d"."BarShort" +FROM "Data" AS "d" +WHERE CASE + WHEN "d"."BarShort" < @__p_0 THEN 0 + ELSE 1 +END +--------------- + +14 Result: +--------------- +{ + "data": { + "root": [ + { + "barShort": null + }, + { + "barShort": 14 + } + ] + } +} +--------------- + +14 SQL: +--------------- +.param set @__p_0 14 + +SELECT "d"."Id", "d"."BarShort" +FROM "Data" AS "d" +WHERE CASE + WHEN "d"."BarShort" < @__p_0 THEN 0 + ELSE 1 +END +--------------- + +null +--------------- +{ + "errors": [ + { + "message": "The provided value for filter `nlt` of type ShortOperationFilterInput is invalid. Null values are not supported.", + "locations": [ + { + "line": 1, + "column": 34 + } + ], + "path": [ + "root" + ], + "extensions": { + "code": "HC0026", + "expectedType": "Short!", + "filterType": "ShortOperationFilterInput" + } + } + ], + "data": { + "root": null + } +} +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorEnumTests.Create_EnumIn_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorEnumTests.Create_EnumIn_Expression_NET9_0.snap new file mode 100644 index 00000000000..5710df922f2 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorEnumTests.Create_EnumIn_Expression_NET9_0.snap @@ -0,0 +1,78 @@ +BarAndFoo Result: +--------------- +{ + "data": { + "root": [ + { + "barEnum": "BAR" + }, + { + "barEnum": "FOO" + } + ] + } +} +--------------- + +BarAndFoo SQL: +--------------- +.param set @__p_0 '[1,0]' + +SELECT "d"."Id", "d"."BarEnum" +FROM "Data" AS "d" +WHERE "d"."BarEnum" IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- + +FOO Result: +--------------- +{ + "data": { + "root": [ + { + "barEnum": "FOO" + } + ] + } +} +--------------- + +FOO SQL: +--------------- +.param set @__p_0 '[0]' + +SELECT "d"."Id", "d"."BarEnum" +FROM "Data" AS "d" +WHERE "d"."BarEnum" IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- + +nullAndFoo Result: +--------------- +{ + "errors": [ + { + "message": "The specified value type of field `in` does not match the field type.", + "locations": [ + { + "line": 1, + "column": 32 + } + ], + "path": [ + "root" + ], + "extensions": { + "fieldName": "in", + "fieldType": "[FooEnum!]", + "locationType": "[FooEnum!]", + "specifiedBy": "https://spec.graphql.org/October2021/#sec-Values-of-Correct-Type" + } + } + ] +} +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorEnumTests.Create_EnumNotIn_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorEnumTests.Create_EnumNotIn_Expression_NET9_0.snap new file mode 100644 index 00000000000..c192855c27b --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorEnumTests.Create_EnumNotIn_Expression_NET9_0.snap @@ -0,0 +1,84 @@ +BarAndFoo Result: +--------------- +{ + "data": { + "root": [ + { + "barEnum": "BAZ" + }, + { + "barEnum": "QUX" + } + ] + } +} +--------------- + +BarAndFoo SQL: +--------------- +.param set @__p_0 '[1,0]' + +SELECT "d"."Id", "d"."BarEnum" +FROM "Data" AS "d" +WHERE "d"."BarEnum" NOT IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- + +FOO Result: +--------------- +{ + "data": { + "root": [ + { + "barEnum": "BAR" + }, + { + "barEnum": "BAZ" + }, + { + "barEnum": "QUX" + } + ] + } +} +--------------- + +FOO SQL: +--------------- +.param set @__p_0 '[0]' + +SELECT "d"."Id", "d"."BarEnum" +FROM "Data" AS "d" +WHERE "d"."BarEnum" NOT IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- + +nullAndFoo Result: +--------------- +{ + "errors": [ + { + "message": "The specified value type of field `nin` does not match the field type.", + "locations": [ + { + "line": 1, + "column": 33 + } + ], + "path": [ + "root" + ], + "extensions": { + "fieldName": "nin", + "fieldType": "[FooEnum!]", + "locationType": "[FooEnum!]", + "specifiedBy": "https://spec.graphql.org/October2021/#sec-Values-of-Correct-Type" + } + } + ] +} +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorEnumTests.Create_NullableEnumIn_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorEnumTests.Create_NullableEnumIn_Expression_NET9_0.snap new file mode 100644 index 00000000000..6e98dc2b031 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorEnumTests.Create_NullableEnumIn_Expression_NET9_0.snap @@ -0,0 +1,80 @@ +BarAndFoo Result: +--------------- +{ + "data": { + "root": [ + { + "barEnum": "BAR" + }, + { + "barEnum": "FOO" + } + ] + } +} +--------------- + +BarAndFoo SQL: +--------------- +.param set @__p_0 '[1,0]' + +SELECT "d"."Id", "d"."BarEnum" +FROM "Data" AS "d" +WHERE "d"."BarEnum" IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- + +FOO Result: +--------------- +{ + "data": { + "root": [ + { + "barEnum": "FOO" + } + ] + } +} +--------------- + +FOO SQL: +--------------- +.param set @__p_0 '[0]' + +SELECT "d"."Id", "d"."BarEnum" +FROM "Data" AS "d" +WHERE "d"."BarEnum" IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- + +nullAndFoo Result: +--------------- +{ + "data": { + "root": [ + { + "barEnum": "FOO" + }, + { + "barEnum": null + } + ] + } +} +--------------- + +nullAndFoo SQL: +--------------- +.param set @__p_0_without_nulls '[0]' + +SELECT "d"."Id", "d"."BarEnum" +FROM "Data" AS "d" +WHERE "d"."BarEnum" IN ( + SELECT "p"."value" + FROM json_each(@__p_0_without_nulls) AS "p" +) OR "d"."BarEnum" IS NULL +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorEnumTests.Create_NullableEnumNotEqual_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorEnumTests.Create_NullableEnumNotEqual_Expression_NET9_0.snap new file mode 100644 index 00000000000..944014046d2 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorEnumTests.Create_NullableEnumNotEqual_Expression_NET9_0.snap @@ -0,0 +1,90 @@ +BAR Result: +--------------- +{ + "data": { + "root": [ + { + "barEnum": "BAZ" + }, + { + "barEnum": "FOO" + }, + { + "barEnum": null + }, + { + "barEnum": "QUX" + } + ] + } +} +--------------- + +BAR SQL: +--------------- +.param set @__p_0 1 + +SELECT "d"."Id", "d"."BarEnum" +FROM "Data" AS "d" +WHERE "d"."BarEnum" <> @__p_0 OR "d"."BarEnum" IS NULL +--------------- + +FOO Result: +--------------- +{ + "data": { + "root": [ + { + "barEnum": "BAR" + }, + { + "barEnum": "BAZ" + }, + { + "barEnum": null + }, + { + "barEnum": "QUX" + } + ] + } +} +--------------- + +FOO SQL: +--------------- +.param set @__p_0 0 + +SELECT "d"."Id", "d"."BarEnum" +FROM "Data" AS "d" +WHERE "d"."BarEnum" <> @__p_0 OR "d"."BarEnum" IS NULL +--------------- + +null Result: +--------------- +{ + "data": { + "root": [ + { + "barEnum": "BAR" + }, + { + "barEnum": "BAZ" + }, + { + "barEnum": "FOO" + }, + { + "barEnum": "QUX" + } + ] + } +} +--------------- + +null SQL: +--------------- +SELECT "d"."Id", "d"."BarEnum" +FROM "Data" AS "d" +WHERE "d"."BarEnum" IS NOT NULL +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorEnumTests.Create_NullableEnumNotIn_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorEnumTests.Create_NullableEnumNotIn_Expression_NET9_0.snap new file mode 100644 index 00000000000..250998cf0fb --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorEnumTests.Create_NullableEnumNotIn_Expression_NET9_0.snap @@ -0,0 +1,95 @@ +BarAndFoo Result: +--------------- +{ + "data": { + "root": [ + { + "barEnum": "BAZ" + }, + { + "barEnum": null + }, + { + "barEnum": "QUX" + } + ] + } +} +--------------- + +BarAndFoo SQL: +--------------- +.param set @__p_0 '[1,0]' + +SELECT "d"."Id", "d"."BarEnum" +FROM "Data" AS "d" +WHERE "d"."BarEnum" NOT IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) OR "d"."BarEnum" IS NULL +--------------- + +FOO Result: +--------------- +{ + "data": { + "root": [ + { + "barEnum": "BAR" + }, + { + "barEnum": "BAZ" + }, + { + "barEnum": null + }, + { + "barEnum": "QUX" + } + ] + } +} +--------------- + +FOO SQL: +--------------- +.param set @__p_0 '[0]' + +SELECT "d"."Id", "d"."BarEnum" +FROM "Data" AS "d" +WHERE "d"."BarEnum" NOT IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) OR "d"."BarEnum" IS NULL +--------------- + +nullAndFoo Result: +--------------- +{ + "data": { + "root": [ + { + "barEnum": "BAR" + }, + { + "barEnum": "BAZ" + }, + { + "barEnum": "QUX" + } + ] + } +} +--------------- + +nullAndFoo SQL: +--------------- +.param set @__p_0_without_nulls '[0]' + +SELECT "d"."Id", "d"."BarEnum" +FROM "Data" AS "d" +WHERE "d"."BarEnum" NOT IN ( + SELECT "p"."value" + FROM json_each(@__p_0_without_nulls) AS "p" +) AND "d"."BarEnum" IS NOT NULL +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorExpressionTests.Create_StringConcatExpression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorExpressionTests.Create_StringConcatExpression_NET9_0.snap new file mode 100644 index 00000000000..efa4d42ba1a --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorExpressionTests.Create_StringConcatExpression_NET9_0.snap @@ -0,0 +1,56 @@ +Sam_Sampleman Result: +--------------- +{ + "data": { + "root": [ + { + "name": "Sam", + "lastName": "Sampleman" + } + ] + } +} +--------------- + +Sam_Sampleman SQL: +--------------- +.param set @__p_0 'Sam Sampleman' + +SELECT "d"."Id", "d"."LastName", "d"."Name" +FROM "Data" AS "d" +WHERE COALESCE("d"."Name", '') || ' ' || COALESCE("d"."LastName", '') = @__p_0 +--------------- + +NoMatch Result: +--------------- +{ + "data": { + "root": [] + } +} +--------------- + +NoMatch SQL: +--------------- +.param set @__p_0 'NoMatch' + +SELECT "d"."Id", "d"."LastName", "d"."Name" +FROM "Data" AS "d" +WHERE COALESCE("d"."Name", '') || ' ' || COALESCE("d"."LastName", '') = @__p_0 +--------------- + +null Result: +--------------- +{ + "data": { + "root": [] + } +} +--------------- + +null SQL: +--------------- +SELECT "d"."Id", "d"."LastName", "d"."Name" +FROM "Data" AS "d" +WHERE 0 +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArrayAllObjectStringEqual_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArrayAllObjectStringEqual_Expression_NET9_0.snap new file mode 100644 index 00000000000..5d6f8c0f35a --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArrayAllObjectStringEqual_Expression_NET9_0.snap @@ -0,0 +1,74 @@ +a Result: +--------------- +{ + "data": { + "root": [ + { + "fooNested": [ + { + "bar": "a" + }, + { + "bar": "a" + }, + { + "bar": "a" + } + ] + } + ] + } +} +--------------- + +a SQL: +--------------- +.param set @__p_0 'a' + +SELECT "d"."Id" +FROM "Data" AS "d" +WHERE NOT EXISTS ( + SELECT 1 + FROM "FooNested" AS "f" + WHERE "d"."Id" = "f"."FooId" AND ("f"."Bar" <> @__p_0 OR "f"."Bar" IS NULL)) +--------------- + +d Result: +--------------- +{ + "data": { + "root": [] + } +} +--------------- + +d SQL: +--------------- +.param set @__p_0 'd' + +SELECT "d"."Id" +FROM "Data" AS "d" +WHERE NOT EXISTS ( + SELECT 1 + FROM "FooNested" AS "f" + WHERE "d"."Id" = "f"."FooId" AND ("f"."Bar" <> @__p_0 OR "f"."Bar" IS NULL)) +--------------- + +null Result: +--------------- +{ + "data": { + "root": [] + } +} +--------------- + +null SQL: +--------------- +SELECT "d"."Id" +FROM "Data" AS "d" +WHERE NOT EXISTS ( + SELECT 1 + FROM "FooNested" AS "f" + WHERE "d"."Id" = "f"."FooId" AND "f"."Bar" IS NOT NULL) +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArrayAnyObjectStringEqual_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArrayAnyObjectStringEqual_Expression_NET9_0.snap new file mode 100644 index 00000000000..0a19b82f044 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArrayAnyObjectStringEqual_Expression_NET9_0.snap @@ -0,0 +1,131 @@ +false Result: +--------------- +{ + "data": { + "root": [] + } +} +--------------- + +false SQL: +--------------- +SELECT "d"."Id" +FROM "Data" AS "d" +WHERE NOT EXISTS ( + SELECT 1 + FROM "FooNested" AS "f" + WHERE "d"."Id" = "f"."FooId") +--------------- + +true Result: +--------------- +{ + "data": { + "root": [ + { + "fooNested": [ + { + "bar": "a" + }, + { + "bar": "a" + }, + { + "bar": "a" + } + ] + }, + { + "fooNested": [ + { + "bar": "c" + }, + { + "bar": "a" + }, + { + "bar": "a" + } + ] + }, + { + "fooNested": [ + { + "bar": "a" + }, + { + "bar": "d" + }, + { + "bar": "b" + } + ] + }, + { + "fooNested": [ + { + "bar": "c" + }, + { + "bar": "d" + }, + { + "bar": "b" + } + ] + }, + { + "fooNested": [ + { + "bar": null + }, + { + "bar": "d" + }, + { + "bar": "b" + } + ] + } + ] + } +} +--------------- + +true SQL: +--------------- +SELECT "d"."Id" +FROM "Data" AS "d" +WHERE EXISTS ( + SELECT 1 + FROM "FooNested" AS "f" + WHERE "d"."Id" = "f"."FooId") +--------------- + +null +--------------- +{ + "errors": [ + { + "message": "The provided value for filter `all` of type FooNestedFilterInput is invalid. Null values are not supported.", + "locations": [ + { + "line": 1, + "column": 35 + } + ], + "path": [ + "root" + ], + "extensions": { + "code": "HC0026", + "expectedType": "FooNestedFilterInput!", + "filterType": "FooNestedFilterInput" + } + } + ], + "data": { + "root": null + } +} +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArrayNoneObjectStringEqual_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArrayNoneObjectStringEqual_Expression_NET9_0.snap new file mode 100644 index 00000000000..63822313708 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArrayNoneObjectStringEqual_Expression_NET9_0.snap @@ -0,0 +1,167 @@ +a Result: +--------------- +{ + "data": { + "root": [ + { + "fooNested": [ + { + "bar": "c" + }, + { + "bar": "d" + }, + { + "bar": "b" + } + ] + }, + { + "fooNested": [ + { + "bar": null + }, + { + "bar": "d" + }, + { + "bar": "b" + } + ] + } + ] + } +} +--------------- + +a SQL: +--------------- +.param set @__p_0 'a' + +SELECT "d"."Id" +FROM "Data" AS "d" +WHERE NOT EXISTS ( + SELECT 1 + FROM "FooNested" AS "f" + WHERE "d"."Id" = "f"."FooId" AND "f"."Bar" = @__p_0) +--------------- + +d Result: +--------------- +{ + "data": { + "root": [ + { + "fooNested": [ + { + "bar": "a" + }, + { + "bar": "a" + }, + { + "bar": "a" + } + ] + }, + { + "fooNested": [ + { + "bar": "c" + }, + { + "bar": "a" + }, + { + "bar": "a" + } + ] + } + ] + } +} +--------------- + +d SQL: +--------------- +.param set @__p_0 'd' + +SELECT "d"."Id" +FROM "Data" AS "d" +WHERE NOT EXISTS ( + SELECT 1 + FROM "FooNested" AS "f" + WHERE "d"."Id" = "f"."FooId" AND "f"."Bar" = @__p_0) +--------------- + +null Result: +--------------- +{ + "data": { + "root": [ + { + "fooNested": [ + { + "bar": "a" + }, + { + "bar": "a" + }, + { + "bar": "a" + } + ] + }, + { + "fooNested": [ + { + "bar": "c" + }, + { + "bar": "a" + }, + { + "bar": "a" + } + ] + }, + { + "fooNested": [ + { + "bar": "a" + }, + { + "bar": "d" + }, + { + "bar": "b" + } + ] + }, + { + "fooNested": [ + { + "bar": "c" + }, + { + "bar": "d" + }, + { + "bar": "b" + } + ] + } + ] + } +} +--------------- + +null SQL: +--------------- +SELECT "d"."Id" +FROM "Data" AS "d" +WHERE NOT EXISTS ( + SELECT 1 + FROM "FooNested" AS "f" + WHERE "d"."Id" = "f"."FooId" AND "f"."Bar" IS NULL) +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArraySomeObjectStringEqualWithNull_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArraySomeObjectStringEqualWithNull_Expression_NET9_0.snap new file mode 100644 index 00000000000..c29562a4db0 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArraySomeObjectStringEqualWithNull_Expression_NET9_0.snap @@ -0,0 +1,154 @@ +a Result: +--------------- +{ + "data": { + "root": [ + { + "fooNested": [ + { + "bar": "a" + }, + { + "bar": "a" + }, + { + "bar": "a" + } + ] + }, + { + "fooNested": [ + { + "bar": "c" + }, + { + "bar": "a" + }, + { + "bar": "a" + } + ] + }, + { + "fooNested": [ + { + "bar": "a" + }, + { + "bar": "d" + }, + { + "bar": "b" + } + ] + } + ] + } +} +--------------- + +a SQL: +--------------- +.param set @__p_0 'a' + +SELECT "d"."Id" +FROM "Data" AS "d" +WHERE EXISTS ( + SELECT 1 + FROM "FooNested" AS "f" + WHERE "d"."Id" = "f"."FooId" AND "f"."Bar" = @__p_0) +--------------- + +d Result: +--------------- +{ + "data": { + "root": [ + { + "fooNested": [ + { + "bar": "a" + }, + { + "bar": "d" + }, + { + "bar": "b" + } + ] + }, + { + "fooNested": [ + { + "bar": "c" + }, + { + "bar": "d" + }, + { + "bar": "b" + } + ] + }, + { + "fooNested": [ + { + "bar": null + }, + { + "bar": "d" + }, + { + "bar": "b" + } + ] + } + ] + } +} +--------------- + +d SQL: +--------------- +.param set @__p_0 'd' + +SELECT "d"."Id" +FROM "Data" AS "d" +WHERE EXISTS ( + SELECT 1 + FROM "FooNested" AS "f" + WHERE "d"."Id" = "f"."FooId" AND "f"."Bar" = @__p_0) +--------------- + +null Result: +--------------- +{ + "data": { + "root": [ + { + "fooNested": [ + { + "bar": null + }, + { + "bar": "d" + }, + { + "bar": "b" + } + ] + } + ] + } +} +--------------- + +null SQL: +--------------- +SELECT "d"."Id" +FROM "Data" AS "d" +WHERE EXISTS ( + SELECT 1 + FROM "FooNested" AS "f" + WHERE "d"."Id" = "f"."FooId" AND "f"."Bar" IS NULL) +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ArrayObjectNestedArrayAnyStringEqual_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ArrayObjectNestedArrayAnyStringEqual_Expression_NET9_0.snap new file mode 100644 index 00000000000..5b892d39517 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ArrayObjectNestedArrayAnyStringEqual_Expression_NET9_0.snap @@ -0,0 +1,143 @@ +false Result: +--------------- +{ + "data": { + "root": [] + } +} +--------------- + +false SQL: +--------------- +SELECT "d"."Id", "d"."FooId" +FROM "Data" AS "d" +INNER JOIN "Foo" AS "f" ON "d"."FooId" = "f"."Id" +WHERE NOT EXISTS ( + SELECT 1 + FROM "Data" AS "d0" + WHERE "f"."Id" = "d0"."FooId") +--------------- + +true Result: +--------------- +{ + "data": { + "root": [ + { + "foo": { + "objectArray": [ + { + "foo": { + "barString": "testatest" + } + }, + { + "foo": { + "barString": "testatest" + } + } + ] + } + }, + { + "foo": { + "objectArray": [ + { + "foo": { + "barString": "testatest" + } + }, + { + "foo": { + "barString": "testatest" + } + } + ] + } + }, + { + "foo": { + "objectArray": [ + { + "foo": { + "barString": "testbtest" + } + }, + { + "foo": { + "barString": "testbtest" + } + } + ] + } + }, + { + "foo": { + "objectArray": [ + { + "foo": { + "barString": "testbtest" + } + }, + { + "foo": { + "barString": "testbtest" + } + } + ] + } + }, + { + "foo": { + "objectArray": [ + { + "foo": { + "barString": "testctest" + } + } + ] + } + } + ] + } +} +--------------- + +true SQL: +--------------- +SELECT "d"."Id", "d"."FooId" +FROM "Data" AS "d" +INNER JOIN "Foo" AS "f" ON "d"."FooId" = "f"."Id" +WHERE EXISTS ( + SELECT 1 + FROM "Data" AS "d0" + WHERE "f"."Id" = "d0"."FooId") +--------------- + +null +--------------- +{ + "errors": [ + { + "message": "The provided value for filter `any` of type ListBarFilterInput is invalid. Null values are not supported.", + "locations": [ + { + "line": 1, + "column": 44 + } + ], + "path": [ + "root" + ], + "extensions": { + "code": "HC0026", + "expectedType": "Boolean!", + "filterType": "ListBarFilterInput" + } + } + ], + "data": { + "root": null + } +} +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ArrayObjectNestedArraySomeStringEqual_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ArrayObjectNestedArraySomeStringEqual_Expression_NET9_0.snap new file mode 100644 index 00000000000..1474f5f43c0 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ArrayObjectNestedArraySomeStringEqual_Expression_NET9_0.snap @@ -0,0 +1,73 @@ +a Result: +--------------- +{ + "data": { + "root": [] + } +} +--------------- + +a SQL: +--------------- +.param set @__p_0 'a' + +SELECT "d"."Id", "d"."FooId" +FROM "Data" AS "d" +INNER JOIN "Foo" AS "f" ON "d"."FooId" = "f"."Id" +WHERE EXISTS ( + SELECT 1 + FROM "Data" AS "d0" + INNER JOIN "Foo" AS "f0" ON "d0"."FooId" = "f0"."Id" + WHERE "f"."Id" = "d0"."FooId" AND "f0"."BarString" = @__p_0) +--------------- + +b Result: +--------------- +{ + "data": { + "root": [] + } +} +--------------- + +b SQL: +--------------- +.param set @__p_0 'd' + +SELECT "d"."Id", "d"."FooId" +FROM "Data" AS "d" +INNER JOIN "Foo" AS "f" ON "d"."FooId" = "f"."Id" +WHERE EXISTS ( + SELECT 1 + FROM "Data" AS "d0" + INNER JOIN "Foo" AS "f0" ON "d0"."FooId" = "f0"."Id" + WHERE "f"."Id" = "d0"."FooId" AND "f0"."BarString" = @__p_0) +--------------- + +null +--------------- +{ + "errors": [ + { + "message": "The provided value for filter `eq` of type StringOperationFilterInput is invalid. Null values are not supported.", + "locations": [ + { + "line": 1, + "column": 70 + } + ], + "path": [ + "root" + ], + "extensions": { + "code": "HC0026", + "expectedType": "String!", + "filterType": "StringOperationFilterInput" + } + } + ], + "data": { + "root": null + } +} +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ObjectEnumIn_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ObjectEnumIn_Expression_NET9_0.snap new file mode 100644 index 00000000000..9a069bb2bed --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ObjectEnumIn_Expression_NET9_0.snap @@ -0,0 +1,91 @@ +BarAndFoo Result: +--------------- +{ + "data": { + "root": [ + { + "foo": { + "barEnum": "BAR" + } + }, + { + "foo": { + "barEnum": "BAR" + } + }, + { + "foo": { + "barEnum": "FOO" + } + } + ] + } +} +--------------- + +BarAndFoo SQL: +--------------- +.param set @__p_0 '[1,0]' + +SELECT "d"."Id", "d"."FooId" +FROM "Data" AS "d" +INNER JOIN "Foo" AS "f" ON "d"."FooId" = "f"."Id" +WHERE "f"."BarEnum" IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- + +FOO Result: +--------------- +{ + "data": { + "root": [ + { + "foo": { + "barEnum": "FOO" + } + } + ] + } +} +--------------- + +FOO SQL: +--------------- +.param set @__p_0 '[0]' + +SELECT "d"."Id", "d"."FooId" +FROM "Data" AS "d" +INNER JOIN "Foo" AS "f" ON "d"."FooId" = "f"."Id" +WHERE "f"."BarEnum" IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- + +nullAndFoo Result: +--------------- +{ + "errors": [ + { + "message": "The specified value type of field `in` does not match the field type.", + "locations": [ + { + "line": 1, + "column": 39 + } + ], + "path": [ + "root" + ], + "extensions": { + "fieldName": "in", + "fieldType": "[BarEnum!]", + "locationType": "[BarEnum!]", + "specifiedBy": "https://spec.graphql.org/October2021/#sec-Values-of-Correct-Type" + } + } + ] +} +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ObjectNullableEnumIn_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ObjectNullableEnumIn_Expression_NET9_0.snap new file mode 100644 index 00000000000..ec82659bb1d --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ObjectNullableEnumIn_Expression_NET9_0.snap @@ -0,0 +1,93 @@ +BarAndFoo Result: +--------------- +{ + "data": { + "root": [ + { + "foo": { + "barEnum": "BAR" + } + }, + { + "foo": { + "barEnum": "BAR" + } + }, + { + "foo": { + "barEnum": "FOO" + } + } + ] + } +} +--------------- + +BarAndFoo SQL: +--------------- +.param set @__p_0 '[1,0]' + +SELECT "d"."Id", "d"."FooId" +FROM "Data" AS "d" +LEFT JOIN "FooNullable" AS "f" ON "d"."FooId" = "f"."Id" +WHERE "f"."BarEnum" IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- + +FOO Result: +--------------- +{ + "data": { + "root": [ + { + "foo": { + "barEnum": "FOO" + } + } + ] + } +} +--------------- + +FOO SQL: +--------------- +.param set @__p_0 '[0]' + +SELECT "d"."Id", "d"."FooId" +FROM "Data" AS "d" +LEFT JOIN "FooNullable" AS "f" ON "d"."FooId" = "f"."Id" +WHERE "f"."BarEnum" IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- + +nullAndFoo Result: +--------------- +{ + "data": { + "root": [ + { + "foo": { + "barEnum": "FOO" + } + } + ] + } +} +--------------- + +nullAndFoo SQL: +--------------- +.param set @__p_0_without_nulls '[0]' + +SELECT "d"."Id", "d"."FooId" +FROM "Data" AS "d" +LEFT JOIN "FooNullable" AS "f" ON "d"."FooId" = "f"."Id" +WHERE "f"."BarEnum" IN ( + SELECT "p"."value" + FROM json_each(@__p_0_without_nulls) AS "p" +) OR "f"."BarEnum" IS NULL +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ObjectNullableShortIn_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ObjectNullableShortIn_Expression_NET9_0.snap new file mode 100644 index 00000000000..51cf4ad1be9 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ObjectNullableShortIn_Expression_NET9_0.snap @@ -0,0 +1,113 @@ +12and13 Result: +--------------- +{ + "data": { + "root": [ + { + "foo": { + "barShort": 12 + } + }, + { + "foo": { + "barShort": 12 + } + }, + { + "foo": { + "barShort": 13 + } + } + ] + } +} +--------------- + +12and13 SQL: +--------------- +.param set @__p_0 '[12,13]' + +SELECT "d"."Id", "d"."FooId" +FROM "Data" AS "d" +LEFT JOIN "FooNullable" AS "f" ON "d"."FooId" = "f"."Id" +WHERE "f"."BarShort" IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- + +13and14 Result: +--------------- +{ + "data": { + "root": [ + { + "foo": { + "barShort": 14 + } + }, + { + "foo": { + "barShort": 14 + } + }, + { + "foo": { + "barShort": 13 + } + } + ] + } +} +--------------- + +13and14 SQL: +--------------- +.param set @__p_0 '[13,14]' + +SELECT "d"."Id", "d"."FooId" +FROM "Data" AS "d" +LEFT JOIN "FooNullable" AS "f" ON "d"."FooId" = "f"."Id" +WHERE "f"."BarShort" IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- + +13andNull Result: +--------------- +{ + "data": { + "root": [ + { + "foo": { + "barShort": null + } + }, + { + "foo": { + "barShort": null + } + }, + { + "foo": { + "barShort": 13 + } + } + ] + } +} +--------------- + +13andNull SQL: +--------------- +.param set @__p_0_without_nulls '[13]' + +SELECT "d"."Id", "d"."FooId" +FROM "Data" AS "d" +LEFT JOIN "FooNullable" AS "f" ON "d"."FooId" = "f"."Id" +WHERE "f"."BarShort" IN ( + SELECT "p"."value" + FROM json_each(@__p_0_without_nulls) AS "p" +) OR "f"."BarShort" IS NULL +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ObjectShortIn_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ObjectShortIn_Expression_NET9_0.snap new file mode 100644 index 00000000000..9e5609e3733 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ObjectShortIn_Expression_NET9_0.snap @@ -0,0 +1,103 @@ +12and13 Result: +--------------- +{ + "data": { + "root": [ + { + "foo": { + "barShort": 12 + } + }, + { + "foo": { + "barShort": 12 + } + }, + { + "foo": { + "barShort": 13 + } + } + ] + } +} +--------------- + +12and13 SQL: +--------------- +.param set @__p_0 '[12,13]' + +SELECT "d"."Id", "d"."FooId" +FROM "Data" AS "d" +INNER JOIN "Foo" AS "f" ON "d"."FooId" = "f"."Id" +WHERE "f"."BarShort" IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- + +13and14 Result: +--------------- +{ + "data": { + "root": [ + { + "foo": { + "barShort": 14 + } + }, + { + "foo": { + "barShort": 14 + } + }, + { + "foo": { + "barShort": 13 + } + } + ] + } +} +--------------- + +13and14 SQL: +--------------- +.param set @__p_0 '[13,14]' + +SELECT "d"."Id", "d"."FooId" +FROM "Data" AS "d" +INNER JOIN "Foo" AS "f" ON "d"."FooId" = "f"."Id" +WHERE "f"."BarShort" IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- + +nullAnd14 +--------------- +{ + "errors": [ + { + "message": "The provided value for filter `in` of type ShortOperationFilterInput is invalid. Null values are not supported.", + "locations": [ + { + "line": 1, + "column": 40 + } + ], + "path": [ + "root" + ], + "extensions": { + "code": "HC0026", + "expectedType": "[Short!]", + "filterType": "ShortOperationFilterInput" + } + } + ], + "data": { + "root": null + } +} +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ObjectStringIn_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ObjectStringIn_Expression_NET9_0.snap new file mode 100644 index 00000000000..15c6c31d2b8 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ObjectStringIn_Expression_NET9_0.snap @@ -0,0 +1,103 @@ +testatestAndtestb Result: +--------------- +{ + "data": { + "root": [ + { + "foo": { + "barString": "testatest" + } + }, + { + "foo": { + "barString": "testatest" + } + }, + { + "foo": { + "barString": "testbtest" + } + }, + { + "foo": { + "barString": "testbtest" + } + } + ] + } +} +--------------- + +testatestAndtestb SQL: +--------------- +.param set @__p_0 '["testatest","testbtest"]' + +SELECT "d"."Id", "d"."FooId" +FROM "Data" AS "d" +INNER JOIN "Foo" AS "f" ON "d"."FooId" = "f"."Id" +WHERE "f"."BarString" IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- + +testbtestAndNull +--------------- +{ + "errors": [ + { + "message": "The provided value for filter `in` of type StringOperationFilterInput is invalid. Null values are not supported.", + "locations": [ + { + "line": 1, + "column": 41 + } + ], + "path": [ + "root" + ], + "extensions": { + "code": "HC0026", + "expectedType": "[String!]", + "filterType": "StringOperationFilterInput" + } + } + ], + "data": { + "root": null + } +} +--------------- + +testatest Result: +--------------- +{ + "data": { + "root": [ + { + "foo": { + "barString": "testatest" + } + }, + { + "foo": { + "barString": "testatest" + } + } + ] + } +} +--------------- + +testatest SQL: +--------------- +.param set @__p_0 '["testatest"]' + +SELECT "d"."Id", "d"."FooId" +FROM "Data" AS "d" +INNER JOIN "Foo" AS "f" ON "d"."FooId" = "f"."Id" +WHERE "f"."BarString" IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringContains_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringContains_Expression_NET9_0.snap new file mode 100644 index 00000000000..65b8d3c118b --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringContains_Expression_NET9_0.snap @@ -0,0 +1,71 @@ +a Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testatest" + } + ] + } +} +--------------- + +a SQL: +--------------- +.param set @__p_0 'a' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" IS NOT NULL AND instr("d"."Bar", @__p_0) > 0 +--------------- + +b Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testbtest" + } + ] + } +} +--------------- + +b SQL: +--------------- +.param set @__p_0 'b' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" IS NOT NULL AND instr("d"."Bar", @__p_0) > 0 +--------------- + +null +--------------- +{ + "errors": [ + { + "message": "The provided value for filter `contains` of type StringOperationFilterInput is invalid. Null values are not supported.", + "locations": [ + { + "line": 1, + "column": 34 + } + ], + "path": [ + "root" + ], + "extensions": { + "code": "HC0026", + "expectedType": "String!", + "filterType": "StringOperationFilterInput" + } + } + ], + "data": { + "root": null + } +} +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringEndsWith_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringEndsWith_Expression_NET9_0.snap new file mode 100644 index 00000000000..1b1d2c51906 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringEndsWith_Expression_NET9_0.snap @@ -0,0 +1,71 @@ +atest Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testatest" + } + ] + } +} +--------------- + +atest SQL: +--------------- +.param set @__p_0_endswith '%atest' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" IS NOT NULL AND "d"."Bar" LIKE @__p_0_endswith ESCAPE '\' +--------------- + +btest Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testbtest" + } + ] + } +} +--------------- + +btest SQL: +--------------- +.param set @__p_0_endswith '%btest' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" IS NOT NULL AND "d"."Bar" LIKE @__p_0_endswith ESCAPE '\' +--------------- + +null +--------------- +{ + "errors": [ + { + "message": "The provided value for filter `endsWith` of type StringOperationFilterInput is invalid. Null values are not supported.", + "locations": [ + { + "line": 1, + "column": 34 + } + ], + "path": [ + "root" + ], + "extensions": { + "code": "HC0026", + "expectedType": "String!", + "filterType": "StringOperationFilterInput" + } + } + ], + "data": { + "root": null + } +} +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringIn_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringIn_Expression_NET9_0.snap new file mode 100644 index 00000000000..9059a2331b1 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringIn_Expression_NET9_0.snap @@ -0,0 +1,80 @@ +testatestAndtestb Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testatest" + }, + { + "bar": "testbtest" + } + ] + } +} +--------------- + +testatestAndtestb SQL: +--------------- +.param set @__p_0 '["testatest","testbtest"]' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- + +testbtestAndNull Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testbtest" + }, + { + "bar": null + } + ] + } +} +--------------- + +testbtestAndNull SQL: +--------------- +.param set @__p_0_without_nulls '["testbtest"]' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" IN ( + SELECT "p"."value" + FROM json_each(@__p_0_without_nulls) AS "p" +) OR "d"."Bar" IS NULL +--------------- + +testatest Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testatest" + } + ] + } +} +--------------- + +testatest SQL: +--------------- +.param set @__p_0 '["testatest"]' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringNoContains_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringNoContains_Expression_NET9_0.snap new file mode 100644 index 00000000000..b1eccdc5016 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringNoContains_Expression_NET9_0.snap @@ -0,0 +1,77 @@ +a Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testbtest" + }, + { + "bar": null + } + ] + } +} +--------------- + +a SQL: +--------------- +.param set @__p_0 'a' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" IS NULL OR instr("d"."Bar", @__p_0) <= 0 +--------------- + +b Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testatest" + }, + { + "bar": null + } + ] + } +} +--------------- + +b SQL: +--------------- +.param set @__p_0 'b' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" IS NULL OR instr("d"."Bar", @__p_0) <= 0 +--------------- + +null +--------------- +{ + "errors": [ + { + "message": "The provided value for filter `ncontains` of type StringOperationFilterInput is invalid. Null values are not supported.", + "locations": [ + { + "line": 1, + "column": 35 + } + ], + "path": [ + "root" + ], + "extensions": { + "code": "HC0026", + "expectedType": "String!", + "filterType": "StringOperationFilterInput" + } + } + ], + "data": { + "root": null + } +} +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringNotEndsWith_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringNotEndsWith_Expression_NET9_0.snap new file mode 100644 index 00000000000..96e427a0820 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringNotEndsWith_Expression_NET9_0.snap @@ -0,0 +1,77 @@ +atest Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testbtest" + }, + { + "bar": null + } + ] + } +} +--------------- + +atest SQL: +--------------- +.param set @__p_0_endswith '%atest' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" IS NULL OR "d"."Bar" NOT LIKE @__p_0_endswith ESCAPE '\' +--------------- + +btest Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testatest" + }, + { + "bar": null + } + ] + } +} +--------------- + +btest SQL: +--------------- +.param set @__p_0_endswith '%btest' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" IS NULL OR "d"."Bar" NOT LIKE @__p_0_endswith ESCAPE '\' +--------------- + +null +--------------- +{ + "errors": [ + { + "message": "The provided value for filter `nendsWith` of type StringOperationFilterInput is invalid. Null values are not supported.", + "locations": [ + { + "line": 1, + "column": 35 + } + ], + "path": [ + "root" + ], + "extensions": { + "code": "HC0026", + "expectedType": "String!", + "filterType": "StringOperationFilterInput" + } + } + ], + "data": { + "root": null + } +} +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringNotEqual_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringNotEqual_Expression_NET9_0.snap new file mode 100644 index 00000000000..53b3004c365 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringNotEqual_Expression_NET9_0.snap @@ -0,0 +1,72 @@ +testatest Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testbtest" + }, + { + "bar": null + } + ] + } +} +--------------- + +testatest SQL: +--------------- +.param set @__p_0 'testatest' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" <> @__p_0 OR "d"."Bar" IS NULL +--------------- + +testbtest Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testatest" + }, + { + "bar": null + } + ] + } +} +--------------- + +testbtest SQL: +--------------- +.param set @__p_0 'testbtest' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" <> @__p_0 OR "d"."Bar" IS NULL +--------------- + +null Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testatest" + }, + { + "bar": "testbtest" + } + ] + } +} +--------------- + +null SQL: +--------------- +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" IS NOT NULL +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringNotIn_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringNotIn_Expression_NET9_0.snap new file mode 100644 index 00000000000..ffa93543cda --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringNotIn_Expression_NET9_0.snap @@ -0,0 +1,77 @@ +testatestAndtestb Result: +--------------- +{ + "data": { + "root": [ + { + "bar": null + } + ] + } +} +--------------- + +testatestAndtestb SQL: +--------------- +.param set @__p_0 '["testatest","testbtest"]' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" NOT IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) OR "d"."Bar" IS NULL +--------------- + +testbtestAndNull Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testatest" + } + ] + } +} +--------------- + +testbtestAndNull SQL: +--------------- +.param set @__p_0_without_nulls '["testbtest"]' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" NOT IN ( + SELECT "p"."value" + FROM json_each(@__p_0_without_nulls) AS "p" +) AND "d"."Bar" IS NOT NULL +--------------- + +testatest Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testbtest" + }, + { + "bar": null + } + ] + } +} +--------------- + +testatest SQL: +--------------- +.param set @__p_0 '["testatest"]' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" NOT IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) OR "d"."Bar" IS NULL +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringNotStartsWith_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringNotStartsWith_Expression_NET9_0.snap new file mode 100644 index 00000000000..77a754414f2 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringNotStartsWith_Expression_NET9_0.snap @@ -0,0 +1,77 @@ +testa Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testbtest" + }, + { + "bar": null + } + ] + } +} +--------------- + +testa SQL: +--------------- +.param set @__p_0_startswith 'testa%' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" IS NULL OR "d"."Bar" NOT LIKE @__p_0_startswith ESCAPE '\' +--------------- + +testb Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testatest" + }, + { + "bar": null + } + ] + } +} +--------------- + +testb SQL: +--------------- +.param set @__p_0_startswith 'testb%' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" IS NULL OR "d"."Bar" NOT LIKE @__p_0_startswith ESCAPE '\' +--------------- + +null +--------------- +{ + "errors": [ + { + "message": "The provided value for filter `nstartsWith` of type StringOperationFilterInput is invalid. Null values are not supported.", + "locations": [ + { + "line": 1, + "column": 37 + } + ], + "path": [ + "root" + ], + "extensions": { + "code": "HC0026", + "expectedType": "String!", + "filterType": "StringOperationFilterInput" + } + } + ], + "data": { + "root": null + } +} +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringStartsWith_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringStartsWith_Expression_NET9_0.snap new file mode 100644 index 00000000000..ef3b1890a74 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_NullableStringStartsWith_Expression_NET9_0.snap @@ -0,0 +1,71 @@ +testa Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testatest" + } + ] + } +} +--------------- + +testa SQL: +--------------- +.param set @__p_0_startswith 'testa%' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" IS NOT NULL AND "d"."Bar" LIKE @__p_0_startswith ESCAPE '\' +--------------- + +testb Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testbtest" + } + ] + } +} +--------------- + +testb SQL: +--------------- +.param set @__p_0_startswith 'testb%' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" IS NOT NULL AND "d"."Bar" LIKE @__p_0_startswith ESCAPE '\' +--------------- + +null +--------------- +{ + "errors": [ + { + "message": "The provided value for filter `startsWith` of type StringOperationFilterInput is invalid. Null values are not supported.", + "locations": [ + { + "line": 1, + "column": 36 + } + ], + "path": [ + "root" + ], + "extensions": { + "code": "HC0026", + "expectedType": "String!", + "filterType": "StringOperationFilterInput" + } + } + ], + "data": { + "root": null + } +} +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringContains_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringContains_Expression_NET9_0.snap new file mode 100644 index 00000000000..06a53f9909b --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringContains_Expression_NET9_0.snap @@ -0,0 +1,71 @@ +a Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testatest" + } + ] + } +} +--------------- + +a SQL: +--------------- +.param set @__p_0 'a' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE instr("d"."Bar", @__p_0) > 0 +--------------- + +b Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testbtest" + } + ] + } +} +--------------- + +b SQL: +--------------- +.param set @__p_0 'b' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE instr("d"."Bar", @__p_0) > 0 +--------------- + +null +--------------- +{ + "errors": [ + { + "message": "The provided value for filter `contains` of type StringOperationFilterInput is invalid. Null values are not supported.", + "locations": [ + { + "line": 1, + "column": 34 + } + ], + "path": [ + "root" + ], + "extensions": { + "code": "HC0026", + "expectedType": "String!", + "filterType": "StringOperationFilterInput" + } + } + ], + "data": { + "root": null + } +} +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringEndsWith_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringEndsWith_Expression_NET9_0.snap new file mode 100644 index 00000000000..bdf5a199f37 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringEndsWith_Expression_NET9_0.snap @@ -0,0 +1,71 @@ +atest Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testatest" + } + ] + } +} +--------------- + +atest SQL: +--------------- +.param set @__p_0_endswith '%atest' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" LIKE @__p_0_endswith ESCAPE '\' +--------------- + +btest Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testbtest" + } + ] + } +} +--------------- + +btest SQL: +--------------- +.param set @__p_0_endswith '%btest' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" LIKE @__p_0_endswith ESCAPE '\' +--------------- + +null +--------------- +{ + "errors": [ + { + "message": "The provided value for filter `endsWith` of type StringOperationFilterInput is invalid. Null values are not supported.", + "locations": [ + { + "line": 1, + "column": 34 + } + ], + "path": [ + "root" + ], + "extensions": { + "code": "HC0026", + "expectedType": "String!", + "filterType": "StringOperationFilterInput" + } + } + ], + "data": { + "root": null + } +} +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringIn_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringIn_Expression_NET9_0.snap new file mode 100644 index 00000000000..ea83f18bfb9 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringIn_Expression_NET9_0.snap @@ -0,0 +1,80 @@ +testatestAndtestb Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testatest" + }, + { + "bar": "testbtest" + } + ] + } +} +--------------- + +testatestAndtestb SQL: +--------------- +.param set @__p_0 '["testatest","testbtest"]' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- + +testbtestAndNull +--------------- +{ + "errors": [ + { + "message": "The provided value for filter `in` of type StringOperationFilterInput is invalid. Null values are not supported.", + "locations": [ + { + "line": 1, + "column": 28 + } + ], + "path": [ + "root" + ], + "extensions": { + "code": "HC0026", + "expectedType": "[String!]", + "filterType": "StringOperationFilterInput" + } + } + ], + "data": { + "root": null + } +} +--------------- + +testatest Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testatest" + } + ] + } +} +--------------- + +testatest SQL: +--------------- +.param set @__p_0 '["testatest"]' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringNoContains_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringNoContains_Expression_NET9_0.snap new file mode 100644 index 00000000000..4ff2d04f65b --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringNoContains_Expression_NET9_0.snap @@ -0,0 +1,71 @@ +a Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testbtest" + } + ] + } +} +--------------- + +a SQL: +--------------- +.param set @__p_0 'a' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE instr("d"."Bar", @__p_0) <= 0 +--------------- + +b Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testatest" + } + ] + } +} +--------------- + +b SQL: +--------------- +.param set @__p_0 'b' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE instr("d"."Bar", @__p_0) <= 0 +--------------- + +null +--------------- +{ + "errors": [ + { + "message": "The provided value for filter `ncontains` of type StringOperationFilterInput is invalid. Null values are not supported.", + "locations": [ + { + "line": 1, + "column": 35 + } + ], + "path": [ + "root" + ], + "extensions": { + "code": "HC0026", + "expectedType": "String!", + "filterType": "StringOperationFilterInput" + } + } + ], + "data": { + "root": null + } +} +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringNotEndsWith_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringNotEndsWith_Expression_NET9_0.snap new file mode 100644 index 00000000000..d9d444861dd --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringNotEndsWith_Expression_NET9_0.snap @@ -0,0 +1,71 @@ +atest Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testbtest" + } + ] + } +} +--------------- + +atest SQL: +--------------- +.param set @__p_0_endswith '%atest' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" NOT LIKE @__p_0_endswith ESCAPE '\' +--------------- + +btest Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testatest" + } + ] + } +} +--------------- + +btest SQL: +--------------- +.param set @__p_0_endswith '%btest' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" NOT LIKE @__p_0_endswith ESCAPE '\' +--------------- + +null +--------------- +{ + "errors": [ + { + "message": "The provided value for filter `nendsWith` of type StringOperationFilterInput is invalid. Null values are not supported.", + "locations": [ + { + "line": 1, + "column": 35 + } + ], + "path": [ + "root" + ], + "extensions": { + "code": "HC0026", + "expectedType": "String!", + "filterType": "StringOperationFilterInput" + } + } + ], + "data": { + "root": null + } +} +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringNotIn_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringNotIn_Expression_NET9_0.snap new file mode 100644 index 00000000000..43523d406a8 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringNotIn_Expression_NET9_0.snap @@ -0,0 +1,73 @@ +testatestAndtestb Result: +--------------- +{ + "data": { + "root": [] + } +} +--------------- + +testatestAndtestb SQL: +--------------- +.param set @__p_0 '["testatest","testbtest"]' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" NOT IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- + +testbtestAndNull +--------------- +{ + "errors": [ + { + "message": "The provided value for filter `nin` of type StringOperationFilterInput is invalid. Null values are not supported.", + "locations": [ + { + "line": 1, + "column": 29 + } + ], + "path": [ + "root" + ], + "extensions": { + "code": "HC0026", + "expectedType": "[String!]", + "filterType": "StringOperationFilterInput" + } + } + ], + "data": { + "root": null + } +} +--------------- + +testatest Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testbtest" + } + ] + } +} +--------------- + +testatest SQL: +--------------- +.param set @__p_0 '["testatest"]' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" NOT IN ( + SELECT "p"."value" + FROM json_each(@__p_0) AS "p" +) +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringNotStartsWith_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringNotStartsWith_Expression_NET9_0.snap new file mode 100644 index 00000000000..058fd6ff261 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringNotStartsWith_Expression_NET9_0.snap @@ -0,0 +1,71 @@ +testa Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testbtest" + } + ] + } +} +--------------- + +testa SQL: +--------------- +.param set @__p_0_startswith 'testa%' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" NOT LIKE @__p_0_startswith ESCAPE '\' +--------------- + +testb Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testatest" + } + ] + } +} +--------------- + +testb SQL: +--------------- +.param set @__p_0_startswith 'testb%' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" NOT LIKE @__p_0_startswith ESCAPE '\' +--------------- + +null +--------------- +{ + "errors": [ + { + "message": "The provided value for filter `nstartsWith` of type StringOperationFilterInput is invalid. Null values are not supported.", + "locations": [ + { + "line": 1, + "column": 37 + } + ], + "path": [ + "root" + ], + "extensions": { + "code": "HC0026", + "expectedType": "String!", + "filterType": "StringOperationFilterInput" + } + } + ], + "data": { + "root": null + } +} +--------------- diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringStartsWith_Expression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringStartsWith_Expression_NET9_0.snap new file mode 100644 index 00000000000..3a8dc9fdd38 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/QueryableFilterVisitorStringTests.Create_StringStartsWith_Expression_NET9_0.snap @@ -0,0 +1,71 @@ +testa Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testatest" + } + ] + } +} +--------------- + +testa SQL: +--------------- +.param set @__p_0_startswith 'testa%' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" LIKE @__p_0_startswith ESCAPE '\' +--------------- + +testb Result: +--------------- +{ + "data": { + "root": [ + { + "bar": "testbtest" + } + ] + } +} +--------------- + +testb SQL: +--------------- +.param set @__p_0_startswith 'testb%' + +SELECT "d"."Id", "d"."Bar" +FROM "Data" AS "d" +WHERE "d"."Bar" LIKE @__p_0_startswith ESCAPE '\' +--------------- + +null +--------------- +{ + "errors": [ + { + "message": "The provided value for filter `startsWith` of type StringOperationFilterInput is invalid. Null values are not supported.", + "locations": [ + { + "line": 1, + "column": 36 + } + ], + "path": [ + "root" + ], + "extensions": { + "code": "HC0026", + "expectedType": "String!", + "filterType": "StringOperationFilterInput" + } + } + ], + "data": { + "root": null + } +} +--------------- diff --git a/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/QueryableProjectionFilterTests.cs b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/QueryableProjectionFilterTests.cs index 17c1d545f96..952a00d4a0e 100644 --- a/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/QueryableProjectionFilterTests.cs +++ b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/QueryableProjectionFilterTests.cs @@ -152,7 +152,11 @@ public async Task Create_DeepFilterObjectTwoProjections() // assert await Snapshot +#if NET9_0 + .Create(postFix: "NET9_0") +#else .Create() +#endif .AddResult(res1) .MatchAsync(); } @@ -192,7 +196,11 @@ public async Task Create_ListObjectDifferentLevelProjection() // assert await Snapshot +#if NET9_0 + .Create(postFix: "NET9_0") +#else .Create() +#endif .AddResult(res1) .MatchAsync(); } @@ -230,7 +238,11 @@ public async Task Create_DeepFilterObjectTwoProjections_Nullable() // assert await Snapshot +#if NET9_0 + .Create(postFix: "NET9_0") +#else .Create() +#endif .AddResult(res1) .MatchAsync(); } @@ -269,7 +281,11 @@ public async Task Create_ListObjectDifferentLevelProjection_Nullable() // assert await Snapshot +#if NET9_0 + .Create(postFix: "NET9_0") +#else .Create() +#endif .AddResult(res1) .MatchAsync(); } diff --git a/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/QueryableProjectionHashSetTest.cs b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/QueryableProjectionHashSetTest.cs index b4fb596c9cd..1d189205005 100644 --- a/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/QueryableProjectionHashSetTest.cs +++ b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/QueryableProjectionHashSetTest.cs @@ -71,7 +71,11 @@ public async Task Create_DeepFilterObjectTwoProjections() // assert await Snapshot +#if NET9_0 + .Create(postFix: "NET9_0") +#else .Create() +#endif .AddResult(res1) .MatchAsync(); } @@ -104,7 +108,11 @@ public async Task Create_ListObjectDifferentLevelProjection() // assert await Snapshot +#if NET9_0 + .Create(postFix: "NET9_0") +#else .Create() +#endif .AddResult(res1) .MatchAsync(); } diff --git a/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/QueryableProjectionSetTest.cs b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/QueryableProjectionSetTest.cs index 31105865b98..3911baa9432 100644 --- a/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/QueryableProjectionSetTest.cs +++ b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/QueryableProjectionSetTest.cs @@ -77,7 +77,11 @@ public async Task Create_DeepFilterObjectTwoProjections() // assert await Snapshot +#if NET9_0 + .Create(postFix: "NET9_0") +#else .Create() +#endif .AddResult(res1) .MatchAsync(); } @@ -110,7 +114,11 @@ public async Task Create_ListObjectDifferentLevelProjection() // assert await Snapshot +#if NET9_0 + .Create(postFix: "NET9_0") +#else .Create() +#endif .AddResult(res1) .MatchAsync(); } diff --git a/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/QueryableProjectionSortedSetTest.cs b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/QueryableProjectionSortedSetTest.cs index 9ca39388c52..52e71d2262d 100644 --- a/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/QueryableProjectionSortedSetTest.cs +++ b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/QueryableProjectionSortedSetTest.cs @@ -71,7 +71,11 @@ public async Task Create_DeepFilterObjectTwoProjections() // assert await Snapshot +#if NET9_0 + .Create(postFix: "NET9_0") +#else .Create() +#endif .AddResult(res1) .MatchAsync(); } @@ -104,7 +108,11 @@ public async Task Create_ListObjectDifferentLevelProjection() // assert await Snapshot +#if NET9_0 + .Create(postFix: "NET9_0") +#else .Create() +#endif .AddResult(res1) .MatchAsync(); } diff --git a/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/QueryableProjectionSortingTests.cs b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/QueryableProjectionSortingTests.cs index 1fb4deaedaf..df1f1713a8e 100644 --- a/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/QueryableProjectionSortingTests.cs +++ b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/QueryableProjectionSortingTests.cs @@ -149,7 +149,11 @@ public async Task Create_DeepFilterObjectTwoProjections() // assert await Snapshot +#if NET9_0 + .Create(postFix: "NET9_0") +#else .Create() +#endif .AddResult(res1) .MatchAsync(); } @@ -187,7 +191,11 @@ public async Task Create_ListObjectDifferentLevelProjection() // assert await Snapshot +#if NET9_0 + .Create(postFix: "NET9_0") +#else .Create() +#endif .AddResult(res1) .MatchAsync(); } diff --git a/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionFilterTests.Create_DeepFilterObjectTwoProjections_NET9_0.snap b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionFilterTests.Create_DeepFilterObjectTwoProjections_NET9_0.snap new file mode 100644 index 00000000000..2b9c7406bfc --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionFilterTests.Create_DeepFilterObjectTwoProjections_NET9_0.snap @@ -0,0 +1,42 @@ +Result: +--------------- +{ + "data": { + "root": [ + { + "foo": { + "objectArray": [ + { + "foo": { + "barString": "a", + "barShort": 12 + } + } + ] + } + }, + { + "foo": { + "objectArray": [] + } + } + ] + } +} +--------------- + +SQL: +--------------- +.param set @__p_0 'a' + +SELECT 1, "d"."Id", "f"."Id", "s"."c", "s"."BarString", "s"."BarShort", "s"."Id", "s"."Id0" +FROM "Data" AS "d" +INNER JOIN "Foo" AS "f" ON "d"."FooId" = "f"."Id" +LEFT JOIN ( + SELECT 1 AS "c", "f0"."BarString", "f0"."BarShort", "b"."Id", "f0"."Id" AS "Id0", "b"."FooId" + FROM "BarDeep" AS "b" + INNER JOIN "FooDeep" AS "f0" ON "b"."FooId1" = "f0"."Id" + WHERE "f0"."BarString" = @__p_0 +) AS "s" ON "f"."Id" = "s"."FooId" +ORDER BY "d"."Id", "f"."Id", "s"."Id" +--------------- diff --git a/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionFilterTests.Create_DeepFilterObjectTwoProjections_Nullable_NET9_0.snap b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionFilterTests.Create_DeepFilterObjectTwoProjections_Nullable_NET9_0.snap new file mode 100644 index 00000000000..069a5cffa62 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionFilterTests.Create_DeepFilterObjectTwoProjections_Nullable_NET9_0.snap @@ -0,0 +1,45 @@ +Result: +--------------- +{ + "data": { + "root": [ + { + "foo": { + "objectArray": [] + } + }, + { + "foo": { + "objectArray": [] + } + }, + { + "foo": { + "objectArray": [] + } + }, + { + "foo": { + "objectArray": [] + } + } + ] + } +} +--------------- + +SQL: +--------------- +.param set @__p_0 'a' + +SELECT "f"."Id" IS NOT NULL, "d"."Id", "f"."Id", "s"."c", "s"."BarString", "s"."BarShort", "s"."Id", "s"."Id0" +FROM "Data" AS "d" +LEFT JOIN "FooNullable" AS "f" ON "d"."FooId" = "f"."Id" +LEFT JOIN ( + SELECT "f0"."Id" IS NOT NULL AS "c", "f0"."BarString", "f0"."BarShort", "b"."Id", "f0"."Id" AS "Id0", "b"."FooNullableId" + FROM "BarNullableDeep" AS "b" + LEFT JOIN "FooDeep" AS "f0" ON "b"."FooId" = "f0"."Id" + WHERE "f0"."BarString" = @__p_0 +) AS "s" ON "f"."Id" = "s"."FooNullableId" +ORDER BY "d"."Id", "f"."Id", "s"."Id" +--------------- diff --git a/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionFilterTests.Create_ListObjectDifferentLevelProjection_NET9_0.snap b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionFilterTests.Create_ListObjectDifferentLevelProjection_NET9_0.snap new file mode 100644 index 00000000000..f21777f3f0a --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionFilterTests.Create_ListObjectDifferentLevelProjection_NET9_0.snap @@ -0,0 +1,44 @@ +Result: +--------------- +{ + "data": { + "root": [ + { + "foo": { + "barString": "testatest", + "objectArray": [ + { + "foo": { + "barString": "a", + "barShort": 12 + } + } + ] + } + }, + { + "foo": { + "barString": "testbtest", + "objectArray": [] + } + } + ] + } +} +--------------- + +SQL: +--------------- +.param set @__p_0 'a' + +SELECT 1, "f"."BarString", "d"."Id", "f"."Id", "s"."c", "s"."BarString", "s"."BarShort", "s"."Id", "s"."Id0" +FROM "Data" AS "d" +INNER JOIN "Foo" AS "f" ON "d"."FooId" = "f"."Id" +LEFT JOIN ( + SELECT 1 AS "c", "f0"."BarString", "f0"."BarShort", "b"."Id", "f0"."Id" AS "Id0", "b"."FooId" + FROM "BarDeep" AS "b" + INNER JOIN "FooDeep" AS "f0" ON "b"."FooId1" = "f0"."Id" + WHERE "f0"."BarString" = @__p_0 +) AS "s" ON "f"."Id" = "s"."FooId" +ORDER BY "d"."Id", "f"."Id", "s"."Id" +--------------- diff --git a/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionFilterTests.Create_ListObjectDifferentLevelProjection_Nullable_NET9_0.snap b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionFilterTests.Create_ListObjectDifferentLevelProjection_Nullable_NET9_0.snap new file mode 100644 index 00000000000..6f2ffe393ac --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionFilterTests.Create_ListObjectDifferentLevelProjection_Nullable_NET9_0.snap @@ -0,0 +1,49 @@ +Result: +--------------- +{ + "data": { + "root": [ + { + "foo": { + "barString": "testatest", + "objectArray": [] + } + }, + { + "foo": { + "barString": "testbtest", + "objectArray": [] + } + }, + { + "foo": { + "barString": "testctest", + "objectArray": [] + } + }, + { + "foo": { + "barString": "testdtest", + "objectArray": [] + } + } + ] + } +} +--------------- + +SQL: +--------------- +.param set @__p_0 'a' + +SELECT "f"."Id" IS NOT NULL, "f"."BarString", "d"."Id", "f"."Id", "s"."c", "s"."BarString", "s"."BarShort", "s"."Id", "s"."Id0" +FROM "Data" AS "d" +LEFT JOIN "FooNullable" AS "f" ON "d"."FooId" = "f"."Id" +LEFT JOIN ( + SELECT "f0"."Id" IS NOT NULL AS "c", "f0"."BarString", "f0"."BarShort", "b"."Id", "f0"."Id" AS "Id0", "b"."FooNullableId" + FROM "BarNullableDeep" AS "b" + LEFT JOIN "FooDeep" AS "f0" ON "b"."FooId" = "f0"."Id" + WHERE "f0"."BarString" = @__p_0 +) AS "s" ON "f"."Id" = "s"."FooNullableId" +ORDER BY "d"."Id", "f"."Id", "s"."Id" +--------------- diff --git a/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionHashSetTests.Create_DeepFilterObjectTwoProjections_NET9_0.snap b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionHashSetTests.Create_DeepFilterObjectTwoProjections_NET9_0.snap new file mode 100644 index 00000000000..10f30603053 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionHashSetTests.Create_DeepFilterObjectTwoProjections_NET9_0.snap @@ -0,0 +1,46 @@ +Result: +--------------- +{ + "data": { + "root": [ + { + "foo": { + "objectSet": [ + { + "foo": { + "barString": "a", + "barShort": 12 + } + } + ] + } + }, + { + "foo": { + "objectSet": [ + { + "foo": { + "barString": "d", + "barShort": 14 + } + } + ] + } + } + ] + } +} +--------------- + +SQL: +--------------- +SELECT 1, "d"."Id", "f"."Id", "s"."c", "s"."BarString", "s"."BarShort", "s"."Id", "s"."Id0" +FROM "Data" AS "d" +INNER JOIN "Foo" AS "f" ON "d"."FooId" = "f"."Id" +LEFT JOIN ( + SELECT 1 AS "c", "f0"."BarString", "f0"."BarShort", "b"."Id", "f0"."Id" AS "Id0", "b"."FooId" + FROM "BarDeep" AS "b" + INNER JOIN "FooDeep" AS "f0" ON "b"."FooId1" = "f0"."Id" +) AS "s" ON "f"."Id" = "s"."FooId" +ORDER BY "d"."Id", "f"."Id", "s"."Id" +--------------- diff --git a/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionHashSetTests.Create_ListObjectDifferentLevelProjection_NET9_0.snap b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionHashSetTests.Create_ListObjectDifferentLevelProjection_NET9_0.snap new file mode 100644 index 00000000000..cf394fbcfa0 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionHashSetTests.Create_ListObjectDifferentLevelProjection_NET9_0.snap @@ -0,0 +1,48 @@ +Result: +--------------- +{ + "data": { + "root": [ + { + "foo": { + "barString": "testatest", + "objectSet": [ + { + "foo": { + "barString": "a", + "barShort": 12 + } + } + ] + } + }, + { + "foo": { + "barString": "testbtest", + "objectSet": [ + { + "foo": { + "barString": "d", + "barShort": 14 + } + } + ] + } + } + ] + } +} +--------------- + +SQL: +--------------- +SELECT 1, "f"."BarString", "d"."Id", "f"."Id", "s"."c", "s"."BarString", "s"."BarShort", "s"."Id", "s"."Id0" +FROM "Data" AS "d" +INNER JOIN "Foo" AS "f" ON "d"."FooId" = "f"."Id" +LEFT JOIN ( + SELECT 1 AS "c", "f0"."BarString", "f0"."BarShort", "b"."Id", "f0"."Id" AS "Id0", "b"."FooId" + FROM "BarDeep" AS "b" + INNER JOIN "FooDeep" AS "f0" ON "b"."FooId1" = "f0"."Id" +) AS "s" ON "f"."Id" = "s"."FooId" +ORDER BY "d"."Id", "f"."Id", "s"."Id" +--------------- diff --git a/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionISetTests.Create_DeepFilterObjectTwoProjections_NET9_0.snap b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionISetTests.Create_DeepFilterObjectTwoProjections_NET9_0.snap new file mode 100644 index 00000000000..10f30603053 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionISetTests.Create_DeepFilterObjectTwoProjections_NET9_0.snap @@ -0,0 +1,46 @@ +Result: +--------------- +{ + "data": { + "root": [ + { + "foo": { + "objectSet": [ + { + "foo": { + "barString": "a", + "barShort": 12 + } + } + ] + } + }, + { + "foo": { + "objectSet": [ + { + "foo": { + "barString": "d", + "barShort": 14 + } + } + ] + } + } + ] + } +} +--------------- + +SQL: +--------------- +SELECT 1, "d"."Id", "f"."Id", "s"."c", "s"."BarString", "s"."BarShort", "s"."Id", "s"."Id0" +FROM "Data" AS "d" +INNER JOIN "Foo" AS "f" ON "d"."FooId" = "f"."Id" +LEFT JOIN ( + SELECT 1 AS "c", "f0"."BarString", "f0"."BarShort", "b"."Id", "f0"."Id" AS "Id0", "b"."FooId" + FROM "BarDeep" AS "b" + INNER JOIN "FooDeep" AS "f0" ON "b"."FooId1" = "f0"."Id" +) AS "s" ON "f"."Id" = "s"."FooId" +ORDER BY "d"."Id", "f"."Id", "s"."Id" +--------------- diff --git a/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionISetTests.Create_ListObjectDifferentLevelProjection_NET9_0.snap b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionISetTests.Create_ListObjectDifferentLevelProjection_NET9_0.snap new file mode 100644 index 00000000000..cf394fbcfa0 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionISetTests.Create_ListObjectDifferentLevelProjection_NET9_0.snap @@ -0,0 +1,48 @@ +Result: +--------------- +{ + "data": { + "root": [ + { + "foo": { + "barString": "testatest", + "objectSet": [ + { + "foo": { + "barString": "a", + "barShort": 12 + } + } + ] + } + }, + { + "foo": { + "barString": "testbtest", + "objectSet": [ + { + "foo": { + "barString": "d", + "barShort": 14 + } + } + ] + } + } + ] + } +} +--------------- + +SQL: +--------------- +SELECT 1, "f"."BarString", "d"."Id", "f"."Id", "s"."c", "s"."BarString", "s"."BarShort", "s"."Id", "s"."Id0" +FROM "Data" AS "d" +INNER JOIN "Foo" AS "f" ON "d"."FooId" = "f"."Id" +LEFT JOIN ( + SELECT 1 AS "c", "f0"."BarString", "f0"."BarShort", "b"."Id", "f0"."Id" AS "Id0", "b"."FooId" + FROM "BarDeep" AS "b" + INNER JOIN "FooDeep" AS "f0" ON "b"."FooId1" = "f0"."Id" +) AS "s" ON "f"."Id" = "s"."FooId" +ORDER BY "d"."Id", "f"."Id", "s"."Id" +--------------- diff --git a/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionSortedSetTests.Create_DeepFilterObjectTwoProjections_NET9_0.snap b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionSortedSetTests.Create_DeepFilterObjectTwoProjections_NET9_0.snap new file mode 100644 index 00000000000..10f30603053 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionSortedSetTests.Create_DeepFilterObjectTwoProjections_NET9_0.snap @@ -0,0 +1,46 @@ +Result: +--------------- +{ + "data": { + "root": [ + { + "foo": { + "objectSet": [ + { + "foo": { + "barString": "a", + "barShort": 12 + } + } + ] + } + }, + { + "foo": { + "objectSet": [ + { + "foo": { + "barString": "d", + "barShort": 14 + } + } + ] + } + } + ] + } +} +--------------- + +SQL: +--------------- +SELECT 1, "d"."Id", "f"."Id", "s"."c", "s"."BarString", "s"."BarShort", "s"."Id", "s"."Id0" +FROM "Data" AS "d" +INNER JOIN "Foo" AS "f" ON "d"."FooId" = "f"."Id" +LEFT JOIN ( + SELECT 1 AS "c", "f0"."BarString", "f0"."BarShort", "b"."Id", "f0"."Id" AS "Id0", "b"."FooId" + FROM "BarDeep" AS "b" + INNER JOIN "FooDeep" AS "f0" ON "b"."FooId1" = "f0"."Id" +) AS "s" ON "f"."Id" = "s"."FooId" +ORDER BY "d"."Id", "f"."Id", "s"."Id" +--------------- diff --git a/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionSortedSetTests.Create_ListObjectDifferentLevelProjection_NET9_0.snap b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionSortedSetTests.Create_ListObjectDifferentLevelProjection_NET9_0.snap new file mode 100644 index 00000000000..cf394fbcfa0 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionSortedSetTests.Create_ListObjectDifferentLevelProjection_NET9_0.snap @@ -0,0 +1,48 @@ +Result: +--------------- +{ + "data": { + "root": [ + { + "foo": { + "barString": "testatest", + "objectSet": [ + { + "foo": { + "barString": "a", + "barShort": 12 + } + } + ] + } + }, + { + "foo": { + "barString": "testbtest", + "objectSet": [ + { + "foo": { + "barString": "d", + "barShort": 14 + } + } + ] + } + } + ] + } +} +--------------- + +SQL: +--------------- +SELECT 1, "f"."BarString", "d"."Id", "f"."Id", "s"."c", "s"."BarString", "s"."BarShort", "s"."Id", "s"."Id0" +FROM "Data" AS "d" +INNER JOIN "Foo" AS "f" ON "d"."FooId" = "f"."Id" +LEFT JOIN ( + SELECT 1 AS "c", "f0"."BarString", "f0"."BarShort", "b"."Id", "f0"."Id" AS "Id0", "b"."FooId" + FROM "BarDeep" AS "b" + INNER JOIN "FooDeep" AS "f0" ON "b"."FooId1" = "f0"."Id" +) AS "s" ON "f"."Id" = "s"."FooId" +ORDER BY "d"."Id", "f"."Id", "s"."Id" +--------------- diff --git a/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionSortingTests.Create_DeepFilterObjectTwoProjections_NET9_0.snap b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionSortingTests.Create_DeepFilterObjectTwoProjections_NET9_0.snap new file mode 100644 index 00000000000..17e0b237d75 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionSortingTests.Create_DeepFilterObjectTwoProjections_NET9_0.snap @@ -0,0 +1,70 @@ +Result: +--------------- +{ + "data": { + "root": [ + { + "foo": { + "objectArray": [ + { + "foo": { + "barString": "a", + "barShort": 1 + } + }, + { + "foo": { + "barString": "a", + "barShort": 3 + } + }, + { + "foo": { + "barString": "a", + "barShort": 12 + } + } + ] + } + }, + { + "foo": { + "objectArray": [ + { + "foo": { + "barString": "a", + "barShort": 1 + } + }, + { + "foo": { + "barString": "a", + "barShort": 3 + } + }, + { + "foo": { + "barString": "a", + "barShort": 12 + } + } + ] + } + } + ] + } +} +--------------- + +SQL: +--------------- +SELECT 1, "d"."Id", "f"."Id", "s"."c", "s"."BarString", "s"."BarShort", "s"."Id", "s"."Id0" +FROM "Data" AS "d" +INNER JOIN "Foo" AS "f" ON "d"."FooId" = "f"."Id" +LEFT JOIN ( + SELECT 1 AS "c", "f0"."BarString", "f0"."BarShort", "b"."Id", "f0"."Id" AS "Id0", "b"."FooId" + FROM "BarDeep" AS "b" + INNER JOIN "FooDeep" AS "f0" ON "b"."FooId1" = "f0"."Id" +) AS "s" ON "f"."Id" = "s"."FooId" +ORDER BY "d"."Id", "f"."Id", "s"."BarShort", "s"."Id" +--------------- diff --git a/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionSortingTests.Create_ListObjectDifferentLevelProjection_NET9_0.snap b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionSortingTests.Create_ListObjectDifferentLevelProjection_NET9_0.snap new file mode 100644 index 00000000000..f90b85ab008 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Projections.SqlServer.Tests/__snapshots__/QueryableProjectionSortingTests.Create_ListObjectDifferentLevelProjection_NET9_0.snap @@ -0,0 +1,72 @@ +Result: +--------------- +{ + "data": { + "root": [ + { + "foo": { + "barString": "testatest", + "objectArray": [ + { + "foo": { + "barString": "a", + "barShort": 1 + } + }, + { + "foo": { + "barString": "a", + "barShort": 3 + } + }, + { + "foo": { + "barString": "a", + "barShort": 12 + } + } + ] + } + }, + { + "foo": { + "barString": "testbtest", + "objectArray": [ + { + "foo": { + "barString": "a", + "barShort": 1 + } + }, + { + "foo": { + "barString": "a", + "barShort": 3 + } + }, + { + "foo": { + "barString": "a", + "barShort": 12 + } + } + ] + } + } + ] + } +} +--------------- + +SQL: +--------------- +SELECT 1, "f"."BarString", "d"."Id", "f"."Id", "s"."c", "s"."BarString", "s"."BarShort", "s"."Id", "s"."Id0" +FROM "Data" AS "d" +INNER JOIN "Foo" AS "f" ON "d"."FooId" = "f"."Id" +LEFT JOIN ( + SELECT 1 AS "c", "f0"."BarString", "f0"."BarShort", "b"."Id", "f0"."Id" AS "Id0", "b"."FooId" + FROM "BarDeep" AS "b" + INNER JOIN "FooDeep" AS "f0" ON "b"."FooId1" = "f0"."Id" +) AS "s" ON "f"."Id" = "s"."FooId" +ORDER BY "d"."Id", "f"."Id", "s"."BarShort", "s"."Id" +--------------- diff --git a/src/HotChocolate/Data/test/Data.Sorting.SqlLite.Tests/__snapshots__/QueryableSortVisitorExpressionTests.Create_StringConcatExpression_NET9_0.snap b/src/HotChocolate/Data/test/Data.Sorting.SqlLite.Tests/__snapshots__/QueryableSortVisitorExpressionTests.Create_StringConcatExpression_NET9_0.snap new file mode 100644 index 00000000000..8bf40a38a50 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Sorting.SqlLite.Tests/__snapshots__/QueryableSortVisitorExpressionTests.Create_StringConcatExpression_NET9_0.snap @@ -0,0 +1,49 @@ +DESC Result: +--------------- +{ + "data": { + "root": [ + { + "name": "Sam", + "lastName": "Sampleman" + }, + { + "name": "Foo", + "lastName": "Galoo" + } + ] + } +} +--------------- + +DESC SQL: +--------------- +SELECT "d"."Id", "d"."LastName", "d"."Name" +FROM "Data" AS "d" +ORDER BY COALESCE("d"."Name", '') || ' ' || COALESCE("d"."LastName", '') DESC +--------------- + +ASC Result: +--------------- +{ + "data": { + "root": [ + { + "name": "Foo", + "lastName": "Galoo" + }, + { + "name": "Sam", + "lastName": "Sampleman" + } + ] + } +} +--------------- + +ASC SQL: +--------------- +SELECT "d"."Id", "d"."LastName", "d"."Name" +FROM "Data" AS "d" +ORDER BY COALESCE("d"."Name", '') || ' ' || COALESCE("d"."LastName", '') +--------------- diff --git a/src/HotChocolate/Data/test/Data.Tests/ExecutableTests.cs b/src/HotChocolate/Data/test/Data.Tests/ExecutableTests.cs index 99ad97cfeba..32035963c89 100644 --- a/src/HotChocolate/Data/test/Data.Tests/ExecutableTests.cs +++ b/src/HotChocolate/Data/test/Data.Tests/ExecutableTests.cs @@ -56,7 +56,12 @@ public async Task ExecuteAsync_Should_OnlyOneItem_When_SingleOrDefault() var result = await executable.SingleOrDefaultAsync(default); // assert - new { result, executable = executable.Print(), }.MatchSnapshot(); + new { result, executable = executable.Print(), } +#if NET9_0 + .MatchSnapshot(postFix: "NET9_0"); +#else + .MatchSnapshot(); +#endif } [Fact] diff --git a/src/HotChocolate/Data/test/Data.Tests/__snapshots__/ExecutableTests.ExecuteAsync_Should_OnlyOneItem_When_SingleOrDefault_NET9_0.snap b/src/HotChocolate/Data/test/Data.Tests/__snapshots__/ExecutableTests.ExecuteAsync_Should_OnlyOneItem_When_SingleOrDefault_NET9_0.snap new file mode 100644 index 00000000000..be38bb5dff6 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Tests/__snapshots__/ExecutableTests.ExecuteAsync_Should_OnlyOneItem_When_SingleOrDefault_NET9_0.snap @@ -0,0 +1,17 @@ +{ + "result": { + "Id": 1, + "Name": "Foo", + "Books": [ + { + "Id": 1, + "AuthorId": 0, + "Title": "Foo1", + "Author": null, + "Publisher": null + } + ], + "Publishers": [] + }, + "executable": "System.Linq.Enumerable+IListSkipTakeIterator`1[HotChocolate.Data.Author]" +} diff --git a/src/HotChocolate/Fusion/Directory.Build.props b/src/HotChocolate/Fusion/Directory.Build.props index 504e90cb29e..ad1b9b88aca 100644 --- a/src/HotChocolate/Fusion/Directory.Build.props +++ b/src/HotChocolate/Fusion/Directory.Build.props @@ -2,7 +2,7 @@ - net8.0 + net9.0; net8.0 diff --git a/src/HotChocolate/Fusion/test/Aspire.Analyzers.Tests/HotChocolate.Fusion.Aspire.Analyzers.Tests.csproj b/src/HotChocolate/Fusion/test/Aspire.Analyzers.Tests/HotChocolate.Fusion.Aspire.Analyzers.Tests.csproj index 57d1d431e9c..8accd17cf9f 100644 --- a/src/HotChocolate/Fusion/test/Aspire.Analyzers.Tests/HotChocolate.Fusion.Aspire.Analyzers.Tests.csproj +++ b/src/HotChocolate/Fusion/test/Aspire.Analyzers.Tests/HotChocolate.Fusion.Aspire.Analyzers.Tests.csproj @@ -1,8 +1,8 @@ - net8.0 - net8.0 + net9.0; net8.0 + net9.0 true diff --git a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/FilterVisitorTestBase.cs b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/FilterVisitorTestBase.cs index 7b99579b235..81b22b2aee7 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/FilterVisitorTestBase.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/FilterVisitorTestBase.cs @@ -19,7 +19,7 @@ public abstract class FilterVisitorTestBase : IAsyncLifetime protected T[] CreateEntity(params T[] entities) => entities; - protected IRequestExecutor CreateSchema( + protected async Task CreateSchemaAsync( TEntity[] entities, FilterConvention? convention = null, bool withPaging = false, @@ -31,7 +31,7 @@ protected IRequestExecutor CreateSchema( Container.Resource.CreateDatabaseAsync(dbName).GetAwaiter().GetResult(); var store = DocumentStore.For(Container.Resource.GetConnectionString(dbName)); - var resolver = BuildResolver(store, entities); + var resolver = await BuildResolverAsync(store, entities); var builder = SchemaBuilder.New() .AddMartenFiltering() @@ -116,19 +116,19 @@ private void ApplyConfigurationToField( field.UseFiltering(); } - private Func> BuildResolver( + private async Task>> BuildResolverAsync( IDocumentStore store, params TResult[] results) where TResult : class { - using var session = store.LightweightSession(); + await using var session = store.LightweightSession(); foreach (var item in results) { session.Store(item); } - session.SaveChanges(); + await session.SaveChangesAsync(); return ctx => ((IDocumentSession)ctx.LocalContextData["session"]!).Query(); } diff --git a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/FilteringAndPaging.cs b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/FilteringAndPaging.cs index 9bd71fc7fc7..6362f7798aa 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/FilteringAndPaging.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/FilteringAndPaging.cs @@ -13,7 +13,7 @@ public class FilteringAndPaging(SchemaCache cache) public async Task Create_BooleanEqual_Expression() { // arrange - var tester = cache.CreateSchema(_fooEntities, true); + var tester = await cache.CreateSchemaAsync(_fooEntities, true); // act var res1 = await tester.ExecuteAsync( diff --git a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorBooleanTests.cs b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorBooleanTests.cs index c6203bcd484..20ec0eb79ce 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorBooleanTests.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorBooleanTests.cs @@ -24,7 +24,7 @@ public class QueryableFilterVisitorBooleanTests(SchemaCache cache) public async Task Create_BooleanEqual_Expression() { // arrange - var tester = cache.CreateSchema(_fooEntities); + var tester = await cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -49,7 +49,7 @@ await Snapshot public async Task Create_BooleanNotEqual_Expression() { // arrange - var tester = cache.CreateSchema(_fooEntities); + var tester = await cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -74,7 +74,7 @@ await Snapshot public async Task Create_NullableBooleanEqual_Expression() { // arrange - var tester = cache.CreateSchema(_fooNullableEntities); + var tester = await cache.CreateSchemaAsync(_fooNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -105,7 +105,7 @@ await Snapshot public async Task Create_NullableBooleanNotEqual_Expression() { // arrange - var tester = cache.CreateSchema( + var tester = await cache.CreateSchemaAsync( _fooNullableEntities); // act diff --git a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorComparableTests.cs b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorComparableTests.cs index 1c815fb0c46..8d054acb2cf 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorComparableTests.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorComparableTests.cs @@ -31,7 +31,7 @@ public QueryableFilterVisitorComparableTests(SchemaCache cache) public async Task Create_ShortEqual_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -65,7 +65,7 @@ await Snapshot public async Task Create_ShortNotEqual_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -99,7 +99,7 @@ await Snapshot public async Task Create_ShortGreaterThan_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -140,7 +140,7 @@ await Snapshot public async Task Create_ShortNotGreaterThan_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -181,7 +181,7 @@ await Snapshot public async Task Create_ShortGreaterThanOrEquals_Expression() { // act - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // assert var res1 = await tester.ExecuteAsync( @@ -222,7 +222,7 @@ await Snapshot public async Task Create_ShortNotGreaterThanOrEquals_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -263,7 +263,7 @@ await Snapshot public async Task Create_ShortLowerThan_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -304,7 +304,7 @@ await Snapshot public async Task Create_ShortNotLowerThan_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -345,7 +345,7 @@ await Snapshot public async Task Create_ShortLowerThanOrEquals_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -386,7 +386,7 @@ await Snapshot public async Task Create_ShortNotLowerThanOrEquals_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -427,7 +427,7 @@ await Snapshot public async Task Create_ShortIn_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -461,7 +461,7 @@ await Snapshot public async Task Create_ShortNotIn_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -495,7 +495,7 @@ await Snapshot public async Task Create_ShortNullableEqual_Expression() { // arrange - var tester = _cache.CreateSchema(_fooNullableEntities); + var tester = await _cache.CreateSchemaAsync(_fooNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -529,7 +529,7 @@ await Snapshot public async Task Create_ShortNullableNotEqual_Expression() { // arrange - var tester = _cache.CreateSchema(_fooNullableEntities); + var tester = await _cache.CreateSchemaAsync(_fooNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -563,7 +563,7 @@ await Snapshot public async Task Create_ShortNullableGreaterThan_Expression() { // arrange - var tester = _cache.CreateSchema(_fooNullableEntities); + var tester = await _cache.CreateSchemaAsync(_fooNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -604,7 +604,7 @@ await Snapshot public async Task Create_ShortNullableNotGreaterThan_Expression() { // arrange - var tester = _cache.CreateSchema(_fooNullableEntities); + var tester = await _cache.CreateSchemaAsync(_fooNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -645,7 +645,7 @@ await Snapshot public async Task Create_ShortNullableGreaterThanOrEquals_Expression() { // arrange - var tester = _cache.CreateSchema(_fooNullableEntities); + var tester = await _cache.CreateSchemaAsync(_fooNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -686,7 +686,7 @@ await Snapshot public async Task Create_ShortNullableNotGreaterThanOrEquals_Expression() { // arrange - var tester = _cache.CreateSchema(_fooNullableEntities); + var tester = await _cache.CreateSchemaAsync(_fooNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -727,7 +727,7 @@ await Snapshot public async Task Create_ShortNullableLowerThan_Expression() { // arrange - var tester = _cache.CreateSchema(_fooNullableEntities); + var tester = await _cache.CreateSchemaAsync(_fooNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -768,7 +768,7 @@ await Snapshot public async Task Create_ShortNullableNotLowerThan_Expression() { // arrange - var tester = _cache.CreateSchema(_fooNullableEntities); + var tester = await _cache.CreateSchemaAsync(_fooNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -809,7 +809,7 @@ await Snapshot public async Task Create_ShortNullableLowerThanOrEquals_Expression() { // arrange - var tester = _cache.CreateSchema(_fooNullableEntities); + var tester = await _cache.CreateSchemaAsync(_fooNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -850,7 +850,7 @@ await Snapshot public async Task Create_ShortNullableNotLowerThanOrEquals_Expression() { // arrange - var tester = _cache.CreateSchema(_fooNullableEntities); + var tester = await _cache.CreateSchemaAsync(_fooNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -891,7 +891,7 @@ await Snapshot public async Task Create_ShortNullableIn_Expression() { // arrange - var tester = _cache.CreateSchema(_fooNullableEntities); + var tester = await _cache.CreateSchemaAsync(_fooNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -925,7 +925,7 @@ await Snapshot public async Task Create_ShortNullableNotIn_Expression() { // arrange - var tester = _cache.CreateSchema(_fooNullableEntities); + var tester = await _cache.CreateSchemaAsync(_fooNullableEntities); // act var res1 = await tester.ExecuteAsync( diff --git a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorEnumTests.cs b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorEnumTests.cs index 35a764361be..da3485dd45b 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorEnumTests.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorEnumTests.cs @@ -35,7 +35,7 @@ public QueryableFilterVisitorEnumTests(SchemaCache cache) public async Task Create_EnumEqual_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -69,7 +69,7 @@ await Snapshot public async Task Create_EnumNotEqual_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -103,7 +103,7 @@ await Snapshot public async Task Create_EnumIn_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -137,7 +137,7 @@ await Snapshot public async Task Create_EnumNotIn_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -170,7 +170,7 @@ await Snapshot [Fact] public async Task Create_NullableEnumEqual_Expression() { - var tester = _cache.CreateSchema( + var tester = await _cache.CreateSchemaAsync( _fooNullableEntities); // act @@ -206,7 +206,7 @@ await Snapshot public async Task Create_NullableEnumNotEqual_Expression() { // arrange - var tester = _cache.CreateSchema(_fooNullableEntities); + var tester = await _cache.CreateSchemaAsync(_fooNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -240,7 +240,7 @@ await Snapshot public async Task Create_NullableEnumIn_Expression() { // arrange - var tester = _cache.CreateSchema(_fooNullableEntities); + var tester = await _cache.CreateSchemaAsync(_fooNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -274,7 +274,7 @@ await Snapshot public async Task Create_NullableEnumNotIn_Expression() { // arrange - var tester = _cache.CreateSchema(_fooNullableEntities); + var tester = await _cache.CreateSchemaAsync(_fooNullableEntities); // act var res1 = await tester.ExecuteAsync( diff --git a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorExecutableTests.cs b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorExecutableTests.cs index c3bef1fe716..54fd0cd5c8e 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorExecutableTests.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorExecutableTests.cs @@ -31,7 +31,7 @@ public QueryableFilterVisitorExecutableTests(SchemaCache cache) public async Task Create_BooleanEqual_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -53,7 +53,7 @@ await Snapshot.Create().AddResult(res1, "true").AddResult(res2, "false") public async Task Create_BooleanNotEqual_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -76,7 +76,7 @@ await Snapshot public async Task Create_NullableBooleanEqual_Expression() { // arrange - var tester = _cache.CreateSchema(_fooNullableEntities); + var tester = await _cache.CreateSchemaAsync(_fooNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -107,7 +107,7 @@ await Snapshot public async Task Create_NullableBooleanNotEqual_Expression() { // arrange - var tester = _cache.CreateSchema(_fooNullableEntities); + var tester = await _cache.CreateSchemaAsync(_fooNullableEntities); // act var res1 = await tester.ExecuteAsync( diff --git a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorInterfacesTests.cs b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorInterfacesTests.cs index 6f379b3fb6d..c73b02eabcb 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorInterfacesTests.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorInterfacesTests.cs @@ -26,8 +26,8 @@ public QueryableFilterVisitorInterfacesTests(SchemaCache cache) public async Task Create_InterfaceStringEqual_Expression() { // arrange - var tester = _cache - .CreateSchema>(_barEntities, + var tester = await _cache + .CreateSchemaAsync>(_barEntities, configure: Configure); // act diff --git a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorListTests.cs b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorListTests.cs index 6def2e227cd..08c70a82dcb 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorListTests.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorListTests.cs @@ -67,7 +67,7 @@ public QueryableFilterVisitorListTests(SchemaCache cache) public async Task Create_ArrayAllObjectStringEqual_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -104,7 +104,7 @@ await Snapshot public async Task Create_ArraySomeObjectStringEqualWithNull_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -152,7 +152,7 @@ await Snapshot.Create().AddResult(res1, "a").AddResult( public async Task Create_ArrayNoneObjectStringEqual_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -188,7 +188,7 @@ await Snapshot.Create().AddResult( public async Task Create_ArrayAnyObjectStringEqual_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( diff --git a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorObjectTests.cs b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorObjectTests.cs index 916a22f0d82..3b582de1569 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorObjectTests.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorObjectTests.cs @@ -118,7 +118,7 @@ public QueryableFilterVisitorObjectTests(SchemaCache cache) public async Task Create_ObjectShortEqual_Expression() { // arrange - var tester = _cache.CreateSchema(_barEntities); + var tester = await _cache.CreateSchemaAsync(_barEntities); // act var res1 = await tester.ExecuteAsync( @@ -158,7 +158,7 @@ await Snapshot public async Task Create_ObjectShortIn_Expression() { // arrange - var tester = _cache.CreateSchema(_barEntities); + var tester = await _cache.CreateSchemaAsync(_barEntities); // act var res1 = await tester.ExecuteAsync( @@ -198,7 +198,7 @@ await Snapshot public async Task Create_ObjectNullableShortEqual_Expression() { // arrange - var tester = _cache.CreateSchema(_barNullableEntities); + var tester = await _cache.CreateSchemaAsync(_barNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -238,7 +238,7 @@ await Snapshot public async Task Create_ObjectNullableShortIn_Expression() { // arrange - var tester = _cache.CreateSchema(_barNullableEntities); + var tester = await _cache.CreateSchemaAsync(_barNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -278,7 +278,7 @@ await Snapshot public async Task Create_ObjectBooleanEqual_Expression() { // arrange - var tester = _cache.CreateSchema(_barEntities); + var tester = await _cache.CreateSchemaAsync(_barEntities); // act var res1 = await tester.ExecuteAsync( @@ -309,7 +309,7 @@ await Snapshot public async Task Create_ObjectNullableBooleanEqual_Expression() { // arrange - var tester = _cache.CreateSchema(_barNullableEntities); + var tester = await _cache.CreateSchemaAsync(_barNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -349,7 +349,7 @@ await Snapshot public async Task Create_ObjectEnumEqual_Expression() { // arrange - var tester = _cache.CreateSchema(_barEntities); + var tester = await _cache.CreateSchemaAsync(_barEntities); // act var res1 = await tester.ExecuteAsync( @@ -389,7 +389,7 @@ await Snapshot public async Task Create_ObjectEnumIn_Expression() { // arrange - var tester = _cache.CreateSchema(_barEntities); + var tester = await _cache.CreateSchemaAsync(_barEntities); // act var res1 = await tester.ExecuteAsync( @@ -429,7 +429,7 @@ await Snapshot public async Task Create_ObjectNullableEnumEqual_Expression() { // arrange - var tester = _cache.CreateSchema( + var tester = await _cache.CreateSchemaAsync( _barNullableEntities); // act @@ -470,7 +470,7 @@ await Snapshot public async Task Create_ObjectNullableEnumIn_Expression() { // arrange - var tester = _cache.CreateSchema(_barNullableEntities); + var tester = await _cache.CreateSchemaAsync(_barNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -510,7 +510,7 @@ await Snapshot public async Task Create_ObjectStringEqual_Expression() { // arrange - var tester = _cache.CreateSchema(_barEntities); + var tester = await _cache.CreateSchemaAsync(_barEntities); // act var res1 = await tester.ExecuteAsync( @@ -549,7 +549,7 @@ await Snapshot public async Task Create_ObjectStringIn_Expression() { // arrange - var tester = _cache.CreateSchema(_barEntities); + var tester = await _cache.CreateSchemaAsync(_barEntities); // act var res1 = await tester.ExecuteAsync( @@ -590,7 +590,7 @@ await Snapshot public async Task Create_ArrayObjectNestedArraySomeStringEqual_Expression() { // arrange - var tester = _cache.CreateSchema(_barEntities); + var tester = await _cache.CreateSchemaAsync(_barEntities); // act var res1 = await tester.ExecuteAsync( @@ -633,7 +633,7 @@ await Snapshot public async Task Create_ArrayObjectNestedArrayAnyStringEqual_Expression() { // arrange - var tester = _cache.CreateSchema(_barEntities); + var tester = await _cache.CreateSchemaAsync(_barEntities); // act var res1 = await tester.ExecuteAsync( diff --git a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorStringTests.cs b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorStringTests.cs index 53a6dd56c94..421a8bd560a 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorStringTests.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorStringTests.cs @@ -31,7 +31,7 @@ public QueryableFilterVisitorStringTests(SchemaCache cache) public async Task Create_StringEqual_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -68,7 +68,7 @@ await Snapshot.Create() public async Task Create_StringNotEqual_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -98,7 +98,7 @@ await Snapshot.Create() public async Task Create_StringIn_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -129,7 +129,7 @@ await Snapshot.Create() public async Task Create_StringNotIn_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -161,7 +161,7 @@ await Snapshot.Create().AddResult(res1, "testatestAndtestb").AddResult( public async Task Create_StringContains_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -192,7 +192,7 @@ await Snapshot.Create().AddResult(res1, "a").AddResult( public async Task Create_StringNoContains_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -223,7 +223,7 @@ await Snapshot.Create().AddResult(res1, "a").AddResult( public async Task Create_StringStartsWith_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act // assert @@ -255,7 +255,7 @@ await Snapshot.Create().AddResult(res1, "testa").AddResult( public async Task Create_StringNotStartsWith_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -286,7 +286,7 @@ await Snapshot.Create().AddResult(res1, "testa").AddResult( public async Task Create_StringEndsWith_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -317,7 +317,7 @@ await Snapshot.Create().AddResult(res1, "atest").AddResult( public async Task Create_StringNotEndsWith_Expression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -348,7 +348,7 @@ await Snapshot.Create().AddResult(res1, "atest").AddResult( public async Task Create_NullableStringEqual_Expression() { // arrange - var tester = _cache.CreateSchema(_fooNullableEntities); + var tester = await _cache.CreateSchemaAsync(_fooNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -379,7 +379,7 @@ await Snapshot.Create().AddResult(res1, "testatest").AddResult( public async Task Create_NullableStringNotEqual_Expression() { // arrange - var tester = _cache.CreateSchema( + var tester = await _cache.CreateSchemaAsync( _fooNullableEntities); // act @@ -411,7 +411,7 @@ await Snapshot.Create().AddResult(res1, "testatest").AddResult( public async Task Create_NullableStringIn_Expression() { // arrange - var tester = _cache.CreateSchema(_fooNullableEntities); + var tester = await _cache.CreateSchemaAsync(_fooNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -443,7 +443,7 @@ await Snapshot.Create().AddResult(res1, "testatestAndtestb").AddResult( public async Task Create_NullableStringNotIn_Expression() { // arrange - var tester = _cache.CreateSchema( + var tester = await _cache.CreateSchemaAsync( _fooNullableEntities); // act @@ -476,7 +476,7 @@ await Snapshot.Create().AddResult(res1, "testatestAndtestb").AddResult( public async Task Create_NullableStringContains_Expression() { // arrange - var tester = _cache.CreateSchema( + var tester = await _cache.CreateSchemaAsync( _fooNullableEntities); // act @@ -508,7 +508,7 @@ await Snapshot.Create().AddResult(res1, "a").AddResult( public async Task Create_NullableStringNoContains_Expression() { // arrange - var tester = _cache.CreateSchema(_fooNullableEntities); + var tester = await _cache.CreateSchemaAsync(_fooNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -539,7 +539,7 @@ await Snapshot.Create().AddResult(res1, "a").AddResult( public async Task Create_NullableStringStartsWith_Expression() { // arrange - var tester = _cache.CreateSchema( + var tester = await _cache.CreateSchemaAsync( _fooNullableEntities); // act @@ -571,7 +571,7 @@ await Snapshot.Create().AddResult(res1, "testa").AddResult( public async Task Create_NullableStringNotStartsWith_Expression() { // arrange - var tester = _cache.CreateSchema( + var tester = await _cache.CreateSchemaAsync( _fooNullableEntities); // act @@ -603,7 +603,7 @@ await Snapshot.Create().AddResult(res1, "testa").AddResult( public async Task Create_NullableStringEndsWith_Expression() { // arrange - var tester = _cache.CreateSchema( + var tester = await _cache.CreateSchemaAsync( _fooNullableEntities); // act @@ -635,7 +635,7 @@ await Snapshot.Create().AddResult(res1, "atest").AddResult( public async Task Create_NullableStringNotEndsWith_Expression() { // arrange - var tester = _cache.CreateSchema( + var tester = await _cache.CreateSchemaAsync( _fooNullableEntities); // act diff --git a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/SchemaCache.cs b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/SchemaCache.cs index e2dc9b4a1a7..db3b42b5275 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/SchemaCache.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/SchemaCache.cs @@ -6,9 +6,9 @@ namespace HotChocolate.Data; public class SchemaCache : FilterVisitorTestBase { - private readonly ConcurrentDictionary<(Type, Type, object), IRequestExecutor> _cache = new(); + private readonly ConcurrentDictionary<(Type, Type, object), Task> _cache = new(); - public IRequestExecutor CreateSchema( + public async Task CreateSchemaAsync( T[] entities, bool withPaging = false, Action? configure = null) @@ -16,9 +16,9 @@ public IRequestExecutor CreateSchema( where TType : FilterInputType { (Type, Type, T[] entites) key = (typeof(T), typeof(TType), entities); - return _cache.GetOrAdd( + return await _cache.GetOrAdd( key, - _ => base.CreateSchema( + async _ => await base.CreateSchemaAsync( entities, withPaging: withPaging, configure: configure)); diff --git a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArrayAnyObjectStringEqual_Expression.snap b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArrayAnyObjectStringEqual_Expression.snap index e7b70d54f63..5e79a543436 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArrayAnyObjectStringEqual_Expression.snap +++ b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArrayAnyObjectStringEqual_Expression.snap @@ -9,7 +9,7 @@ false Result: false SQL: --------------- -select d.id, d.data from public.mt_doc_queryablefiltervisitorlisttests_foo as d where d.data @? '$ ? (@.FooNested[*] == null || @.FooNested[*].size() == 0)'; +select d.id, d.data from public.mt_doc_queryablefiltervisitorlisttests_foo as d where d.data @? '$ ? (@.FooNested == null || @.FooNested.size() == 0)'; --------------- true Result: diff --git a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArrayNoneObjectStringEqual_Expression.snap b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArrayNoneObjectStringEqual_Expression.snap index c6d2584040d..2c32806daf7 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArrayNoneObjectStringEqual_Expression.snap +++ b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArrayNoneObjectStringEqual_Expression.snap @@ -36,7 +36,7 @@ a Result: a SQL: --------------- -select d.id, d.data from public.mt_doc_queryablefiltervisitorlisttests_foo as d where NOT(CAST(d.data ->> 'FooNested' as jsonb) @> :p0); +select d.id, d.data from public.mt_doc_queryablefiltervisitorlisttests_foo as d where NOT(d.data -> 'FooNested' @> :p0); --------------- d Result: @@ -77,7 +77,7 @@ d Result: d SQL: --------------- -select d.id, d.data from public.mt_doc_queryablefiltervisitorlisttests_foo as d where NOT(CAST(d.data ->> 'FooNested' as jsonb) @> :p0); +select d.id, d.data from public.mt_doc_queryablefiltervisitorlisttests_foo as d where NOT(d.data -> 'FooNested' @> :p0); --------------- null Result: diff --git a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArraySomeObjectStringEqualWithNull_Expression.snap b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArraySomeObjectStringEqualWithNull_Expression.snap index 1941823fb38..a2bf7998a0d 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArraySomeObjectStringEqualWithNull_Expression.snap +++ b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/__snapshots__/QueryableFilterVisitorListTests.Create_ArraySomeObjectStringEqualWithNull_Expression.snap @@ -49,7 +49,7 @@ a Result: a SQL: --------------- -select d.id, d.data from public.mt_doc_queryablefiltervisitorlisttests_foo as d where CAST(d.data ->> 'FooNested' as jsonb) @> :p0; +select d.id, d.data from public.mt_doc_queryablefiltervisitorlisttests_foo as d where d.data -> 'FooNested' @> :p0; --------------- d Result: @@ -103,7 +103,7 @@ d Result: d SQL: --------------- -select d.id, d.data from public.mt_doc_queryablefiltervisitorlisttests_foo as d where CAST(d.data ->> 'FooNested' as jsonb) @> :p0; +select d.id, d.data from public.mt_doc_queryablefiltervisitorlisttests_foo as d where d.data -> 'FooNested' @> :p0; --------------- null Result: diff --git a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ArrayObjectNestedArrayAnyStringEqual_Expression.snap b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ArrayObjectNestedArrayAnyStringEqual_Expression.snap index ed29eb6187c..110d08d14de 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ArrayObjectNestedArrayAnyStringEqual_Expression.snap +++ b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ArrayObjectNestedArrayAnyStringEqual_Expression.snap @@ -15,7 +15,7 @@ false Result: false SQL: --------------- -select d.id, d.data from public.mt_doc_queryablefiltervisitorobjecttests_bar as d where d.data @? '$ ? (@.Foo.ObjectArray[*] == null || @.Foo.ObjectArray[*].size() == 0)'; +select d.id, d.data from public.mt_doc_queryablefiltervisitorobjecttests_bar as d where d.data @? '$ ? (@.Foo.ObjectArray == null || @.Foo.ObjectArray.size() == 0)'; --------------- true Result: diff --git a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ArrayObjectNestedArraySomeStringEqual_Expression.snap b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ArrayObjectNestedArraySomeStringEqual_Expression.snap index 442b357f4b8..2d3d682d038 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ArrayObjectNestedArraySomeStringEqual_Expression.snap +++ b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/__snapshots__/QueryableFilterVisitorObjectTests.Create_ArrayObjectNestedArraySomeStringEqual_Expression.snap @@ -21,7 +21,7 @@ a Result: a SQL: --------------- -select d.id, d.data from public.mt_doc_queryablefiltervisitorobjecttests_bar as d where CAST(d.data -> 'Foo' ->> 'ObjectArray' as jsonb) @> :p0; +select d.id, d.data from public.mt_doc_queryablefiltervisitorobjecttests_bar as d where d.data -> 'Foo' -> 'ObjectArray' @> :p0; --------------- b Result: @@ -47,7 +47,7 @@ b Result: b SQL: --------------- -select d.id, d.data from public.mt_doc_queryablefiltervisitorobjecttests_bar as d where CAST(d.data -> 'Foo' ->> 'ObjectArray' as jsonb) @> :p0; +select d.id, d.data from public.mt_doc_queryablefiltervisitorobjecttests_bar as d where d.data -> 'Foo' -> 'ObjectArray' @> :p0; --------------- null diff --git a/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorBooleanTests.cs b/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorBooleanTests.cs index ec479d1a74f..b59fe753501 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorBooleanTests.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorBooleanTests.cs @@ -25,7 +25,7 @@ public QueryableSortVisitorBooleanTests(SchemaCache cache) public async Task Create_Boolean_OrderBy() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -50,7 +50,7 @@ await Snapshot public async Task Create_Boolean_OrderBy_List() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -75,7 +75,7 @@ await Snapshot public async Task Create_Boolean_OrderBy_Nullable() { // arrange - var tester = _cache.CreateSchema( + var tester = await _cache.CreateSchemaAsync( _fooNullableEntities); // act diff --git a/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorComparableTests.cs b/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorComparableTests.cs index f3762fda2be..2ef9fa53e4b 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorComparableTests.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorComparableTests.cs @@ -31,7 +31,7 @@ public QueryableSortVisitorComparableTests(SchemaCache cache) public async Task Create_Short_OrderBy() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -60,7 +60,7 @@ await SnapshotExtensions.AddResult( public async Task Create_Short_OrderBy_Nullable() { // arrange - var tester = _cache.CreateSchema( + var tester = await _cache.CreateSchemaAsync( _fooNullableEntities); // act diff --git a/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorEnumTests.cs b/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorEnumTests.cs index 05f40581f88..deb99ee28ba 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorEnumTests.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorEnumTests.cs @@ -35,7 +35,7 @@ public QueryableSortVisitorEnumTests(SchemaCache cache) public async Task Create_Enum_OrderBy() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -64,7 +64,7 @@ await SnapshotExtensions.AddResult( public async Task Create_Enum_OrderBy_Nullable() { // arrange - var tester = _cache.CreateSchema( + var tester = await _cache.CreateSchemaAsync( _fooNullableEntities); // act diff --git a/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorExecutableTests.cs b/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorExecutableTests.cs index 6538e337e7a..6dfdd05bc90 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorExecutableTests.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorExecutableTests.cs @@ -25,7 +25,7 @@ public QueryableSortVisitorExecutableTests(SchemaCache cache) public async Task Create_Boolean_OrderBy() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -54,7 +54,7 @@ await SnapshotExtensions.AddResult( public async Task Create_Boolean_OrderBy_List() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -76,7 +76,7 @@ public async Task Create_Boolean_OrderBy_List() public async Task Create_Boolean_OrderBy_Nullable() { // arrange - var tester = _cache.CreateSchema( + var tester = await _cache.CreateSchemaAsync( _fooNullableEntities); // act diff --git a/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorExpressionTests.cs b/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorExpressionTests.cs index 5151e42b14e..46ac401f9cb 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorExpressionTests.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorExpressionTests.cs @@ -54,7 +54,7 @@ public async Task Expression_WithMoreThanOneParameter_ThrowsException() public async Task Create_CollectionLengthExpression() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( diff --git a/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorObjectTests.cs b/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorObjectTests.cs index b3c424d2c2d..16a5af2009b 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorObjectTests.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorObjectTests.cs @@ -146,7 +146,7 @@ public class QueryableSortVisitorObjectTests(SchemaCache cache) public async Task Create_ObjectShort_OrderBy() { // arrange - var tester = cache.CreateSchema(_barEntities); + var tester = await cache.CreateSchemaAsync(_barEntities); // act var res1 = await tester.ExecuteAsync( @@ -176,7 +176,7 @@ public async Task Create_ObjectNullableShort_OrderBy() { // arrange var tester = - cache.CreateSchema(_barNullableEntities); + await cache.CreateSchemaAsync(_barNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -205,7 +205,7 @@ await Snapshot public async Task Create_ObjectEnum_OrderBy() { // arrange - var tester = cache.CreateSchema(_barEntities); + var tester = await cache.CreateSchemaAsync(_barEntities); // act var res1 = await tester.ExecuteAsync( @@ -235,7 +235,7 @@ public async Task Create_ObjectNullableEnum_OrderBy() { // arrange var tester = - cache.CreateSchema(_barNullableEntities); + await cache.CreateSchemaAsync(_barNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -264,7 +264,7 @@ await Snapshot public async Task Create_ObjectString_OrderBy() { // arrange - var tester = cache.CreateSchema(_barEntities); + var tester = await cache.CreateSchemaAsync(_barEntities); // act var res1 = await tester.ExecuteAsync( @@ -294,7 +294,7 @@ public async Task Create_ObjectNullableString_OrderBy() { // arrange var tester = - cache.CreateSchema(_barNullableEntities); + await cache.CreateSchemaAsync(_barNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -323,7 +323,7 @@ await Snapshot public async Task Create_ObjectBool_OrderBy() { // arrange - var tester = cache.CreateSchema(_barEntities); + var tester = await cache.CreateSchemaAsync(_barEntities); // act var res1 = await tester.ExecuteAsync( @@ -353,7 +353,7 @@ public async Task Create_ObjectNullableBool_OrderBy() { // arrange var tester = - cache.CreateSchema(_barNullableEntities); + await cache.CreateSchemaAsync(_barNullableEntities); // act var res1 = await tester.ExecuteAsync( @@ -382,7 +382,7 @@ await Snapshot public async Task Create_ObjectString_OrderBy_TwoProperties() { // arrange - var tester = cache.CreateSchema(_barEntities); + var tester = await cache.CreateSchemaAsync(_barEntities); // act var res1 = await tester.ExecuteAsync( @@ -447,7 +447,7 @@ await Snapshot public async Task Create_ObjectString_OrderBy_TwoProperties_Variables() { // arrange - var tester = cache.CreateSchema(_barEntities); + var tester = await cache.CreateSchemaAsync(_barEntities); // act var res1 = await tester.ExecuteAsync( diff --git a/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorStringTests.cs b/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorStringTests.cs index 3bb1b4e40f6..a975f60d100 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorStringTests.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/QueryableSortVisitorStringTests.cs @@ -28,7 +28,7 @@ public QueryableSortVisitorStringTests(SchemaCache cache) public async Task Create_String_OrderBy() { // arrange - var tester = _cache.CreateSchema(_fooEntities); + var tester = await _cache.CreateSchemaAsync(_fooEntities); // act var res1 = await tester.ExecuteAsync( @@ -57,7 +57,7 @@ await SnapshotExtensions.AddResult( public async Task Create_String_OrderBy_Nullable() { // arrange - var tester = _cache.CreateSchema( + var tester = await _cache.CreateSchemaAsync( _fooNullableEntities); // act diff --git a/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/SchemaCache.cs b/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/SchemaCache.cs index 942b2f68742..9ba30df4bf6 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/SchemaCache.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/SchemaCache.cs @@ -6,13 +6,13 @@ namespace HotChocolate.Data; public class SchemaCache : SortVisitorTestBase { - private readonly ConcurrentDictionary<(Type, Type, object), IRequestExecutor> _cache = new(); + private readonly ConcurrentDictionary<(Type, Type, object), Task> _cache = new(); - public IRequestExecutor CreateSchema(T[] entities) + public async Task CreateSchemaAsync(T[] entities) where T : class where TType : SortInputType { var key = (typeof(T), typeof(TType), entities); - return _cache.GetOrAdd(key, (k) => base.CreateSchema(entities)); + return await _cache.GetOrAdd(key, async (k) => await base.CreateSchemaAsync(entities)); } } diff --git a/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/SortVisitorTestBase.cs b/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/SortVisitorTestBase.cs index 50fff6cef2d..1e320309707 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/SortVisitorTestBase.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/SortVisitorTestBase.cs @@ -61,26 +61,26 @@ public class SortVisitorTestBase : IAsyncLifetime public async Task DisposeAsync() => await Container.DisposeAsync(); - private static Func> BuildResolver( + private static async Task>> BuildResolverAsync( IDocumentStore store, params TResult[] results) where TResult : class { - using var session = store.LightweightSession(); + await using var session = store.LightweightSession(); foreach (var item in results) { session.Store(item); } - session.SaveChanges(); + await session.SaveChangesAsync(); return ctx => ((IDocumentSession)ctx.LocalContextData["session"]!).Query(); } protected T[] CreateEntity(params T[] entities) => entities; - protected IRequestExecutor CreateSchema( + protected async Task CreateSchemaAsync( TEntity[] entities, SortConvention? convention = null) where TEntity : class @@ -90,7 +90,7 @@ protected IRequestExecutor CreateSchema( Container.Resource.CreateDatabaseAsync(dbName).GetAwaiter().GetResult(); var store = DocumentStore.For(Container.Resource.GetConnectionString(dbName)); - var resolver = BuildResolver(store, entities); + var resolver = await BuildResolverAsync(store, entities); var builder = SchemaBuilder.New() .AddMartenSorting() diff --git a/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/__snapshots__/QueryableSortVisitorExpressionTests.Create_CollectionLengthExpression.snap b/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/__snapshots__/QueryableSortVisitorExpressionTests.Create_CollectionLengthExpression.snap index 79d410ff201..cac6054bf96 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/__snapshots__/QueryableSortVisitorExpressionTests.Create_CollectionLengthExpression.snap +++ b/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/__snapshots__/QueryableSortVisitorExpressionTests.Create_CollectionLengthExpression.snap @@ -18,7 +18,7 @@ ASC Result: ASC SQL: --------------- -select d.id, d.data from public.mt_doc_queryablesortvisitorexpressiontests_foo as d order by jsonb_array_length(CAST(d.data ->> 'Bars' as jsonb)); +select d.id, d.data from public.mt_doc_queryablesortvisitorexpressiontests_foo as d order by jsonb_array_length(d.data -> 'Bars'); --------------- DESC Result: @@ -41,5 +41,5 @@ DESC Result: DESC SQL: --------------- -select d.id, d.data from public.mt_doc_queryablesortvisitorexpressiontests_foo as d order by jsonb_array_length(CAST(d.data ->> 'Bars' as jsonb)) desc; +select d.id, d.data from public.mt_doc_queryablesortvisitorexpressiontests_foo as d order by jsonb_array_length(d.data -> 'Bars') desc; --------------- diff --git a/src/HotChocolate/MongoDb/src/Data/Relay/ObjectIdNodeIdValueSerializer.cs b/src/HotChocolate/MongoDb/src/Data/Relay/ObjectIdNodeIdValueSerializer.cs index ea0b4cd1e85..d3fbbaa3c9e 100644 --- a/src/HotChocolate/MongoDb/src/Data/Relay/ObjectIdNodeIdValueSerializer.cs +++ b/src/HotChocolate/MongoDb/src/Data/Relay/ObjectIdNodeIdValueSerializer.cs @@ -132,7 +132,7 @@ public bool TryParse(ReadOnlySpan buffer, [NotNullWhen(true)] out object? private static bool TryParseHexString(ReadOnlySpan formattedId, byte[] rawBytes) { - if (formattedId == null) + if (formattedId.IsEmpty) { return false; } diff --git a/src/HotChocolate/OpenApi/Directory.Build.props b/src/HotChocolate/OpenApi/Directory.Build.props index 504e90cb29e..ad1b9b88aca 100644 --- a/src/HotChocolate/OpenApi/Directory.Build.props +++ b/src/HotChocolate/OpenApi/Directory.Build.props @@ -2,7 +2,7 @@ - net8.0 + net9.0; net8.0 diff --git a/src/HotChocolate/OpenApi/src/HotChocolate.OpenApi/Extensions/RequestExecutorBuilderExtensions.cs b/src/HotChocolate/OpenApi/src/HotChocolate.OpenApi/Extensions/RequestExecutorBuilderExtensions.cs index 212be0c8f7e..b2201dcba75 100644 --- a/src/HotChocolate/OpenApi/src/HotChocolate.OpenApi/Extensions/RequestExecutorBuilderExtensions.cs +++ b/src/HotChocolate/OpenApi/src/HotChocolate.OpenApi/Extensions/RequestExecutorBuilderExtensions.cs @@ -17,9 +17,9 @@ public static IRequestExecutorBuilder AddOpenApi( bool enableMutationConventions = false, MutationConventionOptions mutationConventionOptions = default) { - ArgumentNullException.ThrowIfNull(nameof(builder)); - ArgumentException.ThrowIfNullOrEmpty(nameof(httpClientName)); - ArgumentException.ThrowIfNullOrEmpty(nameof(openApiDocumentText)); + ArgumentNullException.ThrowIfNull(builder); + ArgumentException.ThrowIfNullOrEmpty(httpClientName); + ArgumentException.ThrowIfNullOrEmpty(openApiDocumentText); // Create OpenAPI document from the provided string. var openApiStringReader = new OpenApiStringReader(); diff --git a/src/HotChocolate/OpenApi/src/HotChocolate.OpenApi/Extensions/StringExtensions.cs b/src/HotChocolate/OpenApi/src/HotChocolate.OpenApi/Extensions/StringExtensions.cs index bc0a633ad6f..4c33528467c 100644 --- a/src/HotChocolate/OpenApi/src/HotChocolate.OpenApi/Extensions/StringExtensions.cs +++ b/src/HotChocolate/OpenApi/src/HotChocolate.OpenApi/Extensions/StringExtensions.cs @@ -4,7 +4,7 @@ internal static class StringExtensions { public static string FirstCharacterToLower(this string value) { - ArgumentNullException.ThrowIfNull(nameof(value)); + ArgumentNullException.ThrowIfNull(value); if (value.Length is 0) { @@ -19,7 +19,7 @@ public static string FirstCharacterToLower(this string value) public static string FirstCharacterToUpper(this string value) { - ArgumentNullException.ThrowIfNull(nameof(value)); + ArgumentNullException.ThrowIfNull(value); if (value.Length is 0) { diff --git a/src/HotChocolate/OpenApi/src/HotChocolate.OpenApi/Helpers/GraphQLNamingHelper.cs b/src/HotChocolate/OpenApi/src/HotChocolate.OpenApi/Helpers/GraphQLNamingHelper.cs index 7a4e100d3a6..90e386fcd92 100644 --- a/src/HotChocolate/OpenApi/src/HotChocolate.OpenApi/Helpers/GraphQLNamingHelper.cs +++ b/src/HotChocolate/OpenApi/src/HotChocolate.OpenApi/Helpers/GraphQLNamingHelper.cs @@ -21,7 +21,7 @@ public static string CreateInputTypeName( public static string CreateName(string name) { - ArgumentException.ThrowIfNullOrEmpty(nameof(name)); + ArgumentException.ThrowIfNullOrEmpty(name); var stringBuilder = new StringBuilder(); diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs index 1837ef53dd4..bf0ef464d97 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs @@ -961,16 +961,7 @@ public async Task BatchPaging_Last_5() } snapshot.AddQueries(queries); - -#if NET9_0_OR_GREATER - snapshot.MatchMarkdownSnapshot("NET9_0"); -#elif NET8_0 - snapshot.MatchMarkdownSnapshot("NET8_0"); -#elif NET7_0 - snapshot.MatchMarkdownSnapshot("NET7_0"); -#else snapshot.MatchMarkdownSnapshot(); -#endif } [Fact] diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5_NET9_0.md index 7263c51b5f2..34bc584642e 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5_NET9_0.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5_NET9_0.md @@ -126,28 +126,28 @@ ## SQL 0 ```sql -SELECT t."BrandId", t0."Id", t0."AvailableStock", t0."BrandId", t0."Description", t0."ImageFileName", t0."MaxStockThreshold", t0."Name", t0."OnReorder", t0."Price", t0."RestockThreshold", t0."TypeId" +SELECT p1."BrandId", p3."Id", p3."AvailableStock", p3."BrandId", p3."Description", p3."ImageFileName", p3."MaxStockThreshold", p3."Name", p3."OnReorder", p3."Price", p3."RestockThreshold", p3."TypeId" FROM ( SELECT p."BrandId" FROM "Products" AS p WHERE p."BrandId" IN (1, 2, 3) GROUP BY p."BrandId" -) AS t +) AS p1 LEFT JOIN ( - SELECT t1."Id", t1."AvailableStock", t1."BrandId", t1."Description", t1."ImageFileName", t1."MaxStockThreshold", t1."Name", t1."OnReorder", t1."Price", t1."RestockThreshold", t1."TypeId" + SELECT p2."Id", p2."AvailableStock", p2."BrandId", p2."Description", p2."ImageFileName", p2."MaxStockThreshold", p2."Name", p2."OnReorder", p2."Price", p2."RestockThreshold", p2."TypeId" FROM ( SELECT p0."Id", p0."AvailableStock", p0."BrandId", p0."Description", p0."ImageFileName", p0."MaxStockThreshold", p0."Name", p0."OnReorder", p0."Price", p0."RestockThreshold", p0."TypeId", ROW_NUMBER() OVER(PARTITION BY p0."BrandId" ORDER BY p0."Id" DESC) AS row FROM "Products" AS p0 - WHERE ((p0."BrandId" = 1) OR (p0."BrandId" = 2)) OR (p0."BrandId" = 3) - ) AS t1 - WHERE t1.row <= 3 -) AS t0 ON t."BrandId" = t0."BrandId" -ORDER BY t."BrandId", t0."BrandId", t0."Id" DESC + WHERE p0."BrandId" = 1 OR p0."BrandId" = 2 OR p0."BrandId" = 3 + ) AS p2 + WHERE p2.row <= 3 +) AS p3 ON p1."BrandId" = p3."BrandId" +ORDER BY p1."BrandId", p3."BrandId", p3."Id" DESC ``` ## Expression 0 ```text -[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].Where(t => (((t.BrandId == 1) OrElse (t.BrandId == 2)) OrElse (t.BrandId == 3))).GroupBy(k => k.BrandId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderByDescending(p => p.Id).Take(3).ToList()}) +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Where(t => (((t.BrandId == 1) OrElse (t.BrandId == 2)) OrElse (t.BrandId == 3))).GroupBy(k => k.BrandId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderByDescending(p => p.Id).Take(3).ToList()}) ``` diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET9_0.md index 4b0b1ca96b4..9bc3d874080 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET9_0.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET9_0.md @@ -4,21 +4,21 @@ ```sql -- @__p_0='11' -SELECT t."Id", t."Name", (b."Id" IS NULL), b."Id", b."Description", b."SomeField1", b."SomeField2" +SELECT f0."Id", f0."Name", b."Id" IS NULL, b."Id", b."Description", b."SomeField1", b."SomeField2" FROM ( SELECT f."Id", f."BarId", f."Name" FROM "Foos" AS f ORDER BY f."Name", f."Id" LIMIT @__p_0 -) AS t -LEFT JOIN "Bars" AS b ON t."BarId" = b."Id" -ORDER BY t."Name", t."Id" +) AS f0 +LEFT JOIN "Bars" AS b ON f0."BarId" = b."Id" +ORDER BY f0."Name", f0."Id" ``` ## Expression 0 ```text -[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name), Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description), SomeField1 = IIF((root.Bar.SomeField1 == null), null, root.Bar.SomeField1), SomeField2 = IIF((root.Bar.SomeField2 == null), null, root.Bar.SomeField2)})}).Take(11) +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = root.Name, Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = root.Bar.Description, SomeField1 = root.Bar.SomeField1, SomeField2 = root.Bar.SomeField2})}).Take(11) ``` ## Result diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET9_0.md index 0de22cee471..e65b7625c0d 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET9_0.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET9_0.md @@ -4,21 +4,21 @@ ```sql -- @__p_0='11' -SELECT t."Id", t."Name", (b."Id" IS NULL), b."Id", b."Description" +SELECT f0."Id", f0."Name", b."Id" IS NULL, b."Id", b."Description" FROM ( SELECT f."Id", f."BarId", f."Name" FROM "Foos" AS f ORDER BY f."Name", f."Id" LIMIT @__p_0 -) AS t -LEFT JOIN "Bars" AS b ON t."BarId" = b."Id" -ORDER BY t."Name", t."Id" +) AS f0 +LEFT JOIN "Bars" AS b ON f0."BarId" = b."Id" +ORDER BY f0."Name", f0."Id" ``` ## Expression 0 ```text -[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name), Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description)})}).Take(11) +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = root.Name, Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = root.Bar.Description})}).Take(11) ``` ## Result diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage2_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage2_NET9_0.md new file mode 100644 index 00000000000..ac096892d0b --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage2_NET9_0.md @@ -0,0 +1,77 @@ +# GetDefaultPage2 + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(11) +``` + +## Result + +```json +{ + "data": { + "brands2": { + "nodes": [ + { + "id": 1, + "name": "Brand0" + }, + { + "id": 2, + "name": "Brand1" + }, + { + "id": 11, + "name": "Brand10" + }, + { + "id": 12, + "name": "Brand11" + }, + { + "id": 13, + "name": "Brand12" + }, + { + "id": 14, + "name": "Brand13" + }, + { + "id": 15, + "name": "Brand14" + }, + { + "id": 16, + "name": "Brand15" + }, + { + "id": 17, + "name": "Brand16" + }, + { + "id": 18, + "name": "Brand17" + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "QnJhbmQwOjE=", + "endCursor": "QnJhbmQxNzoxOA==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_NET9_0.md new file mode 100644 index 00000000000..c794a8571bb --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_NET9_0.md @@ -0,0 +1,77 @@ +# GetDefaultPage + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(11) +``` + +## Result + +```json +{ + "data": { + "brands": { + "nodes": [ + { + "id": 1, + "name": "Brand0" + }, + { + "id": 2, + "name": "Brand1" + }, + { + "id": 11, + "name": "Brand10" + }, + { + "id": 12, + "name": "Brand11" + }, + { + "id": 13, + "name": "Brand12" + }, + { + "id": 14, + "name": "Brand13" + }, + { + "id": 15, + "name": "Brand14" + }, + { + "id": 16, + "name": "Brand15" + }, + { + "id": 17, + "name": "Brand16" + }, + { + "id": 18, + "name": "Brand17" + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "QnJhbmQwOjE=", + "endCursor": "QnJhbmQxNzoxOA==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_NET9_0.md new file mode 100644 index 00000000000..94e9856164c --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_NET9_0.md @@ -0,0 +1,169 @@ +# GetDefaultPage_With_Deep + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."BrandDetails_Country_Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(x => x.BrandDetails.Country.Name).ThenBy(t => t.Id).Take(11) +``` + +## Result + +```json +{ + "data": { + "brandsDeep": { + "edges": [ + { + "cursor": "Q291bnRyeTA6MQ==" + }, + { + "cursor": "Q291bnRyeTE6Mg==" + }, + { + "cursor": "Q291bnRyeTEwOjEx" + }, + { + "cursor": "Q291bnRyeTExOjEy" + }, + { + "cursor": "Q291bnRyeTEyOjEz" + }, + { + "cursor": "Q291bnRyeTEzOjE0" + }, + { + "cursor": "Q291bnRyeTE0OjE1" + }, + { + "cursor": "Q291bnRyeTE1OjE2" + }, + { + "cursor": "Q291bnRyeTE2OjE3" + }, + { + "cursor": "Q291bnRyeTE3OjE4" + } + ], + "nodes": [ + { + "id": 1, + "name": "Brand0", + "displayName": "BrandDisplay0", + "brandDetails": { + "country": { + "name": "Country0" + } + } + }, + { + "id": 2, + "name": "Brand1", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country1" + } + } + }, + { + "id": 11, + "name": "Brand10", + "displayName": "BrandDisplay10", + "brandDetails": { + "country": { + "name": "Country10" + } + } + }, + { + "id": 12, + "name": "Brand11", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country11" + } + } + }, + { + "id": 13, + "name": "Brand12", + "displayName": "BrandDisplay12", + "brandDetails": { + "country": { + "name": "Country12" + } + } + }, + { + "id": 14, + "name": "Brand13", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country13" + } + } + }, + { + "id": 15, + "name": "Brand14", + "displayName": "BrandDisplay14", + "brandDetails": { + "country": { + "name": "Country14" + } + } + }, + { + "id": 16, + "name": "Brand15", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country15" + } + } + }, + { + "id": 17, + "name": "Brand16", + "displayName": "BrandDisplay16", + "brandDetails": { + "country": { + "name": "Country16" + } + } + }, + { + "id": 18, + "name": "Brand17", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country17" + } + } + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "Q291bnRyeTA6MQ==", + "endCursor": "Q291bnRyeTE3OjE4" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_SecondPage_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_SecondPage_NET9_0.md new file mode 100644 index 00000000000..c18777c97d3 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_SecondPage_NET9_0.md @@ -0,0 +1,68 @@ +# GetDefaultPage_With_Deep_SecondPage + +## SQL 0 + +```sql +-- @__p_0='Country1' +-- @__p_1='2' +-- @__p_2='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +WHERE b."BrandDetails_Country_Name" > @__p_0 OR (b."BrandDetails_Country_Name" = @__p_0 AND b."Id" > @__p_1) +ORDER BY b."BrandDetails_Country_Name", b."Id" +LIMIT @__p_2 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(x => x.BrandDetails.Country.Name).ThenBy(t => t.Id).Where(t => ((t.BrandDetails.Country.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) > 0) OrElse ((t.BrandDetails.Country.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.Id.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.Int32]).value, Int32)) > 0)))).Take(3) +``` + +## Result + +```json +{ + "data": { + "brandsDeep": { + "edges": [ + { + "cursor": "Q291bnRyeTEwOjEx" + }, + { + "cursor": "Q291bnRyeTExOjEy" + } + ], + "nodes": [ + { + "id": 11, + "name": "Brand10", + "displayName": "BrandDisplay10", + "brandDetails": { + "country": { + "name": "Country10" + } + } + }, + { + "id": 12, + "name": "Brand11", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country11" + } + } + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": true, + "startCursor": "Q291bnRyeTEwOjEx", + "endCursor": "Q291bnRyeTExOjEy" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_NET9_0.md new file mode 100644 index 00000000000..84621cf9bb9 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_NET9_0.md @@ -0,0 +1,169 @@ +# GetDefaultPage_With_Nullable_Fallback + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY COALESCE(b."DisplayName", b."Name"), b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => (t.DisplayName ?? t.Name)).ThenBy(t => t.Id).Take(11) +``` + +## Result + +```json +{ + "data": { + "brandsNullableFallback": { + "edges": [ + { + "cursor": "QnJhbmQxOjI=" + }, + { + "cursor": "QnJhbmQxMToxMg==" + }, + { + "cursor": "QnJhbmQxMzoxNA==" + }, + { + "cursor": "QnJhbmQxNToxNg==" + }, + { + "cursor": "QnJhbmQxNzoxOA==" + }, + { + "cursor": "QnJhbmQxOToyMA==" + }, + { + "cursor": "QnJhbmQyMToyMg==" + }, + { + "cursor": "QnJhbmQyMzoyNA==" + }, + { + "cursor": "QnJhbmQyNToyNg==" + }, + { + "cursor": "QnJhbmQyNzoyOA==" + } + ], + "nodes": [ + { + "id": 2, + "name": "Brand1", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country1" + } + } + }, + { + "id": 12, + "name": "Brand11", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country11" + } + } + }, + { + "id": 14, + "name": "Brand13", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country13" + } + } + }, + { + "id": 16, + "name": "Brand15", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country15" + } + } + }, + { + "id": 18, + "name": "Brand17", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country17" + } + } + }, + { + "id": 20, + "name": "Brand19", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country19" + } + } + }, + { + "id": 22, + "name": "Brand21", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country21" + } + } + }, + { + "id": 24, + "name": "Brand23", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country23" + } + } + }, + { + "id": 26, + "name": "Brand25", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country25" + } + } + }, + { + "id": 28, + "name": "Brand27", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country27" + } + } + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "QnJhbmQxOjI=", + "endCursor": "QnJhbmQyNzoyOA==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_SecondPage_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_SecondPage_NET9_0.md new file mode 100644 index 00000000000..6b40ccce2c4 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_SecondPage_NET9_0.md @@ -0,0 +1,68 @@ +# GetDefaultPage_With_Nullable_Fallback_SecondPage + +## SQL 0 + +```sql +-- @__p_0='Brand11' +-- @__p_1='12' +-- @__p_2='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +WHERE COALESCE(b."DisplayName", b."Name") > @__p_0 OR (COALESCE(b."DisplayName", b."Name") = @__p_0 AND b."Id" > @__p_1) +ORDER BY COALESCE(b."DisplayName", b."Name"), b."Id" +LIMIT @__p_2 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => (t.DisplayName ?? t.Name)).ThenBy(t => t.Id).Where(t => (((t.DisplayName ?? t.Name).CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) > 0) OrElse (((t.DisplayName ?? t.Name).CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.Id.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.Int32]).value, Int32)) > 0)))).Take(3) +``` + +## Result + +```json +{ + "data": { + "brandsNullableFallback": { + "edges": [ + { + "cursor": "QnJhbmQxMzoxNA==" + }, + { + "cursor": "QnJhbmQxNToxNg==" + } + ], + "nodes": [ + { + "id": 14, + "name": "Brand13", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country13" + } + } + }, + { + "id": 16, + "name": "Brand15", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country15" + } + } + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": true, + "startCursor": "QnJhbmQxMzoxNA==", + "endCursor": "QnJhbmQxNToxNg==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_NET9_0.md new file mode 100644 index 00000000000..8894d6b129d --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_NET9_0.md @@ -0,0 +1,169 @@ +# GetDefaultPage_With_Nullable + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."AlwaysNull", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(x => x.AlwaysNull).ThenBy(t => t.Id).Take(11) +``` + +## Result + +```json +{ + "data": { + "brandsNullable": { + "edges": [ + { + "cursor": "QnJhbmQwOlxudWxsOjE=" + }, + { + "cursor": "QnJhbmQxOlxudWxsOjI=" + }, + { + "cursor": "QnJhbmQxMDpcbnVsbDoxMQ==" + }, + { + "cursor": "QnJhbmQxMTpcbnVsbDoxMg==" + }, + { + "cursor": "QnJhbmQxMjpcbnVsbDoxMw==" + }, + { + "cursor": "QnJhbmQxMzpcbnVsbDoxNA==" + }, + { + "cursor": "QnJhbmQxNDpcbnVsbDoxNQ==" + }, + { + "cursor": "QnJhbmQxNTpcbnVsbDoxNg==" + }, + { + "cursor": "QnJhbmQxNjpcbnVsbDoxNw==" + }, + { + "cursor": "QnJhbmQxNzpcbnVsbDoxOA==" + } + ], + "nodes": [ + { + "id": 1, + "name": "Brand0", + "displayName": "BrandDisplay0", + "brandDetails": { + "country": { + "name": "Country0" + } + } + }, + { + "id": 2, + "name": "Brand1", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country1" + } + } + }, + { + "id": 11, + "name": "Brand10", + "displayName": "BrandDisplay10", + "brandDetails": { + "country": { + "name": "Country10" + } + } + }, + { + "id": 12, + "name": "Brand11", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country11" + } + } + }, + { + "id": 13, + "name": "Brand12", + "displayName": "BrandDisplay12", + "brandDetails": { + "country": { + "name": "Country12" + } + } + }, + { + "id": 14, + "name": "Brand13", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country13" + } + } + }, + { + "id": 15, + "name": "Brand14", + "displayName": "BrandDisplay14", + "brandDetails": { + "country": { + "name": "Country14" + } + } + }, + { + "id": 16, + "name": "Brand15", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country15" + } + } + }, + { + "id": 17, + "name": "Brand16", + "displayName": "BrandDisplay16", + "brandDetails": { + "country": { + "name": "Country16" + } + } + }, + { + "id": 18, + "name": "Brand17", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country17" + } + } + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": false, + "startCursor": "QnJhbmQwOlxudWxsOjE=", + "endCursor": "QnJhbmQxNzpcbnVsbDoxOA==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_SecondPage_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_SecondPage_NET9_0.md new file mode 100644 index 00000000000..5da7d78b12a --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_SecondPage_NET9_0.md @@ -0,0 +1,68 @@ +# GetDefaultPage_With_Nullable_SecondPage + +## SQL 0 + +```sql +-- @__p_0='Brand10' +-- @__p_2='11' +-- @__p_3='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +WHERE b."Name" > @__p_0 OR (b."Name" = @__p_0 AND b."AlwaysNull" > NULL) OR (b."Name" = @__p_0 AND b."AlwaysNull" IS NULL AND b."Id" > @__p_2) +ORDER BY b."Name", b."AlwaysNull", b."Id" +LIMIT @__p_3 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(x => x.AlwaysNull).ThenBy(t => t.Id).Where(t => (((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) > 0) OrElse ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.AlwaysNull.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) > 0))) OrElse (((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.AlwaysNull.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0)) AndAlso (t.Id.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.Int32]).value, Int32)) > 0)))).Take(3) +``` + +## Result + +```json +{ + "data": { + "brandsNullable": { + "edges": [ + { + "cursor": "QnJhbmQxMTpcbnVsbDoxMg==" + }, + { + "cursor": "QnJhbmQxMjpcbnVsbDoxMw==" + } + ], + "nodes": [ + { + "id": 12, + "name": "Brand11", + "displayName": null, + "brandDetails": { + "country": { + "name": "Country11" + } + } + }, + { + "id": 13, + "name": "Brand12", + "displayName": "BrandDisplay12", + "brandDetails": { + "country": { + "name": "Country12" + } + } + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": true, + "startCursor": "QnJhbmQxMTpcbnVsbDoxMg==", + "endCursor": "QnJhbmQxMjpcbnVsbDoxMw==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetSecondPage_With_2_Items_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetSecondPage_With_2_Items_NET9_0.md new file mode 100644 index 00000000000..94747f17c6a --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetSecondPage_With_2_Items_NET9_0.md @@ -0,0 +1,48 @@ +# GetSecondPage_With_2_Items + +## SQL 0 + +```sql +-- @__p_0='Brand17' +-- @__p_1='18' +-- @__p_2='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +WHERE b."Name" > @__p_0 OR (b."Name" = @__p_0 AND b."Id" > @__p_1) +ORDER BY b."Name", b."Id" +LIMIT @__p_2 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Where(t => ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) > 0) OrElse ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.Id.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.Int32]).value, Int32)) > 0)))).Take(3) +``` + +## Result + +```json +{ + "data": { + "brands": { + "nodes": [ + { + "id": 19, + "name": "Brand18" + }, + { + "id": 20, + "name": "Brand19" + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": true, + "startCursor": "QnJhbmQxODoxOQ==", + "endCursor": "QnJhbmQxOToyMA==" + } + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_2_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_2_NET9_0.md new file mode 100644 index 00000000000..0c6e949bacf --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_2_NET9_0.md @@ -0,0 +1,47 @@ +# Map_Page_To_Connection_With_Dto_2 + +## SQL 0 + +```sql +-- @__p_0='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3) +``` + +## Result 3 + +```json +{ + "data": { + "brands": { + "edges": [ + { + "cursor": "QnJhbmQwOjE=", + "displayName": "BrandDisplay0", + "node": { + "id": 1, + "name": "Brand0" + } + }, + { + "cursor": "QnJhbmQxOjI=", + "displayName": null, + "node": { + "id": 2, + "name": "Brand1" + } + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_NET9_0.md new file mode 100644 index 00000000000..808bca98e86 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_NET9_0.md @@ -0,0 +1,47 @@ +# Map_Page_To_Connection_With_Dto + +## SQL 0 + +```sql +-- @__p_0='3' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3) +``` + +## Result 3 + +```json +{ + "data": { + "brands": { + "edges": [ + { + "cursor": "QnJhbmQwOjE=", + "displayName": "BrandDisplay0", + "node": { + "id": 1, + "name": "Brand0" + } + }, + { + "cursor": "QnJhbmQxOjI=", + "displayName": null, + "node": { + "id": 2, + "name": "Brand1" + } + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_NET9_0.md index 43893634212..5125369ac0e 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_NET9_0.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_NET9_0.md @@ -13,35 +13,36 @@ LIMIT @__p_0 ## Expression 0 ```text -[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3) +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3) ``` ## SQL 1 ```sql -SELECT t."BrandId", t0."Id", t0."AvailableStock", t0."BrandId", t0."Description", t0."ImageFileName", t0."MaxStockThreshold", t0."Name", t0."OnReorder", t0."Price", t0."RestockThreshold", t0."TypeId" +-- @__keys_0={ '2', '1' } (DbType = Object) +SELECT p1."BrandId", p3."Id", p3."AvailableStock", p3."BrandId", p3."Description", p3."ImageFileName", p3."MaxStockThreshold", p3."Name", p3."OnReorder", p3."Price", p3."RestockThreshold", p3."TypeId" FROM ( SELECT p."BrandId" FROM "Products" AS p - WHERE p."BrandId" IN (2, 1) + WHERE p."BrandId" = ANY (@__keys_0) GROUP BY p."BrandId" -) AS t +) AS p1 LEFT JOIN ( - SELECT t1."Id", t1."AvailableStock", t1."BrandId", t1."Description", t1."ImageFileName", t1."MaxStockThreshold", t1."Name", t1."OnReorder", t1."Price", t1."RestockThreshold", t1."TypeId" + SELECT p2."Id", p2."AvailableStock", p2."BrandId", p2."Description", p2."ImageFileName", p2."MaxStockThreshold", p2."Name", p2."OnReorder", p2."Price", p2."RestockThreshold", p2."TypeId" FROM ( SELECT p0."Id", p0."AvailableStock", p0."BrandId", p0."Description", p0."ImageFileName", p0."MaxStockThreshold", p0."Name", p0."OnReorder", p0."Price", p0."RestockThreshold", p0."TypeId", ROW_NUMBER() OVER(PARTITION BY p0."BrandId" ORDER BY p0."Name", p0."Id") AS row FROM "Products" AS p0 - WHERE p0."BrandId" IN (2, 1) - ) AS t1 - WHERE t1.row <= 3 -) AS t0 ON t."BrandId" = t0."BrandId" -ORDER BY t."BrandId", t0."BrandId", t0."Name", t0."Id" + WHERE p0."BrandId" = ANY (@__keys_0) + ) AS p2 + WHERE p2.row <= 3 +) AS p3 ON p1."BrandId" = p3."BrandId" +ORDER BY p1."BrandId", p3."BrandId", p3."Name", p3."Id" ``` ## Expression 1 ```text -[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].Where(t => value(HotChocolate.Data.IntegrationPagingHelperTests+ProductsByBrandDataLoader+<>c__DisplayClass2_0).keys.Contains(t.BrandId)).GroupBy(t => t.BrandId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3).ToList()}) +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Where(t => value(HotChocolate.Data.IntegrationPagingHelperTests+ProductsByBrandDataLoader+<>c__DisplayClass2_0).keys.Contains(t.BrandId)).GroupBy(t => t.BrandId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3).ToList()}) ``` ## Result 5 diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections_NET9_0.md index 4e9f3ffd421..3b5162ff3a7 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections_NET9_0.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections_NET9_0.md @@ -13,35 +13,36 @@ LIMIT @__p_0 ## Expression 0 ```text -[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3) +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3) ``` ## SQL 1 ```sql -SELECT t."BrandId", t0."Name", t0."BrandId", t0."Id" +-- @__keys_0={ '2', '1' } (DbType = Object) +SELECT p1."BrandId", p3."Name", p3."BrandId", p3."Id" FROM ( SELECT p."BrandId" FROM "Products" AS p - WHERE p."BrandId" IN (2, 1) + WHERE p."BrandId" = ANY (@__keys_0) GROUP BY p."BrandId" -) AS t +) AS p1 LEFT JOIN ( - SELECT t1."Name", t1."BrandId", t1."Id" + SELECT p2."Name", p2."BrandId", p2."Id" FROM ( SELECT p0."Name", p0."BrandId", p0."Id", ROW_NUMBER() OVER(PARTITION BY p0."BrandId" ORDER BY p0."Name", p0."Id") AS row FROM "Products" AS p0 - WHERE p0."BrandId" IN (2, 1) - ) AS t1 - WHERE t1.row <= 3 -) AS t0 ON t."BrandId" = t0."BrandId" -ORDER BY t."BrandId", t0."BrandId", t0."Name", t0."Id" + WHERE p0."BrandId" = ANY (@__keys_0) + ) AS p2 + WHERE p2.row <= 3 +) AS p3 ON p1."BrandId" = p3."BrandId" +ORDER BY p1."BrandId", p3."BrandId", p3."Name", p3."Id" ``` ## Expression 1 ```text -[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].Where(t => value(HotChocolate.Data.IntegrationPagingHelperTests+ProductsByBrandDataLoader+<>c__DisplayClass2_0).keys.Contains(t.BrandId)).Select(root => new Product() {Name = IIF((root.Name == null), null, root.Name), BrandId = root.BrandId, Id = root.Id}).GroupBy(t => t.BrandId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3).ToList()}) +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Where(t => value(HotChocolate.Data.IntegrationPagingHelperTests+ProductsByBrandDataLoader+<>c__DisplayClass2_0).keys.Contains(t.BrandId)).Select(root => new Product() {Name = root.Name, BrandId = root.BrandId, Id = root.Id}).GroupBy(t => t.BrandId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderBy(t => t.Name).ThenBy(t => t.Id).Take(3).ToList()}) ``` ## Result diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Empty_PagingArgs_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Empty_PagingArgs_NET9_0.md new file mode 100644 index 00000000000..84ceccc8cab --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Empty_PagingArgs_NET9_0.md @@ -0,0 +1,1236 @@ +# Paging_Empty_PagingArgs + +## SQL 0 + +```sql +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id) +``` + +## Result 3 + +```json +{ + "HasNextPage": false, + "HasPreviousPage": false, + "First": 1, + "FirstCursor": "QnJhbmQwOjE=", + "Last": 100, + "LastCursor": "QnJhbmQ5OToxMDA=" +} +``` + +## Result 4 + +```json +[ + { + "Id": 1, + "Name": "Brand0", + "DisplayName": "BrandDisplay0", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country0" + } + } + }, + { + "Id": 2, + "Name": "Brand1", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country1" + } + } + }, + { + "Id": 11, + "Name": "Brand10", + "DisplayName": "BrandDisplay10", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country10" + } + } + }, + { + "Id": 12, + "Name": "Brand11", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country11" + } + } + }, + { + "Id": 13, + "Name": "Brand12", + "DisplayName": "BrandDisplay12", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country12" + } + } + }, + { + "Id": 14, + "Name": "Brand13", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country13" + } + } + }, + { + "Id": 15, + "Name": "Brand14", + "DisplayName": "BrandDisplay14", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country14" + } + } + }, + { + "Id": 16, + "Name": "Brand15", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country15" + } + } + }, + { + "Id": 17, + "Name": "Brand16", + "DisplayName": "BrandDisplay16", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country16" + } + } + }, + { + "Id": 18, + "Name": "Brand17", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country17" + } + } + }, + { + "Id": 19, + "Name": "Brand18", + "DisplayName": "BrandDisplay18", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country18" + } + } + }, + { + "Id": 20, + "Name": "Brand19", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country19" + } + } + }, + { + "Id": 3, + "Name": "Brand2", + "DisplayName": "BrandDisplay2", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country2" + } + } + }, + { + "Id": 21, + "Name": "Brand20", + "DisplayName": "BrandDisplay20", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country20" + } + } + }, + { + "Id": 22, + "Name": "Brand21", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country21" + } + } + }, + { + "Id": 23, + "Name": "Brand22", + "DisplayName": "BrandDisplay22", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country22" + } + } + }, + { + "Id": 24, + "Name": "Brand23", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country23" + } + } + }, + { + "Id": 25, + "Name": "Brand24", + "DisplayName": "BrandDisplay24", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country24" + } + } + }, + { + "Id": 26, + "Name": "Brand25", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country25" + } + } + }, + { + "Id": 27, + "Name": "Brand26", + "DisplayName": "BrandDisplay26", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country26" + } + } + }, + { + "Id": 28, + "Name": "Brand27", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country27" + } + } + }, + { + "Id": 29, + "Name": "Brand28", + "DisplayName": "BrandDisplay28", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country28" + } + } + }, + { + "Id": 30, + "Name": "Brand29", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country29" + } + } + }, + { + "Id": 4, + "Name": "Brand3", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country3" + } + } + }, + { + "Id": 31, + "Name": "Brand30", + "DisplayName": "BrandDisplay30", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country30" + } + } + }, + { + "Id": 32, + "Name": "Brand31", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country31" + } + } + }, + { + "Id": 33, + "Name": "Brand32", + "DisplayName": "BrandDisplay32", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country32" + } + } + }, + { + "Id": 34, + "Name": "Brand33", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country33" + } + } + }, + { + "Id": 35, + "Name": "Brand34", + "DisplayName": "BrandDisplay34", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country34" + } + } + }, + { + "Id": 36, + "Name": "Brand35", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country35" + } + } + }, + { + "Id": 37, + "Name": "Brand36", + "DisplayName": "BrandDisplay36", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country36" + } + } + }, + { + "Id": 38, + "Name": "Brand37", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country37" + } + } + }, + { + "Id": 39, + "Name": "Brand38", + "DisplayName": "BrandDisplay38", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country38" + } + } + }, + { + "Id": 40, + "Name": "Brand39", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country39" + } + } + }, + { + "Id": 5, + "Name": "Brand4", + "DisplayName": "BrandDisplay4", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country4" + } + } + }, + { + "Id": 41, + "Name": "Brand40", + "DisplayName": "BrandDisplay40", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country40" + } + } + }, + { + "Id": 42, + "Name": "Brand41", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country41" + } + } + }, + { + "Id": 43, + "Name": "Brand42", + "DisplayName": "BrandDisplay42", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country42" + } + } + }, + { + "Id": 44, + "Name": "Brand43", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country43" + } + } + }, + { + "Id": 45, + "Name": "Brand44", + "DisplayName": "BrandDisplay44", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country44" + } + } + }, + { + "Id": 46, + "Name": "Brand45", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country45" + } + } + }, + { + "Id": 47, + "Name": "Brand46", + "DisplayName": "BrandDisplay46", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country46" + } + } + }, + { + "Id": 48, + "Name": "Brand47", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country47" + } + } + }, + { + "Id": 49, + "Name": "Brand48", + "DisplayName": "BrandDisplay48", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country48" + } + } + }, + { + "Id": 50, + "Name": "Brand49", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country49" + } + } + }, + { + "Id": 6, + "Name": "Brand5", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country5" + } + } + }, + { + "Id": 51, + "Name": "Brand50", + "DisplayName": "BrandDisplay50", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country50" + } + } + }, + { + "Id": 52, + "Name": "Brand51", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country51" + } + } + }, + { + "Id": 53, + "Name": "Brand52", + "DisplayName": "BrandDisplay52", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country52" + } + } + }, + { + "Id": 54, + "Name": "Brand53", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country53" + } + } + }, + { + "Id": 55, + "Name": "Brand54", + "DisplayName": "BrandDisplay54", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country54" + } + } + }, + { + "Id": 56, + "Name": "Brand55", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country55" + } + } + }, + { + "Id": 57, + "Name": "Brand56", + "DisplayName": "BrandDisplay56", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country56" + } + } + }, + { + "Id": 58, + "Name": "Brand57", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country57" + } + } + }, + { + "Id": 59, + "Name": "Brand58", + "DisplayName": "BrandDisplay58", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country58" + } + } + }, + { + "Id": 60, + "Name": "Brand59", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country59" + } + } + }, + { + "Id": 7, + "Name": "Brand6", + "DisplayName": "BrandDisplay6", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country6" + } + } + }, + { + "Id": 61, + "Name": "Brand60", + "DisplayName": "BrandDisplay60", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country60" + } + } + }, + { + "Id": 62, + "Name": "Brand61", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country61" + } + } + }, + { + "Id": 63, + "Name": "Brand62", + "DisplayName": "BrandDisplay62", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country62" + } + } + }, + { + "Id": 64, + "Name": "Brand63", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country63" + } + } + }, + { + "Id": 65, + "Name": "Brand64", + "DisplayName": "BrandDisplay64", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country64" + } + } + }, + { + "Id": 66, + "Name": "Brand65", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country65" + } + } + }, + { + "Id": 67, + "Name": "Brand66", + "DisplayName": "BrandDisplay66", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country66" + } + } + }, + { + "Id": 68, + "Name": "Brand67", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country67" + } + } + }, + { + "Id": 69, + "Name": "Brand68", + "DisplayName": "BrandDisplay68", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country68" + } + } + }, + { + "Id": 70, + "Name": "Brand69", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country69" + } + } + }, + { + "Id": 8, + "Name": "Brand7", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country7" + } + } + }, + { + "Id": 71, + "Name": "Brand70", + "DisplayName": "BrandDisplay70", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country70" + } + } + }, + { + "Id": 72, + "Name": "Brand71", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country71" + } + } + }, + { + "Id": 73, + "Name": "Brand72", + "DisplayName": "BrandDisplay72", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country72" + } + } + }, + { + "Id": 74, + "Name": "Brand73", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country73" + } + } + }, + { + "Id": 75, + "Name": "Brand74", + "DisplayName": "BrandDisplay74", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country74" + } + } + }, + { + "Id": 76, + "Name": "Brand75", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country75" + } + } + }, + { + "Id": 77, + "Name": "Brand76", + "DisplayName": "BrandDisplay76", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country76" + } + } + }, + { + "Id": 78, + "Name": "Brand77", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country77" + } + } + }, + { + "Id": 79, + "Name": "Brand78", + "DisplayName": "BrandDisplay78", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country78" + } + } + }, + { + "Id": 80, + "Name": "Brand79", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country79" + } + } + }, + { + "Id": 9, + "Name": "Brand8", + "DisplayName": "BrandDisplay8", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country8" + } + } + }, + { + "Id": 81, + "Name": "Brand80", + "DisplayName": "BrandDisplay80", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country80" + } + } + }, + { + "Id": 82, + "Name": "Brand81", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country81" + } + } + }, + { + "Id": 83, + "Name": "Brand82", + "DisplayName": "BrandDisplay82", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country82" + } + } + }, + { + "Id": 84, + "Name": "Brand83", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country83" + } + } + }, + { + "Id": 85, + "Name": "Brand84", + "DisplayName": "BrandDisplay84", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country84" + } + } + }, + { + "Id": 86, + "Name": "Brand85", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country85" + } + } + }, + { + "Id": 87, + "Name": "Brand86", + "DisplayName": "BrandDisplay86", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country86" + } + } + }, + { + "Id": 88, + "Name": "Brand87", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country87" + } + } + }, + { + "Id": 89, + "Name": "Brand88", + "DisplayName": "BrandDisplay88", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country88" + } + } + }, + { + "Id": 90, + "Name": "Brand89", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country89" + } + } + }, + { + "Id": 10, + "Name": "Brand9", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country9" + } + } + }, + { + "Id": 91, + "Name": "Brand90", + "DisplayName": "BrandDisplay90", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country90" + } + } + }, + { + "Id": 92, + "Name": "Brand91", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country91" + } + } + }, + { + "Id": 93, + "Name": "Brand92", + "DisplayName": "BrandDisplay92", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country92" + } + } + }, + { + "Id": 94, + "Name": "Brand93", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country93" + } + } + }, + { + "Id": 95, + "Name": "Brand94", + "DisplayName": "BrandDisplay94", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country94" + } + } + }, + { + "Id": 96, + "Name": "Brand95", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country95" + } + } + }, + { + "Id": 97, + "Name": "Brand96", + "DisplayName": "BrandDisplay96", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country96" + } + } + }, + { + "Id": 98, + "Name": "Brand97", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country97" + } + } + }, + { + "Id": 99, + "Name": "Brand98", + "DisplayName": "BrandDisplay98", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country98" + } + } + }, + { + "Id": 100, + "Name": "Brand99", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country99" + } + } + } +] +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_After_Id_13_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_After_Id_13_NET9_0.md new file mode 100644 index 00000000000..fbe637b2d28 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_After_Id_13_NET9_0.md @@ -0,0 +1,101 @@ +# Paging_First_5_After_Id_13 + +## SQL 0 + +```sql +-- @__p_0='Brand12' +-- @__p_1='13' +-- @__p_2='6' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +WHERE b."Name" > @__p_0 OR (b."Name" = @__p_0 AND b."Id" > @__p_1) +ORDER BY b."Name", b."Id" +LIMIT @__p_2 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Where(t => ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) > 0) OrElse ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.Id.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.Int32]).value, Int32)) > 0)))).Take(6) +``` + +## Result 3 + +```json +{ + "HasNextPage": true, + "HasPreviousPage": true, + "First": 14, + "FirstCursor": "QnJhbmQxMzoxNA==", + "Last": 18, + "LastCursor": "QnJhbmQxNzoxOA==" +} +``` + +## Result 4 + +```json +[ + { + "Id": 14, + "Name": "Brand13", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country13" + } + } + }, + { + "Id": 15, + "Name": "Brand14", + "DisplayName": "BrandDisplay14", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country14" + } + } + }, + { + "Id": 16, + "Name": "Brand15", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country15" + } + } + }, + { + "Id": 17, + "Name": "Brand16", + "DisplayName": "BrandDisplay16", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country16" + } + } + }, + { + "Id": 18, + "Name": "Brand17", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country17" + } + } + } +] +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_Before_Id_96_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_Before_Id_96_NET9_0.md new file mode 100644 index 00000000000..b30b11b2e0e --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_Before_Id_96_NET9_0.md @@ -0,0 +1,101 @@ +# Paging_First_5_Before_Id_96 + +## SQL 0 + +```sql +-- @__p_0='Brand95' +-- @__p_1='96' +-- @__p_2='6' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +WHERE b."Name" < @__p_0 OR (b."Name" = @__p_0 AND b."Id" < @__p_1) +ORDER BY b."Name" DESC, b."Id" DESC +LIMIT @__p_2 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Where(t => ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) < 0) OrElse ((t.Name.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.String]).value, String)) == 0) AndAlso (t.Id.CompareTo(Convert(value(HotChocolate.Pagination.Expressions.ExpressionHelpers+<>c__DisplayClass7_0`1[System.Int32]).value, Int32)) < 0)))).Reverse().Take(6) +``` + +## Result 3 + +```json +{ + "HasNextPage": true, + "HasPreviousPage": true, + "First": 91, + "FirstCursor": "QnJhbmQ5MDo5MQ==", + "Last": 95, + "LastCursor": "QnJhbmQ5NDo5NQ==" +} +``` + +## Result 4 + +```json +[ + { + "Id": 91, + "Name": "Brand90", + "DisplayName": "BrandDisplay90", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country90" + } + } + }, + { + "Id": 92, + "Name": "Brand91", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country91" + } + } + }, + { + "Id": 93, + "Name": "Brand92", + "DisplayName": "BrandDisplay92", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country92" + } + } + }, + { + "Id": 94, + "Name": "Brand93", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country93" + } + } + }, + { + "Id": 95, + "Name": "Brand94", + "DisplayName": "BrandDisplay94", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country94" + } + } + } +] +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_NET9_0.md new file mode 100644 index 00000000000..3d47e9e96a0 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_NET9_0.md @@ -0,0 +1,98 @@ +# Paging_First_5 + +## SQL 0 + +```sql +-- @__p_0='6' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name", b."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Take(6) +``` + +## Result 3 + +```json +{ + "HasNextPage": true, + "HasPreviousPage": false, + "First": 1, + "FirstCursor": "QnJhbmQwOjE=", + "Last": 13, + "LastCursor": "QnJhbmQxMjoxMw==" +} +``` + +## Result 4 + +```json +[ + { + "Id": 1, + "Name": "Brand0", + "DisplayName": "BrandDisplay0", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country0" + } + } + }, + { + "Id": 2, + "Name": "Brand1", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country1" + } + } + }, + { + "Id": 11, + "Name": "Brand10", + "DisplayName": "BrandDisplay10", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country10" + } + } + }, + { + "Id": 12, + "Name": "Brand11", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country11" + } + } + }, + { + "Id": 13, + "Name": "Brand12", + "DisplayName": "BrandDisplay12", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country12" + } + } + } +] +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Last_5_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Last_5_NET9_0.md new file mode 100644 index 00000000000..1e0341186cf --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Last_5_NET9_0.md @@ -0,0 +1,98 @@ +# Paging_Last_5 + +## SQL 0 + +```sql +-- @__p_0='6' +SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" +FROM "Brands" AS b +ORDER BY b."Name" DESC, b."Id" DESC +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Reverse().Take(6) +``` + +## Result 3 + +```json +{ + "HasNextPage": false, + "HasPreviousPage": true, + "First": 96, + "FirstCursor": "QnJhbmQ5NTo5Ng==", + "Last": 100, + "LastCursor": "QnJhbmQ5OToxMDA=" +} +``` + +## Result 4 + +```json +[ + { + "Id": 96, + "Name": "Brand95", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country95" + } + } + }, + { + "Id": 97, + "Name": "Brand96", + "DisplayName": "BrandDisplay96", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country96" + } + } + }, + { + "Id": 98, + "Name": "Brand97", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country97" + } + } + }, + { + "Id": 99, + "Name": "Brand98", + "DisplayName": "BrandDisplay98", + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country98" + } + } + }, + { + "Id": 100, + "Name": "Brand99", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country99" + } + } + } +] +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_NET9_0.md new file mode 100644 index 00000000000..a0d36730082 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_NET9_0.md @@ -0,0 +1,164 @@ +# Query_Owner_Animals + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT o."Id", o."Name" +FROM "Owners" AS o +ORDER BY o."Name", o."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Owner() {Id = root.Id, Name = root.Name}).Take(11) +``` + +## SQL 1 + +```sql +-- @__keys_0={ '6', '5', '4', '3', '2', ... } (DbType = Object) +SELECT s."OwnerId", s1."Id", s1."AnimalType", s1."Name", s1."OwnerId", s1."IsPurring", s1."IsBarking", s1."Id0" +FROM ( + SELECT p."OwnerId" + FROM "Owners" AS o + INNER JOIN "Pets" AS p ON o."Id" = p."OwnerId" + WHERE o."Id" = ANY (@__keys_0) + GROUP BY p."OwnerId" +) AS s +LEFT JOIN ( + SELECT s0."Id", s0."AnimalType", s0."Name", s0."OwnerId", s0."IsPurring", s0."IsBarking", s0."Id0" + FROM ( + SELECT p0."Id", p0."AnimalType", p0."Name", p0."OwnerId", p0."IsPurring", p0."IsBarking", o0."Id" AS "Id0", ROW_NUMBER() OVER(PARTITION BY p0."OwnerId" ORDER BY p0."Name", p0."Id") AS row + FROM "Owners" AS o0 + INNER JOIN "Pets" AS p0 ON o0."Id" = p0."OwnerId" + WHERE o0."Id" = ANY (@__keys_0) + ) AS s0 + WHERE s0.row <= 11 +) AS s1 ON s."OwnerId" = s1."OwnerId" +ORDER BY s."OwnerId", s1."OwnerId", s1."Name", s1."Id" +``` + +## Expression 1 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Where(t => value(HotChocolate.Data.InterfaceIntegrationTests+AnimalsByOwnerDataLoader+<>c__DisplayClass2_0).keys.Contains(t.Id)).SelectMany(t => t.Pets).GroupBy(t => t.OwnerId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderBy(t => t.Name).ThenBy(t => t.Id).Take(11).ToList()}) +``` + +## Result 5 + +```json +{ + "data": { + "owners": { + "nodes": [ + { + "id": 1, + "name": "Owner 1", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 1, + "name": "Cat 1" + }, + { + "__typename": "Dog", + "id": 5, + "name": "Dog 1" + }, + { + "__typename": "Dog", + "id": 6, + "name": "Dog 2" + } + ] + } + }, + { + "id": 2, + "name": "Owner 2", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 2, + "name": "Cat 2" + }, + { + "__typename": "Dog", + "id": 7, + "name": "Dog 3" + }, + { + "__typename": "Dog", + "id": 8, + "name": "Dog 4" + } + ] + } + }, + { + "id": 3, + "name": "Owner 3", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 3, + "name": "Cat 3 (Not Pure)" + }, + { + "__typename": "Dog", + "id": 9, + "name": "Dog 5" + }, + { + "__typename": "Dog", + "id": 10, + "name": "Dog 6" + } + ] + } + }, + { + "id": 4, + "name": "Owner 4 - No Pets", + "pets": { + "nodes": [] + } + }, + { + "id": 5, + "name": "Owner 5 - Only Cat", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 4, + "name": "Only Cat" + } + ] + } + }, + { + "id": 6, + "name": "Owner 6 - Only Dog", + "pets": { + "nodes": [ + { + "__typename": "Dog", + "id": 11, + "name": "Only Dog" + } + ] + } + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments_NET9_0.md new file mode 100644 index 00000000000..425c3bb3f54 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_Fragments_NET9_0.md @@ -0,0 +1,175 @@ +# Query_Owner_Animals_With_Fragments + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT o."Id", o."Name" +FROM "Owners" AS o +ORDER BY o."Name", o."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Owner() {Id = root.Id, Name = root.Name}).Take(11) +``` + +## SQL 1 + +```sql +-- @__keys_0={ '6', '5', '4', '3', '2', ... } (DbType = Object) +SELECT s."OwnerId", s1."Id", s1."AnimalType", s1."Name", s1."OwnerId", s1."IsPurring", s1."IsBarking", s1."Id0" +FROM ( + SELECT p."OwnerId" + FROM "Owners" AS o + INNER JOIN "Pets" AS p ON o."Id" = p."OwnerId" + WHERE o."Id" = ANY (@__keys_0) + GROUP BY p."OwnerId" +) AS s +LEFT JOIN ( + SELECT s0."Id", s0."AnimalType", s0."Name", s0."OwnerId", s0."IsPurring", s0."IsBarking", s0."Id0" + FROM ( + SELECT p0."Id", p0."AnimalType", p0."Name", p0."OwnerId", p0."IsPurring", p0."IsBarking", o0."Id" AS "Id0", ROW_NUMBER() OVER(PARTITION BY p0."OwnerId" ORDER BY p0."Name", p0."Id") AS row + FROM "Owners" AS o0 + INNER JOIN "Pets" AS p0 ON o0."Id" = p0."OwnerId" + WHERE o0."Id" = ANY (@__keys_0) + ) AS s0 + WHERE s0.row <= 11 +) AS s1 ON s."OwnerId" = s1."OwnerId" +ORDER BY s."OwnerId", s1."OwnerId", s1."Name", s1."Id" +``` + +## Expression 1 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Where(t => value(HotChocolate.Data.InterfaceIntegrationTests+AnimalsByOwnerDataLoader+<>c__DisplayClass2_0).keys.Contains(t.Id)).SelectMany(t => t.Pets).GroupBy(t => t.OwnerId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderBy(t => t.Name).ThenBy(t => t.Id).Take(11).ToList()}) +``` + +## Result 5 + +```json +{ + "data": { + "owners": { + "nodes": [ + { + "id": 1, + "name": "Owner 1", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 1, + "name": "Cat 1", + "isPurring": false + }, + { + "__typename": "Dog", + "id": 5, + "name": "Dog 1", + "isBarking": true + }, + { + "__typename": "Dog", + "id": 6, + "name": "Dog 2", + "isBarking": false + } + ] + } + }, + { + "id": 2, + "name": "Owner 2", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 2, + "name": "Cat 2", + "isPurring": false + }, + { + "__typename": "Dog", + "id": 7, + "name": "Dog 3", + "isBarking": true + }, + { + "__typename": "Dog", + "id": 8, + "name": "Dog 4", + "isBarking": false + } + ] + } + }, + { + "id": 3, + "name": "Owner 3", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 3, + "name": "Cat 3 (Not Pure)", + "isPurring": true + }, + { + "__typename": "Dog", + "id": 9, + "name": "Dog 5", + "isBarking": true + }, + { + "__typename": "Dog", + "id": 10, + "name": "Dog 6", + "isBarking": false + } + ] + } + }, + { + "id": 4, + "name": "Owner 4 - No Pets", + "pets": { + "nodes": [] + } + }, + { + "id": 5, + "name": "Owner 5 - Only Cat", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 4, + "name": "Only Cat", + "isPurring": false + } + ] + } + }, + { + "id": 6, + "name": "Owner 6 - Only Dog", + "pets": { + "nodes": [ + { + "__typename": "Dog", + "id": 11, + "name": "Only Dog", + "isBarking": true + } + ] + } + } + ] + } + } +} +``` + diff --git a/src/HotChocolate/Skimmed/Directory.Build.props b/src/HotChocolate/Skimmed/Directory.Build.props index 504e90cb29e..ad1b9b88aca 100644 --- a/src/HotChocolate/Skimmed/Directory.Build.props +++ b/src/HotChocolate/Skimmed/Directory.Build.props @@ -2,7 +2,7 @@ - net8.0 + net9.0; net8.0 diff --git a/src/HotChocolate/Skimmed/src/Skimmed/Collections/ReadOnlyObjectTypeDefinitionCollection.cs b/src/HotChocolate/Skimmed/src/Skimmed/Collections/ReadOnlyObjectTypeDefinitionCollection.cs index 3d1f1c7a5d3..cd815377fc3 100644 --- a/src/HotChocolate/Skimmed/src/Skimmed/Collections/ReadOnlyObjectTypeDefinitionCollection.cs +++ b/src/HotChocolate/Skimmed/src/Skimmed/Collections/ReadOnlyObjectTypeDefinitionCollection.cs @@ -9,7 +9,7 @@ public sealed class ReadOnlyObjectTypeDefinitionCollection : IObjectTypeDefiniti private ReadOnlyObjectTypeDefinitionCollection(IEnumerable types) { - ArgumentNullException.ThrowIfNull(nameof(types)); + ArgumentNullException.ThrowIfNull(types); _types = types.ToArray(); } diff --git a/src/HotChocolate/Utilities/src/Utilities/DependencyInjection/ServiceFactory.cs b/src/HotChocolate/Utilities/src/Utilities/DependencyInjection/ServiceFactory.cs deleted file mode 100644 index c74b3f1591f..00000000000 --- a/src/HotChocolate/Utilities/src/Utilities/DependencyInjection/ServiceFactory.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Collections.Concurrent; -#if NET6_0_OR_GREATER -using System.Diagnostics.CodeAnalysis; -#endif -using System.Globalization; -using HotChocolate.Utilities.Properties; -using Microsoft.Extensions.DependencyInjection; -#if NET6_0_OR_GREATER -using static System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes; -#endif - -namespace HotChocolate.Utilities; - -public static class ServiceFactory -{ - private static readonly ConcurrentDictionary _factories = new(); - -#if NET6_0_OR_GREATER - public static object CreateInstance( - IServiceProvider services, - [DynamicallyAccessedMembers(PublicConstructors)] Type type) -#else - public static object CreateInstance(IServiceProvider services, Type type) -#endif - { - if (services is null) - { - throw new ArgumentNullException(nameof(services)); - } - - if (type is null) - { - throw new ArgumentNullException(nameof(type)); - } - - try - { - var factory = _factories.GetOrAdd(type, CreateFactory); - return factory(services, null); - } - catch (Exception ex) - { - throw new ServiceException( - string.Format( - CultureInfo.InvariantCulture, - UtilityResources.ServiceFactory_CreateInstanceFailed, - type.FullName), - ex); - } - - static ObjectFactory CreateFactory(Type instanceType) - => ActivatorUtilities.CreateFactory(instanceType, []); - } -} diff --git a/src/HotChocolate/Utilities/test/Utilities.Tests/ServiceFactoryTests.cs b/src/HotChocolate/Utilities/test/Utilities.Tests/ServiceFactoryTests.cs deleted file mode 100644 index 67e37ce1130..00000000000 --- a/src/HotChocolate/Utilities/test/Utilities.Tests/ServiceFactoryTests.cs +++ /dev/null @@ -1,108 +0,0 @@ -using Snapshooter.Xunit; -using Xunit; - -namespace HotChocolate.Utilities; - -public class ServiceFactoryTests -{ - [Fact] - public void TypeArgumentValidation() - { - // arrange - // act - void Error() => ServiceFactory.CreateInstance(EmptyServiceProvider.Instance, null!); - - // assert - Assert.Throws(Error); - } - - [Fact] - public void CreateInstanceWithoutServiceProvider() - { - // arrange - // act - var instance = ServiceFactory.CreateInstance(EmptyServiceProvider.Instance, typeof(ClassWithNoDependencies)); - - // assert - Assert.NotNull(instance); - Assert.IsType(instance); - } - - [Fact] - public void CreateInstanceWithServiceProvider() - { - // arrange - var serviceProvider = new DictionaryServiceProvider( - typeof(ClassWithNoDependencies), - new ClassWithNoDependencies()); - - // act - var instance = ServiceFactory.CreateInstance(serviceProvider, typeof(ClassWithDependencies)); - - // assert - Assert.NotNull(instance); - Assert.IsType(instance); - - var classWithDependencies = - (ClassWithDependencies)instance; - Assert.NotNull(classWithDependencies.Dependency); - } - - [Fact] - public void Catch_Exception_On_Create() - { - // arrange - var type = typeof(ClassWithException); - - // act - void Error() => ServiceFactory.CreateInstance(EmptyServiceProvider.Instance, type); - - // assert - Assert.Throws(Error) - .Message.MatchSnapshot(); - } - - [Fact] - public void Cannot_Resolve_Dependencies() - { - // arrange - var type = typeof(ClassWithDependencies); - - // act - void Error() => ServiceFactory.CreateInstance(EmptyServiceProvider.Instance, type); - - // assert - Assert.Throws(Error) - .Message.MatchSnapshot(); - } - - [Fact] - public void No_Services_Available() - { - // arrange - var type = typeof(ClassWithDependencies); - - // act - void Error() => ServiceFactory.CreateInstance(EmptyServiceProvider.Instance, type); - - // assert - Assert.Throws(Error) - .Message.MatchSnapshot(); - } - - private sealed class ClassWithNoDependencies; - - private sealed class ClassWithDependencies(ClassWithNoDependencies dependency) - { - public ClassWithNoDependencies Dependency { get; } = - dependency ?? throw new ArgumentNullException(nameof(dependency)); - } - - private sealed class ClassWithException - { - public ClassWithException() - { - throw new NullReferenceException(); - } - } -} diff --git a/src/StrawberryShake/Client/src/Razor/StrawberryShake.Razor.csproj b/src/StrawberryShake/Client/src/Razor/StrawberryShake.Razor.csproj index eaa1b9ad0f0..bcaf8c69098 100644 --- a/src/StrawberryShake/Client/src/Razor/StrawberryShake.Razor.csproj +++ b/src/StrawberryShake/Client/src/Razor/StrawberryShake.Razor.csproj @@ -5,7 +5,7 @@ StrawberryShake.Razor StrawberryShake.Razor Provides razor components for using StrawberryShake. - net8.0; net7.0; net6.0 + net9.0; net8.0; net7.0; net6.0 diff --git a/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Analyzers/FieldCollector.cs b/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Analyzers/FieldCollector.cs index 1b1233f53e2..426b49847bb 100644 --- a/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Analyzers/FieldCollector.cs +++ b/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Analyzers/FieldCollector.cs @@ -1,5 +1,7 @@ using HotChocolate; +#if !NET9_0_OR_GREATER using HotChocolate.Execution; +#endif using HotChocolate.Language; using HotChocolate.Types; using HotChocolate.Utilities; diff --git a/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Analyzers/FragmentHelper.cs b/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Analyzers/FragmentHelper.cs index f1106443220..35878d4b39f 100644 --- a/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Analyzers/FragmentHelper.cs +++ b/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Analyzers/FragmentHelper.cs @@ -1,6 +1,8 @@ using System.Text; using HotChocolate; +#if !NET9_0_OR_GREATER using HotChocolate.Execution; +#endif using HotChocolate.Language; using HotChocolate.Types; using HotChocolate.Utilities; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/AnyScalarDefaultSerializationTest.Client.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/AnyScalarDefaultSerializationTest.Client.cs index 62f42c202d9..07bbb65ca4a 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/AnyScalarDefaultSerializationTest.Client.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/AnyScalarDefaultSerializationTest.Client.cs @@ -189,8 +189,33 @@ private GetJsonQueryDocument() public static GetJsonQueryDocument Instance { get; } = new GetJsonQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x4a, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x6a, 0x73, 0x6f, 0x6e, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x4a, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x6a, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "90a00e07ca153decc5937eb356401940e7c6b66a"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -220,6 +245,7 @@ public GetJsonQuery(global::StrawberryShake.IOperationExecutor o } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetJsonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -305,6 +331,7 @@ public GetJsonResultFactory(global::StrawberryShake.IEntityStore entityStore) } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::StrawberryShake.CodeGeneration.CSharp.Integration.AnyScalarDefaultSerialization.IGetJsonResult); + public GetJsonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -340,9 +367,9 @@ public GetJsonResultInfo(global::System.Text.Json.JsonElement json, global::Syst } public global::System.Text.Json.JsonElement Json { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetJsonResultInfo(Json, _entityIds, version); @@ -398,7 +425,9 @@ public GetJsonBuilder(global::StrawberryShake.IEntityStore entityStore, global:: public partial class AnyScalarDefaultSerializationClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/EntityIdOrDataTest.Client.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/EntityIdOrDataTest.Client.cs index 43008ab3721..9b9dd5cff68 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/EntityIdOrDataTest.Client.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/EntityIdOrDataTest.Client.cs @@ -519,8 +519,161 @@ private GetFooQueryDocument() public static GetFooQueryDocument Instance { get; } = new GetFooQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x6f, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x51, 0x75, 0x6f, 0x78, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x32, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x51, 0x75, 0x6f, 0x78, 0x32, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x32, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x46, + 0x6f, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x51, + 0x75, + 0x6f, + 0x78, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x32, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x51, + 0x75, + 0x6f, + 0x78, + 0x32, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x32, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "43715bb5e8ecbe7659e99dcb33abcf1ffeadff8e"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -570,6 +723,7 @@ public GetFooQuery(global::StrawberryShake.IOperationExecutor ope } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetFooResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -703,6 +857,7 @@ public GetFooResultFactory(global::StrawberryShake.IEntityStore entityStore, glo } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::StrawberryShake.CodeGeneration.CSharp.Integration.EntityIdOrData.IGetFooResult); + public GetFooResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -819,9 +974,9 @@ public GetFooResultInfo(global::System.Collections.Generic.IReadOnlyList? Foo { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetFooResultInfo(Foo, _entityIds, version); @@ -975,7 +1130,6 @@ public partial class QuoxData : IBarData } public global::System.String __typename { get; } - public global::System.String? Foo { get; } } @@ -990,7 +1144,6 @@ public partial class Quox2Data : IBarData } public global::System.String __typename { get; } - public global::System.String? Foo { get; } } @@ -1041,7 +1194,9 @@ public GetFoo_Foo_Baz2 Map(global::StrawberryShake.CodeGeneration.CSharp.Integra public partial class EntityIdOrDataClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/MultiProfileTest.Client.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/MultiProfileTest.Client.cs index 55d8567b7b5..22e53efd4ee 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/MultiProfileTest.Client.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/MultiProfileTest.Client.cs @@ -278,7 +278,6 @@ public GetHero_Hero_Droid(global::System.String name, global::StrawberryShake.Co } public global::System.String Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.IGetHero_Hero_Friends? Friends { get; } public virtual global::System.Boolean Equals(GetHero_Hero_Droid? other) @@ -348,7 +347,6 @@ public GetHero_Hero_Human(global::System.String name, global::StrawberryShake.Co } public global::System.String Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.IGetHero_Hero_Friends? Friends { get; } public virtual global::System.Boolean Equals(GetHero_Hero_Human? other) @@ -621,7 +619,6 @@ public partial interface IGetHeroResult public partial interface IGetHero_Hero { public global::System.String Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.IGetHero_Hero_Friends? Friends { get; } } @@ -755,9 +752,7 @@ public OnReviewSub_OnReview_Review(global::System.String __typename, global::Sys /// The name of the current Object type at runtime. /// public global::System.String __typename { get; } - public global::System.Int32 Stars { get; } - public global::System.String? Commentary { get; } public virtual global::System.Boolean Equals(OnReviewSub_OnReview_Review? other) @@ -832,9 +827,7 @@ public partial interface IOnReviewSub_OnReview /// The name of the current Object type at runtime. /// public global::System.String __typename { get; } - public global::System.Int32 Stars { get; } - public global::System.String? Commentary { get; } } @@ -917,7 +910,6 @@ public CreateReviewMut_CreateReview_Review(global::System.Int32 stars, global::S } public global::System.Int32 Stars { get; } - public global::System.String? Commentary { get; } public virtual global::System.Boolean Equals(CreateReviewMut_CreateReview_Review? other) @@ -988,7 +980,6 @@ public partial interface ICreateReviewMutResult public partial interface ICreateReviewMut_CreateReview { public global::System.Int32 Stars { get; } - public global::System.String? Commentary { get; } } @@ -1005,6 +996,7 @@ public partial class ReviewInputInputValueFormatter : global::StrawberryShake.Se private global::StrawberryShake.Serialization.IInputValueFormatter _intFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _stringFormatter = default !; public global::System.String TypeName => "ReviewInput"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _intFormatter = serializerResolver.GetInputValueFormatter("Int"); @@ -1131,6 +1123,7 @@ public partial class ReviewInput : global::StrawberryShake.CodeGeneration.CSharp } global::System.Boolean global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.IReviewInputInfo.IsStarsSet => _set_stars; + public global::System.String? Commentary { get => _value_commentary; @@ -1158,6 +1151,7 @@ public enum Episode public partial class EpisodeSerializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "Episode"; + public Episode Parse(global::System.String serializedValue) { return serializedValue switch @@ -1219,8 +1213,201 @@ private GetHeroQueryDocument() public static GetHeroQueryDocument Instance { get; } = new GetHeroQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x48, 0x65, 0x72, 0x6f, 0x20, 0x7b, 0x20, 0x68, 0x65, 0x72, 0x6f, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x4e, 0x45, 0x57, 0x5f, 0x48, 0x4f, 0x50, 0x45, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x68, + 0x65, + 0x72, + 0x6f, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x4e, + 0x45, + 0x57, + 0x5f, + 0x48, + 0x4f, + 0x50, + 0x45, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x66, + 0x72, + 0x69, + 0x65, + 0x6e, + 0x64, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x6f, + 0x64, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "9f9a72871b1548dfdb9e75702e81c5c5945ff0c3"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -1272,6 +1459,7 @@ public GetHeroQuery(global::StrawberryShake.IOperationExecutor o } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetHeroResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -1360,8 +1548,99 @@ private OnReviewSubSubscriptionDocument() public static OnReviewSubSubscriptionDocument Instance { get; } = new OnReviewSubSubscriptionDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Subscription; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4f, 0x6e, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x53, 0x75, 0x62, 0x20, 0x7b, 0x20, 0x6f, 0x6e, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x4e, 0x45, 0x57, 0x5f, 0x48, 0x4f, 0x50, 0x45, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x74, 0x61, 0x72, 0x73, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x73, + 0x75, + 0x62, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x4f, + 0x6e, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x53, + 0x75, + 0x62, + 0x20, + 0x7b, + 0x20, + 0x6f, + 0x6e, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x4e, + 0x45, + 0x57, + 0x5f, + 0x48, + 0x4f, + 0x50, + 0x45, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x73, + 0x74, + 0x61, + 0x72, + 0x73, + 0x20, + 0x63, + 0x6f, + 0x6d, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x61, + 0x72, + 0x79, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "92220fce37342d7ade3d63a2a81342eb1fb14bac"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -1395,6 +1674,7 @@ public OnReviewSubSubscription(global::StrawberryShake.IOperationExecutor typeof(IOnReviewSubResult); + public global::System.IObservable> Watch(global::StrawberryShake.ExecutionStrategy? strategy = null) { var request = CreateRequest(); @@ -1458,8 +1738,163 @@ private CreateReviewMutMutationDocument() public static CreateReviewMutMutationDocument Instance { get; } = new CreateReviewMutMutationDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Mutation; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x4d, 0x75, 0x74, 0x28, 0x24, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x45, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x21, 0x2c, 0x20, 0x24, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x3a, 0x20, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x24, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x3a, 0x20, 0x24, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x74, 0x61, 0x72, 0x73, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x6d, + 0x75, + 0x74, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x43, + 0x72, + 0x65, + 0x61, + 0x74, + 0x65, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x4d, + 0x75, + 0x74, + 0x28, + 0x24, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x45, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x72, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x3a, + 0x20, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x63, + 0x72, + 0x65, + 0x61, + 0x74, + 0x65, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x24, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x2c, + 0x20, + 0x72, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x3a, + 0x20, + 0x24, + 0x72, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x73, + 0x74, + 0x61, + 0x72, + 0x73, + 0x20, + 0x63, + 0x6f, + 0x6d, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x61, + 0x72, + 0x79, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "7b7488dce3bce5700fe4fab0d349728a5121c153"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -1497,6 +1932,7 @@ public CreateReviewMutMutation(global::StrawberryShake.IOperationExecutor typeof(ICreateReviewMutResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.Episode episode, global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.ReviewInput review, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(episode, review); @@ -1622,7 +2058,6 @@ public partial class DroidEntity } public global::System.String Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.FriendsConnectionData? Friends { get; } } @@ -1637,7 +2072,6 @@ public partial class HumanEntity } public global::System.String Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.FriendsConnectionData? Friends { get; } } @@ -1656,6 +2090,7 @@ public GetHeroResultFactory(global::StrawberryShake.IEntityStore entityStore, gl } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.IGetHeroResult); + public GetHeroResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -1711,9 +2146,9 @@ public GetHeroResultInfo(global::StrawberryShake.EntityId? hero, global::System. } public global::StrawberryShake.EntityId? Hero { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetHeroResultInfo(Hero, _entityIds, version); @@ -1731,6 +2166,7 @@ public OnReviewSubResultFactory(global::StrawberryShake.IEntityStore entityStore } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.IOnReviewSubResult); + public OnReviewSubResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -1781,9 +2217,9 @@ public OnReviewSubResultInfo(global::StrawberryShake.CodeGeneration.CSharp.Integ } public global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.ReviewData OnReview { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new OnReviewSubResultInfo(OnReview, _entityIds, version); @@ -1801,6 +2237,7 @@ public CreateReviewMutResultFactory(global::StrawberryShake.IEntityStore entityS } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.ICreateReviewMutResult); + public CreateReviewMutResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -1851,9 +2288,9 @@ public CreateReviewMutResultInfo(global::StrawberryShake.CodeGeneration.CSharp.I } public global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.ReviewData CreateReview { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new CreateReviewMutResultInfo(CreateReview, _entityIds, version); @@ -2240,7 +2677,6 @@ public partial class FriendsConnectionData } public global::System.String __typename { get; } - ///A flattened list of the nodes. public global::System.Collections.Generic.IReadOnlyList? Nodes { get; } } @@ -2257,9 +2693,7 @@ public partial class ReviewData } public global::System.String __typename { get; } - public global::System.Int32? Stars { get; } - public global::System.String? Commentary { get; } } @@ -2472,7 +2906,9 @@ public GetHero_Hero_Friends_Nodes_Human Map(global::StrawberryShake.CodeGenerati public partial class MultiProfileClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsDeferInListTest.Client.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsDeferInListTest.Client.cs index bfc36c028d0..dcb259cd6f6 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsDeferInListTest.Client.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsDeferInListTest.Client.cs @@ -457,6 +457,7 @@ public GetHero_Hero_Friends_Nodes_Droid(ICharacterName? characterName) } public ICharacterName? CharacterName => _characterName; + public virtual global::System.Boolean Equals(GetHero_Hero_Friends_Nodes_Droid? other) { if (ReferenceEquals(null, other)) @@ -529,6 +530,7 @@ public GetHero_Hero_Friends_Nodes_Human(ICharacterName? characterName) } public ICharacterName? CharacterName => _characterName; + public virtual global::System.Boolean Equals(GetHero_Hero_Friends_Nodes_Human? other) { if (ReferenceEquals(null, other)) @@ -710,8 +712,323 @@ private GetHeroQueryDocument() public static GetHeroQueryDocument Instance { get; } = new GetHeroQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x48, 0x65, 0x72, 0x6f, 0x20, 0x7b, 0x20, 0x68, 0x65, 0x72, 0x6f, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x4e, 0x45, 0x57, 0x5f, 0x48, 0x4f, 0x50, 0x45, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x20, 0x40, 0x64, 0x65, 0x66, 0x65, 0x72, 0x28, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x29, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x20, 0x7b, 0x20, 0x5f, 0x69, 0x73, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x46, 0x75, 0x6c, 0x66, 0x69, 0x6c, 0x6c, 0x65, 0x64, 0x3a, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x68, + 0x65, + 0x72, + 0x6f, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x4e, + 0x45, + 0x57, + 0x5f, + 0x48, + 0x4f, + 0x50, + 0x45, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x66, + 0x72, + 0x69, + 0x65, + 0x6e, + 0x64, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x6f, + 0x64, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x43, + 0x68, + 0x61, + 0x72, + 0x61, + 0x63, + 0x74, + 0x65, + 0x72, + 0x4e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x40, + 0x64, + 0x65, + 0x66, + 0x65, + 0x72, + 0x28, + 0x6c, + 0x61, + 0x62, + 0x65, + 0x6c, + 0x3a, + 0x20, + 0x22, + 0x43, + 0x68, + 0x61, + 0x72, + 0x61, + 0x63, + 0x74, + 0x65, + 0x72, + 0x4e, + 0x61, + 0x6d, + 0x65, + 0x22, + 0x29, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x43, + 0x68, + 0x61, + 0x72, + 0x61, + 0x63, + 0x74, + 0x65, + 0x72, + 0x4e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x43, + 0x68, + 0x61, + 0x72, + 0x61, + 0x63, + 0x74, + 0x65, + 0x72, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x69, + 0x73, + 0x43, + 0x68, + 0x61, + 0x72, + 0x61, + 0x63, + 0x74, + 0x65, + 0x72, + 0x4e, + 0x61, + 0x6d, + 0x65, + 0x46, + 0x75, + 0x6c, + 0x66, + 0x69, + 0x6c, + 0x6c, + 0x65, + 0x64, + 0x3a, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "ba8d6a590e0613253d74fddd5868d5128efd3185"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -767,6 +1084,7 @@ public GetHeroQuery(global::StrawberryShake.IOperationExecutor o } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetHeroResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -879,9 +1197,7 @@ public partial class DroidEntity } public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriendsDeferInList.State.FriendsConnectionData? Friends { get; } - public global::System.Boolean IsCharacterNameFulfilled { get; } - public global::System.String Name { get; } } @@ -897,9 +1213,7 @@ public partial class HumanEntity } public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriendsDeferInList.State.FriendsConnectionData? Friends { get; } - public global::System.Boolean IsCharacterNameFulfilled { get; } - public global::System.String Name { get; } } @@ -918,6 +1232,7 @@ public GetHeroResultFactory(global::StrawberryShake.IEntityStore entityStore, gl } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriendsDeferInList.IGetHeroResult); + public GetHeroResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -973,9 +1288,9 @@ public GetHeroResultInfo(global::StrawberryShake.EntityId? hero, global::System. } public global::StrawberryShake.EntityId? Hero { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetHeroResultInfo(Hero, _entityIds, version); @@ -1172,7 +1487,6 @@ public partial class FriendsConnectionData } public global::System.String __typename { get; } - ///A flattened list of the nodes. public global::System.Collections.Generic.IReadOnlyList? Nodes { get; } } @@ -1452,7 +1766,9 @@ public CharacterName Map(global::StrawberryShake.CodeGeneration.CSharp.Integrati public partial class StarWarsGetFriendsDeferInListClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsDeferredTest.Client.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsDeferredTest.Client.cs index 0003d00bbe4..d57c4b0f98c 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsDeferredTest.Client.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsDeferredTest.Client.cs @@ -252,8 +252,8 @@ public GetHero_Hero_Droid(global::System.String name, IFriendsList? friendsListL } public global::System.String Name { get; } - public IFriendsList? FriendsListLabel => _friendsListLabel; + public virtual global::System.Boolean Equals(GetHero_Hero_Droid? other) { if (ReferenceEquals(null, other)) @@ -328,8 +328,8 @@ public GetHero_Hero_Human(global::System.String name, IFriendsList? friendsListL } public global::System.String Name { get; } - public IFriendsList? FriendsListLabel => _friendsListLabel; + public virtual global::System.Boolean Equals(GetHero_Hero_Human? other) { if (ReferenceEquals(null, other)) @@ -613,7 +613,6 @@ public partial interface IFriendsList public partial interface IGetHero_Hero { public global::System.String Name { get; } - public IFriendsList? FriendsListLabel { get; } } @@ -717,8 +716,325 @@ private GetHeroQueryDocument() public static GetHeroQueryDocument Instance { get; } = new GetHeroQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x48, 0x65, 0x72, 0x6f, 0x20, 0x7b, 0x20, 0x68, 0x65, 0x72, 0x6f, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x4e, 0x45, 0x57, 0x5f, 0x48, 0x4f, 0x50, 0x45, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x20, 0x40, 0x64, 0x65, 0x66, 0x65, 0x72, 0x28, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x3a, 0x20, 0x22, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x22, 0x29, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x20, 0x6f, 0x6e, 0x20, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x20, 0x7b, 0x20, 0x5f, 0x69, 0x73, 0x46, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x75, 0x6c, 0x66, 0x69, 0x6c, 0x6c, 0x65, 0x64, 0x3a, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x68, + 0x65, + 0x72, + 0x6f, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x4e, + 0x45, + 0x57, + 0x5f, + 0x48, + 0x4f, + 0x50, + 0x45, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x46, + 0x72, + 0x69, + 0x65, + 0x6e, + 0x64, + 0x73, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x20, + 0x40, + 0x64, + 0x65, + 0x66, + 0x65, + 0x72, + 0x28, + 0x6c, + 0x61, + 0x62, + 0x65, + 0x6c, + 0x3a, + 0x20, + 0x22, + 0x46, + 0x72, + 0x69, + 0x65, + 0x6e, + 0x64, + 0x73, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x4c, + 0x61, + 0x62, + 0x65, + 0x6c, + 0x22, + 0x29, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x46, + 0x72, + 0x69, + 0x65, + 0x6e, + 0x64, + 0x73, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x43, + 0x68, + 0x61, + 0x72, + 0x61, + 0x63, + 0x74, + 0x65, + 0x72, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x69, + 0x73, + 0x46, + 0x72, + 0x69, + 0x65, + 0x6e, + 0x64, + 0x73, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x46, + 0x75, + 0x6c, + 0x66, + 0x69, + 0x6c, + 0x6c, + 0x65, + 0x64, + 0x3a, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x66, + 0x72, + 0x69, + 0x65, + 0x6e, + 0x64, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x6f, + 0x64, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "14694ab081258c7d8f3467f90c31653193237401"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -775,6 +1091,7 @@ public GetHeroQuery(global::StrawberryShake.IOperationExecutor o } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetHeroResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -888,9 +1205,7 @@ public partial class DroidEntity } public global::System.String Name { get; } - public global::System.Boolean IsFriendsListFulfilled { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriendsDeferred.State.FriendsConnectionData? Friends { get; } } @@ -906,9 +1221,7 @@ public partial class HumanEntity } public global::System.String Name { get; } - public global::System.Boolean IsFriendsListFulfilled { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriendsDeferred.State.FriendsConnectionData? Friends { get; } } @@ -927,6 +1240,7 @@ public GetHeroResultFactory(global::StrawberryShake.IEntityStore entityStore, gl } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriendsDeferred.IGetHeroResult); + public GetHeroResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -982,9 +1296,9 @@ public GetHeroResultInfo(global::StrawberryShake.EntityId? hero, global::System. } public global::StrawberryShake.EntityId? Hero { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetHeroResultInfo(Hero, _entityIds, version); @@ -1181,7 +1495,6 @@ public partial class FriendsConnectionData } public global::System.String __typename { get; } - ///A flattened list of the nodes. public global::System.Collections.Generic.IReadOnlyList? Nodes { get; } } @@ -1461,7 +1774,9 @@ public GetHero_Hero_Friends_Nodes_Human Map(global::StrawberryShake.CodeGenerati public partial class StarWarsGetFriendsDeferredClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsNoStoreTest.Client.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsNoStoreTest.Client.cs index 4ad72c48512..1827aa8f867 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsNoStoreTest.Client.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsNoStoreTest.Client.cs @@ -176,7 +176,6 @@ public GetHero_Hero_Droid(global::System.String name, global::StrawberryShake.Co } public global::System.String Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriendsNoStore.IGetHero_Hero_Friends? Friends { get; } public virtual global::System.Boolean Equals(GetHero_Hero_Droid? other) @@ -246,7 +245,6 @@ public GetHero_Hero_Human(global::System.String name, global::StrawberryShake.Co } public global::System.String Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriendsNoStore.IGetHero_Hero_Friends? Friends { get; } public virtual global::System.Boolean Equals(GetHero_Hero_Human? other) @@ -519,7 +517,6 @@ public partial interface IGetHeroResult public partial interface IGetHero_Hero { public global::System.String Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriendsNoStore.IGetHero_Hero_Friends? Friends { get; } } @@ -604,8 +601,121 @@ private GetHeroQueryDocument() public static GetHeroQueryDocument Instance { get; } = new GetHeroQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x48, 0x65, 0x72, 0x6f, 0x20, 0x7b, 0x20, 0x68, 0x65, 0x72, 0x6f, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x4e, 0x45, 0x57, 0x5f, 0x48, 0x4f, 0x50, 0x45, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x68, + 0x65, + 0x72, + 0x6f, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x4e, + 0x45, + 0x57, + 0x5f, + 0x48, + 0x4f, + 0x50, + 0x45, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x66, + 0x72, + 0x69, + 0x65, + 0x6e, + 0x64, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x6f, + 0x64, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "e420a6956d7dd75be7471b2fc1d22df41d4c6985"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -645,6 +755,7 @@ public GetHeroQuery(global::StrawberryShake.IOperationExecutor o } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetHeroResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -738,6 +849,7 @@ public GetHeroResultFactory() } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriendsNoStore.IGetHeroResult); + public GetHeroResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (dataInfo is GetHeroResultInfo info) @@ -848,9 +960,9 @@ public GetHeroResultInfo(global::StrawberryShake.CodeGeneration.CSharp.Integrati } public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriendsNoStore.State.ICharacterData? Hero { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => global::System.Array.Empty(); public global::System.UInt64 Version => 0; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetHeroResultInfo(Hero); @@ -985,9 +1097,7 @@ public partial class DroidData : ISearchResultData, ICharacterData } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriendsNoStore.State.FriendsConnectionData? Friends { get; } } @@ -1003,9 +1113,7 @@ public partial class HumanData : ISearchResultData, ICharacterData } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriendsNoStore.State.FriendsConnectionData? Friends { get; } } @@ -1021,7 +1129,6 @@ public partial class FriendsConnectionData } public global::System.String __typename { get; } - ///A flattened list of the nodes. public global::System.Collections.Generic.IReadOnlyList? Nodes { get; } } @@ -1033,6 +1140,7 @@ public partial class StarWarsGetFriendsNoStoreClientStoreAccessor : global::Stra public global::StrawberryShake.IOperationStore OperationStore => throw new global::System.NotSupportedException("OperationStore is not supported in store less mode"); public global::StrawberryShake.IEntityStore EntityStore => throw new global::System.NotSupportedException("EntityStore is not supported in store less mode"); public global::StrawberryShake.IEntityIdSerializer EntityIdSerializer => throw new global::System.NotSupportedException("EntityIdSerializer is not supported in store less mode"); + public global::StrawberryShake.IOperationRequestFactory GetOperationRequestFactory(global::System.Type resultType) { throw new global::System.NotSupportedException("GetOperationRequestFactory is not supported in store less mode"); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsTest.Client.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsTest.Client.cs index fc08c657afb..69738c9eb3d 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsTest.Client.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsTest.Client.cs @@ -183,7 +183,6 @@ public GetHero_Hero_Droid(global::System.String name, global::StrawberryShake.Co } public global::System.String Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.IGetHero_Hero_Friends? Friends { get; } public virtual global::System.Boolean Equals(GetHero_Hero_Droid? other) @@ -253,7 +252,6 @@ public GetHero_Hero_Human(global::System.String name, global::StrawberryShake.Co } public global::System.String Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.IGetHero_Hero_Friends? Friends { get; } public virtual global::System.Boolean Equals(GetHero_Hero_Human? other) @@ -526,7 +524,6 @@ public partial interface IGetHeroResult public partial interface IGetHero_Hero { public global::System.String Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.IGetHero_Hero_Friends? Friends { get; } } @@ -623,8 +620,201 @@ private GetHeroQueryDocument() public static GetHeroQueryDocument Instance { get; } = new GetHeroQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x48, 0x65, 0x72, 0x6f, 0x20, 0x7b, 0x20, 0x68, 0x65, 0x72, 0x6f, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x4e, 0x45, 0x57, 0x5f, 0x48, 0x4f, 0x50, 0x45, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x68, + 0x65, + 0x72, + 0x6f, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x4e, + 0x45, + 0x57, + 0x5f, + 0x48, + 0x4f, + 0x50, + 0x45, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x66, + 0x72, + 0x69, + 0x65, + 0x6e, + 0x64, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x6f, + 0x64, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "9f9a72871b1548dfdb9e75702e81c5c5945ff0c3"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -676,6 +866,7 @@ public GetHeroQuery(global::StrawberryShake.IOperationExecutor o } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetHeroResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -783,7 +974,6 @@ public partial class DroidEntity } public global::System.String Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.State.FriendsConnectionData? Friends { get; } } @@ -798,7 +988,6 @@ public partial class HumanEntity } public global::System.String Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.State.FriendsConnectionData? Friends { get; } } @@ -817,6 +1006,7 @@ public GetHeroResultFactory(global::StrawberryShake.IEntityStore entityStore, gl } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.IGetHeroResult); + public GetHeroResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -872,9 +1062,9 @@ public GetHeroResultInfo(global::StrawberryShake.EntityId? hero, global::System. } public global::StrawberryShake.EntityId? Hero { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetHeroResultInfo(Hero, _entityIds, version); @@ -1071,7 +1261,6 @@ public partial class FriendsConnectionData } public global::System.String __typename { get; } - ///A flattened list of the nodes. public global::System.Collections.Generic.IReadOnlyList? Nodes { get; } } @@ -1285,7 +1474,9 @@ public GetHero_Hero_Friends_Nodes_Human Map(global::StrawberryShake.CodeGenerati public partial class StarWarsGetFriendsClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroTest.Client.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroTest.Client.cs index 9e1f103d79b..ef0deaf393c 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroTest.Client.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroTest.Client.cs @@ -347,8 +347,112 @@ private GetHeroQueryDocument() public static GetHeroQueryDocument Instance { get; } = new GetHeroQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x48, 0x65, 0x72, 0x6f, 0x20, 0x7b, 0x20, 0x68, 0x65, 0x72, 0x6f, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x4e, 0x45, 0x57, 0x5f, 0x48, 0x4f, 0x50, 0x45, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x68, + 0x65, + 0x72, + 0x6f, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x4e, + 0x45, + 0x57, + 0x5f, + 0x48, + 0x4f, + 0x50, + 0x45, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "95ca68547e3a55b9ff81efe79b33a417b2f0690b"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -387,6 +491,7 @@ public GetHeroQuery(global::StrawberryShake.IOperationExecutor o } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetHeroResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -509,6 +614,7 @@ public GetHeroResultFactory(global::StrawberryShake.IEntityStore entityStore, gl } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHero.IGetHeroResult); + public GetHeroResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -564,9 +670,9 @@ public GetHeroResultInfo(global::StrawberryShake.EntityId? hero, global::System. } public global::StrawberryShake.EntityId? Hero { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetHeroResultInfo(Hero, _entityIds, version); @@ -711,7 +817,9 @@ public GetHero_Hero_Human Map(global::StrawberryShake.CodeGeneration.CSharp.Inte public partial class StarWarsGetHeroClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroTraitsTest.Client.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroTraitsTest.Client.cs index a37e546f4db..9480da62373 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroTraitsTest.Client.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroTraitsTest.Client.cs @@ -182,7 +182,6 @@ public GetHero_Hero_Droid(global::System.String name, global::System.Text.Json.J } public global::System.String Name { get; } - public global::System.Text.Json.JsonElement? Traits { get; } public virtual global::System.Boolean Equals(GetHero_Hero_Droid? other) @@ -252,7 +251,6 @@ public GetHero_Hero_Human(global::System.String name, global::System.Text.Json.J } public global::System.String Name { get; } - public global::System.Text.Json.JsonElement? Traits { get; } public virtual global::System.Boolean Equals(GetHero_Hero_Human? other) @@ -323,7 +321,6 @@ public partial interface IGetHeroResult public partial interface IGetHero_Hero { public global::System.String Name { get; } - public global::System.Text.Json.JsonElement? Traits { get; } } @@ -367,8 +364,119 @@ private GetHeroQueryDocument() public static GetHeroQueryDocument Instance { get; } = new GetHeroQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x48, 0x65, 0x72, 0x6f, 0x20, 0x7b, 0x20, 0x68, 0x65, 0x72, 0x6f, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x4e, 0x45, 0x57, 0x5f, 0x48, 0x4f, 0x50, 0x45, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x74, 0x72, 0x61, 0x69, 0x74, 0x73, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x68, + 0x65, + 0x72, + 0x6f, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x4e, + 0x45, + 0x57, + 0x5f, + 0x48, + 0x4f, + 0x50, + 0x45, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x74, + 0x72, + 0x61, + 0x69, + 0x74, + 0x73, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "87d584157181184f5ad4cb20798faf6281838dd3"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -408,6 +516,7 @@ public GetHeroQuery(global::StrawberryShake.IOperationExecutor o } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetHeroResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -503,7 +612,6 @@ public partial class DroidEntity } public global::System.String Name { get; } - public global::System.Text.Json.JsonElement? Traits { get; } } @@ -518,7 +626,6 @@ public partial class HumanEntity } public global::System.String Name { get; } - public global::System.Text.Json.JsonElement? Traits { get; } } @@ -537,6 +644,7 @@ public GetHeroResultFactory(global::StrawberryShake.IEntityStore entityStore, gl } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroTraits.IGetHeroResult); + public GetHeroResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -592,9 +700,9 @@ public GetHeroResultInfo(global::StrawberryShake.EntityId? hero, global::System. } public global::StrawberryShake.EntityId? Hero { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetHeroResultInfo(Hero, _entityIds, version); @@ -756,7 +864,9 @@ public GetHero_Hero_Human Map(global::StrawberryShake.CodeGeneration.CSharp.Inte public partial class StarWarsGetHeroTraitsClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsIntrospectionTest.Client.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsIntrospectionTest.Client.cs index 53052e60421..e078cbea5e2 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsIntrospectionTest.Client.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsIntrospectionTest.Client.cs @@ -188,22 +188,18 @@ public IntrospectionQuery___schema___Schema(global::StrawberryShake.CodeGenerati /// The type that query operations will be rooted at. /// public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_QueryType QueryType { get; } - /// /// If this server supports mutation, the type that mutation operations will be rooted at. /// public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_MutationType? MutationType { get; } - /// /// If this server support subscription, the type that subscription operations will be rooted at. /// public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_SubscriptionType? SubscriptionType { get; } - /// /// A list of all types supported by this server. /// public global::System.Collections.Generic.IReadOnlyList Types { get; } - /// /// A list of all directives supported by this server. /// @@ -515,19 +511,12 @@ public IntrospectionQuery___schema_Types___Type(global::StrawberryShake.CodeGene } public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::System.String? Description { get; } - public global::System.Collections.Generic.IReadOnlyList? Fields { get; } - public global::System.Collections.Generic.IReadOnlyList? InputFields { get; } - public global::System.Collections.Generic.IReadOnlyList? Interfaces { get; } - public global::System.Collections.Generic.IReadOnlyList? EnumValues { get; } - public global::System.Collections.Generic.IReadOnlyList? PossibleTypes { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types___Type? other) @@ -651,15 +640,10 @@ public IntrospectionQuery___schema_Directives___Directive(global::System.String } public global::System.String Name { get; } - public global::System.String? Description { get; } - public global::System.Collections.Generic.IReadOnlyList Args { get; } - public global::System.Boolean OnOperation { get; } - public global::System.Boolean OnFragment { get; } - public global::System.Boolean OnField { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Directives___Directive? other) @@ -744,15 +728,10 @@ public IntrospectionQuery___schema_Types_Fields___Field(global::System.String na } public global::System.String Name { get; } - public global::System.String? Description { get; } - public global::System.Collections.Generic.IReadOnlyList Args { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_Types_Fields_Type Type { get; } - public global::System.Boolean IsDeprecated { get; } - public global::System.String? DeprecationReason { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_Fields___Field? other) @@ -839,11 +818,8 @@ public IntrospectionQuery___schema_Types_InputFields___InputValue(global::System } public global::System.String Name { get; } - public global::System.String? Description { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_Types_InputFields_Type Type { get; } - /// /// A GraphQL-formatted string representing the default value for this input value. /// @@ -928,9 +904,7 @@ public IntrospectionQuery___schema_Types_Interfaces___Type(global::StrawberrySha } public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_Types_Interfaces_OfType? OfType { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_Interfaces___Type? other) @@ -1010,11 +984,8 @@ public IntrospectionQuery___schema_Types_EnumValues___EnumValue(global::System.S } public global::System.String Name { get; } - public global::System.String? Description { get; } - public global::System.Boolean IsDeprecated { get; } - public global::System.String? DeprecationReason { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_EnumValues___EnumValue? other) @@ -1096,9 +1067,7 @@ public IntrospectionQuery___schema_Types_PossibleTypes___Type(global::Strawberry } public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_Types_Interfaces_OfType? OfType { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_PossibleTypes___Type? other) @@ -1178,11 +1147,8 @@ public IntrospectionQuery___schema_Directives_Args___InputValue(global::System.S } public global::System.String Name { get; } - public global::System.String? Description { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_Types_InputFields_Type Type { get; } - /// /// A GraphQL-formatted string representing the default value for this input value. /// @@ -1266,11 +1232,8 @@ public IntrospectionQuery___schema_Types_Fields_Args___InputValue(global::System } public global::System.String Name { get; } - public global::System.String? Description { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_Types_InputFields_Type Type { get; } - /// /// A GraphQL-formatted string representing the default value for this input value. /// @@ -1355,9 +1318,7 @@ public IntrospectionQuery___schema_Types_Fields_Type___Type(global::StrawberrySh } public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_Types_Interfaces_OfType? OfType { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_Fields_Type___Type? other) @@ -1438,9 +1399,7 @@ public IntrospectionQuery___schema_Types_InputFields_Type___Type(global::Strawbe } public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_Types_Interfaces_OfType? OfType { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_InputFields_Type___Type? other) @@ -1521,9 +1480,7 @@ public IntrospectionQuery___schema_Types_Interfaces_OfType___Type(global::Strawb } public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_Types_Interfaces_OfType_OfType? OfType { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_Interfaces_OfType___Type? other) @@ -1604,9 +1561,7 @@ public IntrospectionQuery___schema_Types_PossibleTypes_OfType___Type(global::Str } public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_Types_Interfaces_OfType_OfType? OfType { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_PossibleTypes_OfType___Type? other) @@ -1687,9 +1642,7 @@ public IntrospectionQuery___schema_Directives_Args_Type___Type(global::Strawberr } public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_Types_Interfaces_OfType? OfType { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Directives_Args_Type___Type? other) @@ -1770,9 +1723,7 @@ public IntrospectionQuery___schema_Types_Fields_Args_Type___Type(global::Strawbe } public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_Types_Interfaces_OfType? OfType { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_Fields_Args_Type___Type? other) @@ -1853,9 +1804,7 @@ public IntrospectionQuery___schema_Types_Fields_Type_OfType___Type(global::Straw } public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_Types_Interfaces_OfType_OfType? OfType { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_Fields_Type_OfType___Type? other) @@ -1936,9 +1885,7 @@ public IntrospectionQuery___schema_Types_InputFields_Type_OfType___Type(global:: } public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_Types_Interfaces_OfType_OfType? OfType { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_InputFields_Type_OfType___Type? other) @@ -2019,9 +1966,7 @@ public IntrospectionQuery___schema_Types_Interfaces_OfType_OfType___Type(global: } public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_Types_Interfaces_OfType_OfType_OfType? OfType { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_Interfaces_OfType_OfType___Type? other) @@ -2101,7 +2046,6 @@ public IntrospectionQuery___schema_Types_Interfaces_OfType_OfType_OfType___Type( } public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.__TypeKind Kind { get; } - public global::System.String? Name { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_Interfaces_OfType_OfType_OfType___Type? other) @@ -2181,22 +2125,18 @@ public partial interface IIntrospectionQuery___schema /// The type that query operations will be rooted at. /// public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_QueryType QueryType { get; } - /// /// If this server supports mutation, the type that mutation operations will be rooted at. /// public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_MutationType? MutationType { get; } - /// /// If this server support subscription, the type that subscription operations will be rooted at. /// public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_SubscriptionType? SubscriptionType { get; } - /// /// A list of all types supported by this server. /// public global::System.Collections.Generic.IReadOnlyList Types { get; } - /// /// A list of all directives supported by this server. /// @@ -2291,19 +2231,12 @@ public partial interface IIntrospectionQuery___schema_SubscriptionType___Type : public partial interface IFullType { public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::System.String? Description { get; } - public global::System.Collections.Generic.IReadOnlyList? Fields { get; } - public global::System.Collections.Generic.IReadOnlyList? InputFields { get; } - public global::System.Collections.Generic.IReadOnlyList? Interfaces { get; } - public global::System.Collections.Generic.IReadOnlyList? EnumValues { get; } - public global::System.Collections.Generic.IReadOnlyList? PossibleTypes { get; } } @@ -2339,15 +2272,10 @@ public partial interface IIntrospectionQuery___schema_Types___Type : IIntrospect public partial interface IIntrospectionQuery___schema_Directives { public global::System.String Name { get; } - public global::System.String? Description { get; } - public global::System.Collections.Generic.IReadOnlyList Args { get; } - public global::System.Boolean OnOperation { get; } - public global::System.Boolean OnFragment { get; } - public global::System.Boolean OnField { get; } } @@ -2370,15 +2298,10 @@ public partial interface IIntrospectionQuery___schema_Directives___Directive : I public partial interface IIntrospectionQuery___schema_Types_Fields { public global::System.String Name { get; } - public global::System.String? Description { get; } - public global::System.Collections.Generic.IReadOnlyList Args { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_Types_Fields_Type Type { get; } - public global::System.Boolean IsDeprecated { get; } - public global::System.String? DeprecationReason { get; } } @@ -2399,11 +2322,8 @@ public partial interface IIntrospectionQuery___schema_Types_Fields___Field : IIn public partial interface IInputValue { public global::System.String Name { get; } - public global::System.String? Description { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_Types_InputFields_Type Type { get; } - /// /// A GraphQL-formatted string representing the default value for this input value. /// @@ -2438,9 +2358,7 @@ public partial interface IIntrospectionQuery___schema_Types_InputFields___InputV public partial interface ITypeRef { public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_Types_Interfaces_OfType? OfType { get; } } @@ -2474,11 +2392,8 @@ public partial interface IIntrospectionQuery___schema_Types_Interfaces___Type : public partial interface IIntrospectionQuery___schema_Types_EnumValues { public global::System.String Name { get; } - public global::System.String? Description { get; } - public global::System.Boolean IsDeprecated { get; } - public global::System.String? DeprecationReason { get; } } @@ -2603,9 +2518,7 @@ public partial interface IIntrospectionQuery___schema_Types_InputFields_Type___T public partial interface IIntrospectionQuery___schema_Types_Interfaces_OfType { public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_Types_Interfaces_OfType_OfType? OfType { get; } } @@ -2630,9 +2543,7 @@ public partial interface IIntrospectionQuery___schema_Types_Interfaces_OfType___ public partial interface IIntrospectionQuery___schema_Types_PossibleTypes_OfType { public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_Types_Interfaces_OfType_OfType? OfType { get; } } @@ -2701,9 +2612,7 @@ public partial interface IIntrospectionQuery___schema_Types_Fields_Args_Type___T public partial interface IIntrospectionQuery___schema_Types_Fields_Type_OfType { public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_Types_Interfaces_OfType_OfType? OfType { get; } } @@ -2728,9 +2637,7 @@ public partial interface IIntrospectionQuery___schema_Types_Fields_Type_OfType__ public partial interface IIntrospectionQuery___schema_Types_InputFields_Type_OfType { public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_Types_Interfaces_OfType_OfType? OfType { get; } } @@ -2755,9 +2662,7 @@ public partial interface IIntrospectionQuery___schema_Types_InputFields_Type_OfT public partial interface IIntrospectionQuery___schema_Types_Interfaces_OfType_OfType { public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQuery___schema_Types_Interfaces_OfType_OfType_OfType? OfType { get; } } @@ -2782,7 +2687,6 @@ public partial interface IIntrospectionQuery___schema_Types_Interfaces_OfType_Of public partial interface IIntrospectionQuery___schema_Types_Interfaces_OfType_OfType_OfType { public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.__TypeKind Kind { get; } - public global::System.String? Name { get; } } @@ -2843,6 +2747,7 @@ public enum __TypeKind public partial class __TypeKindSerializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "__TypeKind"; + public __TypeKind Parse(global::System.String serializedValue) { return serializedValue switch @@ -2992,8 +2897,981 @@ private IntrospectionQueryQueryDocument() public static IntrospectionQueryQueryDocument Instance { get; } = new IntrospectionQueryQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x49, 0x6e, 0x74, 0x72, 0x6f, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x71, 0x75, 0x65, 0x72, 0x79, 0x54, 0x79, 0x70, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7d, 0x20, 0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7d, 0x20, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7d, 0x20, 0x74, 0x79, 0x70, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x46, 0x75, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x20, 0x7d, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x72, 0x67, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x7d, 0x20, 0x6f, 0x6e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x6e, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x6e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x46, 0x75, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x5f, 0x5f, 0x54, 0x79, 0x70, 0x65, 0x20, 0x7b, 0x20, 0x6b, 0x69, 0x6e, 0x64, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x28, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x44, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x3a, 0x20, 0x74, 0x72, 0x75, 0x65, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x72, 0x67, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x7d, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x66, 0x20, 0x7d, 0x20, 0x69, 0x73, 0x44, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x20, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x20, 0x7d, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x7d, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x66, 0x20, 0x7d, 0x20, 0x65, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x28, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x44, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x3a, 0x20, 0x74, 0x72, 0x75, 0x65, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x44, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x20, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x20, 0x7d, 0x20, 0x70, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x66, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x5f, 0x5f, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x66, 0x20, 0x7d, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x66, 0x20, 0x6f, 0x6e, 0x20, 0x5f, 0x5f, 0x54, 0x79, 0x70, 0x65, 0x20, 0x7b, 0x20, 0x6b, 0x69, 0x6e, 0x64, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6f, 0x66, 0x54, 0x79, 0x70, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6b, 0x69, 0x6e, 0x64, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6f, 0x66, 0x54, 0x79, 0x70, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6b, 0x69, 0x6e, 0x64, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6f, 0x66, 0x54, 0x79, 0x70, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6b, 0x69, 0x6e, 0x64, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x49, + 0x6e, + 0x74, + 0x72, + 0x6f, + 0x73, + 0x70, + 0x65, + 0x63, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x51, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x73, + 0x63, + 0x68, + 0x65, + 0x6d, + 0x61, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x54, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x6d, + 0x75, + 0x74, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x54, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x73, + 0x75, + 0x62, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x54, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x74, + 0x79, + 0x70, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x46, + 0x75, + 0x6c, + 0x6c, + 0x54, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x64, + 0x69, + 0x72, + 0x65, + 0x63, + 0x74, + 0x69, + 0x76, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x64, + 0x65, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x61, + 0x72, + 0x67, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x56, + 0x61, + 0x6c, + 0x75, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x6f, + 0x6e, + 0x4f, + 0x70, + 0x65, + 0x72, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x6f, + 0x6e, + 0x46, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x6f, + 0x6e, + 0x46, + 0x69, + 0x65, + 0x6c, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x46, + 0x75, + 0x6c, + 0x6c, + 0x54, + 0x79, + 0x70, + 0x65, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x5f, + 0x5f, + 0x54, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x6b, + 0x69, + 0x6e, + 0x64, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x64, + 0x65, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x66, + 0x69, + 0x65, + 0x6c, + 0x64, + 0x73, + 0x28, + 0x69, + 0x6e, + 0x63, + 0x6c, + 0x75, + 0x64, + 0x65, + 0x44, + 0x65, + 0x70, + 0x72, + 0x65, + 0x63, + 0x61, + 0x74, + 0x65, + 0x64, + 0x3a, + 0x20, + 0x74, + 0x72, + 0x75, + 0x65, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x64, + 0x65, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x61, + 0x72, + 0x67, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x56, + 0x61, + 0x6c, + 0x75, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x74, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x54, + 0x79, + 0x70, + 0x65, + 0x52, + 0x65, + 0x66, + 0x20, + 0x7d, + 0x20, + 0x69, + 0x73, + 0x44, + 0x65, + 0x70, + 0x72, + 0x65, + 0x63, + 0x61, + 0x74, + 0x65, + 0x64, + 0x20, + 0x64, + 0x65, + 0x70, + 0x72, + 0x65, + 0x63, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x52, + 0x65, + 0x61, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7d, + 0x20, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x46, + 0x69, + 0x65, + 0x6c, + 0x64, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x56, + 0x61, + 0x6c, + 0x75, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x69, + 0x6e, + 0x74, + 0x65, + 0x72, + 0x66, + 0x61, + 0x63, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x54, + 0x79, + 0x70, + 0x65, + 0x52, + 0x65, + 0x66, + 0x20, + 0x7d, + 0x20, + 0x65, + 0x6e, + 0x75, + 0x6d, + 0x56, + 0x61, + 0x6c, + 0x75, + 0x65, + 0x73, + 0x28, + 0x69, + 0x6e, + 0x63, + 0x6c, + 0x75, + 0x64, + 0x65, + 0x44, + 0x65, + 0x70, + 0x72, + 0x65, + 0x63, + 0x61, + 0x74, + 0x65, + 0x64, + 0x3a, + 0x20, + 0x74, + 0x72, + 0x75, + 0x65, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x64, + 0x65, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x69, + 0x73, + 0x44, + 0x65, + 0x70, + 0x72, + 0x65, + 0x63, + 0x61, + 0x74, + 0x65, + 0x64, + 0x20, + 0x64, + 0x65, + 0x70, + 0x72, + 0x65, + 0x63, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x52, + 0x65, + 0x61, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7d, + 0x20, + 0x70, + 0x6f, + 0x73, + 0x73, + 0x69, + 0x62, + 0x6c, + 0x65, + 0x54, + 0x79, + 0x70, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x54, + 0x79, + 0x70, + 0x65, + 0x52, + 0x65, + 0x66, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x56, + 0x61, + 0x6c, + 0x75, + 0x65, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x5f, + 0x5f, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x56, + 0x61, + 0x6c, + 0x75, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x64, + 0x65, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x74, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x54, + 0x79, + 0x70, + 0x65, + 0x52, + 0x65, + 0x66, + 0x20, + 0x7d, + 0x20, + 0x64, + 0x65, + 0x66, + 0x61, + 0x75, + 0x6c, + 0x74, + 0x56, + 0x61, + 0x6c, + 0x75, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x54, + 0x79, + 0x70, + 0x65, + 0x52, + 0x65, + 0x66, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x5f, + 0x5f, + 0x54, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x6b, + 0x69, + 0x6e, + 0x64, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6f, + 0x66, + 0x54, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6b, + 0x69, + 0x6e, + 0x64, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6f, + 0x66, + 0x54, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6b, + 0x69, + 0x6e, + 0x64, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6f, + 0x66, + 0x54, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6b, + 0x69, + 0x6e, + 0x64, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "5902e9f9fd92bc2e9785f0821db6ff925e660fde"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -3123,6 +4001,7 @@ public IntrospectionQueryQuery(global::StrawberryShake.IOperationExecutor typeof(IIntrospectionQueryResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -3308,6 +4187,7 @@ public IntrospectionQueryResultFactory(global::StrawberryShake.IEntityStore enti } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.IIntrospectionQueryResult); + public IntrospectionQueryResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -3785,9 +4665,9 @@ public IntrospectionQueryResultInfo(global::StrawberryShake.CodeGeneration.CShar /// Access the current type schema of this server. /// public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.State.__SchemaData __schema { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new IntrospectionQueryResultInfo(__schema, _entityIds, version); @@ -4470,19 +5350,14 @@ public partial class __SchemaData } public global::System.String __typename { get; } - ///The type that query operations will be rooted at. public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.State.__TypeData? QueryType { get; } - ///If this server supports mutation, the type that mutation operations will be rooted at. public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.State.__TypeData? MutationType { get; } - ///If this server support subscription, the type that subscription operations will be rooted at. public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.State.__TypeData? SubscriptionType { get; } - ///A list of all types supported by this server. public global::System.Collections.Generic.IReadOnlyList? Types { get; } - ///A list of all directives supported by this server. public global::System.Collections.Generic.IReadOnlyList? Directives { get; } } @@ -4507,23 +5382,14 @@ public partial class __TypeData } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.__TypeKind? Kind { get; } - public global::System.String? Description { get; } - public global::System.Collections.Generic.IReadOnlyList? Fields { get; } - public global::System.Collections.Generic.IReadOnlyList? InputFields { get; } - public global::System.Collections.Generic.IReadOnlyList? Interfaces { get; } - public global::System.Collections.Generic.IReadOnlyList? EnumValues { get; } - public global::System.Collections.Generic.IReadOnlyList? PossibleTypes { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.State.__TypeData? OfType { get; } } @@ -4544,17 +5410,11 @@ public partial class __DirectiveData } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::System.String? Description { get; } - public global::System.Collections.Generic.IReadOnlyList? Args { get; } - public global::System.Boolean? OnOperation { get; } - public global::System.Boolean? OnFragment { get; } - public global::System.Boolean? OnField { get; } } @@ -4575,17 +5435,11 @@ public partial class __FieldData } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::System.String? Description { get; } - public global::System.Collections.Generic.IReadOnlyList? Args { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.State.__TypeData? Type { get; } - public global::System.Boolean? IsDeprecated { get; } - public global::System.String? DeprecationReason { get; } } @@ -4604,13 +5458,9 @@ public partial class __InputValueData } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::System.String? Description { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsIntrospection.State.__TypeData? Type { get; } - ///A GraphQL-formatted string representing the default value for this input value. public global::System.String? DefaultValue { get; } } @@ -4630,13 +5480,9 @@ public partial class __EnumValueData } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::System.String? Description { get; } - public global::System.Boolean? IsDeprecated { get; } - public global::System.String? DeprecationReason { get; } } @@ -4645,7 +5491,9 @@ public partial class __EnumValueData public partial class StarWarsIntrospectionClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsOnReviewSubCompletionTest.Client.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsOnReviewSubCompletionTest.Client.cs index 9c35c5ea9b1..0e10f9080dd 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsOnReviewSubCompletionTest.Client.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsOnReviewSubCompletionTest.Client.cs @@ -227,9 +227,7 @@ public OnReviewSub_OnReview_Review(global::System.String __typename, global::Sys /// The name of the current Object type at runtime. /// public global::System.String __typename { get; } - public global::System.Int32 Stars { get; } - public global::System.String? Commentary { get; } public virtual global::System.Boolean Equals(OnReviewSub_OnReview_Review? other) @@ -304,9 +302,7 @@ public partial interface IOnReviewSub_OnReview /// The name of the current Object type at runtime. /// public global::System.String __typename { get; } - public global::System.Int32 Stars { get; } - public global::System.String? Commentary { get; } } @@ -338,8 +334,99 @@ private OnReviewSubSubscriptionDocument() public static OnReviewSubSubscriptionDocument Instance { get; } = new OnReviewSubSubscriptionDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Subscription; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4f, 0x6e, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x53, 0x75, 0x62, 0x20, 0x7b, 0x20, 0x6f, 0x6e, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x4e, 0x45, 0x57, 0x5f, 0x48, 0x4f, 0x50, 0x45, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x74, 0x61, 0x72, 0x73, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x73, + 0x75, + 0x62, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x4f, + 0x6e, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x53, + 0x75, + 0x62, + 0x20, + 0x7b, + 0x20, + 0x6f, + 0x6e, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x4e, + 0x45, + 0x57, + 0x5f, + 0x48, + 0x4f, + 0x50, + 0x45, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x73, + 0x74, + 0x61, + 0x72, + 0x73, + 0x20, + 0x63, + 0x6f, + 0x6d, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x61, + 0x72, + 0x79, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "92220fce37342d7ade3d63a2a81342eb1fb14bac"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -373,6 +460,7 @@ public OnReviewSubSubscription(global::StrawberryShake.IOperationExecutor typeof(IOnReviewSubResult); + public global::System.IObservable> Watch(global::StrawberryShake.ExecutionStrategy? strategy = null) { var request = CreateRequest(); @@ -463,6 +551,7 @@ public OnReviewSubResultFactory(global::StrawberryShake.IEntityStore entityStore } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsOnReviewSubCompletion.IOnReviewSubResult); + public OnReviewSubResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -513,9 +602,9 @@ public OnReviewSubResultInfo(global::StrawberryShake.CodeGeneration.CSharp.Integ } public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsOnReviewSubCompletion.State.ReviewData OnReview { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new OnReviewSubResultInfo(OnReview, _entityIds, version); @@ -631,9 +720,7 @@ public partial class ReviewData } public global::System.String __typename { get; } - public global::System.Int32? Stars { get; } - public global::System.String? Commentary { get; } } @@ -642,7 +729,9 @@ public partial class ReviewData public partial class StarWarsOnReviewSubCompletionClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsOnReviewSubGraphQLSSETest.Client.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsOnReviewSubGraphQLSSETest.Client.cs index a06f7b0e20d..d9e27d509c9 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsOnReviewSubGraphQLSSETest.Client.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsOnReviewSubGraphQLSSETest.Client.cs @@ -179,9 +179,7 @@ public OnReviewSub_OnReview_Review(global::System.String __typename, global::Sys /// The name of the current Object type at runtime. /// public global::System.String __typename { get; } - public global::System.Int32 Stars { get; } - public global::System.String? Commentary { get; } public virtual global::System.Boolean Equals(OnReviewSub_OnReview_Review? other) @@ -256,9 +254,7 @@ public partial interface IOnReviewSub_OnReview /// The name of the current Object type at runtime. /// public global::System.String __typename { get; } - public global::System.Int32 Stars { get; } - public global::System.String? Commentary { get; } } @@ -290,8 +286,99 @@ private OnReviewSubSubscriptionDocument() public static OnReviewSubSubscriptionDocument Instance { get; } = new OnReviewSubSubscriptionDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Subscription; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4f, 0x6e, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x53, 0x75, 0x62, 0x20, 0x7b, 0x20, 0x6f, 0x6e, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x4e, 0x45, 0x57, 0x5f, 0x48, 0x4f, 0x50, 0x45, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x74, 0x61, 0x72, 0x73, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x73, + 0x75, + 0x62, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x4f, + 0x6e, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x53, + 0x75, + 0x62, + 0x20, + 0x7b, + 0x20, + 0x6f, + 0x6e, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x4e, + 0x45, + 0x57, + 0x5f, + 0x48, + 0x4f, + 0x50, + 0x45, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x73, + 0x74, + 0x61, + 0x72, + 0x73, + 0x20, + 0x63, + 0x6f, + 0x6d, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x61, + 0x72, + 0x79, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "92220fce37342d7ade3d63a2a81342eb1fb14bac"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -325,6 +412,7 @@ public OnReviewSubSubscription(global::StrawberryShake.IOperationExecutor typeof(IOnReviewSubResult); + public global::System.IObservable> Watch(global::StrawberryShake.ExecutionStrategy? strategy = null) { var request = CreateRequest(); @@ -407,6 +495,7 @@ public OnReviewSubResultFactory(global::StrawberryShake.IEntityStore entityStore } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsOnReviewSubGraphQLSSE.IOnReviewSubResult); + public OnReviewSubResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -457,9 +546,9 @@ public OnReviewSubResultInfo(global::StrawberryShake.CodeGeneration.CSharp.Integ } public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsOnReviewSubGraphQLSSE.State.ReviewData OnReview { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new OnReviewSubResultInfo(OnReview, _entityIds, version); @@ -575,9 +664,7 @@ public partial class ReviewData } public global::System.String __typename { get; } - public global::System.Int32? Stars { get; } - public global::System.String? Commentary { get; } } @@ -586,7 +673,9 @@ public partial class ReviewData public partial class StarWarsOnReviewSubGraphQLSSEClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsOnReviewSubNoStoreTest.Client.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsOnReviewSubNoStoreTest.Client.cs index e4ee0b1b7a7..f33e9ec0610 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsOnReviewSubNoStoreTest.Client.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsOnReviewSubNoStoreTest.Client.cs @@ -176,9 +176,7 @@ public OnReviewSub_OnReview_Review(global::System.String __typename, global::Sys /// The name of the current Object type at runtime. /// public global::System.String __typename { get; } - public global::System.Int32 Stars { get; } - public global::System.String? Commentary { get; } public virtual global::System.Boolean Equals(OnReviewSub_OnReview_Review? other) @@ -253,9 +251,7 @@ public partial interface IOnReviewSub_OnReview /// The name of the current Object type at runtime. /// public global::System.String __typename { get; } - public global::System.Int32 Stars { get; } - public global::System.String? Commentary { get; } } @@ -287,8 +283,99 @@ private OnReviewSubSubscriptionDocument() public static OnReviewSubSubscriptionDocument Instance { get; } = new OnReviewSubSubscriptionDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Subscription; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4f, 0x6e, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x53, 0x75, 0x62, 0x20, 0x7b, 0x20, 0x6f, 0x6e, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x4e, 0x45, 0x57, 0x5f, 0x48, 0x4f, 0x50, 0x45, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x74, 0x61, 0x72, 0x73, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x73, + 0x75, + 0x62, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x4f, + 0x6e, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x53, + 0x75, + 0x62, + 0x20, + 0x7b, + 0x20, + 0x6f, + 0x6e, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x4e, + 0x45, + 0x57, + 0x5f, + 0x48, + 0x4f, + 0x50, + 0x45, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x73, + 0x74, + 0x61, + 0x72, + 0x73, + 0x20, + 0x63, + 0x6f, + 0x6d, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x61, + 0x72, + 0x79, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "92220fce37342d7ade3d63a2a81342eb1fb14bac"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -322,6 +409,7 @@ public OnReviewSubSubscription(global::StrawberryShake.IOperationExecutor typeof(IOnReviewSubResult); + public global::System.IObservable> Watch(global::StrawberryShake.ExecutionStrategy? strategy = null) { var request = CreateRequest(); @@ -402,6 +490,7 @@ public OnReviewSubResultFactory() } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsOnReviewSubNoStore.IOnReviewSubResult); + public OnReviewSubResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (dataInfo is OnReviewSubResultInfo info) @@ -443,9 +532,9 @@ public OnReviewSubResultInfo(global::StrawberryShake.CodeGeneration.CSharp.Integ } public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsOnReviewSubNoStore.State.ReviewData OnReview { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => global::System.Array.Empty(); public global::System.UInt64 Version => 0; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new OnReviewSubResultInfo(OnReview); @@ -551,9 +640,7 @@ public partial class ReviewData } public global::System.String __typename { get; } - public global::System.Int32? Stars { get; } - public global::System.String? Commentary { get; } } @@ -564,6 +651,7 @@ public partial class StarWarsOnReviewSubNoStoreClientStoreAccessor : global::Str public global::StrawberryShake.IOperationStore OperationStore => throw new global::System.NotSupportedException("OperationStore is not supported in store less mode"); public global::StrawberryShake.IEntityStore EntityStore => throw new global::System.NotSupportedException("EntityStore is not supported in store less mode"); public global::StrawberryShake.IEntityIdSerializer EntityIdSerializer => throw new global::System.NotSupportedException("EntityIdSerializer is not supported in store less mode"); + public global::StrawberryShake.IOperationRequestFactory GetOperationRequestFactory(global::System.Type resultType) { throw new global::System.NotSupportedException("GetOperationRequestFactory is not supported in store less mode"); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsTypeNameOnInterfacesTest.Client.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsTypeNameOnInterfacesTest.Client.cs index 34198774cd3..b39670c3508 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsTypeNameOnInterfacesTest.Client.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsTypeNameOnInterfacesTest.Client.cs @@ -352,8 +352,107 @@ private GetHeroQueryDocument() public static GetHeroQueryDocument Instance { get; } = new GetHeroQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x48, 0x65, 0x72, 0x6f, 0x20, 0x7b, 0x20, 0x68, 0x65, 0x72, 0x6f, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x4e, 0x45, 0x57, 0x5f, 0x48, 0x4f, 0x50, 0x45, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x68, + 0x65, + 0x72, + 0x6f, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x4e, + 0x45, + 0x57, + 0x5f, + 0x48, + 0x4f, + 0x50, + 0x45, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "f8ce4e3a52ec254e48424fe573eb27a039aece1a"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -391,6 +490,7 @@ public GetHeroQuery(global::StrawberryShake.IOperationExecutor o } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetHeroResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -514,6 +614,7 @@ public GetHeroResultFactory(global::StrawberryShake.IEntityStore entityStore, gl } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsTypeNameOnInterfaces.IGetHeroResult); + public GetHeroResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -569,9 +670,9 @@ public GetHeroResultInfo(global::StrawberryShake.EntityId? hero, global::System. } public global::StrawberryShake.EntityId? Hero { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetHeroResultInfo(Hero, _entityIds, version); @@ -716,7 +817,9 @@ public GetHero_Hero_Human Map(global::StrawberryShake.CodeGeneration.CSharp.Inte public partial class StarWarsTypeNameOnInterfacesClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsTypeNameOnUnionsTest.Client.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsTypeNameOnUnionsTest.Client.cs index 990e8f76394..e5b8a86b6d5 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsTypeNameOnUnionsTest.Client.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsTypeNameOnUnionsTest.Client.cs @@ -433,8 +433,127 @@ private SearchHeroQueryDocument() public static SearchHeroQueryDocument Instance { get; } = new SearchHeroQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x48, 0x65, 0x72, 0x6f, 0x20, 0x7b, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x28, 0x74, 0x65, 0x78, 0x74, 0x3a, 0x20, 0x22, 0x6c, 0x22, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x53, 0x74, 0x61, 0x72, 0x73, 0x68, 0x69, 0x70, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x53, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x28, + 0x74, + 0x65, + 0x78, + 0x74, + 0x3a, + 0x20, + 0x22, + 0x6c, + 0x22, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x53, + 0x74, + 0x61, + 0x72, + 0x73, + 0x68, + 0x69, + 0x70, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "e2347e3fc516d7742122125fa68a1aca286f128c"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -475,6 +594,7 @@ public SearchHeroQuery(global::StrawberryShake.IOperationExecutor typeof(ISearchHeroResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -616,6 +736,7 @@ public SearchHeroResultFactory(global::StrawberryShake.IEntityStore entityStore, } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsTypeNameOnUnions.ISearchHeroResult); + public SearchHeroResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -692,9 +813,9 @@ public SearchHeroResultInfo(global::System.Collections.Generic.IReadOnlyList? Search { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new SearchHeroResultInfo(Search, _entityIds, version); @@ -895,7 +1016,9 @@ public SearchHero_Search_Droid Map(global::StrawberryShake.CodeGeneration.CSharp public partial class StarWarsTypeNameOnUnionsClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsUnionListTest.Client.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsUnionListTest.Client.cs index 3758ad08d25..6b7748ac6ad 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsUnionListTest.Client.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsUnionListTest.Client.cs @@ -248,7 +248,6 @@ public SearchHero_Search_Human(global::System.String name, global::StrawberrySha } public global::System.String Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsUnionList.ISearchHero_Search_Friends? Friends { get; } public virtual global::System.Boolean Equals(SearchHero_Search_Human? other) @@ -595,7 +594,6 @@ public partial interface ISearchHero_Search_Starship : ISearchHero_Search public partial interface ISearchHero_Search_Human : ISearchHero_Search { public global::System.String Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsUnionList.ISearchHero_Search_Friends? Friends { get; } } @@ -695,8 +693,260 @@ private SearchHeroQueryDocument() public static SearchHeroQueryDocument Instance { get; } = new SearchHeroQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x48, 0x65, 0x72, 0x6f, 0x20, 0x7b, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x28, 0x74, 0x65, 0x78, 0x74, 0x3a, 0x20, 0x22, 0x6c, 0x22, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x53, 0x74, 0x61, 0x72, 0x73, 0x68, 0x69, 0x70, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x53, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x28, + 0x74, + 0x65, + 0x78, + 0x74, + 0x3a, + 0x20, + 0x22, + 0x6c, + 0x22, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x66, + 0x72, + 0x69, + 0x65, + 0x6e, + 0x64, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x6f, + 0x64, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x53, + 0x74, + 0x61, + 0x72, + 0x73, + 0x68, + 0x69, + 0x70, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "ab273d4e301554c50a6325fad57fa04d3744ef1a"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -756,6 +1006,7 @@ public SearchHeroQuery(global::StrawberryShake.IOperationExecutor typeof(ISearchHeroResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -877,7 +1128,6 @@ public partial class HumanEntity } public global::System.String Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsUnionList.State.FriendsConnectionData? Friends { get; } } @@ -910,6 +1160,7 @@ public SearchHeroResultFactory(global::StrawberryShake.IEntityStore entityStore, } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsUnionList.ISearchHeroResult); + public SearchHeroResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -986,9 +1237,9 @@ public SearchHeroResultInfo(global::System.Collections.Generic.IReadOnlyList? Search { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new SearchHeroResultInfo(Search, _entityIds, version); @@ -1220,7 +1471,6 @@ public partial class FriendsConnectionData } public global::System.String __typename { get; } - ///A flattened list of the nodes. public global::System.Collections.Generic.IReadOnlyList? Nodes { get; } } @@ -1395,7 +1645,9 @@ public SearchHero_Search_Friends_Nodes_Human Map(global::StrawberryShake.CodeGen public partial class StarWarsUnionListClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/UploadScalarTest.Client.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/UploadScalarTest.Client.cs index 86f5abb35f5..7ac3a279588 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/UploadScalarTest.Client.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/UploadScalarTest.Client.cs @@ -185,6 +185,7 @@ public partial class TestInputInputValueFormatter : global::StrawberryShake.Seri { private global::StrawberryShake.Serialization.IInputValueFormatter _barInputFormatter = default !; public global::System.String TypeName => "TestInput"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _barInputFormatter = serializerResolver.GetInputValueFormatter("BarInput"); @@ -305,6 +306,7 @@ public partial class BarInputInputValueFormatter : global::StrawberryShake.Seria { private global::StrawberryShake.Serialization.IInputValueFormatter _bazInputFormatter = default !; public global::System.String TypeName => "BarInput"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _bazInputFormatter = serializerResolver.GetInputValueFormatter("BazInput"); @@ -425,6 +427,7 @@ public partial class BazInputInputValueFormatter : global::StrawberryShake.Seria { private global::StrawberryShake.Serialization.IInputValueFormatter _uploadFormatter = default !; public global::System.String TypeName => "BazInput"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _uploadFormatter = serializerResolver.GetInputValueFormatter("Upload"); @@ -557,8 +560,330 @@ private TestUploadQueryDocument() public static TestUploadQueryDocument Instance { get; } = new TestUploadQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x54, 0x65, 0x73, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x24, 0x6e, 0x6f, 0x6e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x24, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x3a, 0x20, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x2c, 0x20, 0x24, 0x6c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x5b, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5d, 0x2c, 0x20, 0x24, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x3a, 0x20, 0x5b, 0x5b, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5d, 0x5d, 0x2c, 0x20, 0x24, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3a, 0x20, 0x54, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x2c, 0x20, 0x24, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x5b, 0x54, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5d, 0x2c, 0x20, 0x24, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x3a, 0x20, 0x5b, 0x5b, 0x54, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5d, 0x5d, 0x29, 0x20, 0x7b, 0x20, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x6e, 0x6f, 0x6e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x20, 0x24, 0x6e, 0x6f, 0x6e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x2c, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x3a, 0x20, 0x24, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x24, 0x6c, 0x69, 0x73, 0x74, 0x2c, 0x20, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x3a, 0x20, 0x24, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3a, 0x20, 0x24, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x24, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x3a, 0x20, 0x24, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x29, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x54, + 0x65, + 0x73, + 0x74, + 0x55, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x28, + 0x24, + 0x6e, + 0x6f, + 0x6e, + 0x55, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x3a, + 0x20, + 0x53, + 0x74, + 0x72, + 0x69, + 0x6e, + 0x67, + 0x2c, + 0x20, + 0x24, + 0x73, + 0x69, + 0x6e, + 0x67, + 0x6c, + 0x65, + 0x3a, + 0x20, + 0x55, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x2c, + 0x20, + 0x24, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x5b, + 0x55, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x5d, + 0x2c, + 0x20, + 0x24, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x3a, + 0x20, + 0x5b, + 0x5b, + 0x55, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x5d, + 0x5d, + 0x2c, + 0x20, + 0x24, + 0x6f, + 0x62, + 0x6a, + 0x65, + 0x63, + 0x74, + 0x3a, + 0x20, + 0x54, + 0x65, + 0x73, + 0x74, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x2c, + 0x20, + 0x24, + 0x6f, + 0x62, + 0x6a, + 0x65, + 0x63, + 0x74, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x5b, + 0x54, + 0x65, + 0x73, + 0x74, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x5d, + 0x2c, + 0x20, + 0x24, + 0x6f, + 0x62, + 0x6a, + 0x65, + 0x63, + 0x74, + 0x4e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x3a, + 0x20, + 0x5b, + 0x5b, + 0x54, + 0x65, + 0x73, + 0x74, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x5d, + 0x5d, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x75, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x28, + 0x6e, + 0x6f, + 0x6e, + 0x55, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x3a, + 0x20, + 0x24, + 0x6e, + 0x6f, + 0x6e, + 0x55, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x2c, + 0x20, + 0x73, + 0x69, + 0x6e, + 0x67, + 0x6c, + 0x65, + 0x3a, + 0x20, + 0x24, + 0x73, + 0x69, + 0x6e, + 0x67, + 0x6c, + 0x65, + 0x2c, + 0x20, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x2c, + 0x20, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x3a, + 0x20, + 0x24, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x2c, + 0x20, + 0x6f, + 0x62, + 0x6a, + 0x65, + 0x63, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x6f, + 0x62, + 0x6a, + 0x65, + 0x63, + 0x74, + 0x2c, + 0x20, + 0x6f, + 0x62, + 0x6a, + 0x65, + 0x63, + 0x74, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x6f, + 0x62, + 0x6a, + 0x65, + 0x63, + 0x74, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x2c, + 0x20, + 0x6f, + 0x62, + 0x6a, + 0x65, + 0x63, + 0x74, + 0x4e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x3a, + 0x20, + 0x24, + 0x6f, + 0x62, + 0x6a, + 0x65, + 0x63, + 0x74, + 0x4e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x29, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "7cd8f1db7bb9937a9b48c70ce9dc152f52782f96"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -594,6 +919,7 @@ public TestUploadQuery(global::StrawberryShake.IOperationExecutor typeof(ITestUploadResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.String? nonUpload, global::StrawberryShake.Upload? single, global::System.Collections.Generic.IReadOnlyList? list, global::System.Collections.Generic.IReadOnlyList?>? nested, global::StrawberryShake.CodeGeneration.CSharp.Integration.UploadScalar.TestInput? @object, global::System.Collections.Generic.IReadOnlyList? objectList, global::System.Collections.Generic.IReadOnlyList?>? objectNested, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(nonUpload, single, list, nested, @object, objectList, objectNested); @@ -970,6 +1296,7 @@ public TestUploadResultFactory(global::StrawberryShake.IEntityStore entityStore) } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::StrawberryShake.CodeGeneration.CSharp.Integration.UploadScalar.ITestUploadResult); + public TestUploadResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -1005,9 +1332,9 @@ public TestUploadResultInfo(global::System.String? upload, global::System.Collec } public global::System.String? Upload { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new TestUploadResultInfo(Upload, _entityIds, version); @@ -1086,7 +1413,9 @@ public TestUploadBuilder(global::StrawberryShake.IEntityStore entityStore, globa public partial class UploadScalarClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/UploadScalar_InMemoryTest.Client.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/UploadScalar_InMemoryTest.Client.cs index d3dd1553795..80810cc8771 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/UploadScalar_InMemoryTest.Client.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/UploadScalar_InMemoryTest.Client.cs @@ -185,6 +185,7 @@ public partial class TestInputInputValueFormatter : global::StrawberryShake.Seri { private global::StrawberryShake.Serialization.IInputValueFormatter _barInputFormatter = default !; public global::System.String TypeName => "TestInput"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _barInputFormatter = serializerResolver.GetInputValueFormatter("BarInput"); @@ -305,6 +306,7 @@ public partial class BarInputInputValueFormatter : global::StrawberryShake.Seria { private global::StrawberryShake.Serialization.IInputValueFormatter _bazInputFormatter = default !; public global::System.String TypeName => "BarInput"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _bazInputFormatter = serializerResolver.GetInputValueFormatter("BazInput"); @@ -425,6 +427,7 @@ public partial class BazInputInputValueFormatter : global::StrawberryShake.Seria { private global::StrawberryShake.Serialization.IInputValueFormatter _uploadFormatter = default !; public global::System.String TypeName => "BazInput"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _uploadFormatter = serializerResolver.GetInputValueFormatter("Upload"); @@ -557,8 +560,330 @@ private TestUploadQueryDocument() public static TestUploadQueryDocument Instance { get; } = new TestUploadQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x54, 0x65, 0x73, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x24, 0x6e, 0x6f, 0x6e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x24, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x3a, 0x20, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x2c, 0x20, 0x24, 0x6c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x5b, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5d, 0x2c, 0x20, 0x24, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x3a, 0x20, 0x5b, 0x5b, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5d, 0x5d, 0x2c, 0x20, 0x24, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3a, 0x20, 0x54, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x2c, 0x20, 0x24, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x5b, 0x54, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5d, 0x2c, 0x20, 0x24, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x3a, 0x20, 0x5b, 0x5b, 0x54, 0x65, 0x73, 0x74, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x5d, 0x5d, 0x29, 0x20, 0x7b, 0x20, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x28, 0x6e, 0x6f, 0x6e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x20, 0x24, 0x6e, 0x6f, 0x6e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x2c, 0x20, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x3a, 0x20, 0x24, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x24, 0x6c, 0x69, 0x73, 0x74, 0x2c, 0x20, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x3a, 0x20, 0x24, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x3a, 0x20, 0x24, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x24, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x2c, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x3a, 0x20, 0x24, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x29, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x54, + 0x65, + 0x73, + 0x74, + 0x55, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x28, + 0x24, + 0x6e, + 0x6f, + 0x6e, + 0x55, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x3a, + 0x20, + 0x53, + 0x74, + 0x72, + 0x69, + 0x6e, + 0x67, + 0x2c, + 0x20, + 0x24, + 0x73, + 0x69, + 0x6e, + 0x67, + 0x6c, + 0x65, + 0x3a, + 0x20, + 0x55, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x2c, + 0x20, + 0x24, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x5b, + 0x55, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x5d, + 0x2c, + 0x20, + 0x24, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x3a, + 0x20, + 0x5b, + 0x5b, + 0x55, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x5d, + 0x5d, + 0x2c, + 0x20, + 0x24, + 0x6f, + 0x62, + 0x6a, + 0x65, + 0x63, + 0x74, + 0x3a, + 0x20, + 0x54, + 0x65, + 0x73, + 0x74, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x2c, + 0x20, + 0x24, + 0x6f, + 0x62, + 0x6a, + 0x65, + 0x63, + 0x74, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x5b, + 0x54, + 0x65, + 0x73, + 0x74, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x5d, + 0x2c, + 0x20, + 0x24, + 0x6f, + 0x62, + 0x6a, + 0x65, + 0x63, + 0x74, + 0x4e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x3a, + 0x20, + 0x5b, + 0x5b, + 0x54, + 0x65, + 0x73, + 0x74, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x5d, + 0x5d, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x75, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x28, + 0x6e, + 0x6f, + 0x6e, + 0x55, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x3a, + 0x20, + 0x24, + 0x6e, + 0x6f, + 0x6e, + 0x55, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x2c, + 0x20, + 0x73, + 0x69, + 0x6e, + 0x67, + 0x6c, + 0x65, + 0x3a, + 0x20, + 0x24, + 0x73, + 0x69, + 0x6e, + 0x67, + 0x6c, + 0x65, + 0x2c, + 0x20, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x2c, + 0x20, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x3a, + 0x20, + 0x24, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x2c, + 0x20, + 0x6f, + 0x62, + 0x6a, + 0x65, + 0x63, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x6f, + 0x62, + 0x6a, + 0x65, + 0x63, + 0x74, + 0x2c, + 0x20, + 0x6f, + 0x62, + 0x6a, + 0x65, + 0x63, + 0x74, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x6f, + 0x62, + 0x6a, + 0x65, + 0x63, + 0x74, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x2c, + 0x20, + 0x6f, + 0x62, + 0x6a, + 0x65, + 0x63, + 0x74, + 0x4e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x3a, + 0x20, + 0x24, + 0x6f, + 0x62, + 0x6a, + 0x65, + 0x63, + 0x74, + 0x4e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x29, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "7cd8f1db7bb9937a9b48c70ce9dc152f52782f96"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -594,6 +919,7 @@ public TestUploadQuery(global::StrawberryShake.IOperationExecutor typeof(ITestUploadResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.String? nonUpload, global::StrawberryShake.Upload? single, global::System.Collections.Generic.IReadOnlyList? list, global::System.Collections.Generic.IReadOnlyList?>? nested, global::StrawberryShake.CodeGeneration.CSharp.Integration.UploadScalar_InMemory.TestInput? @object, global::System.Collections.Generic.IReadOnlyList? objectList, global::System.Collections.Generic.IReadOnlyList?>? objectNested, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(nonUpload, single, list, nested, @object, objectList, objectNested); @@ -970,6 +1296,7 @@ public TestUploadResultFactory(global::StrawberryShake.IEntityStore entityStore) } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::StrawberryShake.CodeGeneration.CSharp.Integration.UploadScalar_InMemory.ITestUploadResult); + public TestUploadResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -1005,9 +1332,9 @@ public TestUploadResultInfo(global::System.String? upload, global::System.Collec } public global::System.String? Upload { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new TestUploadResultInfo(Upload, _entityIds, version); @@ -1086,7 +1413,9 @@ public TestUploadBuilder(global::StrawberryShake.IEntityStore entityStore, globa public partial class UploadScalar_InMemoryClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_Combined.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_Combined.snap index 82f59346438..9c7b7755a02 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_Combined.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_Combined.snap @@ -485,8 +485,78 @@ namespace Foo.Bar public static GetPersonQueryDocument Instance { get; } = new GetPersonQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "c11160a60d74905834b4c99aa93c6523954df324"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -522,6 +592,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -596,8 +667,86 @@ namespace Foo.Bar public static OnPersonSubscriptionDocument Instance { get; } = new OnPersonSubscriptionDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Subscription; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4f, 0x6e, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x73, + 0x75, + 0x62, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x4f, + 0x6e, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x6f, + 0x6e, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "c3ed517156362cb7471bdf5a8737d05dae09940c"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -633,6 +782,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IOnPersonResult); + public global::System.IObservable> Watch(global::StrawberryShake.ExecutionStrategy? strategy = null) { var request = CreateRequest(); @@ -700,8 +850,97 @@ namespace Foo.Bar public static CreatePersonMutationDocument Instance { get; } = new CreatePersonMutationDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Mutation; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x28, 0x69, 0x64, 0x3a, 0x20, 0x31, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x6d, + 0x75, + 0x74, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x43, + 0x72, + 0x65, + 0x61, + 0x74, + 0x65, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x63, + 0x72, + 0x65, + 0x61, + 0x74, + 0x65, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x28, + 0x69, + 0x64, + 0x3a, + 0x20, + 0x31, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "36eb0176de845ee1b1deda2d3edd28d5debbc50a"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -737,6 +976,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ICreatePersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -852,6 +1092,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPersonResult); + public GetPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -902,9 +1143,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? Person { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPersonResultInfo(Person, _entityIds, version); @@ -924,6 +1165,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IOnPersonResult); + public OnPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -974,9 +1216,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? OnPerson { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new OnPersonResultInfo(OnPerson, _entityIds, version); @@ -996,6 +1238,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ICreatePersonResult); + public CreatePersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -1046,9 +1289,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? CreatePerson { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new CreatePersonResultInfo(CreatePerson, _entityIds, version); @@ -1354,7 +1597,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_DifferentTransportMethods.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_DifferentTransportMethods.snap index b7224a9fb5c..79d958bb52c 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_DifferentTransportMethods.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_DifferentTransportMethods.snap @@ -485,8 +485,78 @@ namespace Foo.Bar public static GetPersonQueryDocument Instance { get; } = new GetPersonQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "c11160a60d74905834b4c99aa93c6523954df324"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -522,6 +592,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -596,8 +667,86 @@ namespace Foo.Bar public static OnPersonSubscriptionDocument Instance { get; } = new OnPersonSubscriptionDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Subscription; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4f, 0x6e, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x73, + 0x75, + 0x62, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x4f, + 0x6e, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x6f, + 0x6e, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "c3ed517156362cb7471bdf5a8737d05dae09940c"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -633,6 +782,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IOnPersonResult); + public global::System.IObservable> Watch(global::StrawberryShake.ExecutionStrategy? strategy = null) { var request = CreateRequest(); @@ -700,8 +850,97 @@ namespace Foo.Bar public static CreatePersonMutationDocument Instance { get; } = new CreatePersonMutationDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Mutation; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x28, 0x69, 0x64, 0x3a, 0x20, 0x31, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x6d, + 0x75, + 0x74, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x43, + 0x72, + 0x65, + 0x61, + 0x74, + 0x65, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x63, + 0x72, + 0x65, + 0x61, + 0x74, + 0x65, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x28, + 0x69, + 0x64, + 0x3a, + 0x20, + 0x31, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "36eb0176de845ee1b1deda2d3edd28d5debbc50a"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -737,6 +976,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ICreatePersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -852,6 +1092,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPersonResult); + public GetPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -902,9 +1143,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? Person { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPersonResultInfo(Person, _entityIds, version); @@ -924,6 +1165,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IOnPersonResult); + public OnPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -974,9 +1216,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? OnPerson { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new OnPersonResultInfo(OnPerson, _entityIds, version); @@ -996,6 +1238,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ICreatePersonResult); + public CreatePersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -1046,9 +1289,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? CreatePerson { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new CreatePersonResultInfo(CreatePerson, _entityIds, version); @@ -1354,7 +1597,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_InMemory.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_InMemory.snap index e5349ab831d..32a28fba6d4 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_InMemory.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_InMemory.snap @@ -485,8 +485,78 @@ namespace Foo.Bar public static GetPersonQueryDocument Instance { get; } = new GetPersonQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "c11160a60d74905834b4c99aa93c6523954df324"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -522,6 +592,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -596,8 +667,86 @@ namespace Foo.Bar public static OnPersonSubscriptionDocument Instance { get; } = new OnPersonSubscriptionDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Subscription; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4f, 0x6e, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x73, + 0x75, + 0x62, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x4f, + 0x6e, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x6f, + 0x6e, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "c3ed517156362cb7471bdf5a8737d05dae09940c"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -633,6 +782,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IOnPersonResult); + public global::System.IObservable> Watch(global::StrawberryShake.ExecutionStrategy? strategy = null) { var request = CreateRequest(); @@ -700,8 +850,97 @@ namespace Foo.Bar public static CreatePersonMutationDocument Instance { get; } = new CreatePersonMutationDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Mutation; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x28, 0x69, 0x64, 0x3a, 0x20, 0x31, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x6d, + 0x75, + 0x74, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x43, + 0x72, + 0x65, + 0x61, + 0x74, + 0x65, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x63, + 0x72, + 0x65, + 0x61, + 0x74, + 0x65, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x28, + 0x69, + 0x64, + 0x3a, + 0x20, + 0x31, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "36eb0176de845ee1b1deda2d3edd28d5debbc50a"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -737,6 +976,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ICreatePersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -852,6 +1092,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPersonResult); + public GetPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -902,9 +1143,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? Person { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPersonResultInfo(Person, _entityIds, version); @@ -924,6 +1165,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IOnPersonResult); + public OnPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -974,9 +1216,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? OnPerson { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new OnPersonResultInfo(OnPerson, _entityIds, version); @@ -996,6 +1238,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ICreatePersonResult); + public CreatePersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -1046,9 +1289,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? CreatePerson { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new CreatePersonResultInfo(CreatePerson, _entityIds, version); @@ -1354,7 +1597,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_MultiProfile.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_MultiProfile.snap index 5dffef6c87b..2a9a722f895 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_MultiProfile.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_MultiProfile.snap @@ -485,8 +485,78 @@ namespace Foo.Bar public static GetPersonQueryDocument Instance { get; } = new GetPersonQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "c11160a60d74905834b4c99aa93c6523954df324"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -522,6 +592,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -596,8 +667,86 @@ namespace Foo.Bar public static OnPersonSubscriptionDocument Instance { get; } = new OnPersonSubscriptionDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Subscription; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4f, 0x6e, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x73, + 0x75, + 0x62, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x4f, + 0x6e, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x6f, + 0x6e, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "c3ed517156362cb7471bdf5a8737d05dae09940c"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -633,6 +782,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IOnPersonResult); + public global::System.IObservable> Watch(global::StrawberryShake.ExecutionStrategy? strategy = null) { var request = CreateRequest(); @@ -700,8 +850,97 @@ namespace Foo.Bar public static CreatePersonMutationDocument Instance { get; } = new CreatePersonMutationDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Mutation; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x28, 0x69, 0x64, 0x3a, 0x20, 0x31, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x6d, + 0x75, + 0x74, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x43, + 0x72, + 0x65, + 0x61, + 0x74, + 0x65, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x63, + 0x72, + 0x65, + 0x61, + 0x74, + 0x65, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x28, + 0x69, + 0x64, + 0x3a, + 0x20, + 0x31, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "36eb0176de845ee1b1deda2d3edd28d5debbc50a"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -737,6 +976,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ICreatePersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -860,6 +1100,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPersonResult); + public GetPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -910,9 +1151,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? Person { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPersonResultInfo(Person, _entityIds, version); @@ -932,6 +1173,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IOnPersonResult); + public OnPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -982,9 +1224,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? OnPerson { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new OnPersonResultInfo(OnPerson, _entityIds, version); @@ -1004,6 +1246,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ICreatePersonResult); + public CreatePersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -1054,9 +1297,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? CreatePerson { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new CreatePersonResultInfo(CreatePerson, _entityIds, version); @@ -1362,7 +1605,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_Mutation.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_Mutation.snap index 357546b92f2..6a6a27bb320 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_Mutation.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_Mutation.snap @@ -189,8 +189,97 @@ namespace Foo.Bar public static CreatePersonMutationDocument Instance { get; } = new CreatePersonMutationDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Mutation; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x28, 0x69, 0x64, 0x3a, 0x20, 0x31, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x6d, + 0x75, + 0x74, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x43, + 0x72, + 0x65, + 0x61, + 0x74, + 0x65, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x63, + 0x72, + 0x65, + 0x61, + 0x74, + 0x65, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x28, + 0x69, + 0x64, + 0x3a, + 0x20, + 0x31, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "36eb0176de845ee1b1deda2d3edd28d5debbc50a"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -226,6 +315,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ICreatePersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -331,6 +421,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ICreatePersonResult); + public CreatePersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -381,9 +472,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? CreatePerson { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new CreatePersonResultInfo(CreatePerson, _entityIds, version); @@ -493,7 +584,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_Query.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_Query.snap index 2b3ff6357cb..b1b924ed4cc 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_Query.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_Query.snap @@ -189,8 +189,78 @@ namespace Foo.Bar public static GetPersonQueryDocument Instance { get; } = new GetPersonQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "c11160a60d74905834b4c99aa93c6523954df324"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -226,6 +296,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -331,6 +402,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPersonResult); + public GetPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -381,9 +453,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? Person { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPersonResultInfo(Person, _entityIds, version); @@ -493,7 +565,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_Subscription.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_Subscription.snap index 57a4c94efaa..4ad4ad1d72b 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_Subscription.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/DependencyInjectionGeneratorTests.Default_Subscription.snap @@ -189,8 +189,86 @@ namespace Foo.Bar public static OnPersonSubscriptionDocument Instance { get; } = new OnPersonSubscriptionDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Subscription; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4f, 0x6e, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x73, + 0x75, + 0x62, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x4f, + 0x6e, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x6f, + 0x6e, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "c3ed517156362cb7471bdf5a8737d05dae09940c"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -226,6 +304,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IOnPersonResult); + public global::System.IObservable> Watch(global::StrawberryShake.ExecutionStrategy? strategy = null) { var request = CreateRequest(); @@ -324,6 +403,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IOnPersonResult); + public OnPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -374,9 +454,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? OnPerson { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new OnPersonResultInfo(OnPerson, _entityIds, version); @@ -486,7 +566,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_BookClient_DataInEntity_UnionDataTypes.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_BookClient_DataInEntity_UnionDataTypes.snap index e799f6eed4a..8e026e628f0 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_BookClient_DataInEntity_UnionDataTypes.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_BookClient_DataInEntity_UnionDataTypes.snap @@ -91,9 +91,7 @@ namespace Foo.Bar } public global::System.String Id { get; } - public global::System.Collections.Generic.IReadOnlyList? Books { get; } - public global::Foo.Bar.IGetStore2_SearchableStore_Search? Search { get; } public virtual global::System.Boolean Equals(GetStore2_SearchableStore_SearchableStore? other) @@ -174,7 +172,6 @@ namespace Foo.Bar } public global::System.String Isbn { get; } - public global::System.String Title { get; } public virtual global::System.Boolean Equals(GetStore2_SearchableStore_Books_Book? other) @@ -298,7 +295,6 @@ namespace Foo.Bar } public global::System.String Title { get; } - public global::System.String Isbn { get; } public virtual global::System.Boolean Equals(GetStore2_SearchableStore_Search_Book? other) @@ -365,9 +361,7 @@ namespace Foo.Bar public partial interface IGetStore2_SearchableStore { public global::System.String Id { get; } - public global::System.Collections.Generic.IReadOnlyList? Books { get; } - public global::Foo.Bar.IGetStore2_SearchableStore_Search? Search { get; } } @@ -382,7 +376,6 @@ namespace Foo.Bar public partial interface IGetStore2_SearchableStore_Books { public global::System.String Isbn { get; } - public global::System.String Title { get; } } @@ -409,7 +402,6 @@ namespace Foo.Bar public partial interface IGetStore2_SearchableStore_Search_Book : IGetStore2_SearchableStore_Search { public global::System.String Title { get; } - public global::System.String Isbn { get; } } @@ -449,8 +441,175 @@ namespace Foo.Bar public static GetStore2QueryDocument Instance { get; } = new GetStore2QueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x32, 0x20, 0x7b, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x64, 0x20, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x73, 0x62, 0x6e, 0x20, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x20, 0x7d, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x6f, 0x6f, 0x6b, 0x20, 0x7b, 0x20, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x20, 0x69, 0x73, 0x62, 0x6e, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x53, + 0x74, + 0x6f, + 0x72, + 0x65, + 0x32, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x61, + 0x62, + 0x6c, + 0x65, + 0x53, + 0x74, + 0x6f, + 0x72, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x69, + 0x64, + 0x20, + 0x62, + 0x6f, + 0x6f, + 0x6b, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x69, + 0x73, + 0x62, + 0x6e, + 0x20, + 0x74, + 0x69, + 0x74, + 0x6c, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x73, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x6f, + 0x6f, + 0x6b, + 0x20, + 0x7b, + 0x20, + 0x74, + 0x69, + 0x74, + 0x6c, + 0x65, + 0x20, + 0x69, + 0x73, + 0x62, + 0x6e, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x53, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x61, + 0x62, + 0x6c, + 0x65, + 0x53, + 0x74, + 0x6f, + 0x72, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "3319c32a6efc271a924c607e95150aabe701c260"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -498,6 +657,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetStore2Result); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -602,9 +762,7 @@ namespace Foo.Bar.State } public global::System.String Id { get; } - public global::System.Collections.Generic.IReadOnlyList? Books { get; } - public global::Foo.Bar.State.ISearchResultData? Search { get; } } @@ -621,6 +779,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetStore2Result); + public GetStore2Result Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -666,9 +825,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId SearchableStore { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetStore2ResultInfo(SearchableStore, _entityIds, version); @@ -846,9 +1005,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Isbn { get; } - public global::System.String? Title { get; } } @@ -950,7 +1107,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_BookClient_DataInEntity_UnionDataTypes_With_Records.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_BookClient_DataInEntity_UnionDataTypes_With_Records.snap index 1ecf4510ccd..70fa62c3bda 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_BookClient_DataInEntity_UnionDataTypes_With_Records.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_BookClient_DataInEntity_UnionDataTypes_With_Records.snap @@ -91,9 +91,7 @@ namespace Foo.Bar } public global::System.String Id { get; } - public global::System.Collections.Generic.IReadOnlyList? Books { get; } - public global::Foo.Bar.IGetStore2_SearchableStore_Search? Search { get; } public virtual global::System.Boolean Equals(GetStore2_SearchableStore_SearchableStore? other) @@ -174,7 +172,6 @@ namespace Foo.Bar } public global::System.String Isbn { get; } - public global::System.String Title { get; } public virtual global::System.Boolean Equals(GetStore2_SearchableStore_Books_Book? other) @@ -298,7 +295,6 @@ namespace Foo.Bar } public global::System.String Title { get; } - public global::System.String Isbn { get; } public virtual global::System.Boolean Equals(GetStore2_SearchableStore_Search_Book? other) @@ -365,9 +361,7 @@ namespace Foo.Bar public partial interface IGetStore2_SearchableStore { public global::System.String Id { get; } - public global::System.Collections.Generic.IReadOnlyList? Books { get; } - public global::Foo.Bar.IGetStore2_SearchableStore_Search? Search { get; } } @@ -382,7 +376,6 @@ namespace Foo.Bar public partial interface IGetStore2_SearchableStore_Books { public global::System.String Isbn { get; } - public global::System.String Title { get; } } @@ -409,7 +402,6 @@ namespace Foo.Bar public partial interface IGetStore2_SearchableStore_Search_Book : IGetStore2_SearchableStore_Search { public global::System.String Title { get; } - public global::System.String Isbn { get; } } @@ -449,8 +441,175 @@ namespace Foo.Bar public static GetStore2QueryDocument Instance { get; } = new GetStore2QueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x32, 0x20, 0x7b, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x64, 0x20, 0x62, 0x6f, 0x6f, 0x6b, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x73, 0x62, 0x6e, 0x20, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x20, 0x7d, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x6f, 0x6f, 0x6b, 0x20, 0x7b, 0x20, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x20, 0x69, 0x73, 0x62, 0x6e, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x53, + 0x74, + 0x6f, + 0x72, + 0x65, + 0x32, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x61, + 0x62, + 0x6c, + 0x65, + 0x53, + 0x74, + 0x6f, + 0x72, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x69, + 0x64, + 0x20, + 0x62, + 0x6f, + 0x6f, + 0x6b, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x69, + 0x73, + 0x62, + 0x6e, + 0x20, + 0x74, + 0x69, + 0x74, + 0x6c, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x73, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x6f, + 0x6f, + 0x6b, + 0x20, + 0x7b, + 0x20, + 0x74, + 0x69, + 0x74, + 0x6c, + 0x65, + 0x20, + 0x69, + 0x73, + 0x62, + 0x6e, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x53, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x61, + 0x62, + 0x6c, + 0x65, + 0x53, + 0x74, + 0x6f, + 0x72, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "3319c32a6efc271a924c607e95150aabe701c260"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -498,6 +657,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetStore2Result); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -602,9 +762,7 @@ namespace Foo.Bar.State } public global::System.String Id { get; init; } - public global::System.Collections.Generic.IReadOnlyList? Books { get; init; } - public global::Foo.Bar.State.ISearchResultData? Search { get; init; } } @@ -621,6 +779,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetStore2Result); + public GetStore2Result Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -666,9 +825,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId SearchableStore { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetStore2ResultInfo(SearchableStore, _entityIds, version); @@ -846,9 +1005,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; init; } - public global::System.String? Isbn { get; init; } - public global::System.String? Title { get; init; } } @@ -950,7 +1107,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_BookClient_DataOnly_InterfaceDataTypes.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_BookClient_DataOnly_InterfaceDataTypes.snap index fda2d7b7f62..cb278441956 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_BookClient_DataOnly_InterfaceDataTypes.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_BookClient_DataOnly_InterfaceDataTypes.snap @@ -100,7 +100,6 @@ namespace Foo.Bar } public global::System.String Isbn { get; } - public global::System.String Title { get; } public virtual global::System.Boolean Equals(SearchSomething_Prints_Book? other) @@ -166,7 +165,6 @@ namespace Foo.Bar } public global::System.String Isbn { get; } - public global::System.String? CoverImageUrl { get; } public virtual global::System.Boolean Equals(SearchSomething_Prints_Magazine? other) @@ -280,8 +278,119 @@ namespace Foo.Bar public static SearchSomethingQueryDocument Instance { get; } = new SearchSomethingQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x53, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x7b, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x73, 0x62, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x6f, 0x6f, 0x6b, 0x20, 0x7b, 0x20, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x4d, 0x61, 0x67, 0x61, 0x7a, 0x69, 0x6e, 0x65, 0x20, 0x7b, 0x20, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x55, 0x72, 0x6c, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x53, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x53, + 0x6f, + 0x6d, + 0x65, + 0x74, + 0x68, + 0x69, + 0x6e, + 0x67, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x72, + 0x69, + 0x6e, + 0x74, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x69, + 0x73, + 0x62, + 0x6e, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x6f, + 0x6f, + 0x6b, + 0x20, + 0x7b, + 0x20, + 0x74, + 0x69, + 0x74, + 0x6c, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4d, + 0x61, + 0x67, + 0x61, + 0x7a, + 0x69, + 0x6e, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x63, + 0x6f, + 0x76, + 0x65, + 0x72, + 0x49, + 0x6d, + 0x61, + 0x67, + 0x65, + 0x55, + 0x72, + 0x6c, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "7a4fedad68dbe533ab701426db5e156d911a5b2f"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -320,6 +429,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ISearchSomethingResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -414,6 +524,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ISearchSomethingResult); + public SearchSomethingResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -489,9 +600,9 @@ namespace Foo.Bar.State } public global::System.Collections.Generic.IReadOnlyList? Prints { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new SearchSomethingResultInfo(Prints, _entityIds, version); @@ -630,9 +741,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Isbn { get; } - public global::System.String? Title { get; } } @@ -648,9 +757,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Isbn { get; } - public global::System.String? CoverImageUrl { get; } } @@ -659,7 +766,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_BookClient_DataOnly_InterfaceDataTypes_With_Records.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_BookClient_DataOnly_InterfaceDataTypes_With_Records.snap index d7bcb7e78b1..19d4a9a60fa 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_BookClient_DataOnly_InterfaceDataTypes_With_Records.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_BookClient_DataOnly_InterfaceDataTypes_With_Records.snap @@ -100,7 +100,6 @@ namespace Foo.Bar } public global::System.String Isbn { get; } - public global::System.String Title { get; } public virtual global::System.Boolean Equals(SearchSomething_Prints_Book? other) @@ -166,7 +165,6 @@ namespace Foo.Bar } public global::System.String Isbn { get; } - public global::System.String? CoverImageUrl { get; } public virtual global::System.Boolean Equals(SearchSomething_Prints_Magazine? other) @@ -280,8 +278,119 @@ namespace Foo.Bar public static SearchSomethingQueryDocument Instance { get; } = new SearchSomethingQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x53, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x7b, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x73, 0x62, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x6f, 0x6f, 0x6b, 0x20, 0x7b, 0x20, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x4d, 0x61, 0x67, 0x61, 0x7a, 0x69, 0x6e, 0x65, 0x20, 0x7b, 0x20, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x55, 0x72, 0x6c, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x53, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x53, + 0x6f, + 0x6d, + 0x65, + 0x74, + 0x68, + 0x69, + 0x6e, + 0x67, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x72, + 0x69, + 0x6e, + 0x74, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x69, + 0x73, + 0x62, + 0x6e, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x6f, + 0x6f, + 0x6b, + 0x20, + 0x7b, + 0x20, + 0x74, + 0x69, + 0x74, + 0x6c, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4d, + 0x61, + 0x67, + 0x61, + 0x7a, + 0x69, + 0x6e, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x63, + 0x6f, + 0x76, + 0x65, + 0x72, + 0x49, + 0x6d, + 0x61, + 0x67, + 0x65, + 0x55, + 0x72, + 0x6c, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "7a4fedad68dbe533ab701426db5e156d911a5b2f"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -320,6 +429,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ISearchSomethingResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -414,6 +524,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ISearchSomethingResult); + public SearchSomethingResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -489,9 +600,9 @@ namespace Foo.Bar.State } public global::System.Collections.Generic.IReadOnlyList? Prints { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new SearchSomethingResultInfo(Prints, _entityIds, version); @@ -630,9 +741,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; init; } - public global::System.String? Isbn { get; init; } - public global::System.String? Title { get; init; } } @@ -648,9 +757,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; init; } - public global::System.String? Isbn { get; init; } - public global::System.String? CoverImageUrl { get; init; } } @@ -659,7 +766,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_BookClient_DataOnly_UnionDataTypes.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_BookClient_DataOnly_UnionDataTypes.snap index b39b224f724..ff611ac3ee8 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_BookClient_DataOnly_UnionDataTypes.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_BookClient_DataOnly_UnionDataTypes.snap @@ -260,8 +260,103 @@ namespace Foo.Bar public static SearchSomethingQueryDocument Instance { get; } = new SearchSomethingQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x53, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x7b, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x6f, 0x6f, 0x6b, 0x20, 0x7b, 0x20, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x53, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x53, + 0x6f, + 0x6d, + 0x65, + 0x74, + 0x68, + 0x69, + 0x6e, + 0x67, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x41, + 0x75, + 0x74, + 0x68, + 0x6f, + 0x72, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x6f, + 0x6f, + 0x6b, + 0x20, + 0x7b, + 0x20, + 0x74, + 0x69, + 0x74, + 0x6c, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "66043ca93911dbadcf22907978f9c38f3cc096ae"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -299,6 +394,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ISearchSomethingResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -392,6 +488,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ISearchSomethingResult); + public SearchSomethingResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -451,9 +548,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.ISearchResultData? Search { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new SearchSomethingResultInfo(Search, _entityIds, version); @@ -548,7 +645,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Name { get; } } @@ -570,7 +666,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Title { get; } } @@ -579,7 +674,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_BookClient_DataOnly_UnionDataTypes_With_Records.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_BookClient_DataOnly_UnionDataTypes_With_Records.snap index 4f7650b1d54..dedd4e913a9 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_BookClient_DataOnly_UnionDataTypes_With_Records.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_BookClient_DataOnly_UnionDataTypes_With_Records.snap @@ -260,8 +260,103 @@ namespace Foo.Bar public static SearchSomethingQueryDocument Instance { get; } = new SearchSomethingQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x53, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x7b, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x6f, 0x6f, 0x6b, 0x20, 0x7b, 0x20, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x53, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x53, + 0x6f, + 0x6d, + 0x65, + 0x74, + 0x68, + 0x69, + 0x6e, + 0x67, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x41, + 0x75, + 0x74, + 0x68, + 0x6f, + 0x72, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x6f, + 0x6f, + 0x6b, + 0x20, + 0x7b, + 0x20, + 0x74, + 0x69, + 0x74, + 0x6c, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "66043ca93911dbadcf22907978f9c38f3cc096ae"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -299,6 +394,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ISearchSomethingResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -392,6 +488,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ISearchSomethingResult); + public SearchSomethingResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -451,9 +548,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.ISearchResultData? Search { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new SearchSomethingResultInfo(Search, _entityIds, version); @@ -548,7 +645,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; init; } - public global::System.String? Name { get; init; } } @@ -570,7 +666,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; init; } - public global::System.String? Title { get; init; } } @@ -579,7 +674,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_ChatClient_ConnectionNotAnEntity.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_ChatClient_ConnectionNotAnEntity.snap index df8bbcbf38e..67043ed1e37 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_ChatClient_ConnectionNotAnEntity.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_ChatClient_ConnectionNotAnEntity.snap @@ -185,12 +185,10 @@ namespace Foo.Bar /// Field name /// public global::System.String Name { get; } - /// /// Field email /// public global::System.String Email { get; } - /// /// Field isOnline /// @@ -295,12 +293,10 @@ namespace Foo.Bar /// Field name /// public global::System.String Name { get; } - /// /// Field email /// public global::System.String Email { get; } - /// /// Field isOnline /// @@ -345,8 +341,139 @@ namespace Foo.Bar public static GetPeopleQueryDocument Instance { get; } = new GetPeopleQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x28, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x79, 0x3a, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x20, 0x41, 0x53, 0x43, 0x20, 0x7d, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x20, 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x6f, + 0x70, + 0x6c, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x6f, + 0x70, + 0x6c, + 0x65, + 0x28, + 0x6f, + 0x72, + 0x64, + 0x65, + 0x72, + 0x5f, + 0x62, + 0x79, + 0x3a, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x3a, + 0x20, + 0x41, + 0x53, + 0x43, + 0x20, + 0x7d, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x6f, + 0x64, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x20, + 0x69, + 0x73, + 0x4f, + 0x6e, + 0x6c, + 0x69, + 0x6e, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "4b9666a143384a9c7ccc4410ea4026c9b68ee86c"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -387,6 +514,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPeopleResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -486,10 +614,8 @@ namespace Foo.Bar.State ///Field name public global::System.String Name { get; } - ///Field email public global::System.String Email { get; } - ///Field isOnline public global::System.Boolean IsOnline { get; } } @@ -507,6 +633,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPeopleResult); + public GetPeopleResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -596,9 +723,9 @@ namespace Foo.Bar.State /// Gets access to all the people known to this service. /// public global::Foo.Bar.State.PersonConnectionData? People { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPeopleResultInfo(People, _entityIds, version); @@ -753,7 +880,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - ///A flattened list of the nodes. public global::System.Collections.Generic.IReadOnlyList? Nodes { get; } } @@ -784,7 +910,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_ChatClient_ConnectionNotAnEntity_With_Records.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_ChatClient_ConnectionNotAnEntity_With_Records.snap index 991563eab3b..d6485d5452a 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_ChatClient_ConnectionNotAnEntity_With_Records.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_ChatClient_ConnectionNotAnEntity_With_Records.snap @@ -185,12 +185,10 @@ namespace Foo.Bar /// Field name /// public global::System.String Name { get; } - /// /// Field email /// public global::System.String Email { get; } - /// /// Field isOnline /// @@ -295,12 +293,10 @@ namespace Foo.Bar /// Field name /// public global::System.String Name { get; } - /// /// Field email /// public global::System.String Email { get; } - /// /// Field isOnline /// @@ -345,8 +341,139 @@ namespace Foo.Bar public static GetPeopleQueryDocument Instance { get; } = new GetPeopleQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x28, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x79, 0x3a, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x20, 0x41, 0x53, 0x43, 0x20, 0x7d, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x20, 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x6f, + 0x70, + 0x6c, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x6f, + 0x70, + 0x6c, + 0x65, + 0x28, + 0x6f, + 0x72, + 0x64, + 0x65, + 0x72, + 0x5f, + 0x62, + 0x79, + 0x3a, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x3a, + 0x20, + 0x41, + 0x53, + 0x43, + 0x20, + 0x7d, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x6f, + 0x64, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x20, + 0x69, + 0x73, + 0x4f, + 0x6e, + 0x6c, + 0x69, + 0x6e, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "4b9666a143384a9c7ccc4410ea4026c9b68ee86c"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -387,6 +514,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPeopleResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -486,10 +614,8 @@ namespace Foo.Bar.State ///Field name public global::System.String Name { get; init; } - ///Field email public global::System.String Email { get; init; } - ///Field isOnline public global::System.Boolean IsOnline { get; init; } } @@ -507,6 +633,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPeopleResult); + public GetPeopleResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -596,9 +723,9 @@ namespace Foo.Bar.State /// Gets access to all the people known to this service. /// public global::Foo.Bar.State.PersonConnectionData? People { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPeopleResultInfo(People, _entityIds, version); @@ -753,7 +880,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; init; } - ///A flattened list of the nodes. public global::System.Collections.Generic.IReadOnlyList? Nodes { get; init; } } @@ -784,7 +910,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_ChatClient_MapperMapsEntityOnRootCorrectly.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_ChatClient_MapperMapsEntityOnRootCorrectly.snap index 16e8f4c39ca..1c170967b0f 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_ChatClient_MapperMapsEntityOnRootCorrectly.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_ChatClient_MapperMapsEntityOnRootCorrectly.snap @@ -184,7 +184,6 @@ namespace Foo.Bar /// Field name /// public global::System.String Name { get; } - /// /// Field messages /// @@ -341,7 +340,6 @@ namespace Foo.Bar /// Field sender /// public global::Foo.Bar.IGetPeople_People_Nodes_Messages_Nodes_Sender Sender { get; } - /// /// Field text /// @@ -570,7 +568,6 @@ namespace Foo.Bar /// Field sender /// public global::Foo.Bar.IGetPeople_People_Nodes_Messages_Nodes_Sender Sender { get; } - /// /// Field text /// @@ -766,7 +763,6 @@ namespace Foo.Bar /// Field sender /// public global::Foo.Bar.IGetPeople_People_Nodes_Messages_Nodes_Sender Sender { get; } - /// /// Field text /// @@ -1015,8 +1011,335 @@ namespace Foo.Bar public static GetPeopleQueryDocument Instance { get; } = new GetPeopleQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x28, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x79, 0x3a, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x20, 0x41, 0x53, 0x43, 0x20, 0x7d, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x7b, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x6f, + 0x70, + 0x6c, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x6f, + 0x70, + 0x6c, + 0x65, + 0x28, + 0x6f, + 0x72, + 0x64, + 0x65, + 0x72, + 0x5f, + 0x62, + 0x79, + 0x3a, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x3a, + 0x20, + 0x41, + 0x53, + 0x43, + 0x20, + 0x7d, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x6f, + 0x64, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x6d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x6f, + 0x64, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x65, + 0x6e, + 0x64, + 0x65, + 0x72, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x74, + 0x65, + 0x78, + 0x74, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "0967ca09947e3a18370454229ece650b8ba8e2ef"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -1080,6 +1403,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPeopleResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -1200,8 +1524,331 @@ namespace Foo.Bar public static WriteMessageMutationDocument Instance { get; } = new WriteMessageMutationDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Mutation; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x28, 0x24, 0x74, 0x65, 0x78, 0x74, 0x3a, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x21, 0x2c, 0x20, 0x24, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x28, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x7b, 0x20, 0x74, 0x65, 0x78, 0x74, 0x3a, 0x20, 0x24, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x3a, 0x20, 0x24, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x7d, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x7b, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x6d, + 0x75, + 0x74, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x57, + 0x72, + 0x69, + 0x74, + 0x65, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x28, + 0x24, + 0x74, + 0x65, + 0x78, + 0x74, + 0x3a, + 0x20, + 0x53, + 0x74, + 0x72, + 0x69, + 0x6e, + 0x67, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x61, + 0x64, + 0x64, + 0x72, + 0x65, + 0x73, + 0x73, + 0x3a, + 0x20, + 0x53, + 0x74, + 0x72, + 0x69, + 0x6e, + 0x67, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x65, + 0x6e, + 0x64, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x28, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x3a, + 0x20, + 0x7b, + 0x20, + 0x74, + 0x65, + 0x78, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x74, + 0x65, + 0x78, + 0x74, + 0x2c, + 0x20, + 0x72, + 0x65, + 0x63, + 0x69, + 0x70, + 0x69, + 0x65, + 0x6e, + 0x74, + 0x45, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x3a, + 0x20, + 0x24, + 0x61, + 0x64, + 0x64, + 0x72, + 0x65, + 0x73, + 0x73, + 0x20, + 0x7d, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x65, + 0x6e, + 0x64, + 0x65, + 0x72, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x74, + 0x65, + 0x78, + 0x74, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "5b649ed10b73d2f35595b9d5f11fee8759ac4d72"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -1257,6 +1904,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IWriteMessageResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.String text, global::System.String address, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(text, address); @@ -1396,7 +2044,6 @@ namespace Foo.Bar.State ///Field name public global::System.String Name { get; } - ///Field messages public global::Foo.Bar.State.MessageConnectionData? Messages { get; } } @@ -1414,7 +2061,6 @@ namespace Foo.Bar.State ///Field sender public global::StrawberryShake.EntityId Sender { get; } - ///Field text public global::System.String Text { get; } } @@ -1436,6 +2082,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPeopleResult); + public GetPeopleResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -1586,9 +2233,9 @@ namespace Foo.Bar.State /// Gets access to all the people known to this service. /// public global::Foo.Bar.State.PersonConnectionData? People { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPeopleResultInfo(People, _entityIds, version); @@ -1610,6 +2257,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IWriteMessageResult); + public WriteMessageResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -1683,9 +2331,9 @@ namespace Foo.Bar.State /// Field sendMessage /// public global::Foo.Bar.State.SendMessagePayloadData SendMessage { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new WriteMessageResultInfo(SendMessage, _entityIds, version); @@ -2056,7 +2704,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - ///A flattened list of the nodes. public global::System.Collections.Generic.IReadOnlyList? Nodes { get; } } @@ -2073,7 +2720,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - ///A flattened list of the nodes. public global::System.Collections.Generic.IReadOnlyList? Nodes { get; } } @@ -2090,7 +2736,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - ///Field message public global::StrawberryShake.EntityId? Message { get; } } @@ -2294,7 +2939,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_ChatClient_MapperMapsEntityOnRootCorrectly_With_Records.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_ChatClient_MapperMapsEntityOnRootCorrectly_With_Records.snap index 58203f18130..bcaf7003074 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_ChatClient_MapperMapsEntityOnRootCorrectly_With_Records.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityGeneratorTests.Generate_ChatClient_MapperMapsEntityOnRootCorrectly_With_Records.snap @@ -184,7 +184,6 @@ namespace Foo.Bar /// Field name /// public global::System.String Name { get; } - /// /// Field messages /// @@ -341,7 +340,6 @@ namespace Foo.Bar /// Field sender /// public global::Foo.Bar.IGetPeople_People_Nodes_Messages_Nodes_Sender Sender { get; } - /// /// Field text /// @@ -570,7 +568,6 @@ namespace Foo.Bar /// Field sender /// public global::Foo.Bar.IGetPeople_People_Nodes_Messages_Nodes_Sender Sender { get; } - /// /// Field text /// @@ -766,7 +763,6 @@ namespace Foo.Bar /// Field sender /// public global::Foo.Bar.IGetPeople_People_Nodes_Messages_Nodes_Sender Sender { get; } - /// /// Field text /// @@ -1015,8 +1011,335 @@ namespace Foo.Bar public static GetPeopleQueryDocument Instance { get; } = new GetPeopleQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x28, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x79, 0x3a, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x20, 0x41, 0x53, 0x43, 0x20, 0x7d, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x7b, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x6f, + 0x70, + 0x6c, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x6f, + 0x70, + 0x6c, + 0x65, + 0x28, + 0x6f, + 0x72, + 0x64, + 0x65, + 0x72, + 0x5f, + 0x62, + 0x79, + 0x3a, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x3a, + 0x20, + 0x41, + 0x53, + 0x43, + 0x20, + 0x7d, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x6f, + 0x64, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x6d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x6f, + 0x64, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x65, + 0x6e, + 0x64, + 0x65, + 0x72, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x74, + 0x65, + 0x78, + 0x74, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "0967ca09947e3a18370454229ece650b8ba8e2ef"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -1080,6 +1403,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPeopleResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -1200,8 +1524,331 @@ namespace Foo.Bar public static WriteMessageMutationDocument Instance { get; } = new WriteMessageMutationDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Mutation; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x57, 0x72, 0x69, 0x74, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x28, 0x24, 0x74, 0x65, 0x78, 0x74, 0x3a, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x21, 0x2c, 0x20, 0x24, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x3a, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x28, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x7b, 0x20, 0x74, 0x65, 0x78, 0x74, 0x3a, 0x20, 0x24, 0x74, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x3a, 0x20, 0x24, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x20, 0x7d, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x7b, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x6d, + 0x75, + 0x74, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x57, + 0x72, + 0x69, + 0x74, + 0x65, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x28, + 0x24, + 0x74, + 0x65, + 0x78, + 0x74, + 0x3a, + 0x20, + 0x53, + 0x74, + 0x72, + 0x69, + 0x6e, + 0x67, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x61, + 0x64, + 0x64, + 0x72, + 0x65, + 0x73, + 0x73, + 0x3a, + 0x20, + 0x53, + 0x74, + 0x72, + 0x69, + 0x6e, + 0x67, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x65, + 0x6e, + 0x64, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x28, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x3a, + 0x20, + 0x7b, + 0x20, + 0x74, + 0x65, + 0x78, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x74, + 0x65, + 0x78, + 0x74, + 0x2c, + 0x20, + 0x72, + 0x65, + 0x63, + 0x69, + 0x70, + 0x69, + 0x65, + 0x6e, + 0x74, + 0x45, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x3a, + 0x20, + 0x24, + 0x61, + 0x64, + 0x64, + 0x72, + 0x65, + 0x73, + 0x73, + 0x20, + 0x7d, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x65, + 0x6e, + 0x64, + 0x65, + 0x72, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x74, + 0x65, + 0x78, + 0x74, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "5b649ed10b73d2f35595b9d5f11fee8759ac4d72"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -1257,6 +1904,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IWriteMessageResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.String text, global::System.String address, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(text, address); @@ -1396,7 +2044,6 @@ namespace Foo.Bar.State ///Field name public global::System.String Name { get; init; } - ///Field messages public global::Foo.Bar.State.MessageConnectionData? Messages { get; init; } } @@ -1414,7 +2061,6 @@ namespace Foo.Bar.State ///Field sender public global::StrawberryShake.EntityId Sender { get; init; } - ///Field text public global::System.String Text { get; init; } } @@ -1436,6 +2082,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPeopleResult); + public GetPeopleResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -1586,9 +2233,9 @@ namespace Foo.Bar.State /// Gets access to all the people known to this service. /// public global::Foo.Bar.State.PersonConnectionData? People { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPeopleResultInfo(People, _entityIds, version); @@ -1610,6 +2257,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IWriteMessageResult); + public WriteMessageResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -1683,9 +2331,9 @@ namespace Foo.Bar.State /// Field sendMessage /// public global::Foo.Bar.State.SendMessagePayloadData SendMessage { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new WriteMessageResultInfo(SendMessage, _entityIds, version); @@ -2056,7 +2704,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; init; } - ///A flattened list of the nodes. public global::System.Collections.Generic.IReadOnlyList? Nodes { get; init; } } @@ -2073,7 +2720,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; init; } - ///A flattened list of the nodes. public global::System.Collections.Generic.IReadOnlyList? Nodes { get; init; } } @@ -2090,7 +2736,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; init; } - ///Field message public global::StrawberryShake.EntityId? Message { get; init; } } @@ -2294,7 +2939,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityIdFactoryGeneratorTests.Simple_ComplexEntity.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityIdFactoryGeneratorTests.Simple_ComplexEntity.snap index facf039c360..36342b97d0b 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityIdFactoryGeneratorTests.Simple_ComplexEntity.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityIdFactoryGeneratorTests.Simple_ComplexEntity.snap @@ -94,7 +94,6 @@ namespace Foo.Bar } public global::System.String Id { get; } - public global::System.String? Email { get; } public virtual global::System.Boolean Equals(GetPerson_Person_Person? other) @@ -165,7 +164,6 @@ namespace Foo.Bar public partial interface IGetPerson_Person { public global::System.String Id { get; } - public global::System.String? Email { get; } } @@ -201,8 +199,88 @@ namespace Foo.Bar public static GetPersonQueryDocument Instance { get; } = new GetPersonQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x64, 0x20, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x69, + 0x64, + 0x20, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "2c3e7c825b50ee7a8f7072749b64372a61a8ffc0"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -240,6 +318,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -333,7 +412,6 @@ namespace Foo.Bar.State } public global::System.String Id { get; } - public global::System.String? Email { get; } } @@ -350,6 +428,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPersonResult); + public GetPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -400,9 +479,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? Person { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPersonResultInfo(Person, _entityIds, version); @@ -527,7 +606,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityIdFactoryGeneratorTests.Simple_DateTimeOffset_Entity.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityIdFactoryGeneratorTests.Simple_DateTimeOffset_Entity.snap index af9fb487ac5..6d2291164e8 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityIdFactoryGeneratorTests.Simple_DateTimeOffset_Entity.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityIdFactoryGeneratorTests.Simple_DateTimeOffset_Entity.snap @@ -94,7 +94,6 @@ namespace Foo.Bar } public global::System.DateTimeOffset Id { get; } - public global::System.String? Email { get; } public virtual global::System.Boolean Equals(GetPerson_Person_Person? other) @@ -165,7 +164,6 @@ namespace Foo.Bar public partial interface IGetPerson_Person { public global::System.DateTimeOffset Id { get; } - public global::System.String? Email { get; } } @@ -200,8 +198,82 @@ namespace Foo.Bar public static GetPersonQueryDocument Instance { get; } = new GetPersonQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x64, 0x20, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x69, + 0x64, + 0x20, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "3181904b8c35da1f034a61ae48a1e3c875d69cbf"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -238,6 +310,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -330,7 +403,6 @@ namespace Foo.Bar.State } public global::System.DateTimeOffset Id { get; } - public global::System.String? Email { get; } } @@ -347,6 +419,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPersonResult); + public GetPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -397,9 +470,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? Person { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPersonResultInfo(Person, _entityIds, version); @@ -526,7 +599,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityIdFactoryGeneratorTests.Simple_IdEntity.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityIdFactoryGeneratorTests.Simple_IdEntity.snap index 0fc1f07af95..cb56faf44f3 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityIdFactoryGeneratorTests.Simple_IdEntity.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityIdFactoryGeneratorTests.Simple_IdEntity.snap @@ -94,7 +94,6 @@ namespace Foo.Bar } public global::System.String Id { get; } - public global::System.String? Email { get; } public virtual global::System.Boolean Equals(GetPerson_Person_Person? other) @@ -165,7 +164,6 @@ namespace Foo.Bar public partial interface IGetPerson_Person { public global::System.String Id { get; } - public global::System.String? Email { get; } } @@ -200,8 +198,82 @@ namespace Foo.Bar public static GetPersonQueryDocument Instance { get; } = new GetPersonQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x64, 0x20, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x69, + 0x64, + 0x20, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "3181904b8c35da1f034a61ae48a1e3c875d69cbf"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -238,6 +310,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -330,7 +403,6 @@ namespace Foo.Bar.State } public global::System.String Id { get; } - public global::System.String? Email { get; } } @@ -347,6 +419,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPersonResult); + public GetPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -397,9 +470,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? Person { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPersonResultInfo(Person, _entityIds, version); @@ -524,7 +597,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityIdFactoryGeneratorTests.Simple_NoEntity.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityIdFactoryGeneratorTests.Simple_NoEntity.snap index 70f5adbf91f..22563544a76 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityIdFactoryGeneratorTests.Simple_NoEntity.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityIdFactoryGeneratorTests.Simple_NoEntity.snap @@ -94,7 +94,6 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.String? Email { get; } public virtual global::System.Boolean Equals(GetPerson_Person_Person? other) @@ -165,7 +164,6 @@ namespace Foo.Bar public partial interface IGetPerson_Person { public global::System.String Name { get; } - public global::System.String? Email { get; } } @@ -197,8 +195,63 @@ namespace Foo.Bar public static GetPersonQueryDocument Instance { get; } = new GetPersonQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "90e3850be405988debbddcbd8810ef6e54f016e5"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -232,6 +285,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -321,6 +375,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPersonResult); + public GetPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -376,9 +431,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.PersonData? Person { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPersonResultInfo(Person, _entityIds, version); @@ -477,9 +532,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::System.String? Email { get; } } @@ -488,7 +541,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityIdFactoryGeneratorTests.Simple_Uuid_Entity.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityIdFactoryGeneratorTests.Simple_Uuid_Entity.snap index f55550fc4c8..f97d7b61eac 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityIdFactoryGeneratorTests.Simple_Uuid_Entity.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityIdFactoryGeneratorTests.Simple_Uuid_Entity.snap @@ -94,7 +94,6 @@ namespace Foo.Bar } public global::System.Guid Id { get; } - public global::System.String? Email { get; } public virtual global::System.Boolean Equals(GetPerson_Person_Person? other) @@ -165,7 +164,6 @@ namespace Foo.Bar public partial interface IGetPerson_Person { public global::System.Guid Id { get; } - public global::System.String? Email { get; } } @@ -200,8 +198,82 @@ namespace Foo.Bar public static GetPersonQueryDocument Instance { get; } = new GetPersonQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x64, 0x20, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x69, + 0x64, + 0x20, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "3181904b8c35da1f034a61ae48a1e3c875d69cbf"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -238,6 +310,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -330,7 +403,6 @@ namespace Foo.Bar.State } public global::System.Guid Id { get; } - public global::System.String? Email { get; } } @@ -347,6 +419,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPersonResult); + public GetPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -397,9 +470,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? Person { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPersonResultInfo(Person, _entityIds, version); @@ -526,7 +599,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.InterfaceField.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.InterfaceField.snap index 7b3e7b570cb..6eac4747958 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.InterfaceField.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.InterfaceField.snap @@ -94,7 +94,6 @@ namespace Foo.Bar } public global::System.String? Foo { get; } - public global::System.String? Id { get; } public virtual global::System.Boolean Equals(GetFoo_Foo_Baz? other) @@ -168,7 +167,6 @@ namespace Foo.Bar } public global::System.String? Foo { get; } - public global::System.String? Id { get; } public virtual global::System.Boolean Equals(GetFoo_Foo_Baz2? other) @@ -242,7 +240,6 @@ namespace Foo.Bar } public global::System.String? Foo { get; } - public global::System.String? Baz { get; } public virtual global::System.Boolean Equals(GetFoo_Foo_Quox? other) @@ -316,7 +313,6 @@ namespace Foo.Bar } public global::System.String? Foo { get; } - public global::System.String? Bar { get; } public virtual global::System.Boolean Equals(GetFoo_Foo_Quox2? other) @@ -460,8 +456,165 @@ namespace Foo.Bar public static GetFooQueryDocument Instance { get; } = new GetFooQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x6f, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x51, 0x75, 0x6f, 0x78, 0x20, 0x7b, 0x20, 0x62, 0x61, 0x7a, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x32, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x51, 0x75, 0x6f, 0x78, 0x32, 0x20, 0x7b, 0x20, 0x62, 0x61, 0x72, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x32, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x46, + 0x6f, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x51, + 0x75, + 0x6f, + 0x78, + 0x20, + 0x7b, + 0x20, + 0x62, + 0x61, + 0x7a, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x32, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x51, + 0x75, + 0x6f, + 0x78, + 0x32, + 0x20, + 0x7b, + 0x20, + 0x62, + 0x61, + 0x72, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x32, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "4da3763dd1c1e9a34ce3bc150614ec228c54879d"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -512,6 +665,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetFooResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -618,7 +772,6 @@ namespace Foo.Bar.State } public global::System.String? Foo { get; } - public global::System.String? Id { get; } } @@ -633,7 +786,6 @@ namespace Foo.Bar.State } public global::System.String? Foo { get; } - public global::System.String? Id { get; } } @@ -652,6 +804,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetFooResult); + public GetFooResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -752,9 +905,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityIdOrData? Foo { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetFooResultInfo(Foo, _entityIds, version); @@ -888,9 +1041,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Foo { get; } - public global::System.String? Baz { get; } } @@ -906,9 +1057,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Foo { get; } - public global::System.String? Bar { get; } } @@ -959,7 +1108,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.InterfaceList.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.InterfaceList.snap index 1ab0319c14f..8f7d346bac7 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.InterfaceList.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.InterfaceList.snap @@ -100,7 +100,6 @@ namespace Foo.Bar } public global::System.String? Foo { get; } - public global::System.String? Id { get; } public virtual global::System.Boolean Equals(GetFoo_Foo_Baz? other) @@ -174,7 +173,6 @@ namespace Foo.Bar } public global::System.String? Foo { get; } - public global::System.String? Id { get; } public virtual global::System.Boolean Equals(GetFoo_Foo_Baz2? other) @@ -248,7 +246,6 @@ namespace Foo.Bar } public global::System.String? Foo { get; } - public global::System.String? Baz { get; } public virtual global::System.Boolean Equals(GetFoo_Foo_Quox? other) @@ -322,7 +319,6 @@ namespace Foo.Bar } public global::System.String? Foo { get; } - public global::System.String? Bar { get; } public virtual global::System.Boolean Equals(GetFoo_Foo_Quox2? other) @@ -466,8 +462,165 @@ namespace Foo.Bar public static GetFooQueryDocument Instance { get; } = new GetFooQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x6f, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x51, 0x75, 0x6f, 0x78, 0x20, 0x7b, 0x20, 0x62, 0x61, 0x7a, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x32, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x51, 0x75, 0x6f, 0x78, 0x32, 0x20, 0x7b, 0x20, 0x62, 0x61, 0x72, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x32, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x46, + 0x6f, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x51, + 0x75, + 0x6f, + 0x78, + 0x20, + 0x7b, + 0x20, + 0x62, + 0x61, + 0x7a, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x32, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x51, + 0x75, + 0x6f, + 0x78, + 0x32, + 0x20, + 0x7b, + 0x20, + 0x62, + 0x61, + 0x72, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x32, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "4da3763dd1c1e9a34ce3bc150614ec228c54879d"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -518,6 +671,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetFooResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -624,7 +778,6 @@ namespace Foo.Bar.State } public global::System.String? Foo { get; } - public global::System.String? Id { get; } } @@ -639,7 +792,6 @@ namespace Foo.Bar.State } public global::System.String? Foo { get; } - public global::System.String? Id { get; } } @@ -658,6 +810,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetFooResult); + public GetFooResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -774,9 +927,9 @@ namespace Foo.Bar.State } public global::System.Collections.Generic.IReadOnlyList? Foo { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetFooResultInfo(Foo, _entityIds, version); @@ -931,9 +1084,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Foo { get; } - public global::System.String? Baz { get; } } @@ -949,9 +1100,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Foo { get; } - public global::System.String? Bar { get; } } @@ -1002,7 +1151,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.NonNullableValueTypeId.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.NonNullableValueTypeId.snap index 391cadd628d..131a13a1031 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.NonNullableValueTypeId.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.NonNullableValueTypeId.snap @@ -416,8 +416,161 @@ namespace Foo.Bar public static GetFooQueryDocument Instance { get; } = new GetFooQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x6f, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x51, 0x75, 0x6f, 0x78, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x32, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x51, 0x75, 0x6f, 0x78, 0x32, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x32, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x46, + 0x6f, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x51, + 0x75, + 0x6f, + 0x78, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x32, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x51, + 0x75, + 0x6f, + 0x78, + 0x32, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x32, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "43715bb5e8ecbe7659e99dcb33abcf1ffeadff8e"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -467,6 +620,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetFooResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -600,6 +754,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetFooResult); + public GetFooResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -726,9 +881,9 @@ namespace Foo.Bar.State } public global::System.Collections.Generic.IReadOnlyList? Foo { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetFooResultInfo(Foo, _entityIds, version); @@ -882,7 +1037,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.Int32? Foo { get; } } @@ -897,7 +1051,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.Int32? Foo { get; } } @@ -948,7 +1101,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.UnionField.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.UnionField.snap index e05a86030a5..cb6e4f33554 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.UnionField.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.UnionField.snap @@ -426,8 +426,161 @@ namespace Foo.Bar public static GetFooQueryDocument Instance { get; } = new GetFooQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x6f, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x51, 0x75, 0x6f, 0x78, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x32, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x51, 0x75, 0x6f, 0x78, 0x32, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x32, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x46, + 0x6f, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x51, + 0x75, + 0x6f, + 0x78, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x32, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x51, + 0x75, + 0x6f, + 0x78, + 0x32, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x32, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "43715bb5e8ecbe7659e99dcb33abcf1ffeadff8e"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -477,6 +630,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetFooResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -610,6 +764,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetFooResult); + public GetFooResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -710,9 +865,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityIdOrData? Foo { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetFooResultInfo(Foo, _entityIds, version); @@ -845,7 +1000,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Foo { get; } } @@ -860,7 +1014,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Foo { get; } } @@ -911,7 +1064,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.UnionList.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.UnionList.snap index c66bfe66966..a39df85a7fb 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.UnionList.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.UnionList.snap @@ -432,8 +432,161 @@ namespace Foo.Bar public static GetFooQueryDocument Instance { get; } = new GetFooQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x6f, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x51, 0x75, 0x6f, 0x78, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x32, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x51, 0x75, 0x6f, 0x78, 0x32, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x32, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x46, + 0x6f, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x51, + 0x75, + 0x6f, + 0x78, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x32, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x51, + 0x75, + 0x6f, + 0x78, + 0x32, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x32, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "43715bb5e8ecbe7659e99dcb33abcf1ffeadff8e"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -483,6 +636,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetFooResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -616,6 +770,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetFooResult); + public GetFooResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -732,9 +887,9 @@ namespace Foo.Bar.State } public global::System.Collections.Generic.IReadOnlyList? Foo { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetFooResultInfo(Foo, _entityIds, version); @@ -888,7 +1043,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Foo { get; } } @@ -903,7 +1057,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Foo { get; } } @@ -954,7 +1107,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.UnionListInEntity.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.UnionListInEntity.snap index 00dfa43b5ac..d100887235f 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.UnionListInEntity.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.UnionListInEntity.snap @@ -517,8 +517,200 @@ namespace Foo.Bar public static GetFooQueryDocument Instance { get; } = new GetFooQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x6f, 0x20, 0x7b, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x51, 0x75, 0x6f, 0x78, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x32, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x51, 0x75, 0x6f, 0x78, 0x32, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x7a, 0x32, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x46, + 0x6f, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x74, + 0x65, + 0x73, + 0x74, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x51, + 0x75, + 0x6f, + 0x78, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x32, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x51, + 0x75, + 0x6f, + 0x78, + 0x32, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x7a, + 0x32, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x54, + 0x65, + 0x73, + 0x74, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "37a8a7754957a9a3183a938a539273990e84c894"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -574,6 +766,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetFooResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -723,6 +916,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetFooResult); + public GetFooResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -773,9 +967,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? Test { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetFooResultInfo(Test, _entityIds, version); @@ -960,7 +1154,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Foo { get; } } @@ -975,7 +1168,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Foo { get; } } @@ -1132,7 +1324,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.UnionWithNestedObject.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.UnionWithNestedObject.snap index 639785d611f..a94a3f81595 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.UnionWithNestedObject.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/EntityOrIdGeneratorTests.UnionWithNestedObject.snap @@ -218,7 +218,6 @@ namespace Foo.Bar } public global::Foo.Bar.ErrorCode? Code { get; } - public global::System.String? Message { get; } public virtual global::System.Boolean Equals(StoreUserSettingFor_StoreUserSettingFor_Errors_ErrorNode? other) @@ -313,7 +312,6 @@ namespace Foo.Bar public partial interface IStoreUserSettingFor_StoreUserSettingFor_Errors { public global::Foo.Bar.ErrorCode? Code { get; } - public global::System.String? Message { get; } } @@ -329,6 +327,7 @@ namespace Foo.Bar { private global::StrawberryShake.Serialization.IInputValueFormatter _stringFormatter = default !; public global::System.String TypeName => "StoreUserSettingForInput"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _stringFormatter = serializerResolver.GetInputValueFormatter("String"); @@ -465,6 +464,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IStoreUserSettingForInputInfo.IsPortalSet => _set_portal; + public global::System.String? Mobile { get => _value_mobile; @@ -493,6 +493,7 @@ namespace Foo.Bar public partial class ErrorCodeSerializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "ErrorCode"; + public ErrorCode Parse(global::System.String serializedValue) { return serializedValue switch @@ -549,8 +550,333 @@ namespace Foo.Bar public static StoreUserSettingForMutationDocument Instance { get; } = new StoreUserSettingForMutationDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Mutation; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x46, 0x6f, 0x72, 0x28, 0x24, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x3a, 0x20, 0x49, 0x6e, 0x74, 0x21, 0x2c, 0x20, 0x24, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x65, 0x72, 0x49, 0x64, 0x3a, 0x20, 0x49, 0x6e, 0x74, 0x21, 0x2c, 0x20, 0x24, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x46, 0x6f, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x46, 0x6f, 0x72, 0x28, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x3a, 0x20, 0x24, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x2c, 0x20, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x65, 0x72, 0x49, 0x64, 0x3a, 0x20, 0x24, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x65, 0x72, 0x49, 0x64, 0x2c, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x24, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x20, 0x7b, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x6d, + 0x75, + 0x74, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x53, + 0x74, + 0x6f, + 0x72, + 0x65, + 0x55, + 0x73, + 0x65, + 0x72, + 0x53, + 0x65, + 0x74, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x46, + 0x6f, + 0x72, + 0x28, + 0x24, + 0x75, + 0x73, + 0x65, + 0x72, + 0x49, + 0x64, + 0x3a, + 0x20, + 0x49, + 0x6e, + 0x74, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x63, + 0x75, + 0x73, + 0x74, + 0x6f, + 0x6d, + 0x65, + 0x72, + 0x49, + 0x64, + 0x3a, + 0x20, + 0x49, + 0x6e, + 0x74, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x3a, + 0x20, + 0x53, + 0x74, + 0x6f, + 0x72, + 0x65, + 0x55, + 0x73, + 0x65, + 0x72, + 0x53, + 0x65, + 0x74, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x46, + 0x6f, + 0x72, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x74, + 0x6f, + 0x72, + 0x65, + 0x55, + 0x73, + 0x65, + 0x72, + 0x53, + 0x65, + 0x74, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x46, + 0x6f, + 0x72, + 0x28, + 0x75, + 0x73, + 0x65, + 0x72, + 0x49, + 0x64, + 0x3a, + 0x20, + 0x24, + 0x75, + 0x73, + 0x65, + 0x72, + 0x49, + 0x64, + 0x2c, + 0x20, + 0x63, + 0x75, + 0x73, + 0x74, + 0x6f, + 0x6d, + 0x65, + 0x72, + 0x49, + 0x64, + 0x3a, + 0x20, + 0x24, + 0x63, + 0x75, + 0x73, + 0x74, + 0x6f, + 0x6d, + 0x65, + 0x72, + 0x49, + 0x64, + 0x2c, + 0x20, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x55, + 0x73, + 0x65, + 0x72, + 0x53, + 0x65, + 0x74, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x53, + 0x75, + 0x63, + 0x63, + 0x65, + 0x73, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x55, + 0x73, + 0x65, + 0x72, + 0x53, + 0x65, + 0x74, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x45, + 0x72, + 0x72, + 0x6f, + 0x72, + 0x20, + 0x7b, + 0x20, + 0x65, + 0x72, + 0x72, + 0x6f, + 0x72, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x63, + 0x6f, + 0x64, + 0x65, + 0x20, + 0x6d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x55, + 0x73, + 0x65, + 0x72, + 0x53, + 0x65, + 0x74, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x53, + 0x75, + 0x63, + 0x63, + 0x65, + 0x73, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "f33df9abf35b15407c80896d34adc22dd9dc8695"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -599,6 +925,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IStoreUserSettingForResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Int32 userId, global::System.Int32 customerId, global::Foo.Bar.StoreUserSettingForInput input, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(userId, customerId, input); @@ -737,6 +1064,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IStoreUserSettingForResult); + public StoreUserSettingForResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -844,9 +1172,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityIdOrData StoreUserSettingFor { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new StoreUserSettingForResultInfo(StoreUserSettingFor, _entityIds, version); @@ -1041,7 +1369,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.Collections.Generic.IReadOnlyList? Errors { get; } } @@ -1057,9 +1384,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::Foo.Bar.ErrorCode? Code { get; } - public global::System.String? Message { get; } } @@ -1089,7 +1414,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ErrorGeneratorTests.Generate_ChatClient_InvalidNullCheck.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ErrorGeneratorTests.Generate_ChatClient_InvalidNullCheck.snap index 38c37ae14df..20cc6f65df3 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ErrorGeneratorTests.Generate_ChatClient_InvalidNullCheck.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ErrorGeneratorTests.Generate_ChatClient_InvalidNullCheck.snap @@ -253,12 +253,10 @@ namespace Foo.Bar /// Field name /// public global::System.String Name { get; } - /// /// Field email /// public global::System.String Email { get; } - /// /// Field isOnline /// @@ -385,12 +383,10 @@ namespace Foo.Bar /// Field name /// public global::System.String Name { get; } - /// /// Field email /// public global::System.String Email { get; } - /// /// Field isOnline /// @@ -441,8 +437,154 @@ namespace Foo.Bar public static GetPeopleQueryDocument Instance { get; } = new GetPeopleQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x20, 0x7b, 0x20, 0x6d, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x20, 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x6f, + 0x70, + 0x6c, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x6d, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x66, + 0x72, + 0x69, + 0x65, + 0x6e, + 0x64, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x6f, + 0x64, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x20, + 0x69, + 0x73, + 0x4f, + 0x6e, + 0x6c, + 0x69, + 0x6e, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "5565200bdba96e58d993331c3b46aa68011088b8"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -489,6 +631,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPeopleResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -595,13 +738,10 @@ namespace Foo.Bar.State ///Field friends public global::Foo.Bar.State.PersonConnectionData? Friends { get; } - ///Field name public global::System.String Name { get; } - ///Field email public global::System.String Email { get; } - ///Field isOnline public global::System.Boolean IsOnline { get; } } @@ -619,6 +759,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPeopleResult); + public GetPeopleResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -667,9 +808,9 @@ namespace Foo.Bar.State /// Gets the currently logged in user. /// public global::StrawberryShake.EntityId Me { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPeopleResultInfo(Me, _entityIds, version); @@ -855,7 +996,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - ///A flattened list of the nodes. public global::System.Collections.Generic.IReadOnlyList? Nodes { get; } } @@ -960,7 +1100,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ErrorGeneratorTests.Generate_NoErrors.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ErrorGeneratorTests.Generate_NoErrors.snap index 83a9b304cc9..2ff4ceef1fa 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ErrorGeneratorTests.Generate_NoErrors.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ErrorGeneratorTests.Generate_NoErrors.snap @@ -94,7 +94,6 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } public virtual global::System.Boolean Equals(GetHero_Hero_Droid? other) @@ -170,7 +169,6 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } public virtual global::System.Boolean Equals(GetHero_Hero_Human? other) @@ -247,7 +245,6 @@ namespace Foo.Bar public partial interface IGetHero_Hero { public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } } @@ -277,6 +274,7 @@ namespace Foo.Bar public partial class EpisodeSerializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "Episode"; + public Episode Parse(global::System.String serializedValue) { return serializedValue switch @@ -326,8 +324,122 @@ namespace Foo.Bar public static GetHeroQueryDocument Instance { get; } = new GetHeroQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x48, 0x65, 0x72, 0x6f, 0x20, 0x7b, 0x20, 0x68, 0x65, 0x72, 0x6f, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x4e, 0x45, 0x57, 0x5f, 0x48, 0x4f, 0x50, 0x45, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72, 0x73, 0x49, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x68, + 0x65, + 0x72, + 0x6f, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x4e, + 0x45, + 0x57, + 0x5f, + 0x48, + 0x4f, + 0x50, + 0x45, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x61, + 0x70, + 0x70, + 0x65, + 0x61, + 0x72, + 0x73, + 0x49, + 0x6e, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "a0ab93285495bb156c6c436ef4b49a3922666647"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -367,6 +479,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetHeroResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -462,7 +575,6 @@ namespace Foo.Bar.State } public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } } @@ -477,7 +589,6 @@ namespace Foo.Bar.State } public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } } @@ -496,6 +607,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetHeroResult); + public GetHeroResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -551,9 +663,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? Hero { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetHeroResultInfo(Hero, _entityIds, version); @@ -736,7 +848,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Input_Type_Fields_Are_Inspected_For_LeafTypes.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Input_Type_Fields_Are_Inspected_For_LeafTypes.snap index 41d4df86e86..61cf39b31f6 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Input_Type_Fields_Are_Inspected_For_LeafTypes.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Input_Type_Fields_Are_Inspected_For_LeafTypes.snap @@ -262,6 +262,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _intFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _stringFormatter = default !; public global::System.String TypeName => "ChangeHomePlanetInput"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _intFormatter = serializerResolver.GetInputValueFormatter("Int"); @@ -382,6 +383,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IChangeHomePlanetInputInfo.IsIdSet => _set_id; + public global::System.String HomePlanet { get => _value_homePlanet; @@ -422,8 +424,171 @@ namespace Foo.Bar public static ChangeHomePlanetMutationDocument Instance { get; } = new ChangeHomePlanetMutationDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Mutation; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x48, 0x6f, 0x6d, 0x65, 0x50, 0x6c, 0x61, 0x6e, 0x65, 0x74, 0x28, 0x24, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x48, 0x6f, 0x6d, 0x65, 0x50, 0x6c, 0x61, 0x6e, 0x65, 0x74, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x48, 0x6f, 0x6d, 0x65, 0x50, 0x6c, 0x61, 0x6e, 0x65, 0x74, 0x28, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x24, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x68, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x68, 0x6f, 0x6d, 0x65, 0x50, 0x6c, 0x61, 0x6e, 0x65, 0x74, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x6d, + 0x75, + 0x74, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x43, + 0x68, + 0x61, + 0x6e, + 0x67, + 0x65, + 0x48, + 0x6f, + 0x6d, + 0x65, + 0x50, + 0x6c, + 0x61, + 0x6e, + 0x65, + 0x74, + 0x28, + 0x24, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x3a, + 0x20, + 0x43, + 0x68, + 0x61, + 0x6e, + 0x67, + 0x65, + 0x48, + 0x6f, + 0x6d, + 0x65, + 0x50, + 0x6c, + 0x61, + 0x6e, + 0x65, + 0x74, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x63, + 0x68, + 0x61, + 0x6e, + 0x67, + 0x65, + 0x48, + 0x6f, + 0x6d, + 0x65, + 0x50, + 0x6c, + 0x61, + 0x6e, + 0x65, + 0x74, + 0x28, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x68, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x68, + 0x6f, + 0x6d, + 0x65, + 0x50, + 0x6c, + 0x61, + 0x6e, + 0x65, + 0x74, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "45cbc2ae79f3f3a850a21ba472689a46f0cc19c4"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -464,6 +629,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IChangeHomePlanetResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::Foo.Bar.ChangeHomePlanetInput input, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(input); @@ -586,6 +752,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IChangeHomePlanetResult); + public ChangeHomePlanetResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -646,9 +813,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.ChangeHomePlanetPayloadData ChangeHomePlanet { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new ChangeHomePlanetResultInfo(ChangeHomePlanet, _entityIds, version); @@ -775,7 +942,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::StrawberryShake.EntityId? Human { get; } } @@ -805,7 +971,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.KeywordCollisions.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.KeywordCollisions.snap index e3f3fc72f38..959eaa45b2a 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.KeywordCollisions.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.KeywordCollisions.snap @@ -28,7 +28,6 @@ namespace Foo.Bar } public global::Foo.Bar.IReadonly_Readonly? Readonly { get; } - public global::Foo.Bar.IReadonly_ReadonlyEntity? ReadonlyEntity { get; } public virtual global::System.Boolean Equals(ReadonlyResult? other) @@ -168,7 +167,6 @@ namespace Foo.Bar } public global::System.String? Id { get; } - public global::System.String? Abstract { get; } public virtual global::System.Boolean Equals(Readonly_ReadonlyEntity_readonlyEntity? other) @@ -236,7 +234,6 @@ namespace Foo.Bar public partial interface IReadonlyResult { public global::Foo.Bar.IReadonly_Readonly? Readonly { get; } - public global::Foo.Bar.IReadonly_ReadonlyEntity? ReadonlyEntity { get; } } @@ -258,7 +255,6 @@ namespace Foo.Bar public partial interface IReadonly_ReadonlyEntity { public global::System.String? Id { get; } - public global::System.String? Abstract { get; } } @@ -274,6 +270,7 @@ namespace Foo.Bar { private global::StrawberryShake.Serialization.IInputValueFormatter _stringFormatter = default !; public global::System.String TypeName => "abstract"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _stringFormatter = serializerResolver.GetInputValueFormatter("String"); @@ -417,8 +414,170 @@ namespace Foo.Bar public static ReadonlyQueryDocument Instance { get; } = new ReadonlyQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x52, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x28, 0x24, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x61, 0x62, 0x73, 0x74, 0x72, 0x61, 0x63, 0x74, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x72, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x28, 0x72, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x3a, 0x20, 0x24, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x61, 0x62, 0x73, 0x74, 0x72, 0x61, 0x63, 0x74, 0x20, 0x7d, 0x20, 0x72, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x64, 0x20, 0x61, 0x62, 0x73, 0x74, 0x72, 0x61, 0x63, 0x74, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x52, + 0x65, + 0x61, + 0x64, + 0x6f, + 0x6e, + 0x6c, + 0x79, + 0x28, + 0x24, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x3a, + 0x20, + 0x61, + 0x62, + 0x73, + 0x74, + 0x72, + 0x61, + 0x63, + 0x74, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x72, + 0x65, + 0x61, + 0x64, + 0x6f, + 0x6e, + 0x6c, + 0x79, + 0x28, + 0x72, + 0x65, + 0x61, + 0x64, + 0x6f, + 0x6e, + 0x6c, + 0x79, + 0x3a, + 0x20, + 0x24, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x61, + 0x62, + 0x73, + 0x74, + 0x72, + 0x61, + 0x63, + 0x74, + 0x20, + 0x7d, + 0x20, + 0x72, + 0x65, + 0x61, + 0x64, + 0x6f, + 0x6e, + 0x6c, + 0x79, + 0x45, + 0x6e, + 0x74, + 0x69, + 0x74, + 0x79, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x69, + 0x64, + 0x20, + 0x61, + 0x62, + 0x73, + 0x74, + 0x72, + 0x61, + 0x63, + 0x74, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x72, + 0x65, + 0x61, + 0x64, + 0x6f, + 0x6e, + 0x6c, + 0x79, + 0x45, + 0x6e, + 0x74, + 0x69, + 0x74, + 0x79, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "4d47a790915012cc300f474c2c7cf0e7663d0911"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -461,6 +620,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IReadonlyResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::Foo.Bar.@abstract input, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(input); @@ -569,7 +729,6 @@ namespace Foo.Bar.State } public global::System.String? Id { get; } - public global::System.String? Abstract { get; } } @@ -586,6 +745,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IReadonlyResult); + public ReadonlyResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -657,11 +817,10 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.readonlyData? Readonly { get; } - public global::StrawberryShake.EntityId? ReadonlyEntity { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new ReadonlyResultInfo(Readonly, ReadonlyEntity, _entityIds, version); @@ -786,7 +945,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Abstract { get; } } @@ -816,7 +974,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_Comments.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_Comments.snap index 641768c478f..93ca9124a72 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_Comments.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_Comments.snap @@ -97,6 +97,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _stringFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _barFormatter = default !; public global::System.String TypeName => "Bar"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _stringFormatter = serializerResolver.GetInputValueFormatter("String"); @@ -349,6 +350,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBarInfo.IsStrSet => _set_str; + ///Field strNonNullable public global::System.String StrNonNullable { @@ -361,6 +363,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBarInfo.IsStrNonNullableSet => _set_strNonNullable; + ///Field nested public global::Foo.Bar.Bar? Nested { @@ -373,6 +376,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBarInfo.IsNestedSet => _set_nested; + ///Field nestedList public global::System.Collections.Generic.IReadOnlyList NestedList { @@ -385,6 +389,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBarInfo.IsNestedListSet => _set_nestedList; + ///Field nestedMatrix public global::System.Collections.Generic.IReadOnlyList?>? NestedMatrix { @@ -417,8 +422,138 @@ namespace Foo.Bar public static TestQueryDocument Instance { get; } = new TestQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x54, 0x65, 0x73, 0x74, 0x28, 0x24, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x3a, 0x20, 0x42, 0x61, 0x72, 0x21, 0x2c, 0x20, 0x24, 0x6c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x5b, 0x42, 0x61, 0x72, 0x21, 0x5d, 0x21, 0x2c, 0x20, 0x24, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x5b, 0x5b, 0x42, 0x61, 0x72, 0x21, 0x5d, 0x5d, 0x29, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x28, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x3a, 0x20, 0x24, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x24, 0x6c, 0x69, 0x73, 0x74, 0x2c, 0x20, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x24, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x54, + 0x65, + 0x73, + 0x74, + 0x28, + 0x24, + 0x73, + 0x69, + 0x6e, + 0x67, + 0x6c, + 0x65, + 0x3a, + 0x20, + 0x42, + 0x61, + 0x72, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x5b, + 0x42, + 0x61, + 0x72, + 0x21, + 0x5d, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x5b, + 0x5b, + 0x42, + 0x61, + 0x72, + 0x21, + 0x5d, + 0x5d, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x28, + 0x73, + 0x69, + 0x6e, + 0x67, + 0x6c, + 0x65, + 0x3a, + 0x20, + 0x24, + 0x73, + 0x69, + 0x6e, + 0x67, + 0x6c, + 0x65, + 0x2c, + 0x20, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x2c, + 0x20, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x29, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "dbfdc9d76b5dee9c878b782cdb27dc4b2c339cdd"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -450,6 +585,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ITestResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::Foo.Bar.Bar single, global::System.Collections.Generic.IReadOnlyList list, global::System.Collections.Generic.IReadOnlyList?>? nestedList, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(single, list, nestedList); @@ -606,6 +742,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ITestResult); + public TestResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -641,9 +778,9 @@ namespace Foo.Bar.State } public global::System.String? Foo { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new TestResultInfo(Foo, _entityIds, version); @@ -714,7 +851,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_Comments_With_Input_Records.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_Comments_With_Input_Records.snap index c217cb07cbb..8e8041d4639 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_Comments_With_Input_Records.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_Comments_With_Input_Records.snap @@ -97,6 +97,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _stringFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _barFormatter = default !; public global::System.String TypeName => "Bar"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _stringFormatter = serializerResolver.GetInputValueFormatter("String"); @@ -329,6 +330,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBarInfo.IsStrSet => _set_str; + ///Field strNonNullable public global::System.String StrNonNullable { @@ -341,6 +343,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBarInfo.IsStrNonNullableSet => _set_strNonNullable; + ///Field nested public global::Foo.Bar.Bar? Nested { @@ -353,6 +356,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBarInfo.IsNestedSet => _set_nested; + ///Field nestedList public global::System.Collections.Generic.IReadOnlyList NestedList { @@ -365,6 +369,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBarInfo.IsNestedListSet => _set_nestedList; + ///Field nestedMatrix public global::System.Collections.Generic.IReadOnlyList?>? NestedMatrix { @@ -397,8 +402,138 @@ namespace Foo.Bar public static TestQueryDocument Instance { get; } = new TestQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x54, 0x65, 0x73, 0x74, 0x28, 0x24, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x3a, 0x20, 0x42, 0x61, 0x72, 0x21, 0x2c, 0x20, 0x24, 0x6c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x5b, 0x42, 0x61, 0x72, 0x21, 0x5d, 0x21, 0x2c, 0x20, 0x24, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x5b, 0x5b, 0x42, 0x61, 0x72, 0x21, 0x5d, 0x5d, 0x29, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x28, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x3a, 0x20, 0x24, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x24, 0x6c, 0x69, 0x73, 0x74, 0x2c, 0x20, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x24, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x54, + 0x65, + 0x73, + 0x74, + 0x28, + 0x24, + 0x73, + 0x69, + 0x6e, + 0x67, + 0x6c, + 0x65, + 0x3a, + 0x20, + 0x42, + 0x61, + 0x72, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x5b, + 0x42, + 0x61, + 0x72, + 0x21, + 0x5d, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x5b, + 0x5b, + 0x42, + 0x61, + 0x72, + 0x21, + 0x5d, + 0x5d, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x28, + 0x73, + 0x69, + 0x6e, + 0x67, + 0x6c, + 0x65, + 0x3a, + 0x20, + 0x24, + 0x73, + 0x69, + 0x6e, + 0x67, + 0x6c, + 0x65, + 0x2c, + 0x20, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x2c, + 0x20, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x29, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "dbfdc9d76b5dee9c878b782cdb27dc4b2c339cdd"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -430,6 +565,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ITestResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::Foo.Bar.Bar single, global::System.Collections.Generic.IReadOnlyList list, global::System.Collections.Generic.IReadOnlyList?>? nestedList, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(single, list, nestedList); @@ -586,6 +722,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ITestResult); + public TestResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -621,9 +758,9 @@ namespace Foo.Bar.State } public global::System.String? Foo { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new TestResultInfo(Foo, _entityIds, version); @@ -694,7 +831,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_ComplexInputTypes.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_ComplexInputTypes.snap index aeaeb3098a4..c1e987386bb 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_ComplexInputTypes.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_ComplexInputTypes.snap @@ -99,6 +99,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _uploadFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _photoFormatter = default !; public global::System.String TypeName => "User"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _stringFormatter = serializerResolver.GetInputValueFormatter("String"); @@ -282,6 +283,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IUserInfo.IsUsernameSet => _set_username; + public global::Foo.Bar.Address Address { get => _value_address; @@ -293,6 +295,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IUserInfo.IsAddressSet => _set_address; + public global::StrawberryShake.Upload ProfilePicture { get => _value_profilePicture; @@ -304,6 +307,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IUserInfo.IsProfilePictureSet => _set_profilePicture; + public global::System.Collections.Generic.IReadOnlyList? Photos { get => _value_photos; @@ -323,6 +327,7 @@ namespace Foo.Bar { private global::StrawberryShake.Serialization.IInputValueFormatter _stringFormatter = default !; public global::System.String TypeName => "Address"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _stringFormatter = serializerResolver.GetInputValueFormatter("String"); @@ -438,6 +443,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _uploadFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _photoMetadataFormatter = default !; public global::System.String TypeName => "Photo"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _uploadFormatter = serializerResolver.GetInputValueFormatter("Upload"); @@ -564,6 +570,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IPhotoInfo.IsDataSet => _set_data; + public global::Foo.Bar.PhotoMetadata? Metadata { get => _value_metadata; @@ -583,6 +590,7 @@ namespace Foo.Bar { private global::StrawberryShake.Serialization.IInputValueFormatter _uploadFormatter = default !; public global::System.String TypeName => "PhotoMetadata"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _uploadFormatter = serializerResolver.GetInputValueFormatter("Upload"); @@ -704,8 +712,59 @@ namespace Foo.Bar public static TestQueryDocument Instance { get; } = new TestQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x54, 0x65, 0x73, 0x74, 0x28, 0x24, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x55, 0x73, 0x65, 0x72, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x28, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x24, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x29, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x54, + 0x65, + 0x73, + 0x74, + 0x28, + 0x24, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x3a, + 0x20, + 0x55, + 0x73, + 0x65, + 0x72, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x28, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x29, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "7b9a0178a8a9bf466cf7d2111c420bf1a948b822"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -737,6 +796,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ITestResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::Foo.Bar.User input, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(input); @@ -885,6 +945,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ITestResult); + public TestResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -920,9 +981,9 @@ namespace Foo.Bar.State } public global::System.String? Foo { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new TestResultInfo(Foo, _entityIds, version); @@ -1016,7 +1077,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_Complex_Arguments.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_Complex_Arguments.snap index fd32ce3ca52..dd4359d668a 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_Complex_Arguments.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_Complex_Arguments.snap @@ -97,6 +97,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _stringFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _barFormatter = default !; public global::System.String TypeName => "Bar"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _stringFormatter = serializerResolver.GetInputValueFormatter("String"); @@ -347,6 +348,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBarInfo.IsStrSet => _set_str; + public global::System.String StrNonNullable { get => _value_strNonNullable; @@ -358,6 +360,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBarInfo.IsStrNonNullableSet => _set_strNonNullable; + public global::Foo.Bar.Bar? Nested { get => _value_nested; @@ -369,6 +372,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBarInfo.IsNestedSet => _set_nested; + public global::System.Collections.Generic.IReadOnlyList NestedList { get => _value_nestedList; @@ -380,6 +384,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBarInfo.IsNestedListSet => _set_nestedList; + public global::System.Collections.Generic.IReadOnlyList?>? NestedMatrix { get => _value_nestedMatrix; @@ -411,8 +416,138 @@ namespace Foo.Bar public static TestQueryDocument Instance { get; } = new TestQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x54, 0x65, 0x73, 0x74, 0x28, 0x24, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x3a, 0x20, 0x42, 0x61, 0x72, 0x21, 0x2c, 0x20, 0x24, 0x6c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x5b, 0x42, 0x61, 0x72, 0x21, 0x5d, 0x21, 0x2c, 0x20, 0x24, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x5b, 0x5b, 0x42, 0x61, 0x72, 0x21, 0x5d, 0x5d, 0x29, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x28, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x3a, 0x20, 0x24, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x24, 0x6c, 0x69, 0x73, 0x74, 0x2c, 0x20, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x24, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x54, + 0x65, + 0x73, + 0x74, + 0x28, + 0x24, + 0x73, + 0x69, + 0x6e, + 0x67, + 0x6c, + 0x65, + 0x3a, + 0x20, + 0x42, + 0x61, + 0x72, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x5b, + 0x42, + 0x61, + 0x72, + 0x21, + 0x5d, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x5b, + 0x5b, + 0x42, + 0x61, + 0x72, + 0x21, + 0x5d, + 0x5d, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x28, + 0x73, + 0x69, + 0x6e, + 0x67, + 0x6c, + 0x65, + 0x3a, + 0x20, + 0x24, + 0x73, + 0x69, + 0x6e, + 0x67, + 0x6c, + 0x65, + 0x2c, + 0x20, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x2c, + 0x20, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x29, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "dbfdc9d76b5dee9c878b782cdb27dc4b2c339cdd"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -444,6 +579,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ITestResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::Foo.Bar.Bar single, global::System.Collections.Generic.IReadOnlyList list, global::System.Collections.Generic.IReadOnlyList?>? nestedList, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(single, list, nestedList); @@ -600,6 +736,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ITestResult); + public TestResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -635,9 +772,9 @@ namespace Foo.Bar.State } public global::System.String? Foo { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new TestResultInfo(Foo, _entityIds, version); @@ -708,7 +845,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_FirstNonUpload.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_FirstNonUpload.snap index 93d11231eef..9a22b1c52bb 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_FirstNonUpload.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_FirstNonUpload.snap @@ -108,8 +108,99 @@ namespace Foo.Bar public static TestQueryDocument Instance { get; } = new TestQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x54, 0x65, 0x73, 0x74, 0x28, 0x24, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x21, 0x2c, 0x20, 0x24, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x20, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x28, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x24, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x20, 0x24, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x29, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x54, + 0x65, + 0x73, + 0x74, + 0x28, + 0x24, + 0x73, + 0x74, + 0x72, + 0x69, + 0x6e, + 0x67, + 0x3a, + 0x20, + 0x53, + 0x74, + 0x72, + 0x69, + 0x6e, + 0x67, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x75, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x3a, + 0x20, + 0x55, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x28, + 0x73, + 0x74, + 0x72, + 0x69, + 0x6e, + 0x67, + 0x3a, + 0x20, + 0x24, + 0x73, + 0x74, + 0x72, + 0x69, + 0x6e, + 0x67, + 0x2c, + 0x20, + 0x75, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x3a, + 0x20, + 0x24, + 0x75, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x29, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "35fd86e6fe5db1cabaf4a986a0976560add8afcd"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -143,6 +234,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ITestResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.String @string, global::StrawberryShake.Upload upload, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(@string, upload); @@ -253,6 +345,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ITestResult); + public TestResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -288,9 +381,9 @@ namespace Foo.Bar.State } public global::System.String? Foo { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new TestResultInfo(Foo, _entityIds, version); @@ -348,7 +441,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_LastNonUpload.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_LastNonUpload.snap index 32ef5ec10cf..0e4f4acf01c 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_LastNonUpload.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_LastNonUpload.snap @@ -108,8 +108,99 @@ namespace Foo.Bar public static TestQueryDocument Instance { get; } = new TestQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x54, 0x65, 0x73, 0x74, 0x28, 0x24, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x20, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x21, 0x2c, 0x20, 0x24, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x28, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x24, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2c, 0x20, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x20, 0x24, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x29, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x54, + 0x65, + 0x73, + 0x74, + 0x28, + 0x24, + 0x75, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x3a, + 0x20, + 0x55, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x73, + 0x74, + 0x72, + 0x69, + 0x6e, + 0x67, + 0x3a, + 0x20, + 0x53, + 0x74, + 0x72, + 0x69, + 0x6e, + 0x67, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x28, + 0x73, + 0x74, + 0x72, + 0x69, + 0x6e, + 0x67, + 0x3a, + 0x20, + 0x24, + 0x73, + 0x74, + 0x72, + 0x69, + 0x6e, + 0x67, + 0x2c, + 0x20, + 0x75, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x3a, + 0x20, + 0x24, + 0x75, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x29, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "923286e3634ae4122c172e158505589d75ff1363"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -143,6 +234,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ITestResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::StrawberryShake.Upload upload, global::System.String @string, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(upload, @string); @@ -253,6 +345,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ITestResult); + public TestResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -288,9 +381,9 @@ namespace Foo.Bar.State } public global::System.String? Foo { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new TestResultInfo(Foo, _entityIds, version); @@ -348,7 +441,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_UploadAsArg.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_UploadAsArg.snap index 8890a57cb85..0ed186152f1 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_UploadAsArg.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_UploadAsArg.snap @@ -108,8 +108,337 @@ namespace Foo.Bar public static TestQueryDocument Instance { get; } = new TestQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x54, 0x65, 0x73, 0x74, 0x28, 0x24, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x20, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x21, 0x2c, 0x20, 0x24, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x75, 0x6c, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x3a, 0x20, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x2c, 0x20, 0x24, 0x6c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x5b, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x21, 0x5d, 0x21, 0x2c, 0x20, 0x24, 0x6c, 0x69, 0x73, 0x74, 0x4e, 0x75, 0x6c, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x3a, 0x20, 0x5b, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x21, 0x5d, 0x2c, 0x20, 0x24, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x5b, 0x5b, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x21, 0x5d, 0x21, 0x5d, 0x21, 0x2c, 0x20, 0x24, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x75, 0x6c, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x3a, 0x20, 0x5b, 0x5b, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x21, 0x5d, 0x5d, 0x29, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x28, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x20, 0x24, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x2c, 0x20, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x75, 0x6c, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x3a, 0x20, 0x24, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x75, 0x6c, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x24, 0x6c, 0x69, 0x73, 0x74, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x4e, 0x75, 0x6c, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x3a, 0x20, 0x24, 0x6c, 0x69, 0x73, 0x74, 0x4e, 0x75, 0x6c, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x2c, 0x20, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x24, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x2c, 0x20, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x75, 0x6c, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x3a, 0x20, 0x24, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x75, 0x6c, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x29, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x54, + 0x65, + 0x73, + 0x74, + 0x28, + 0x24, + 0x75, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x3a, + 0x20, + 0x55, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x75, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x4e, + 0x75, + 0x6c, + 0x6c, + 0x61, + 0x62, + 0x6c, + 0x65, + 0x3a, + 0x20, + 0x55, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x2c, + 0x20, + 0x24, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x5b, + 0x55, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x21, + 0x5d, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x4e, + 0x75, + 0x6c, + 0x6c, + 0x61, + 0x62, + 0x6c, + 0x65, + 0x3a, + 0x20, + 0x5b, + 0x55, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x21, + 0x5d, + 0x2c, + 0x20, + 0x24, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x5b, + 0x5b, + 0x55, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x21, + 0x5d, + 0x21, + 0x5d, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x4e, + 0x75, + 0x6c, + 0x6c, + 0x61, + 0x62, + 0x6c, + 0x65, + 0x3a, + 0x20, + 0x5b, + 0x5b, + 0x55, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x21, + 0x5d, + 0x5d, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x28, + 0x75, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x3a, + 0x20, + 0x24, + 0x75, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x2c, + 0x20, + 0x75, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x4e, + 0x75, + 0x6c, + 0x6c, + 0x61, + 0x62, + 0x6c, + 0x65, + 0x3a, + 0x20, + 0x24, + 0x75, + 0x70, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x4e, + 0x75, + 0x6c, + 0x6c, + 0x61, + 0x62, + 0x6c, + 0x65, + 0x2c, + 0x20, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x2c, + 0x20, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x4e, + 0x75, + 0x6c, + 0x6c, + 0x61, + 0x62, + 0x6c, + 0x65, + 0x3a, + 0x20, + 0x24, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x4e, + 0x75, + 0x6c, + 0x6c, + 0x61, + 0x62, + 0x6c, + 0x65, + 0x2c, + 0x20, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x2c, + 0x20, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x4e, + 0x75, + 0x6c, + 0x6c, + 0x61, + 0x62, + 0x6c, + 0x65, + 0x3a, + 0x20, + 0x24, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x4e, + 0x75, + 0x6c, + 0x6c, + 0x61, + 0x62, + 0x6c, + 0x65, + 0x29, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "dd7323e985f8e99eb299dff04bcbef6ee01dcec9"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -141,6 +470,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ITestResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::StrawberryShake.Upload upload, global::StrawberryShake.Upload? uploadNullable, global::System.Collections.Generic.IReadOnlyList list, global::System.Collections.Generic.IReadOnlyList? listNullable, global::System.Collections.Generic.IReadOnlyList> nestedList, global::System.Collections.Generic.IReadOnlyList?>? nestedListNullable, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(upload, uploadNullable, list, listNullable, nestedList, nestedListNullable); @@ -412,6 +742,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ITestResult); + public TestResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -447,9 +778,9 @@ namespace Foo.Bar.State } public global::System.String? Foo { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new TestResultInfo(Foo, _entityIds, version); @@ -507,7 +838,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_UploadInDeepInputObject.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_UploadInDeepInputObject.snap index edea5105178..53d22a6ec27 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_UploadInDeepInputObject.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_UploadInDeepInputObject.snap @@ -96,6 +96,7 @@ namespace Foo.Bar { private global::StrawberryShake.Serialization.IInputValueFormatter _barFormatter = default !; public global::System.String TypeName => "Test"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _barFormatter = serializerResolver.GetInputValueFormatter("Bar"); @@ -210,6 +211,7 @@ namespace Foo.Bar { private global::StrawberryShake.Serialization.IInputValueFormatter _bazFormatter = default !; public global::System.String TypeName => "Bar"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _bazFormatter = serializerResolver.GetInputValueFormatter("Baz"); @@ -324,6 +326,7 @@ namespace Foo.Bar { private global::StrawberryShake.Serialization.IInputValueFormatter _quxFormatter = default !; public global::System.String TypeName => "Baz"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _quxFormatter = serializerResolver.GetInputValueFormatter("Qux"); @@ -438,6 +441,7 @@ namespace Foo.Bar { private global::StrawberryShake.Serialization.IInputValueFormatter _uploadFormatter = default !; public global::System.String TypeName => "Qux"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _uploadFormatter = serializerResolver.GetInputValueFormatter("Upload"); @@ -559,8 +563,59 @@ namespace Foo.Bar public static TestQueryDocument Instance { get; } = new TestQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x54, 0x65, 0x73, 0x74, 0x28, 0x24, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x54, 0x65, 0x73, 0x74, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x28, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x24, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x29, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x54, + 0x65, + 0x73, + 0x74, + 0x28, + 0x24, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x3a, + 0x20, + 0x54, + 0x65, + 0x73, + 0x74, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x28, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x29, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "752fe96425b2cd20ad0beb97fb2d8ebe909c0ace"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -592,6 +647,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ITestResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::Foo.Bar.Test input, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(input); @@ -736,6 +792,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ITestResult); + public Test_1Result Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -771,9 +828,9 @@ namespace Foo.Bar.State } public global::System.String? Foo { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new Test_1ResultInfo(Foo, _entityIds, version); @@ -859,7 +916,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_UploadInInputObject.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_UploadInInputObject.snap index 673aaf91318..4cf653f331f 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_UploadInInputObject.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/InputGeneratorTests.Operation_With_UploadInInputObject.snap @@ -96,6 +96,7 @@ namespace Foo.Bar { private global::StrawberryShake.Serialization.IInputValueFormatter _uploadFormatter = default !; public global::System.String TypeName => "Test"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _uploadFormatter = serializerResolver.GetInputValueFormatter("Upload"); @@ -217,8 +218,59 @@ namespace Foo.Bar public static TestQueryDocument Instance { get; } = new TestQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x54, 0x65, 0x73, 0x74, 0x28, 0x24, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x54, 0x65, 0x73, 0x74, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x28, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x24, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x29, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x54, + 0x65, + 0x73, + 0x74, + 0x28, + 0x24, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x3a, + 0x20, + 0x54, + 0x65, + 0x73, + 0x74, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x28, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x29, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "752fe96425b2cd20ad0beb97fb2d8ebe909c0ace"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -250,6 +302,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ITestResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::Foo.Bar.Test input, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(input); @@ -364,6 +417,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ITestResult); + public Test_1Result Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -399,9 +453,9 @@ namespace Foo.Bar.State } public global::System.String? Foo { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new Test_1ResultInfo(Foo, _entityIds, version); @@ -466,7 +520,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.Generate_StarWarsIntegrationTest.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.Generate_StarWarsIntegrationTest.snap index db14cc06dc2..72a5fe09bf1 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.Generate_StarWarsIntegrationTest.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.Generate_StarWarsIntegrationTest.snap @@ -326,8 +326,148 @@ namespace Foo.Bar public static CreateReviewMutationDocument Instance { get; } = new CreateReviewMutationDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Mutation; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x28, 0x24, 0x73, 0x74, 0x61, 0x72, 0x73, 0x3a, 0x20, 0x49, 0x6e, 0x74, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x45, 0x4d, 0x50, 0x49, 0x52, 0x45, 0x2c, 0x20, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x3a, 0x20, 0x7b, 0x20, 0x73, 0x74, 0x61, 0x72, 0x73, 0x3a, 0x20, 0x24, 0x73, 0x74, 0x61, 0x72, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x3a, 0x20, 0x22, 0x67, 0x6f, 0x6f, 0x64, 0x22, 0x20, 0x7d, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x74, 0x61, 0x72, 0x73, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x6d, + 0x75, + 0x74, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x43, + 0x72, + 0x65, + 0x61, + 0x74, + 0x65, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x28, + 0x24, + 0x73, + 0x74, + 0x61, + 0x72, + 0x73, + 0x3a, + 0x20, + 0x49, + 0x6e, + 0x74, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x63, + 0x72, + 0x65, + 0x61, + 0x74, + 0x65, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x45, + 0x4d, + 0x50, + 0x49, + 0x52, + 0x45, + 0x2c, + 0x20, + 0x72, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x3a, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x74, + 0x61, + 0x72, + 0x73, + 0x3a, + 0x20, + 0x24, + 0x73, + 0x74, + 0x61, + 0x72, + 0x73, + 0x2c, + 0x20, + 0x63, + 0x6f, + 0x6d, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x61, + 0x72, + 0x79, + 0x3a, + 0x20, + 0x22, + 0x67, + 0x6f, + 0x6f, + 0x64, + 0x22, + 0x20, + 0x7d, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x73, + 0x74, + 0x61, + 0x72, + 0x73, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "409320ca8b7539016115e55a6bbeceeae0184bdf"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -362,6 +502,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ICreateReviewResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Int32 stars, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(stars); @@ -437,8 +578,83 @@ namespace Foo.Bar public static OnReviewSubscriptionDocument Instance { get; } = new OnReviewSubscriptionDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Subscription; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4f, 0x6e, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x20, 0x7b, 0x20, 0x6f, 0x6e, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x45, 0x4d, 0x50, 0x49, 0x52, 0x45, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x74, 0x61, 0x72, 0x73, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x73, + 0x75, + 0x62, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x4f, + 0x6e, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x20, + 0x7b, + 0x20, + 0x6f, + 0x6e, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x45, + 0x4d, + 0x50, + 0x49, + 0x52, + 0x45, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x73, + 0x74, + 0x61, + 0x72, + 0x73, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "57bd8aa0a7a43840c2b938fb5b484014327cf406"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -471,6 +687,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IOnReviewResult); + public global::System.IObservable> Watch(global::StrawberryShake.ExecutionStrategy? strategy = null) { var request = CreateRequest(); @@ -555,6 +772,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ICreateReviewResult); + public CreateReviewResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (dataInfo is CreateReviewResultInfo info) @@ -596,9 +814,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.ReviewData CreateReview { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => global::System.Array.Empty(); public global::System.UInt64 Version => 0; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new CreateReviewResultInfo(CreateReview); @@ -614,6 +832,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IOnReviewResult); + public OnReviewResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (dataInfo is OnReviewResultInfo info) @@ -655,9 +874,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.ReviewData OnReview { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => global::System.Array.Empty(); public global::System.UInt64 Version => 0; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new OnReviewResultInfo(OnReview); @@ -785,7 +1004,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.Int32? Stars { get; } } @@ -796,6 +1014,7 @@ namespace Foo.Bar.State public global::StrawberryShake.IOperationStore OperationStore => throw new global::System.NotSupportedException("OperationStore is not supported in store less mode"); public global::StrawberryShake.IEntityStore EntityStore => throw new global::System.NotSupportedException("EntityStore is not supported in store less mode"); public global::StrawberryShake.IEntityIdSerializer EntityIdSerializer => throw new global::System.NotSupportedException("EntityIdSerializer is not supported in store less mode"); + public global::StrawberryShake.IOperationRequestFactory GetOperationRequestFactory(global::System.Type resultType) { throw new global::System.NotSupportedException("GetOperationRequestFactory is not supported in store less mode"); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.Interface_With_Default_Names.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.Interface_With_Default_Names.snap index 88e8b2ddb17..152c54c80e5 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.Interface_With_Default_Names.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.Interface_With_Default_Names.snap @@ -94,7 +94,6 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } public virtual global::System.Boolean Equals(GetHero_Hero_Droid? other) @@ -170,7 +169,6 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } public virtual global::System.Boolean Equals(GetHero_Hero_Human? other) @@ -247,7 +245,6 @@ namespace Foo.Bar public partial interface IGetHero_Hero { public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } } @@ -277,6 +274,7 @@ namespace Foo.Bar public partial class EpisodeSerializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "Episode"; + public Episode Parse(global::System.String serializedValue) { return serializedValue switch @@ -320,8 +318,82 @@ namespace Foo.Bar public static GetHeroQueryDocument Instance { get; } = new GetHeroQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x48, 0x65, 0x72, 0x6f, 0x20, 0x7b, 0x20, 0x68, 0x65, 0x72, 0x6f, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x4e, 0x45, 0x57, 0x5f, 0x48, 0x4f, 0x50, 0x45, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72, 0x73, 0x49, 0x6e, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x68, + 0x65, + 0x72, + 0x6f, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x4e, + 0x45, + 0x57, + 0x5f, + 0x48, + 0x4f, + 0x50, + 0x45, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x61, + 0x70, + 0x70, + 0x65, + 0x61, + 0x72, + 0x73, + 0x49, + 0x6e, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "3af8162f0dd3bae23cda79298068608839991c65"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -355,6 +427,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetHeroResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -442,6 +515,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetHeroResult); + public GetHeroResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (dataInfo is GetHeroResultInfo info) @@ -492,9 +566,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.ICharacterData? Hero { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => global::System.Array.Empty(); public global::System.UInt64 Version => 0; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetHeroResultInfo(Hero); @@ -625,9 +699,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } } @@ -643,9 +715,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } } @@ -656,6 +726,7 @@ namespace Foo.Bar.State public global::StrawberryShake.IOperationStore OperationStore => throw new global::System.NotSupportedException("OperationStore is not supported in store less mode"); public global::StrawberryShake.IEntityStore EntityStore => throw new global::System.NotSupportedException("EntityStore is not supported in store less mode"); public global::StrawberryShake.IEntityIdSerializer EntityIdSerializer => throw new global::System.NotSupportedException("EntityIdSerializer is not supported in store less mode"); + public global::StrawberryShake.IOperationRequestFactory GetOperationRequestFactory(global::System.Type resultType) { throw new global::System.NotSupportedException("GetOperationRequestFactory is not supported in store less mode"); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.Interface_With_Fragment_Definition_Two_Models.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.Interface_With_Fragment_Definition_Two_Models.snap index b297b415594..cc00be0bd08 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.Interface_With_Fragment_Definition_Two_Models.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.Interface_With_Fragment_Definition_Two_Models.snap @@ -95,9 +95,7 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.String? PrimaryFunction { get; } - public global::Foo.Bar.IGetHero_Hero_Friends? Friends { get; } public virtual global::System.Boolean Equals(GetHero_Hero_Droid? other) @@ -173,9 +171,7 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.String? HomePlanet { get; } - public global::Foo.Bar.IGetHero_Hero_Friends? Friends { get; } public virtual global::System.Boolean Equals(GetHero_Hero_Human? other) @@ -453,7 +449,6 @@ namespace Foo.Bar public partial interface IHero { public global::System.String Name { get; } - public global::Foo.Bar.IGetHero_Hero_Friends? Friends { get; } } @@ -475,7 +470,6 @@ namespace Foo.Bar public partial interface IHero_Droid : IDroid { public global::System.String Name { get; } - public global::Foo.Bar.IGetHero_Hero_Friends? Friends { get; } } @@ -497,7 +491,6 @@ namespace Foo.Bar public partial interface IHero_Human : IHuman { public global::System.String Name { get; } - public global::Foo.Bar.IGetHero_Hero_Friends? Friends { get; } } @@ -590,8 +583,264 @@ namespace Foo.Bar public static GetHeroQueryDocument Instance { get; } = new GetHeroQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x48, 0x65, 0x72, 0x6f, 0x20, 0x7b, 0x20, 0x68, 0x65, 0x72, 0x6f, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x4e, 0x45, 0x57, 0x5f, 0x48, 0x4f, 0x50, 0x45, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x48, 0x65, 0x72, 0x6f, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x48, 0x65, 0x72, 0x6f, 0x20, 0x6f, 0x6e, 0x20, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x68, 0x6f, 0x6d, 0x65, 0x50, 0x6c, 0x61, 0x6e, 0x65, 0x74, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x68, + 0x65, + 0x72, + 0x6f, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x4e, + 0x45, + 0x57, + 0x5f, + 0x48, + 0x4f, + 0x50, + 0x45, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x43, + 0x68, + 0x61, + 0x72, + 0x61, + 0x63, + 0x74, + 0x65, + 0x72, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x66, + 0x72, + 0x69, + 0x65, + 0x6e, + 0x64, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x6f, + 0x64, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x68, + 0x6f, + 0x6d, + 0x65, + 0x50, + 0x6c, + 0x61, + 0x6e, + 0x65, + 0x74, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x72, + 0x69, + 0x6d, + 0x61, + 0x72, + 0x79, + 0x46, + 0x75, + 0x6e, + 0x63, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "7659b8661f962a731bed9124abcb483f2ea17ca7"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -645,6 +894,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetHeroResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -752,6 +1002,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetHeroResult); + public GetHeroResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (dataInfo is GetHeroResultInfo info) @@ -862,9 +1113,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.ICharacterData? Hero { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => global::System.Array.Empty(); public global::System.UInt64 Version => 0; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetHeroResultInfo(Hero); @@ -1015,11 +1266,8 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::System.String? PrimaryFunction { get; } - public global::Foo.Bar.State.FriendsConnectionData? Friends { get; } } @@ -1036,11 +1284,8 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::System.String? HomePlanet { get; } - public global::Foo.Bar.State.FriendsConnectionData? Friends { get; } } @@ -1056,7 +1301,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - ///A flattened list of the nodes. public global::System.Collections.Generic.IReadOnlyList? Nodes { get; } } @@ -1068,6 +1312,7 @@ namespace Foo.Bar.State public global::StrawberryShake.IOperationStore OperationStore => throw new global::System.NotSupportedException("OperationStore is not supported in store less mode"); public global::StrawberryShake.IEntityStore EntityStore => throw new global::System.NotSupportedException("EntityStore is not supported in store less mode"); public global::StrawberryShake.IEntityIdSerializer EntityIdSerializer => throw new global::System.NotSupportedException("EntityIdSerializer is not supported in store less mode"); + public global::StrawberryShake.IOperationRequestFactory GetOperationRequestFactory(global::System.Type resultType) { throw new global::System.NotSupportedException("GetOperationRequestFactory is not supported in store less mode"); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.Operation_With_Leaf_Argument.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.Operation_With_Leaf_Argument.snap index 78fd6c54ffe..c26c1837b81 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.Operation_With_Leaf_Argument.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.Operation_With_Leaf_Argument.snap @@ -94,7 +94,6 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } public virtual global::System.Boolean Equals(GetHero_Hero_Droid? other) @@ -170,7 +169,6 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } public virtual global::System.Boolean Equals(GetHero_Hero_Human? other) @@ -247,7 +245,6 @@ namespace Foo.Bar public partial interface IGetHero_Hero { public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } } @@ -277,6 +274,7 @@ namespace Foo.Bar public partial class EpisodeSerializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "Episode"; + public Episode Parse(global::System.String serializedValue) { return serializedValue switch @@ -320,8 +318,101 @@ namespace Foo.Bar public static GetHeroQueryDocument Instance { get; } = new GetHeroQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x48, 0x65, 0x72, 0x6f, 0x28, 0x24, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x45, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x29, 0x20, 0x7b, 0x20, 0x68, 0x65, 0x72, 0x6f, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x24, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72, 0x73, 0x49, 0x6e, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x28, + 0x24, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x45, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x68, + 0x65, + 0x72, + 0x6f, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x24, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x61, + 0x70, + 0x70, + 0x65, + 0x61, + 0x72, + 0x73, + 0x49, + 0x6e, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "25b97a7cb87d66116635786c695e1e7f868dc0ee"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -357,6 +448,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetHeroResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::Foo.Bar.Episode? episode, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(episode); @@ -458,6 +550,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetHeroResult); + public GetHeroResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (dataInfo is GetHeroResultInfo info) @@ -508,9 +601,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.ICharacterData? Hero { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => global::System.Array.Empty(); public global::System.UInt64 Version => 0; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetHeroResultInfo(Hero); @@ -641,9 +734,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } } @@ -659,9 +750,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } } @@ -672,6 +761,7 @@ namespace Foo.Bar.State public global::StrawberryShake.IOperationStore OperationStore => throw new global::System.NotSupportedException("OperationStore is not supported in store less mode"); public global::StrawberryShake.IEntityStore EntityStore => throw new global::System.NotSupportedException("EntityStore is not supported in store less mode"); public global::StrawberryShake.IEntityIdSerializer EntityIdSerializer => throw new global::System.NotSupportedException("EntityIdSerializer is not supported in store less mode"); + public global::StrawberryShake.IOperationRequestFactory GetOperationRequestFactory(global::System.Type resultType) { throw new global::System.NotSupportedException("GetOperationRequestFactory is not supported in store less mode"); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.Operation_With_Type_Argument.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.Operation_With_Type_Argument.snap index 48c0739972b..6583e360d34 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.Operation_With_Type_Argument.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.Operation_With_Type_Argument.snap @@ -90,7 +90,6 @@ namespace Foo.Bar } public global::System.Int32 Stars { get; } - public global::System.String? Commentary { get; } public virtual global::System.Boolean Equals(CreateReviewMut_CreateReview_Review? other) @@ -161,7 +160,6 @@ namespace Foo.Bar public partial interface ICreateReviewMut_CreateReview { public global::System.Int32 Stars { get; } - public global::System.String? Commentary { get; } } @@ -178,6 +176,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _intFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _stringFormatter = default !; public global::System.String TypeName => "ReviewInput"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _intFormatter = serializerResolver.GetInputValueFormatter("Int"); @@ -304,6 +303,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IReviewInputInfo.IsStarsSet => _set_stars; + public global::System.String? Commentary { get => _value_commentary; @@ -331,6 +331,7 @@ namespace Foo.Bar public partial class EpisodeSerializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "Episode"; + public Episode Parse(global::System.String serializedValue) { return serializedValue switch @@ -374,8 +375,163 @@ namespace Foo.Bar public static CreateReviewMutMutationDocument Instance { get; } = new CreateReviewMutMutationDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Mutation; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x4d, 0x75, 0x74, 0x28, 0x24, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x45, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x21, 0x2c, 0x20, 0x24, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x3a, 0x20, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x24, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x3a, 0x20, 0x24, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x74, 0x61, 0x72, 0x73, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x6d, + 0x75, + 0x74, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x43, + 0x72, + 0x65, + 0x61, + 0x74, + 0x65, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x4d, + 0x75, + 0x74, + 0x28, + 0x24, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x45, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x72, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x3a, + 0x20, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x63, + 0x72, + 0x65, + 0x61, + 0x74, + 0x65, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x24, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x2c, + 0x20, + 0x72, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x3a, + 0x20, + 0x24, + 0x72, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x73, + 0x74, + 0x61, + 0x72, + 0x73, + 0x20, + 0x63, + 0x6f, + 0x6d, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x61, + 0x72, + 0x79, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "7b7488dce3bce5700fe4fab0d349728a5121c153"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -413,6 +569,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ICreateReviewMutResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::Foo.Bar.Episode episode, global::Foo.Bar.ReviewInput review, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(episode, review); @@ -518,6 +675,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ICreateReviewMutResult); + public CreateReviewMutResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (dataInfo is CreateReviewMutResultInfo info) @@ -559,9 +717,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.ReviewData CreateReview { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => global::System.Array.Empty(); public global::System.UInt64 Version => 0; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new CreateReviewMutResultInfo(CreateReview); @@ -663,9 +821,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.Int32? Stars { get; } - public global::System.String? Commentary { get; } } @@ -676,6 +832,7 @@ namespace Foo.Bar.State public global::StrawberryShake.IOperationStore OperationStore => throw new global::System.NotSupportedException("OperationStore is not supported in store less mode"); public global::StrawberryShake.IEntityStore EntityStore => throw new global::System.NotSupportedException("EntityStore is not supported in store less mode"); public global::StrawberryShake.IEntityIdSerializer EntityIdSerializer => throw new global::System.NotSupportedException("EntityIdSerializer is not supported in store less mode"); + public global::StrawberryShake.IOperationRequestFactory GetOperationRequestFactory(global::System.Type resultType) { throw new global::System.NotSupportedException("GetOperationRequestFactory is not supported in store less mode"); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.StarWarsTypeNameOnUnions.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.StarWarsTypeNameOnUnions.snap index e45c34b977f..21eb46a3b03 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.StarWarsTypeNameOnUnions.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.StarWarsTypeNameOnUnions.snap @@ -336,8 +336,64 @@ namespace Foo.Bar public static SearchHeroQueryDocument Instance { get; } = new SearchHeroQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x48, 0x65, 0x72, 0x6f, 0x20, 0x7b, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x28, 0x74, 0x65, 0x78, 0x74, 0x3a, 0x20, 0x22, 0x6c, 0x22, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x53, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x28, + 0x74, + 0x65, + 0x78, + 0x74, + 0x3a, + 0x20, + 0x22, + 0x6c, + 0x22, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "a7b73fff8132c809064a1f07d9a51ad34a90b3ff"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -369,6 +425,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ISearchHeroResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -454,6 +511,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ISearchHeroResult); + public SearchHeroResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (dataInfo is SearchHeroResultInfo info) @@ -524,9 +582,9 @@ namespace Foo.Bar.State } public global::System.Collections.Generic.IReadOnlyList? Search { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => global::System.Array.Empty(); public global::System.UInt64 Version => 0; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new SearchHeroResultInfo(Search); @@ -676,6 +734,7 @@ namespace Foo.Bar.State public global::StrawberryShake.IOperationStore OperationStore => throw new global::System.NotSupportedException("OperationStore is not supported in store less mode"); public global::StrawberryShake.IEntityStore EntityStore => throw new global::System.NotSupportedException("EntityStore is not supported in store less mode"); public global::StrawberryShake.IEntityIdSerializer EntityIdSerializer => throw new global::System.NotSupportedException("EntityIdSerializer is not supported in store less mode"); + public global::StrawberryShake.IOperationRequestFactory GetOperationRequestFactory(global::System.Type resultType) { throw new global::System.NotSupportedException("GetOperationRequestFactory is not supported in store less mode"); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.StarWarsUnionList.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.StarWarsUnionList.snap index c7a17208f2a..f26cbfdc318 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.StarWarsUnionList.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.StarWarsUnionList.snap @@ -330,8 +330,108 @@ namespace Foo.Bar public static SearchHeroQueryDocument Instance { get; } = new SearchHeroQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x48, 0x65, 0x72, 0x6f, 0x20, 0x7b, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x28, 0x74, 0x65, 0x78, 0x74, 0x3a, 0x20, 0x22, 0x6c, 0x22, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x53, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x28, + 0x74, + 0x65, + 0x78, + 0x74, + 0x3a, + 0x20, + 0x22, + 0x6c, + 0x22, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "2918628297e45b19c18b1edaf728b18d712f8db1"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -369,6 +469,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ISearchHeroResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -460,6 +561,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ISearchHeroResult); + public SearchHeroResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (dataInfo is SearchHeroResultInfo info) @@ -530,9 +632,9 @@ namespace Foo.Bar.State } public global::System.Collections.Generic.IReadOnlyList? Search { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => global::System.Array.Empty(); public global::System.UInt64 Version => 0; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new SearchHeroResultInfo(Search); @@ -662,7 +764,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Name { get; } } @@ -677,7 +778,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Name { get; } } @@ -688,6 +788,7 @@ namespace Foo.Bar.State public global::StrawberryShake.IOperationStore OperationStore => throw new global::System.NotSupportedException("OperationStore is not supported in store less mode"); public global::StrawberryShake.IEntityStore EntityStore => throw new global::System.NotSupportedException("EntityStore is not supported in store less mode"); public global::StrawberryShake.IEntityIdSerializer EntityIdSerializer => throw new global::System.NotSupportedException("EntityIdSerializer is not supported in store less mode"); + public global::StrawberryShake.IOperationRequestFactory GetOperationRequestFactory(global::System.Type resultType) { throw new global::System.NotSupportedException("GetOperationRequestFactory is not supported in store less mode"); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.Subscription_With_Default_Names.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.Subscription_With_Default_Names.snap index 16cfcf7cf92..9f5af316670 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.Subscription_With_Default_Names.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/NoStoreStarWarsGeneratorTests.Subscription_With_Default_Names.snap @@ -90,7 +90,6 @@ namespace Foo.Bar } public global::System.Int32 Stars { get; } - public global::System.String? Commentary { get; } public virtual global::System.Boolean Equals(OnReviewSub_OnReview_Review? other) @@ -161,7 +160,6 @@ namespace Foo.Bar public partial interface IOnReviewSub_OnReview { public global::System.Int32 Stars { get; } - public global::System.String? Commentary { get; } } @@ -193,8 +191,99 @@ namespace Foo.Bar public static OnReviewSubSubscriptionDocument Instance { get; } = new OnReviewSubSubscriptionDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Subscription; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4f, 0x6e, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x53, 0x75, 0x62, 0x20, 0x7b, 0x20, 0x6f, 0x6e, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x4e, 0x45, 0x57, 0x5f, 0x48, 0x4f, 0x50, 0x45, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x74, 0x61, 0x72, 0x73, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x73, + 0x75, + 0x62, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x4f, + 0x6e, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x53, + 0x75, + 0x62, + 0x20, + 0x7b, + 0x20, + 0x6f, + 0x6e, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x4e, + 0x45, + 0x57, + 0x5f, + 0x48, + 0x4f, + 0x50, + 0x45, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x73, + 0x74, + 0x61, + 0x72, + 0x73, + 0x20, + 0x63, + 0x6f, + 0x6d, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x61, + 0x72, + 0x79, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "92220fce37342d7ade3d63a2a81342eb1fb14bac"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -228,6 +317,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IOnReviewSubResult); + public global::System.IObservable> Watch(global::StrawberryShake.ExecutionStrategy? strategy = null) { var request = CreateRequest(); @@ -308,6 +398,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IOnReviewSubResult); + public OnReviewSubResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (dataInfo is OnReviewSubResultInfo info) @@ -349,9 +440,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.ReviewData OnReview { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => global::System.Array.Empty(); public global::System.UInt64 Version => 0; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new OnReviewSubResultInfo(OnReview); @@ -442,9 +533,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.Int32? Stars { get; } - public global::System.String? Commentary { get; } } @@ -455,6 +544,7 @@ namespace Foo.Bar.State public global::StrawberryShake.IOperationStore OperationStore => throw new global::System.NotSupportedException("OperationStore is not supported in store less mode"); public global::StrawberryShake.IEntityStore EntityStore => throw new global::System.NotSupportedException("EntityStore is not supported in store less mode"); public global::StrawberryShake.IEntityIdSerializer EntityIdSerializer => throw new global::System.NotSupportedException("EntityIdSerializer is not supported in store less mode"); + public global::StrawberryShake.IOperationRequestFactory GetOperationRequestFactory(global::System.Type resultType) { throw new global::System.NotSupportedException("GetOperationRequestFactory is not supported in store less mode"); diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.Generate_ChatClient_AllOperations.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.Generate_ChatClient_AllOperations.snap index a4448558638..09f69aa7e6a 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.Generate_ChatClient_AllOperations.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.Generate_ChatClient_AllOperations.snap @@ -187,22 +187,18 @@ namespace Foo.Bar /// Field name /// public global::System.String Name { get; } - /// /// Field email /// public global::System.String Email { get; } - /// /// Field isOnline /// public global::System.Boolean IsOnline { get; } - /// /// Field imageUri /// public global::System.Uri? ImageUri { get; } - /// /// Field lastSeen /// @@ -313,12 +309,10 @@ namespace Foo.Bar /// Field name /// public global::System.String Name { get; } - /// /// Field email /// public global::System.String Email { get; } - /// /// Field isOnline /// @@ -336,7 +330,6 @@ namespace Foo.Bar /// Field imageUri /// public global::System.Uri? ImageUri { get; } - /// /// Field lastSeen /// @@ -600,27 +593,22 @@ namespace Foo.Bar /// Field id /// public global::System.String Id { get; } - /// /// Field text /// public global::System.String Text { get; } - /// /// Field direction /// public global::Foo.Bar.Direction Direction { get; } - /// /// Fiejld recipient /// public global::Foo.Bar.IGetMessages_PersonByEmail_Messages_Nodes_Recipient Recipient { get; } - /// /// Field sender /// public global::Foo.Bar.IGetMessages_PersonByEmail_Messages_Nodes_Sender Sender { get; } - /// /// Field sent /// @@ -700,12 +688,10 @@ namespace Foo.Bar /// Field name /// public global::System.String Name { get; } - /// /// Field email /// public global::System.String Email { get; } - /// /// Field isOnline /// @@ -782,12 +768,10 @@ namespace Foo.Bar /// Field name /// public global::System.String Name { get; } - /// /// Field email /// public global::System.String Email { get; } - /// /// Field isOnline /// @@ -914,27 +898,22 @@ namespace Foo.Bar /// Field id /// public global::System.String Id { get; } - /// /// Field text /// public global::System.String Text { get; } - /// /// Field direction /// public global::Foo.Bar.Direction Direction { get; } - /// /// Fiejld recipient /// public global::Foo.Bar.IGetMessages_PersonByEmail_Messages_Nodes_Recipient Recipient { get; } - /// /// Field sender /// public global::Foo.Bar.IGetMessages_PersonByEmail_Messages_Nodes_Sender Sender { get; } - /// /// Field sent /// @@ -1152,27 +1131,22 @@ namespace Foo.Bar /// Field id /// public global::System.String Id { get; } - /// /// Field text /// public global::System.String Text { get; } - /// /// Field direction /// public global::Foo.Bar.Direction Direction { get; } - /// /// Fiejld recipient /// public global::Foo.Bar.IGetMessages_PersonByEmail_Messages_Nodes_Recipient Recipient { get; } - /// /// Field sender /// public global::Foo.Bar.IGetMessages_PersonByEmail_Messages_Nodes_Sender Sender { get; } - /// /// Field sent /// @@ -1252,12 +1226,10 @@ namespace Foo.Bar /// Field name /// public global::System.String Name { get; } - /// /// Field email /// public global::System.String Email { get; } - /// /// Field isOnline /// @@ -1334,12 +1306,10 @@ namespace Foo.Bar /// Field name /// public global::System.String Name { get; } - /// /// Field email /// public global::System.String Email { get; } - /// /// Field isOnline /// @@ -1644,27 +1614,22 @@ namespace Foo.Bar /// Field id /// public global::System.String Id { get; } - /// /// Field text /// public global::System.String Text { get; } - /// /// Field direction /// public global::Foo.Bar.Direction Direction { get; } - /// /// Fiejld recipient /// public global::Foo.Bar.IGetMessages_PersonByEmail_Messages_Nodes_Recipient Recipient { get; } - /// /// Field sender /// public global::Foo.Bar.IGetMessages_PersonByEmail_Messages_Nodes_Sender Sender { get; } - /// /// Field sent /// @@ -1744,12 +1709,10 @@ namespace Foo.Bar /// Field name /// public global::System.String Name { get; } - /// /// Field email /// public global::System.String Email { get; } - /// /// Field isOnline /// @@ -1826,12 +1789,10 @@ namespace Foo.Bar /// Field name /// public global::System.String Name { get; } - /// /// Field email /// public global::System.String Email { get; } - /// /// Field isOnline /// @@ -2068,27 +2029,22 @@ namespace Foo.Bar /// Field id /// public global::System.String Id { get; } - /// /// Field text /// public global::System.String Text { get; } - /// /// Field direction /// public global::Foo.Bar.Direction Direction { get; } - /// /// Fiejld recipient /// public global::Foo.Bar.IGetMessages_PersonByEmail_Messages_Nodes_Recipient Recipient { get; } - /// /// Field sender /// public global::Foo.Bar.IGetMessages_PersonByEmail_Messages_Nodes_Sender Sender { get; } - /// /// Field sent /// @@ -2168,12 +2124,10 @@ namespace Foo.Bar /// Field name /// public global::System.String Name { get; } - /// /// Field email /// public global::System.String Email { get; } - /// /// Field isOnline /// @@ -2250,12 +2204,10 @@ namespace Foo.Bar /// Field name /// public global::System.String Name { get; } - /// /// Field email /// public global::System.String Email { get; } - /// /// Field isOnline /// @@ -2387,6 +2339,7 @@ namespace Foo.Bar { private global::StrawberryShake.Serialization.IInputValueFormatter _stringFormatter = default !; public global::System.String TypeName => "SendMessageInput"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _stringFormatter = serializerResolver.GetInputValueFormatter("String"); @@ -2535,6 +2488,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.ISendMessageInputInfo.IsClientMutationIdSet => _set_clientMutationId; + ///The email of the person to which a message shall be send. public global::System.String RecipientEmail { @@ -2547,6 +2501,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.ISendMessageInputInfo.IsRecipientEmailSet => _set_recipientEmail; + ///The message text. public global::System.String Text { @@ -2583,6 +2538,7 @@ namespace Foo.Bar public partial class DirectionSerializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "Direction"; + public Direction Parse(global::System.String serializedValue) { return serializedValue switch @@ -2641,8 +2597,249 @@ namespace Foo.Bar public static GetPeopleQueryDocument Instance { get; } = new GetPeopleQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x28, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x79, 0x3a, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x20, 0x41, 0x53, 0x43, 0x20, 0x7d, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x20, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x55, 0x72, 0x69, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x20, 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x6f, + 0x70, + 0x6c, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x6f, + 0x70, + 0x6c, + 0x65, + 0x28, + 0x6f, + 0x72, + 0x64, + 0x65, + 0x72, + 0x5f, + 0x62, + 0x79, + 0x3a, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x3a, + 0x20, + 0x41, + 0x53, + 0x43, + 0x20, + 0x7d, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x6f, + 0x64, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x50, + 0x61, + 0x72, + 0x74, + 0x69, + 0x63, + 0x69, + 0x70, + 0x61, + 0x6e, + 0x74, + 0x20, + 0x69, + 0x6d, + 0x61, + 0x67, + 0x65, + 0x55, + 0x72, + 0x69, + 0x20, + 0x6c, + 0x61, + 0x73, + 0x74, + 0x53, + 0x65, + 0x65, + 0x6e, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x50, + 0x61, + 0x72, + 0x74, + 0x69, + 0x63, + 0x69, + 0x70, + 0x61, + 0x6e, + 0x74, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x20, + 0x69, + 0x73, + 0x4f, + 0x6e, + 0x6c, + 0x69, + 0x6e, + 0x65, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "91253402b90b965c3f43c2d774692ae8366270c6"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -2693,6 +2890,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPeopleResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -2818,8 +3016,449 @@ namespace Foo.Bar public static GetMessagesQueryDocument Instance { get; } = new GetMessagesQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x28, 0x24, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x3a, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x42, 0x79, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x28, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x3a, 0x20, 0x24, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x28, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x79, 0x3a, 0x20, 0x7b, 0x20, 0x73, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x41, 0x53, 0x43, 0x20, 0x7d, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x20, 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x73, + 0x28, + 0x24, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x3a, + 0x20, + 0x53, + 0x74, + 0x72, + 0x69, + 0x6e, + 0x67, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x42, + 0x79, + 0x45, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x28, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x3a, + 0x20, + 0x24, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x73, + 0x28, + 0x6f, + 0x72, + 0x64, + 0x65, + 0x72, + 0x5f, + 0x62, + 0x79, + 0x3a, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x65, + 0x6e, + 0x74, + 0x3a, + 0x20, + 0x41, + 0x53, + 0x43, + 0x20, + 0x7d, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x6f, + 0x64, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x74, + 0x65, + 0x78, + 0x74, + 0x20, + 0x64, + 0x69, + 0x72, + 0x65, + 0x63, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x72, + 0x65, + 0x63, + 0x69, + 0x70, + 0x69, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x50, + 0x61, + 0x72, + 0x74, + 0x69, + 0x63, + 0x69, + 0x70, + 0x61, + 0x6e, + 0x74, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x73, + 0x65, + 0x6e, + 0x64, + 0x65, + 0x72, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x50, + 0x61, + 0x72, + 0x74, + 0x69, + 0x63, + 0x69, + 0x70, + 0x61, + 0x6e, + 0x74, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x73, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x50, + 0x61, + 0x72, + 0x74, + 0x69, + 0x63, + 0x69, + 0x70, + 0x61, + 0x6e, + 0x74, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x20, + 0x69, + 0x73, + 0x4f, + 0x6e, + 0x6c, + 0x69, + 0x6e, + 0x65, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "c9975fca1ca78b7c96f298130fccfc181f3d5309"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -2893,6 +3532,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetMessagesResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.String email, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(email); @@ -3045,8 +3685,397 @@ namespace Foo.Bar public static SendMessageInputMutationDocument Instance { get; } = new SendMessageInputMutationDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Mutation; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x28, 0x24, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x28, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x24, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x20, 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x6d, + 0x75, + 0x74, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x53, + 0x65, + 0x6e, + 0x64, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x28, + 0x24, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x3a, + 0x20, + 0x53, + 0x65, + 0x6e, + 0x64, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x65, + 0x6e, + 0x64, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x28, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x74, + 0x65, + 0x78, + 0x74, + 0x20, + 0x64, + 0x69, + 0x72, + 0x65, + 0x63, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x72, + 0x65, + 0x63, + 0x69, + 0x70, + 0x69, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x50, + 0x61, + 0x72, + 0x74, + 0x69, + 0x63, + 0x69, + 0x70, + 0x61, + 0x6e, + 0x74, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x73, + 0x65, + 0x6e, + 0x64, + 0x65, + 0x72, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x50, + 0x61, + 0x72, + 0x74, + 0x69, + 0x63, + 0x69, + 0x70, + 0x61, + 0x6e, + 0x74, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x73, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x50, + 0x61, + 0x72, + 0x74, + 0x69, + 0x63, + 0x69, + 0x70, + 0x61, + 0x6e, + 0x74, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x20, + 0x69, + 0x73, + 0x4f, + 0x6e, + 0x6c, + 0x69, + 0x6e, + 0x65, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "abbee600b2246c20ab39b641561c4a0312560356"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -3114,6 +4143,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ISendMessageInputResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::Foo.Bar.SendMessageInput input, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(input); @@ -3260,8 +4290,434 @@ namespace Foo.Bar public static SendMessageMutMutationDocument Instance { get; } = new SendMessageMutMutationDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Mutation; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4d, 0x75, 0x74, 0x28, 0x24, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x3a, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x21, 0x2c, 0x20, 0x24, 0x74, 0x65, 0x78, 0x74, 0x3a, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x28, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x7b, 0x20, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x3a, 0x20, 0x24, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x2c, 0x20, 0x74, 0x65, 0x78, 0x74, 0x3a, 0x20, 0x24, 0x74, 0x65, 0x78, 0x74, 0x20, 0x7d, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x20, 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x6d, + 0x75, + 0x74, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x53, + 0x65, + 0x6e, + 0x64, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x4d, + 0x75, + 0x74, + 0x28, + 0x24, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x3a, + 0x20, + 0x53, + 0x74, + 0x72, + 0x69, + 0x6e, + 0x67, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x74, + 0x65, + 0x78, + 0x74, + 0x3a, + 0x20, + 0x53, + 0x74, + 0x72, + 0x69, + 0x6e, + 0x67, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x65, + 0x6e, + 0x64, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x28, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x3a, + 0x20, + 0x7b, + 0x20, + 0x72, + 0x65, + 0x63, + 0x69, + 0x70, + 0x69, + 0x65, + 0x6e, + 0x74, + 0x45, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x3a, + 0x20, + 0x24, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x2c, + 0x20, + 0x74, + 0x65, + 0x78, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x74, + 0x65, + 0x78, + 0x74, + 0x20, + 0x7d, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x74, + 0x65, + 0x78, + 0x74, + 0x20, + 0x64, + 0x69, + 0x72, + 0x65, + 0x63, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x72, + 0x65, + 0x63, + 0x69, + 0x70, + 0x69, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x50, + 0x61, + 0x72, + 0x74, + 0x69, + 0x63, + 0x69, + 0x70, + 0x61, + 0x6e, + 0x74, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x73, + 0x65, + 0x6e, + 0x64, + 0x65, + 0x72, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x50, + 0x61, + 0x72, + 0x74, + 0x69, + 0x63, + 0x69, + 0x70, + 0x61, + 0x6e, + 0x74, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x73, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x50, + 0x61, + 0x72, + 0x74, + 0x69, + 0x63, + 0x69, + 0x70, + 0x61, + 0x6e, + 0x74, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x20, + 0x69, + 0x73, + 0x4f, + 0x6e, + 0x6c, + 0x69, + 0x6e, + 0x65, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "0a0b8bf5246ce3dca815397d3d9be98e9a35a867"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -3329,6 +4785,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ISendMessageMutResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.String email, global::System.String text, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(email, text); @@ -3483,8 +4940,347 @@ namespace Foo.Bar public static ReadMessagesSubscriptionDocument Instance { get; } = new ReadMessagesSubscriptionDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Subscription; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x52, 0x65, 0x61, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x3a, 0x20, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x73, 0x65, 0x6e, 0x74, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x6e, 0x74, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x20, 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x73, + 0x75, + 0x62, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x52, + 0x65, + 0x61, + 0x64, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x6d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x3a, + 0x20, + 0x6f, + 0x6e, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x52, + 0x65, + 0x63, + 0x65, + 0x69, + 0x76, + 0x65, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4d, + 0x65, + 0x73, + 0x73, + 0x61, + 0x67, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x74, + 0x65, + 0x78, + 0x74, + 0x20, + 0x64, + 0x69, + 0x72, + 0x65, + 0x63, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x72, + 0x65, + 0x63, + 0x69, + 0x70, + 0x69, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x50, + 0x61, + 0x72, + 0x74, + 0x69, + 0x63, + 0x69, + 0x70, + 0x61, + 0x6e, + 0x74, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x73, + 0x65, + 0x6e, + 0x64, + 0x65, + 0x72, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x50, + 0x61, + 0x72, + 0x74, + 0x69, + 0x63, + 0x69, + 0x70, + 0x61, + 0x6e, + 0x74, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x73, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x50, + 0x61, + 0x72, + 0x74, + 0x69, + 0x63, + 0x69, + 0x70, + 0x61, + 0x6e, + 0x74, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x20, + 0x69, + 0x73, + 0x4f, + 0x6e, + 0x6c, + 0x69, + 0x6e, + 0x65, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "d814e26af4f48099826de75f91993d52b3568a02"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -3547,6 +5343,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IReadMessagesResult); + public global::System.IObservable> Watch(global::StrawberryShake.ExecutionStrategy? strategy = null) { var request = CreateRequest(); @@ -3684,19 +5481,14 @@ namespace Foo.Bar.State ///Field name public global::System.String Name { get; } - ///Field email public global::System.String Email { get; } - ///Field isOnline public global::System.Boolean IsOnline { get; } - ///Field imageUri public global::System.Uri? ImageUri { get; } - ///Field lastSeen public global::System.DateTimeOffset LastSeen { get; } - ///Field messages public global::Foo.Bar.State.MessageConnectionData? Messages { get; } } @@ -3718,19 +5510,14 @@ namespace Foo.Bar.State ///Field id public global::System.String Id { get; } - ///Field text public global::System.String Text { get; } - ///Field direction public global::Foo.Bar.Direction Direction { get; } - ///Fiejld recipient public global::StrawberryShake.EntityId Recipient { get; } - ///Field sender public global::StrawberryShake.EntityId Sender { get; } - ///Field sent public global::System.DateTimeOffset Sent { get; } } @@ -3748,6 +5535,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPeopleResult); + public GetPeopleResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -3837,9 +5625,9 @@ namespace Foo.Bar.State /// Gets access to all the people known to this service. /// public global::Foo.Bar.State.PersonConnectionData? People { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPeopleResultInfo(People, _entityIds, version); @@ -3859,6 +5647,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetMessagesResult); + public GetMessagesResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -3907,9 +5696,9 @@ namespace Foo.Bar.State /// Field personByEmail /// public global::StrawberryShake.EntityId PersonByEmail { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetMessagesResultInfo(PersonByEmail, _entityIds, version); @@ -3933,6 +5722,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ISendMessageInputResult); + public SendMessageInput_1Result Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -4016,9 +5806,9 @@ namespace Foo.Bar.State /// Field sendMessage /// public global::Foo.Bar.State.SendMessagePayloadData SendMessage { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new SendMessageInput_1ResultInfo(SendMessage, _entityIds, version); @@ -4042,6 +5832,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ISendMessageMutResult); + public SendMessageMutResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -4125,9 +5916,9 @@ namespace Foo.Bar.State /// Field sendMessage /// public global::Foo.Bar.State.SendMessagePayloadData SendMessage { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new SendMessageMutResultInfo(SendMessage, _entityIds, version); @@ -4147,6 +5938,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IReadMessagesResult); + public ReadMessagesResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -4195,9 +5987,9 @@ namespace Foo.Bar.State /// field onMessageReceived /// public global::StrawberryShake.EntityId Message { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new ReadMessagesResultInfo(Message, _entityIds, version); @@ -5280,7 +7072,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - ///A flattened list of the nodes. public global::System.Collections.Generic.IReadOnlyList? Nodes { get; } } @@ -5297,7 +7088,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - ///A flattened list of the nodes. public global::System.Collections.Generic.IReadOnlyList? Nodes { get; } } @@ -5314,7 +7104,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - ///Field message public global::StrawberryShake.EntityId? Message { get; } } @@ -5791,7 +7580,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.NonNullableValueType_WithoutGlobal_Input.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.NonNullableValueType_WithoutGlobal_Input.snap index 3642be2d6d9..94c552f4738 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.NonNullableValueType_WithoutGlobal_Input.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.NonNullableValueType_WithoutGlobal_Input.snap @@ -96,6 +96,7 @@ namespace Foo.Bar { private global::StrawberryShake.Serialization.IInputValueFormatter _intPtrFormatter = default !; public global::System.String TypeName => "Bar"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _intPtrFormatter = serializerResolver.GetInputValueFormatter("IntPtr"); @@ -217,8 +218,59 @@ namespace Foo.Bar public static GetSomethingQueryDocument Instance { get; } = new GetSomethingQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x53, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x28, 0x24, 0x62, 0x61, 0x72, 0x3a, 0x20, 0x42, 0x61, 0x72, 0x29, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x28, 0x62, 0x61, 0x72, 0x3a, 0x20, 0x24, 0x62, 0x61, 0x72, 0x29, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x53, + 0x6f, + 0x6d, + 0x65, + 0x74, + 0x68, + 0x69, + 0x6e, + 0x67, + 0x28, + 0x24, + 0x62, + 0x61, + 0x72, + 0x3a, + 0x20, + 0x42, + 0x61, + 0x72, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x28, + 0x62, + 0x61, + 0x72, + 0x3a, + 0x20, + 0x24, + 0x62, + 0x61, + 0x72, + 0x29, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "da8906d1f936b0ba2f53ecc3b4f994f97a74891d"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -250,6 +302,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetSomethingResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::Foo.Bar.Bar? bar, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(bar); @@ -349,6 +402,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetSomethingResult); + public GetSomethingResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -384,9 +438,9 @@ namespace Foo.Bar.State } public global::System.String? Foo { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetSomethingResultInfo(Foo, _entityIds, version); @@ -451,7 +505,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.NonNullable_ValueType_Input.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.NonNullable_ValueType_Input.snap index 3642be2d6d9..94c552f4738 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.NonNullable_ValueType_Input.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.NonNullable_ValueType_Input.snap @@ -96,6 +96,7 @@ namespace Foo.Bar { private global::StrawberryShake.Serialization.IInputValueFormatter _intPtrFormatter = default !; public global::System.String TypeName => "Bar"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _intPtrFormatter = serializerResolver.GetInputValueFormatter("IntPtr"); @@ -217,8 +218,59 @@ namespace Foo.Bar public static GetSomethingQueryDocument Instance { get; } = new GetSomethingQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x53, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x28, 0x24, 0x62, 0x61, 0x72, 0x3a, 0x20, 0x42, 0x61, 0x72, 0x29, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x28, 0x62, 0x61, 0x72, 0x3a, 0x20, 0x24, 0x62, 0x61, 0x72, 0x29, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x53, + 0x6f, + 0x6d, + 0x65, + 0x74, + 0x68, + 0x69, + 0x6e, + 0x67, + 0x28, + 0x24, + 0x62, + 0x61, + 0x72, + 0x3a, + 0x20, + 0x42, + 0x61, + 0x72, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x28, + 0x62, + 0x61, + 0x72, + 0x3a, + 0x20, + 0x24, + 0x62, + 0x61, + 0x72, + 0x29, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "da8906d1f936b0ba2f53ecc3b4f994f97a74891d"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -250,6 +302,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetSomethingResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::Foo.Bar.Bar? bar, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(bar); @@ -349,6 +402,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetSomethingResult); + public GetSomethingResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -384,9 +438,9 @@ namespace Foo.Bar.State } public global::System.String? Foo { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetSomethingResultInfo(Foo, _entityIds, version); @@ -451,7 +505,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.Nullable_List_Input.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.Nullable_List_Input.snap index 7d6aebbff9f..637492a281c 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.Nullable_List_Input.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.Nullable_List_Input.snap @@ -96,6 +96,7 @@ namespace Foo.Bar { private global::StrawberryShake.Serialization.IInputValueFormatter _bazFormatter = default !; public global::System.String TypeName => "Bar"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _bazFormatter = serializerResolver.GetInputValueFormatter("Baz"); @@ -235,6 +236,7 @@ namespace Foo.Bar { private global::StrawberryShake.Serialization.IInputValueFormatter _stringFormatter = default !; public global::System.String TypeName => "Baz"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _stringFormatter = serializerResolver.GetInputValueFormatter("String"); @@ -367,8 +369,59 @@ namespace Foo.Bar public static GetSomethingQueryDocument Instance { get; } = new GetSomethingQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x53, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x28, 0x24, 0x62, 0x61, 0x72, 0x3a, 0x20, 0x42, 0x61, 0x72, 0x29, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x28, 0x62, 0x61, 0x72, 0x3a, 0x20, 0x24, 0x62, 0x61, 0x72, 0x29, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x53, + 0x6f, + 0x6d, + 0x65, + 0x74, + 0x68, + 0x69, + 0x6e, + 0x67, + 0x28, + 0x24, + 0x62, + 0x61, + 0x72, + 0x3a, + 0x20, + 0x42, + 0x61, + 0x72, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x28, + 0x62, + 0x61, + 0x72, + 0x3a, + 0x20, + 0x24, + 0x62, + 0x61, + 0x72, + 0x29, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "da8906d1f936b0ba2f53ecc3b4f994f97a74891d"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -400,6 +453,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetSomethingResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::Foo.Bar.Bar? bar, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(bar); @@ -499,6 +553,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetSomethingResult); + public GetSomethingResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -534,9 +589,9 @@ namespace Foo.Bar.State } public global::System.String? Foo { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetSomethingResultInfo(Foo, _entityIds, version); @@ -606,7 +661,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.Nullable_ValueType_Input.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.Nullable_ValueType_Input.snap index 84422042e2f..a7b3c259bcf 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.Nullable_ValueType_Input.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.Nullable_ValueType_Input.snap @@ -96,6 +96,7 @@ namespace Foo.Bar { private global::StrawberryShake.Serialization.IInputValueFormatter _intPtrFormatter = default !; public global::System.String TypeName => "Bar"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _intPtrFormatter = serializerResolver.GetInputValueFormatter("IntPtr"); @@ -228,8 +229,59 @@ namespace Foo.Bar public static GetSomethingQueryDocument Instance { get; } = new GetSomethingQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x53, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x28, 0x24, 0x62, 0x61, 0x72, 0x3a, 0x20, 0x42, 0x61, 0x72, 0x29, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x28, 0x62, 0x61, 0x72, 0x3a, 0x20, 0x24, 0x62, 0x61, 0x72, 0x29, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x53, + 0x6f, + 0x6d, + 0x65, + 0x74, + 0x68, + 0x69, + 0x6e, + 0x67, + 0x28, + 0x24, + 0x62, + 0x61, + 0x72, + 0x3a, + 0x20, + 0x42, + 0x61, + 0x72, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x28, + 0x62, + 0x61, + 0x72, + 0x3a, + 0x20, + 0x24, + 0x62, + 0x61, + 0x72, + 0x29, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "da8906d1f936b0ba2f53ecc3b4f994f97a74891d"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -261,6 +313,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetSomethingResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::Foo.Bar.Bar? bar, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(bar); @@ -360,6 +413,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetSomethingResult); + public GetSomethingResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -395,9 +449,9 @@ namespace Foo.Bar.State } public global::System.String? Foo { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetSomethingResultInfo(Foo, _entityIds, version); @@ -462,7 +516,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.Operation_With_MultipleOperations.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.Operation_With_MultipleOperations.snap index 3f2ae90ad29..4887bd6707d 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.Operation_With_MultipleOperations.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.Operation_With_MultipleOperations.snap @@ -243,6 +243,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _stringFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _barFormatter = default !; public global::System.String TypeName => "Bar"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _stringFormatter = serializerResolver.GetInputValueFormatter("String"); @@ -493,6 +494,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBarInfo.IsStrSet => _set_str; + public global::System.String StrNonNullable { get => _value_strNonNullable; @@ -504,6 +506,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBarInfo.IsStrNonNullableSet => _set_strNonNullable; + public global::Foo.Bar.Bar? Nested { get => _value_nested; @@ -515,6 +518,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBarInfo.IsNestedSet => _set_nested; + public global::System.Collections.Generic.IReadOnlyList NestedList { get => _value_nestedList; @@ -526,6 +530,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBarInfo.IsNestedListSet => _set_nestedList; + public global::System.Collections.Generic.IReadOnlyList?>? NestedMatrix { get => _value_nestedMatrix; @@ -557,8 +562,147 @@ namespace Foo.Bar public static TestOperationQueryDocument Instance { get; } = new TestOperationQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x54, 0x65, 0x73, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x24, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x3a, 0x20, 0x42, 0x61, 0x72, 0x21, 0x2c, 0x20, 0x24, 0x6c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x5b, 0x42, 0x61, 0x72, 0x21, 0x5d, 0x21, 0x2c, 0x20, 0x24, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x5b, 0x5b, 0x42, 0x61, 0x72, 0x21, 0x5d, 0x5d, 0x29, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x28, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x3a, 0x20, 0x24, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x24, 0x6c, 0x69, 0x73, 0x74, 0x2c, 0x20, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x24, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x54, + 0x65, + 0x73, + 0x74, + 0x4f, + 0x70, + 0x65, + 0x72, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x28, + 0x24, + 0x73, + 0x69, + 0x6e, + 0x67, + 0x6c, + 0x65, + 0x3a, + 0x20, + 0x42, + 0x61, + 0x72, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x5b, + 0x42, + 0x61, + 0x72, + 0x21, + 0x5d, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x5b, + 0x5b, + 0x42, + 0x61, + 0x72, + 0x21, + 0x5d, + 0x5d, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x28, + 0x73, + 0x69, + 0x6e, + 0x67, + 0x6c, + 0x65, + 0x3a, + 0x20, + 0x24, + 0x73, + 0x69, + 0x6e, + 0x67, + 0x6c, + 0x65, + 0x2c, + 0x20, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x2c, + 0x20, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x29, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "6aca82b03753ea1d58eba946d9b8a2ed36586c9c"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -590,6 +734,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ITestOperationResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::Foo.Bar.Bar single, global::System.Collections.Generic.IReadOnlyList list, global::System.Collections.Generic.IReadOnlyList?>? nestedList, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(single, list, nestedList); @@ -723,8 +868,148 @@ namespace Foo.Bar public static TestOperation2QueryDocument Instance { get; } = new TestOperation2QueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x54, 0x65, 0x73, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x28, 0x24, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x3a, 0x20, 0x42, 0x61, 0x72, 0x21, 0x2c, 0x20, 0x24, 0x6c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x5b, 0x42, 0x61, 0x72, 0x21, 0x5d, 0x21, 0x2c, 0x20, 0x24, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x5b, 0x5b, 0x42, 0x61, 0x72, 0x21, 0x5d, 0x5d, 0x29, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x28, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x3a, 0x20, 0x24, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x24, 0x6c, 0x69, 0x73, 0x74, 0x2c, 0x20, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x24, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x54, + 0x65, + 0x73, + 0x74, + 0x4f, + 0x70, + 0x65, + 0x72, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x32, + 0x28, + 0x24, + 0x73, + 0x69, + 0x6e, + 0x67, + 0x6c, + 0x65, + 0x3a, + 0x20, + 0x42, + 0x61, + 0x72, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x5b, + 0x42, + 0x61, + 0x72, + 0x21, + 0x5d, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x5b, + 0x5b, + 0x42, + 0x61, + 0x72, + 0x21, + 0x5d, + 0x5d, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x28, + 0x73, + 0x69, + 0x6e, + 0x67, + 0x6c, + 0x65, + 0x3a, + 0x20, + 0x24, + 0x73, + 0x69, + 0x6e, + 0x67, + 0x6c, + 0x65, + 0x2c, + 0x20, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x2c, + 0x20, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x29, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "4c447d93a3741461bd6929b4e3290bebf942e0f4"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -756,6 +1041,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ITestOperation2Result); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::Foo.Bar.Bar single, global::System.Collections.Generic.IReadOnlyList list, global::System.Collections.Generic.IReadOnlyList?>? nestedList, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(single, list, nestedList); @@ -889,8 +1175,148 @@ namespace Foo.Bar public static TestOperation3QueryDocument Instance { get; } = new TestOperation3QueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x54, 0x65, 0x73, 0x74, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x33, 0x28, 0x24, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x3a, 0x20, 0x42, 0x61, 0x72, 0x21, 0x2c, 0x20, 0x24, 0x6c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x5b, 0x42, 0x61, 0x72, 0x21, 0x5d, 0x21, 0x2c, 0x20, 0x24, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x5b, 0x5b, 0x42, 0x61, 0x72, 0x21, 0x5d, 0x5d, 0x29, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x28, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x3a, 0x20, 0x24, 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x2c, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x24, 0x6c, 0x69, 0x73, 0x74, 0x2c, 0x20, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x3a, 0x20, 0x24, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x29, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x54, + 0x65, + 0x73, + 0x74, + 0x4f, + 0x70, + 0x65, + 0x72, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x33, + 0x28, + 0x24, + 0x73, + 0x69, + 0x6e, + 0x67, + 0x6c, + 0x65, + 0x3a, + 0x20, + 0x42, + 0x61, + 0x72, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x5b, + 0x42, + 0x61, + 0x72, + 0x21, + 0x5d, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x5b, + 0x5b, + 0x42, + 0x61, + 0x72, + 0x21, + 0x5d, + 0x5d, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x28, + 0x73, + 0x69, + 0x6e, + 0x67, + 0x6c, + 0x65, + 0x3a, + 0x20, + 0x24, + 0x73, + 0x69, + 0x6e, + 0x67, + 0x6c, + 0x65, + 0x2c, + 0x20, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x2c, + 0x20, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x29, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "a377633fa3ecfd86e7171c57e87aec71a1224663"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -922,6 +1348,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ITestOperation3Result); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::Foo.Bar.Bar single, global::System.Collections.Generic.IReadOnlyList list, global::System.Collections.Generic.IReadOnlyList?>? nestedList, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(single, list, nestedList); @@ -1088,6 +1515,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ITestOperationResult); + public TestOperationResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -1123,9 +1551,9 @@ namespace Foo.Bar.State } public global::System.String? Foo { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new TestOperationResultInfo(Foo, _entityIds, version); @@ -1143,6 +1571,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ITestOperation2Result); + public TestOperation2Result Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -1178,9 +1607,9 @@ namespace Foo.Bar.State } public global::System.String? Foo { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new TestOperation2ResultInfo(Foo, _entityIds, version); @@ -1198,6 +1627,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ITestOperation3Result); + public TestOperation3Result Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -1233,9 +1663,9 @@ namespace Foo.Bar.State } public global::System.String? Foo { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new TestOperation3ResultInfo(Foo, _entityIds, version); @@ -1394,7 +1824,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.Response_Name_Is_Correctly_Cased.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.Response_Name_Is_Correctly_Cased.snap index d34667a143b..c3bd122d840 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.Response_Name_Is_Correctly_Cased.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/OperationGeneratorTests.Response_Name_Is_Correctly_Cased.snap @@ -108,8 +108,58 @@ namespace Foo.Bar public static GetSomethingQueryDocument Instance { get; } = new GetSomethingQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x53, 0x6f, 0x6d, 0x65, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x7b, 0x20, 0x62, 0x61, 0x72, 0x5f, 0x62, 0x61, 0x7a, 0x5f, 0x66, 0x6f, 0x6f, 0x3a, 0x20, 0x66, 0x6f, 0x6f, 0x5f, 0x62, 0x61, 0x72, 0x5f, 0x62, 0x61, 0x7a, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x53, + 0x6f, + 0x6d, + 0x65, + 0x74, + 0x68, + 0x69, + 0x6e, + 0x67, + 0x20, + 0x7b, + 0x20, + 0x62, + 0x61, + 0x72, + 0x5f, + 0x62, + 0x61, + 0x7a, + 0x5f, + 0x66, + 0x6f, + 0x6f, + 0x3a, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x5f, + 0x62, + 0x61, + 0x72, + 0x5f, + 0x62, + 0x61, + 0x7a, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "9507f9500bdf3672c4c7762930e240f720fcce40"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -139,6 +189,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetSomethingResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -224,6 +275,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetSomethingResult); + public GetSomethingResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -259,9 +311,9 @@ namespace Foo.Bar.State } public global::System.String? Bar_baz_foo { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetSomethingResultInfo(Bar_baz_foo, _entityIds, version); @@ -317,7 +369,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/PersistedOperationGeneratorTests.Simple_Custom_Scalar.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/PersistedOperationGeneratorTests.Simple_Custom_Scalar.snap index 621f6cbd087..c19b4d43a44 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/PersistedOperationGeneratorTests.Simple_Custom_Scalar.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/PersistedOperationGeneratorTests.Simple_Custom_Scalar.snap @@ -94,7 +94,6 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.String? Email { get; } public virtual global::System.Boolean Equals(GetPerson_Person_Person? other) @@ -165,7 +164,6 @@ namespace Foo.Bar public partial interface IGetPerson_Person { public global::System.String Name { get; } - public global::System.String? Email { get; } } @@ -199,6 +197,7 @@ namespace Foo.Bar public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; public global::System.ReadOnlySpan Body => new global::System.Byte[0]; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "90e3850be405988debbddcbd8810ef6e54f016e5"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -232,6 +231,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -321,6 +321,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPersonResult); + public GetPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -376,9 +377,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.PersonData? Person { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPersonResultInfo(Person, _entityIds, version); @@ -479,9 +480,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::System.String? Email { get; } } @@ -490,7 +489,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ResultTypeGeneratorTests.Nested_Entity.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ResultTypeGeneratorTests.Nested_Entity.snap index 79c5f7286cd..f6ad3950d53 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ResultTypeGeneratorTests.Nested_Entity.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ResultTypeGeneratorTests.Nested_Entity.snap @@ -91,9 +91,7 @@ namespace Foo.Bar } public global::System.Boolean IsValid { get; } - public global::Foo.Bar.IDecodeVIN_DecodeVIN_VehicleMake? VehicleMake { get; } - public global::Foo.Bar.IDecodeVIN_DecodeVIN_VehicleModel? VehicleModel { get; } public virtual global::System.Boolean Equals(DecodeVIN_DecodeVIN_DecodeVIN? other) @@ -170,11 +168,8 @@ namespace Foo.Bar } public global::System.Guid Id { get; } - public global::System.String? Make { get; } - public global::System.String? MakeCode { get; } - public global::System.Boolean IsDisabled { get; } public virtual global::System.Boolean Equals(DecodeVIN_DecodeVIN_VehicleMake_VehicleMake? other) @@ -254,15 +249,10 @@ namespace Foo.Bar } public global::System.Guid Id { get; } - public global::System.String? Model { get; } - public global::System.String? ModelCode { get; } - public global::System.Guid VehicleMakeId { get; } - public global::System.Boolean IsDisabled { get; } - public global::System.String? ModelType { get; } public virtual global::System.Boolean Equals(DecodeVIN_DecodeVIN_VehicleModel_VehicleModel? other) @@ -345,9 +335,7 @@ namespace Foo.Bar public partial interface IDecodeVINModel { public global::System.Boolean IsValid { get; } - public global::Foo.Bar.IDecodeVIN_DecodeVIN_VehicleMake? VehicleMake { get; } - public global::Foo.Bar.IDecodeVIN_DecodeVIN_VehicleModel? VehicleModel { get; } } @@ -368,11 +356,8 @@ namespace Foo.Bar public partial interface IVehicleMakeModel { public global::System.Guid Id { get; } - public global::System.String? Make { get; } - public global::System.String? MakeCode { get; } - public global::System.Boolean IsDisabled { get; } } @@ -393,15 +378,10 @@ namespace Foo.Bar public partial interface IVehicleModelModel { public global::System.Guid Id { get; } - public global::System.String? Model { get; } - public global::System.String? ModelCode { get; } - public global::System.Guid VehicleMakeId { get; } - public global::System.Boolean IsDisabled { get; } - public global::System.String? ModelType { get; } } @@ -472,8 +452,448 @@ namespace Foo.Bar public static DecodeVINQueryDocument Instance { get; } = new DecodeVINQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x56, 0x49, 0x4e, 0x20, 0x7b, 0x20, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x56, 0x49, 0x4e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x56, 0x49, 0x4e, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x56, 0x49, 0x4e, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x56, 0x49, 0x4e, 0x20, 0x7b, 0x20, 0x69, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x76, 0x65, 0x68, 0x69, 0x63, 0x6c, 0x65, 0x4d, 0x61, 0x6b, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x56, 0x65, 0x68, 0x69, 0x63, 0x6c, 0x65, 0x4d, 0x61, 0x6b, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x56, 0x65, 0x68, 0x69, 0x63, 0x6c, 0x65, 0x4d, 0x61, 0x6b, 0x65, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x76, 0x65, 0x68, 0x69, 0x63, 0x6c, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x56, 0x65, 0x68, 0x69, 0x63, 0x6c, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x56, 0x65, 0x68, 0x69, 0x63, 0x6c, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x56, 0x65, 0x68, 0x69, 0x63, 0x6c, 0x65, 0x4d, 0x61, 0x6b, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x20, 0x6f, 0x6e, 0x20, 0x56, 0x65, 0x68, 0x69, 0x63, 0x6c, 0x65, 0x4d, 0x61, 0x6b, 0x65, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x20, 0x6d, 0x61, 0x6b, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x56, 0x65, 0x68, 0x69, 0x63, 0x6c, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x20, 0x6f, 0x6e, 0x20, 0x56, 0x65, 0x68, 0x69, 0x63, 0x6c, 0x65, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x43, 0x6f, 0x64, 0x65, 0x20, 0x76, 0x65, 0x68, 0x69, 0x63, 0x6c, 0x65, 0x4d, 0x61, 0x6b, 0x65, 0x49, 0x64, 0x20, 0x69, 0x73, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x20, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x44, + 0x65, + 0x63, + 0x6f, + 0x64, + 0x65, + 0x56, + 0x49, + 0x4e, + 0x20, + 0x7b, + 0x20, + 0x64, + 0x65, + 0x63, + 0x6f, + 0x64, + 0x65, + 0x56, + 0x49, + 0x4e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x44, + 0x65, + 0x63, + 0x6f, + 0x64, + 0x65, + 0x56, + 0x49, + 0x4e, + 0x4d, + 0x6f, + 0x64, + 0x65, + 0x6c, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x44, + 0x65, + 0x63, + 0x6f, + 0x64, + 0x65, + 0x56, + 0x49, + 0x4e, + 0x4d, + 0x6f, + 0x64, + 0x65, + 0x6c, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x65, + 0x63, + 0x6f, + 0x64, + 0x65, + 0x56, + 0x49, + 0x4e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x73, + 0x56, + 0x61, + 0x6c, + 0x69, + 0x64, + 0x20, + 0x76, + 0x65, + 0x68, + 0x69, + 0x63, + 0x6c, + 0x65, + 0x4d, + 0x61, + 0x6b, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x56, + 0x65, + 0x68, + 0x69, + 0x63, + 0x6c, + 0x65, + 0x4d, + 0x61, + 0x6b, + 0x65, + 0x4d, + 0x6f, + 0x64, + 0x65, + 0x6c, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x56, + 0x65, + 0x68, + 0x69, + 0x63, + 0x6c, + 0x65, + 0x4d, + 0x61, + 0x6b, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x76, + 0x65, + 0x68, + 0x69, + 0x63, + 0x6c, + 0x65, + 0x4d, + 0x6f, + 0x64, + 0x65, + 0x6c, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x56, + 0x65, + 0x68, + 0x69, + 0x63, + 0x6c, + 0x65, + 0x4d, + 0x6f, + 0x64, + 0x65, + 0x6c, + 0x4d, + 0x6f, + 0x64, + 0x65, + 0x6c, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x56, + 0x65, + 0x68, + 0x69, + 0x63, + 0x6c, + 0x65, + 0x4d, + 0x6f, + 0x64, + 0x65, + 0x6c, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x56, + 0x65, + 0x68, + 0x69, + 0x63, + 0x6c, + 0x65, + 0x4d, + 0x61, + 0x6b, + 0x65, + 0x4d, + 0x6f, + 0x64, + 0x65, + 0x6c, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x56, + 0x65, + 0x68, + 0x69, + 0x63, + 0x6c, + 0x65, + 0x4d, + 0x61, + 0x6b, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x6d, + 0x61, + 0x6b, + 0x65, + 0x20, + 0x6d, + 0x61, + 0x6b, + 0x65, + 0x43, + 0x6f, + 0x64, + 0x65, + 0x20, + 0x69, + 0x73, + 0x44, + 0x69, + 0x73, + 0x61, + 0x62, + 0x6c, + 0x65, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x56, + 0x65, + 0x68, + 0x69, + 0x63, + 0x6c, + 0x65, + 0x4d, + 0x6f, + 0x64, + 0x65, + 0x6c, + 0x4d, + 0x6f, + 0x64, + 0x65, + 0x6c, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x56, + 0x65, + 0x68, + 0x69, + 0x63, + 0x6c, + 0x65, + 0x4d, + 0x6f, + 0x64, + 0x65, + 0x6c, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x6d, + 0x6f, + 0x64, + 0x65, + 0x6c, + 0x20, + 0x6d, + 0x6f, + 0x64, + 0x65, + 0x6c, + 0x43, + 0x6f, + 0x64, + 0x65, + 0x20, + 0x76, + 0x65, + 0x68, + 0x69, + 0x63, + 0x6c, + 0x65, + 0x4d, + 0x61, + 0x6b, + 0x65, + 0x49, + 0x64, + 0x20, + 0x69, + 0x73, + 0x44, + 0x69, + 0x73, + 0x61, + 0x62, + 0x6c, + 0x65, + 0x64, + 0x20, + 0x6d, + 0x6f, + 0x64, + 0x65, + 0x6c, + 0x54, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "3f6909b5b7145f91544b46b59247799ab1582d49"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -540,6 +960,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IDecodeVINResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -664,11 +1085,8 @@ namespace Foo.Bar.State } public global::System.Guid Id { get; } - public global::System.String? Make { get; } - public global::System.String? MakeCode { get; } - public global::System.Boolean IsDisabled { get; } } @@ -687,15 +1105,10 @@ namespace Foo.Bar.State } public global::System.Guid Id { get; } - public global::System.String? Model { get; } - public global::System.String? ModelCode { get; } - public global::System.Guid VehicleMakeId { get; } - public global::System.Boolean IsDisabled { get; } - public global::System.String? ModelType { get; } } @@ -714,6 +1127,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IDecodeVINResult); + public DecodeVINResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -794,9 +1208,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.DecodeVINData DecodeVIN { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new DecodeVINResultInfo(DecodeVIN, _entityIds, version); @@ -979,11 +1393,8 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.Boolean? IsValid { get; } - public global::StrawberryShake.EntityId? VehicleMake { get; } - public global::StrawberryShake.EntityId? VehicleModel { get; } } @@ -1034,7 +1445,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ResultTypeGeneratorTests.Operation_With_Comments.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ResultTypeGeneratorTests.Operation_With_Comments.snap index 19feebacf7b..d5b4278842b 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ResultTypeGeneratorTests.Operation_With_Comments.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ResultTypeGeneratorTests.Operation_With_Comments.snap @@ -103,22 +103,18 @@ namespace Foo.Bar /// Field str /// public global::System.String? Str { get; } - /// /// Field strNonNullable /// public global::System.String StrNonNullable { get; } - /// /// Field nested /// public global::Foo.Bar.IGetFoo_Foo_Nested? Nested { get; } - /// /// Field nestedList /// public global::System.Collections.Generic.IReadOnlyList NestedList { get; } - /// /// Field nestedMatrix /// @@ -441,22 +437,18 @@ namespace Foo.Bar /// Field str /// public global::System.String? Str { get; } - /// /// Field strNonNullable /// public global::System.String StrNonNullable { get; } - /// /// Field nested /// public global::Foo.Bar.IGetFoo_Foo_Nested? Nested { get; } - /// /// Field nestedList /// public global::System.Collections.Generic.IReadOnlyList NestedList { get; } - /// /// Field nestedMatrix /// @@ -572,8 +564,153 @@ namespace Foo.Bar public static GetFooQueryDocument Instance { get; } = new GetFooQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x6f, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x74, 0x72, 0x20, 0x73, 0x74, 0x72, 0x4e, 0x6f, 0x6e, 0x4e, 0x75, 0x6c, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x74, 0x72, 0x20, 0x7d, 0x20, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x74, 0x72, 0x20, 0x7d, 0x20, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x74, 0x72, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x46, + 0x6f, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x73, + 0x74, + 0x72, + 0x20, + 0x73, + 0x74, + 0x72, + 0x4e, + 0x6f, + 0x6e, + 0x4e, + 0x75, + 0x6c, + 0x6c, + 0x61, + 0x62, + 0x6c, + 0x65, + 0x20, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x73, + 0x74, + 0x72, + 0x20, + 0x7d, + 0x20, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x73, + 0x74, + 0x72, + 0x20, + 0x7d, + 0x20, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4d, + 0x61, + 0x74, + 0x72, + 0x69, + 0x78, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x73, + 0x74, + 0x72, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "389e0dede74e9628a1ba44caa630c03e186e3344"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -619,6 +756,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetFooResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -720,6 +858,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetFooResult); + public GetFooResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -878,9 +1017,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.BazData? Foo { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetFooResultInfo(Foo, _entityIds, version); @@ -1109,19 +1248,14 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - ///Field str public global::System.String? Str { get; } - ///Field strNonNullable public global::System.String? StrNonNullable { get; } - ///Field nested public global::Foo.Bar.State.BazData? Nested { get; } - ///Field nestedList public global::System.Collections.Generic.IReadOnlyList? NestedList { get; } - ///Field nestedMatrix public global::System.Collections.Generic.IReadOnlyList?>? NestedMatrix { get; } } @@ -1131,7 +1265,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ResultTypeGeneratorTests.Operation_With_Complex_Types.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ResultTypeGeneratorTests.Operation_With_Complex_Types.snap index eac5da405bd..b420c95a322 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ResultTypeGeneratorTests.Operation_With_Complex_Types.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ResultTypeGeneratorTests.Operation_With_Complex_Types.snap @@ -97,13 +97,9 @@ namespace Foo.Bar } public global::System.String? Str { get; } - public global::System.String StrNonNullable { get; } - public global::Foo.Bar.IGetFoo_Foo_Nested? Nested { get; } - public global::System.Collections.Generic.IReadOnlyList NestedList { get; } - public global::System.Collections.Generic.IReadOnlyList?>? NestedMatrix { get; } public virtual global::System.Boolean Equals(GetFoo_Foo_Baz? other) @@ -399,13 +395,9 @@ namespace Foo.Bar public partial interface IGetFoo_Foo { public global::System.String? Str { get; } - public global::System.String StrNonNullable { get; } - public global::Foo.Bar.IGetFoo_Foo_Nested? Nested { get; } - public global::System.Collections.Generic.IReadOnlyList NestedList { get; } - public global::System.Collections.Generic.IReadOnlyList?>? NestedMatrix { get; } } @@ -488,8 +480,153 @@ namespace Foo.Bar public static GetFooQueryDocument Instance { get; } = new GetFooQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x46, 0x6f, 0x6f, 0x20, 0x7b, 0x20, 0x66, 0x6f, 0x6f, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x74, 0x72, 0x20, 0x73, 0x74, 0x72, 0x4e, 0x6f, 0x6e, 0x4e, 0x75, 0x6c, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x74, 0x72, 0x20, 0x7d, 0x20, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x74, 0x72, 0x20, 0x7d, 0x20, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x74, 0x72, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x46, + 0x6f, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x73, + 0x74, + 0x72, + 0x20, + 0x73, + 0x74, + 0x72, + 0x4e, + 0x6f, + 0x6e, + 0x4e, + 0x75, + 0x6c, + 0x6c, + 0x61, + 0x62, + 0x6c, + 0x65, + 0x20, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x73, + 0x74, + 0x72, + 0x20, + 0x7d, + 0x20, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x73, + 0x74, + 0x72, + 0x20, + 0x7d, + 0x20, + 0x6e, + 0x65, + 0x73, + 0x74, + 0x65, + 0x64, + 0x4d, + 0x61, + 0x74, + 0x72, + 0x69, + 0x78, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x73, + 0x74, + 0x72, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "389e0dede74e9628a1ba44caa630c03e186e3344"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -535,6 +672,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetFooResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -636,6 +774,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetFooResult); + public GetFooResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -794,9 +933,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.BazData? Foo { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetFooResultInfo(Foo, _entityIds, version); @@ -1024,15 +1163,10 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Str { get; } - public global::System.String? StrNonNullable { get; } - public global::Foo.Bar.State.BazData? Nested { get; } - public global::System.Collections.Generic.IReadOnlyList? NestedList { get; } - public global::System.Collections.Generic.IReadOnlyList?>? NestedMatrix { get; } } @@ -1041,7 +1175,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ResultTypeGeneratorTests.Operation_With_NullableData.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ResultTypeGeneratorTests.Operation_With_NullableData.snap index 3280e00dca4..bf2801be5c0 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ResultTypeGeneratorTests.Operation_With_NullableData.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ResultTypeGeneratorTests.Operation_With_NullableData.snap @@ -90,7 +90,6 @@ namespace Foo.Bar } public global::System.String Action { get; } - public global::Foo.Bar.IOnFooUpdated_OnFooUpdated_Data Data { get; } public virtual global::System.Boolean Equals(OnFooUpdated_OnFooUpdated_FooNotification? other) @@ -162,19 +161,12 @@ namespace Foo.Bar } public global::System.String BarGUID { get; } - public global::System.String ThingGUID { get; } - public global::System.String ThingDATE { get; } - public global::System.String? ThingSTATUS { get; } - public global::System.String FooGUID { get; } - public global::System.String? FooAUTHOR { get; } - public global::System.String FooDATE { get; } - public global::System.String? FooTEXT { get; } public virtual global::System.Boolean Equals(OnFooUpdated_OnFooUpdated_Data_FooNotificationData? other) @@ -259,7 +251,6 @@ namespace Foo.Bar public partial interface IOnFooUpdated_OnFooUpdated { public global::System.String Action { get; } - public global::Foo.Bar.IOnFooUpdated_OnFooUpdated_Data Data { get; } } @@ -274,19 +265,12 @@ namespace Foo.Bar public partial interface IOnFooUpdated_OnFooUpdated_Data { public global::System.String BarGUID { get; } - public global::System.String ThingGUID { get; } - public global::System.String ThingDATE { get; } - public global::System.String? ThingSTATUS { get; } - public global::System.String FooGUID { get; } - public global::System.String? FooAUTHOR { get; } - public global::System.String FooDATE { get; } - public global::System.String? FooTEXT { get; } } @@ -328,8 +312,169 @@ namespace Foo.Bar public static OnFooUpdatedSubscriptionDocument Instance { get; } = new OnFooUpdatedSubscriptionDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Subscription; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4f, 0x6e, 0x46, 0x6f, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x20, 0x7b, 0x20, 0x6f, 0x6e, 0x46, 0x6f, 0x6f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x62, 0x61, 0x72, 0x47, 0x55, 0x49, 0x44, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x47, 0x55, 0x49, 0x44, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x44, 0x41, 0x54, 0x45, 0x20, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x20, 0x66, 0x6f, 0x6f, 0x47, 0x55, 0x49, 0x44, 0x20, 0x66, 0x6f, 0x6f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x20, 0x66, 0x6f, 0x6f, 0x44, 0x41, 0x54, 0x45, 0x20, 0x66, 0x6f, 0x6f, 0x54, 0x45, 0x58, 0x54, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x73, + 0x75, + 0x62, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x4f, + 0x6e, + 0x46, + 0x6f, + 0x6f, + 0x55, + 0x70, + 0x64, + 0x61, + 0x74, + 0x65, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x6f, + 0x6e, + 0x46, + 0x6f, + 0x6f, + 0x55, + 0x70, + 0x64, + 0x61, + 0x74, + 0x65, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x61, + 0x63, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x64, + 0x61, + 0x74, + 0x61, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x62, + 0x61, + 0x72, + 0x47, + 0x55, + 0x49, + 0x44, + 0x20, + 0x74, + 0x68, + 0x69, + 0x6e, + 0x67, + 0x47, + 0x55, + 0x49, + 0x44, + 0x20, + 0x74, + 0x68, + 0x69, + 0x6e, + 0x67, + 0x44, + 0x41, + 0x54, + 0x45, + 0x20, + 0x74, + 0x68, + 0x69, + 0x6e, + 0x67, + 0x53, + 0x54, + 0x41, + 0x54, + 0x55, + 0x53, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x47, + 0x55, + 0x49, + 0x44, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x41, + 0x55, + 0x54, + 0x48, + 0x4f, + 0x52, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x44, + 0x41, + 0x54, + 0x45, + 0x20, + 0x66, + 0x6f, + 0x6f, + 0x54, + 0x45, + 0x58, + 0x54, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "e03070cdeadb90b2eff58618790e1892dbf89b15"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -373,6 +518,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IOnFooUpdatedResult); + public global::System.IObservable> Watch(global::StrawberryShake.ExecutionStrategy? strategy = null) { var request = CreateRequest(); @@ -465,6 +611,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IOnFooUpdatedResult); + public OnFooUpdatedResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -530,9 +677,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.FooNotificationData OnFooUpdated { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new OnFooUpdatedResultInfo(OnFooUpdated, _entityIds, version); @@ -652,9 +799,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Action { get; } - public global::Foo.Bar.State.FooNotificationDataData? Data { get; } } @@ -676,21 +821,13 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? BarGUID { get; } - public global::System.String? ThingGUID { get; } - public global::System.String? ThingDATE { get; } - public global::System.String? ThingSTATUS { get; } - public global::System.String? FooGUID { get; } - public global::System.String? FooAUTHOR { get; } - public global::System.String? FooDATE { get; } - public global::System.String? FooTEXT { get; } } @@ -699,7 +836,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Any_Scalar.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Any_Scalar.snap index 91c555e9ebf..190fdec8c79 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Any_Scalar.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Any_Scalar.snap @@ -94,7 +94,6 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.Object? Data { get; } public virtual global::System.Boolean Equals(GetPerson_Person_Person? other) @@ -165,7 +164,6 @@ namespace Foo.Bar public partial interface IGetPerson_Person { public global::System.String Name { get; } - public global::System.Object? Data { get; } } @@ -197,8 +195,62 @@ namespace Foo.Bar public static GetPersonQueryDocument Instance { get; } = new GetPersonQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x64, + 0x61, + 0x74, + 0x61, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "368598981f25055985a805466e9d638691b4b1e6"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -232,6 +284,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -321,6 +374,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPersonResult); + public GetPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -376,9 +430,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.PersonData? Person { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPersonResultInfo(Person, _entityIds, version); @@ -479,9 +533,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::System.Object? Data { get; } } @@ -490,7 +542,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Any_Type.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Any_Type.snap index 6aed069759b..a4e5571493c 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Any_Type.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Any_Type.snap @@ -94,7 +94,6 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.Text.Json.JsonElement? Email { get; } public virtual global::System.Boolean Equals(GetPerson_Person_Person? other) @@ -165,7 +164,6 @@ namespace Foo.Bar public partial interface IGetPerson_Person { public global::System.String Name { get; } - public global::System.Text.Json.JsonElement? Email { get; } } @@ -197,8 +195,63 @@ namespace Foo.Bar public static GetPersonQueryDocument Instance { get; } = new GetPersonQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "90e3850be405988debbddcbd8810ef6e54f016e5"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -232,6 +285,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -321,6 +375,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPersonResult); + public GetPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -376,9 +431,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.PersonData? Person { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPersonResultInfo(Person, _entityIds, version); @@ -479,9 +534,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::System.Text.Json.JsonElement? Email { get; } } @@ -490,7 +543,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.ByteArray_ScalarType.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.ByteArray_ScalarType.snap index d7bc10c297e..e15fd8e211d 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.ByteArray_ScalarType.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.ByteArray_ScalarType.snap @@ -104,8 +104,44 @@ namespace Foo.Bar public static GetAttachmentQueryDocument Instance { get; } = new GetAttachmentQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x7b, 0x20, 0x62, 0x79, 0x74, 0x65, 0x41, 0x72, 0x72, 0x61, 0x79, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x41, + 0x74, + 0x74, + 0x61, + 0x63, + 0x68, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x7b, + 0x20, + 0x62, + 0x79, + 0x74, + 0x65, + 0x41, + 0x72, + 0x72, + 0x61, + 0x79, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "8705e9978f3ff458aeb2844ad87162d9028f0801"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -135,6 +171,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetAttachmentResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -220,6 +257,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetAttachmentResult); + public GetAttachmentResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -255,9 +293,9 @@ namespace Foo.Bar.State } public global::System.Byte[] ByteArray { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetAttachmentResultInfo(ByteArray, _entityIds, version); @@ -313,7 +351,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Complete_Schema_With_Uuid_And_DateTime.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Complete_Schema_With_Uuid_And_DateTime.snap index 9bd61bb7176..f328f530b80 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Complete_Schema_With_Uuid_And_DateTime.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Complete_Schema_With_Uuid_And_DateTime.snap @@ -105,17 +105,11 @@ namespace Foo.Bar } public global::System.Guid Id { get; } - public global::System.Decimal Amount { get; } - public global::System.DateTimeOffset Date { get; } - public global::System.String? Description { get; } - public global::Foo.Bar.ExpenseCategory Category { get; } - public global::Foo.Bar.PaymentMethod PaymentMethod { get; } - public global::System.Collections.Generic.IReadOnlyList? Tags { get; } public virtual global::System.Boolean Equals(AllExpenses_Expense_Expense? other) @@ -267,17 +261,11 @@ namespace Foo.Bar public partial interface IAllExpenses_Expense { public global::System.Guid Id { get; } - public global::System.Decimal Amount { get; } - public global::System.DateTimeOffset Date { get; } - public global::System.String? Description { get; } - public global::Foo.Bar.ExpenseCategory Category { get; } - public global::Foo.Bar.PaymentMethod PaymentMethod { get; } - public global::System.Collections.Generic.IReadOnlyList? Tags { get; } } @@ -327,6 +315,7 @@ namespace Foo.Bar public partial class ExpenseCategorySerializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "ExpenseCategory"; + public ExpenseCategory Parse(global::System.String serializedValue) { return serializedValue switch @@ -390,6 +379,7 @@ namespace Foo.Bar public partial class PaymentMethodSerializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "PaymentMethod"; + public PaymentMethod Parse(global::System.String serializedValue) { return serializedValue switch @@ -448,8 +438,152 @@ namespace Foo.Bar public static AllExpensesQueryDocument Instance { get; } = new AllExpensesQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x41, 0x6c, 0x6c, 0x45, 0x78, 0x70, 0x65, 0x6e, 0x73, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x65, 0x78, 0x70, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x64, 0x20, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x64, 0x61, 0x74, 0x65, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x20, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x20, 0x74, 0x61, 0x67, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x45, 0x78, 0x70, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x41, + 0x6c, + 0x6c, + 0x45, + 0x78, + 0x70, + 0x65, + 0x6e, + 0x73, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x65, + 0x78, + 0x70, + 0x65, + 0x6e, + 0x73, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x69, + 0x64, + 0x20, + 0x61, + 0x6d, + 0x6f, + 0x75, + 0x6e, + 0x74, + 0x20, + 0x64, + 0x61, + 0x74, + 0x65, + 0x20, + 0x64, + 0x65, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x63, + 0x61, + 0x74, + 0x65, + 0x67, + 0x6f, + 0x72, + 0x79, + 0x20, + 0x70, + 0x61, + 0x79, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x4d, + 0x65, + 0x74, + 0x68, + 0x6f, + 0x64, + 0x20, + 0x74, + 0x61, + 0x67, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x45, + 0x78, + 0x70, + 0x65, + 0x6e, + 0x73, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "2c6c9362bd54e9b97f02c5b99f7ac6c1fab8169b"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -494,6 +628,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IAllExpensesResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -599,17 +734,11 @@ namespace Foo.Bar.State } public global::System.Guid Id { get; } - public global::System.Decimal Amount { get; } - public global::System.DateTimeOffset Date { get; } - public global::System.String? Description { get; } - public global::Foo.Bar.ExpenseCategory Category { get; } - public global::Foo.Bar.PaymentMethod PaymentMethod { get; } - public global::System.Collections.Generic.IReadOnlyList? Tags { get; } } @@ -626,6 +755,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IAllExpensesResult); + public AllExpensesResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -692,9 +822,9 @@ namespace Foo.Bar.State } public global::System.Collections.Generic.IReadOnlyList? Expense { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new AllExpensesResultInfo(Expense, _entityIds, version); @@ -937,7 +1067,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Name { get; } } @@ -1003,7 +1132,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Custom_Scalar_With_RuntimeType.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Custom_Scalar_With_RuntimeType.snap index da0c74a70c6..01a90075581 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Custom_Scalar_With_RuntimeType.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Custom_Scalar_With_RuntimeType.snap @@ -94,7 +94,6 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.Int32? Email { get; } public virtual global::System.Boolean Equals(GetPerson_Person_Person? other) @@ -165,7 +164,6 @@ namespace Foo.Bar public partial interface IGetPerson_Person { public global::System.String Name { get; } - public global::System.Int32? Email { get; } } @@ -197,8 +195,63 @@ namespace Foo.Bar public static GetPersonQueryDocument Instance { get; } = new GetPersonQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "90e3850be405988debbddcbd8810ef6e54f016e5"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -232,6 +285,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -321,6 +375,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPersonResult); + public GetPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -376,9 +431,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.PersonData? Person { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPersonResultInfo(Person, _entityIds, version); @@ -479,9 +534,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::System.Int32? Email { get; } } @@ -490,7 +543,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Custom_Scalar_With_RuntimeType_ValueType_AsInput.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Custom_Scalar_With_RuntimeType_ValueType_AsInput.snap index e8eada7c271..057875c1a4a 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Custom_Scalar_With_RuntimeType_ValueType_AsInput.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Custom_Scalar_With_RuntimeType_ValueType_AsInput.snap @@ -186,8 +186,92 @@ namespace Foo.Bar public static SetPersonQueryDocument Instance { get; } = new SetPersonQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x53, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x28, 0x24, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x3a, 0x20, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x65, 0x74, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x28, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x3a, 0x20, 0x24, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x29, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x53, + 0x65, + 0x74, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x28, + 0x24, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x3a, + 0x20, + 0x45, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x73, + 0x65, + 0x74, + 0x45, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x28, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x3a, + 0x20, + 0x24, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x29, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "969f32b130bc0037d5f598a45e088f8bc7690fc8"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -222,6 +306,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ISetPersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Index email, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(email); @@ -317,6 +402,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ISetPersonResult); + public SetPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -372,9 +458,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.PersonData? Person { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new SetPersonResultInfo(Person, _entityIds, version); @@ -457,7 +543,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.Index? SetEmail { get; } } @@ -466,7 +551,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Custom_Scalar_With_SerializationType.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Custom_Scalar_With_SerializationType.snap index 472c1cd032a..8b20f97f7ac 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Custom_Scalar_With_SerializationType.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Custom_Scalar_With_SerializationType.snap @@ -94,7 +94,6 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.String? Email { get; } public virtual global::System.Boolean Equals(GetPerson_Person_Person? other) @@ -165,7 +164,6 @@ namespace Foo.Bar public partial interface IGetPerson_Person { public global::System.String Name { get; } - public global::System.String? Email { get; } } @@ -197,8 +195,63 @@ namespace Foo.Bar public static GetPersonQueryDocument Instance { get; } = new GetPersonQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "90e3850be405988debbddcbd8810ef6e54f016e5"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -232,6 +285,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -321,6 +375,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPersonResult); + public GetPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -376,9 +431,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.PersonData? Person { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPersonResultInfo(Person, _entityIds, version); @@ -479,9 +534,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::System.String? Email { get; } } @@ -490,7 +543,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Custom_Scalar_With_SerializationType_And_RuntimeType.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Custom_Scalar_With_SerializationType_And_RuntimeType.snap index bf7fddfdb1a..1feb96b946e 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Custom_Scalar_With_SerializationType_And_RuntimeType.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Custom_Scalar_With_SerializationType_And_RuntimeType.snap @@ -94,7 +94,6 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.Int64? Email { get; } public virtual global::System.Boolean Equals(GetPerson_Person_Person? other) @@ -165,7 +164,6 @@ namespace Foo.Bar public partial interface IGetPerson_Person { public global::System.String Name { get; } - public global::System.Int64? Email { get; } } @@ -197,8 +195,63 @@ namespace Foo.Bar public static GetPersonQueryDocument Instance { get; } = new GetPersonQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "90e3850be405988debbddcbd8810ef6e54f016e5"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -232,6 +285,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -321,6 +375,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPersonResult); + public GetPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -376,9 +431,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.PersonData? Person { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPersonResultInfo(Person, _entityIds, version); @@ -479,9 +534,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::System.Int64? Email { get; } } @@ -490,7 +543,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Custom_Scalar_With_Unknown_RuntimeType.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Custom_Scalar_With_Unknown_RuntimeType.snap index e24dae8abd6..b6cb7e78491 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Custom_Scalar_With_Unknown_RuntimeType.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Custom_Scalar_With_Unknown_RuntimeType.snap @@ -94,7 +94,6 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Custom? Email { get; } public virtual global::System.Boolean Equals(GetPerson_Person_Person? other) @@ -165,7 +164,6 @@ namespace Foo.Bar public partial interface IGetPerson_Person { public global::System.String Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Custom? Email { get; } } @@ -197,8 +195,63 @@ namespace Foo.Bar public static GetPersonQueryDocument Instance { get; } = new GetPersonQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "90e3850be405988debbddcbd8810ef6e54f016e5"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -232,6 +285,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -321,6 +375,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPersonResult); + public GetPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -376,9 +431,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.PersonData? Person { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPersonResultInfo(Person, _entityIds, version); @@ -479,9 +534,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::StrawberryShake.CodeGeneration.CSharp.Custom? Email { get; } } @@ -490,7 +543,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Only_Custom_Scalars.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Only_Custom_Scalars.snap index 5be98f68427..5908e4dae06 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Only_Custom_Scalars.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Only_Custom_Scalars.snap @@ -190,8 +190,58 @@ namespace Foo.Bar public static GetPersonQueryDocument Instance { get; } = new GetPersonQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "5584daf20236e87cc2159892887cd0fa5c76a944"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -224,6 +274,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -312,6 +363,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPersonResult); + public GetPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -367,9 +419,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.PersonData? Person { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPersonResultInfo(Person, _entityIds, version); @@ -452,7 +504,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Email { get; } } @@ -461,7 +512,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Scalars_Are_Correctly_Inferred.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Scalars_Are_Correctly_Inferred.snap index 7913ee5cb44..3c33b4b154f 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Scalars_Are_Correctly_Inferred.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Scalars_Are_Correctly_Inferred.snap @@ -94,7 +94,6 @@ namespace Foo.Bar } public global::System.Double? NumberFloat { get; } - public global::System.Int32? NumberInt { get; } public virtual global::System.Boolean Equals(GetAll_Listings_Offer? other) @@ -169,7 +168,6 @@ namespace Foo.Bar public partial interface IOffer { public global::System.Double? NumberFloat { get; } - public global::System.Int32? NumberInt { get; } } @@ -211,8 +209,111 @@ namespace Foo.Bar public static GetAllQueryDocument Instance { get; } = new GetAllQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x20, 0x7b, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x20, 0x6f, 0x6e, 0x20, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x20, 0x7b, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x49, 0x6e, 0x74, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x41, + 0x6c, + 0x6c, + 0x20, + 0x7b, + 0x20, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x4f, + 0x66, + 0x66, + 0x65, + 0x72, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x4f, + 0x66, + 0x66, + 0x65, + 0x72, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4f, + 0x66, + 0x66, + 0x65, + 0x72, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x75, + 0x6d, + 0x62, + 0x65, + 0x72, + 0x46, + 0x6c, + 0x6f, + 0x61, + 0x74, + 0x20, + 0x6e, + 0x75, + 0x6d, + 0x62, + 0x65, + 0x72, + 0x49, + 0x6e, + 0x74, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "f1d55f52ab8224f1113e9ad0cb8321f1bafff815"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -250,6 +351,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetAllResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -343,6 +445,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetAllResult); + public GetAllResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -409,9 +512,9 @@ namespace Foo.Bar.State } public global::System.Collections.Generic.IReadOnlyList Listings { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetAllResultInfo(Listings, _entityIds, version); @@ -533,9 +636,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.Double? NumberFloat { get; } - public global::System.Int32? NumberInt { get; } } @@ -544,7 +645,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Simple_Custom_Scalar.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Simple_Custom_Scalar.snap index 9097a3874cb..5bb09bfedc7 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Simple_Custom_Scalar.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Simple_Custom_Scalar.snap @@ -94,7 +94,6 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.String? Email { get; } public virtual global::System.Boolean Equals(GetPerson_Person_Person? other) @@ -165,7 +164,6 @@ namespace Foo.Bar public partial interface IGetPerson_Person { public global::System.String Name { get; } - public global::System.String? Email { get; } } @@ -197,8 +195,63 @@ namespace Foo.Bar public static GetPersonQueryDocument Instance { get; } = new GetPersonQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x65, + 0x6d, + 0x61, + 0x69, + 0x6c, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "90e3850be405988debbddcbd8810ef6e54f016e5"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -232,6 +285,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -321,6 +375,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPersonResult); + public GetPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -376,9 +431,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.PersonData? Person { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPersonResultInfo(Person, _entityIds, version); @@ -479,9 +534,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::System.String? Email { get; } } @@ -490,7 +543,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.TimeSpan_Not_Detected.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.TimeSpan_Not_Detected.snap index 2b2c32b6083..97d66b201ef 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.TimeSpan_Not_Detected.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.TimeSpan_Not_Detected.snap @@ -289,8 +289,128 @@ namespace Foo.Bar public static GetSessionsQueryDocument Instance { get; } = new GetSessionsQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x7b, 0x20, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x28, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x20, 0x7b, 0x20, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x3a, 0x20, 0x41, 0x53, 0x43, 0x20, 0x7d, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x53, + 0x65, + 0x73, + 0x73, + 0x69, + 0x6f, + 0x6e, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x65, + 0x73, + 0x73, + 0x69, + 0x6f, + 0x6e, + 0x73, + 0x28, + 0x6f, + 0x72, + 0x64, + 0x65, + 0x72, + 0x3a, + 0x20, + 0x7b, + 0x20, + 0x74, + 0x69, + 0x74, + 0x6c, + 0x65, + 0x3a, + 0x20, + 0x41, + 0x53, + 0x43, + 0x20, + 0x7d, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x6f, + 0x64, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x74, + 0x69, + 0x74, + 0x6c, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x53, + 0x65, + 0x73, + 0x73, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "4198cc75513f14cb8a7abfa91bb409dcdb89138b"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -329,6 +449,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetSessionsResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -437,6 +558,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetSessionsResult); + public GetSessionsResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -518,9 +640,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.SessionConnectionData? Sessions { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetSessionsResultInfo(Sessions, _entityIds, version); @@ -658,7 +780,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - ///A flattened list of the nodes. public global::System.Collections.Generic.IReadOnlyList? Nodes { get; } } @@ -689,7 +810,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Uri_Type.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Uri_Type.snap index 1116f4b365a..750aedaa708 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Uri_Type.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Uri_Type.snap @@ -94,7 +94,6 @@ namespace Foo.Bar } public global::System.Uri? Uri { get; } - public global::System.Uri? URI { get; } public virtual global::System.Boolean Equals(GetPerson_Person_Person? other) @@ -169,7 +168,6 @@ namespace Foo.Bar public partial interface IGetPerson_Person { public global::System.Uri? Uri { get; } - public global::System.Uri? URI { get; } } @@ -201,8 +199,60 @@ namespace Foo.Bar public static GetPersonQueryDocument Instance { get; } = new GetPersonQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x75, 0x72, 0x69, 0x20, 0x55, 0x52, 0x49, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x75, + 0x72, + 0x69, + 0x20, + 0x55, + 0x52, + 0x49, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "0935b507b123c9b8b4e075f238ff2dcf5047abaa"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -236,6 +286,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -325,6 +376,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPersonResult); + public GetPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -380,9 +432,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.PersonData? Person { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPersonResultInfo(Person, _entityIds, version); @@ -468,9 +520,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.Uri? Uri { get; } - public global::System.Uri? URI { get; } } @@ -479,7 +529,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Uuid_Type.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Uuid_Type.snap index 99dbb7df728..b4556f6787b 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Uuid_Type.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/ScalarGeneratorTests.Uuid_Type.snap @@ -94,7 +94,6 @@ namespace Foo.Bar } public global::System.Guid? Uuid { get; } - public global::System.Guid? UUID { get; } public virtual global::System.Boolean Equals(GetPerson_Person_Person? other) @@ -169,7 +168,6 @@ namespace Foo.Bar public partial interface IGetPerson_Person { public global::System.Guid? Uuid { get; } - public global::System.Guid? UUID { get; } } @@ -201,8 +199,62 @@ namespace Foo.Bar public static GetPersonQueryDocument Instance { get; } = new GetPersonQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x55, 0x75, 0x69, 0x64, 0x20, 0x55, 0x55, 0x49, 0x44, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x55, + 0x75, + 0x69, + 0x64, + 0x20, + 0x55, + 0x55, + 0x49, + 0x44, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "63833e12a095519476031ce95747d0c564ae6b49"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -236,6 +288,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPersonResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -325,6 +378,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPersonResult); + public GetPersonResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -380,9 +434,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.PersonData? Person { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPersonResultInfo(Person, _entityIds, version); @@ -468,9 +522,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.Guid? Uuid { get; } - public global::System.Guid? UUID { get; } } @@ -479,7 +531,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Create_DataType_Query.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Create_DataType_Query.snap index 72c68d528bb..785d8ff5cd1 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Create_DataType_Query.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Create_DataType_Query.snap @@ -268,8 +268,79 @@ namespace Foo.Bar public static GetAllFoosQueryDocument Instance { get; } = new GetAllFoosQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x46, 0x6f, 0x6f, 0x73, 0x20, 0x7b, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x41, + 0x6c, + 0x6c, + 0x46, + 0x6f, + 0x6f, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x74, + 0x65, + 0x73, + 0x74, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x70, + 0x72, + 0x6f, + 0x66, + 0x69, + 0x6c, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "0bed3e95cee021d9969785b8c1aac3425587955c"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -305,6 +376,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetAllFoosResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -396,6 +468,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetAllFoosResult); + public GetAllFoosResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -477,9 +550,9 @@ namespace Foo.Bar.State } public global::System.Collections.Generic.IReadOnlyList Test { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetAllFoosResultInfo(Test, _entityIds, version); @@ -604,7 +677,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::Foo.Bar.State.ProfileData? Profile { get; } } @@ -619,7 +691,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Name { get; } } @@ -628,7 +699,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Create_GetFeatById.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Create_GetFeatById.snap index e2666312bfc..52bf0f8c3ae 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Create_GetFeatById.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Create_GetFeatById.snap @@ -168,11 +168,8 @@ namespace Foo.Bar } public global::System.Guid Id { get; } - public global::System.String Name { get; } - public global::System.Int32 Level { get; } - public global::System.Collections.Generic.IReadOnlyList Details { get; } public virtual global::System.Boolean Equals(GetFeatById_Feats_Items_Feat? other) @@ -320,11 +317,8 @@ namespace Foo.Bar public partial interface IGetFeatById_Feats_Items { public global::System.Guid Id { get; } - public global::System.String Name { get; } - public global::System.Int32 Level { get; } - public global::System.Collections.Generic.IReadOnlyList Details { get; } } @@ -383,8 +377,206 @@ namespace Foo.Bar public static GetFeatByIdQueryDocument Instance { get; } = new GetFeatByIdQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x46, 0x65, 0x61, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x24, 0x69, 0x64, 0x3a, 0x20, 0x55, 0x55, 0x49, 0x44, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x66, 0x65, 0x61, 0x74, 0x73, 0x28, 0x77, 0x68, 0x65, 0x72, 0x65, 0x3a, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x3a, 0x20, 0x7b, 0x20, 0x65, 0x71, 0x3a, 0x20, 0x24, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x64, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x20, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x46, 0x65, 0x61, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x46, 0x65, 0x61, 0x74, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x46, + 0x65, + 0x61, + 0x74, + 0x42, + 0x79, + 0x49, + 0x64, + 0x28, + 0x24, + 0x69, + 0x64, + 0x3a, + 0x20, + 0x55, + 0x55, + 0x49, + 0x44, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x65, + 0x61, + 0x74, + 0x73, + 0x28, + 0x77, + 0x68, + 0x65, + 0x72, + 0x65, + 0x3a, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x3a, + 0x20, + 0x7b, + 0x20, + 0x65, + 0x71, + 0x3a, + 0x20, + 0x24, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x69, + 0x74, + 0x65, + 0x6d, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x69, + 0x64, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6c, + 0x65, + 0x76, + 0x65, + 0x6c, + 0x20, + 0x64, + 0x65, + 0x74, + 0x61, + 0x69, + 0x6c, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x74, + 0x65, + 0x78, + 0x74, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x46, + 0x65, + 0x61, + 0x74, + 0x44, + 0x65, + 0x74, + 0x61, + 0x69, + 0x6c, + 0x73, + 0x42, + 0x6c, + 0x6f, + 0x63, + 0x6b, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x46, + 0x65, + 0x61, + 0x74, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "d036805a9b57f740e08b76f17d57f3de1b7e6d25"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -434,6 +626,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetFeatByIdResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Guid id, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(id); @@ -546,11 +739,8 @@ namespace Foo.Bar.State } public global::System.Guid Id { get; } - public global::System.String Name { get; } - public global::System.Int32 Level { get; } - public global::System.Collections.Generic.IReadOnlyList Details { get; } } @@ -581,6 +771,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetFeatByIdResult); + public GetFeatByIdResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -693,9 +884,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.FeatCollectionSegmentData? Feats { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetFeatByIdResultInfo(Feats, _entityIds, version); @@ -918,7 +1109,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.Collections.Generic.IReadOnlyList? Items { get; } } @@ -997,7 +1187,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Create_GetFeatsPage.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Create_GetFeatsPage.snap index bf1eb6f0ab2..60f0703dc1f 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Create_GetFeatsPage.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Create_GetFeatsPage.snap @@ -168,11 +168,8 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.Int32 Level { get; } - public global::System.Boolean CanBeLearnedMoreThanOnce { get; } - public global::Foo.Bar.IGetFeatsPage_Feats_Items_ActionType ActionType { get; } public virtual global::System.Boolean Equals(GetFeatsPage_Feats_Items_Feat? other) @@ -316,11 +313,8 @@ namespace Foo.Bar public partial interface IGetFeatsPage_Feats_Items { public global::System.String Name { get; } - public global::System.Int32 Level { get; } - public global::System.Boolean CanBeLearnedMoreThanOnce { get; } - public global::Foo.Bar.IGetFeatsPage_Feats_Items_ActionType ActionType { get; } } @@ -379,8 +373,236 @@ namespace Foo.Bar public static GetFeatsPageQueryDocument Instance { get; } = new GetFeatsPageQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x46, 0x65, 0x61, 0x74, 0x73, 0x50, 0x61, 0x67, 0x65, 0x28, 0x24, 0x73, 0x6b, 0x69, 0x70, 0x3a, 0x20, 0x49, 0x6e, 0x74, 0x2c, 0x20, 0x24, 0x74, 0x61, 0x6b, 0x65, 0x3a, 0x20, 0x49, 0x6e, 0x74, 0x29, 0x20, 0x7b, 0x20, 0x66, 0x65, 0x61, 0x74, 0x73, 0x28, 0x73, 0x6b, 0x69, 0x70, 0x3a, 0x20, 0x24, 0x73, 0x6b, 0x69, 0x70, 0x2c, 0x20, 0x74, 0x61, 0x6b, 0x65, 0x3a, 0x20, 0x24, 0x74, 0x61, 0x6b, 0x65, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x20, 0x63, 0x61, 0x6e, 0x42, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x4d, 0x6f, 0x72, 0x65, 0x54, 0x68, 0x61, 0x6e, 0x4f, 0x6e, 0x63, 0x65, 0x20, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x46, 0x65, 0x61, 0x74, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x46, + 0x65, + 0x61, + 0x74, + 0x73, + 0x50, + 0x61, + 0x67, + 0x65, + 0x28, + 0x24, + 0x73, + 0x6b, + 0x69, + 0x70, + 0x3a, + 0x20, + 0x49, + 0x6e, + 0x74, + 0x2c, + 0x20, + 0x24, + 0x74, + 0x61, + 0x6b, + 0x65, + 0x3a, + 0x20, + 0x49, + 0x6e, + 0x74, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x65, + 0x61, + 0x74, + 0x73, + 0x28, + 0x73, + 0x6b, + 0x69, + 0x70, + 0x3a, + 0x20, + 0x24, + 0x73, + 0x6b, + 0x69, + 0x70, + 0x2c, + 0x20, + 0x74, + 0x61, + 0x6b, + 0x65, + 0x3a, + 0x20, + 0x24, + 0x74, + 0x61, + 0x6b, + 0x65, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x69, + 0x74, + 0x65, + 0x6d, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6c, + 0x65, + 0x76, + 0x65, + 0x6c, + 0x20, + 0x63, + 0x61, + 0x6e, + 0x42, + 0x65, + 0x4c, + 0x65, + 0x61, + 0x72, + 0x6e, + 0x65, + 0x64, + 0x4d, + 0x6f, + 0x72, + 0x65, + 0x54, + 0x68, + 0x61, + 0x6e, + 0x4f, + 0x6e, + 0x63, + 0x65, + 0x20, + 0x61, + 0x63, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x54, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x41, + 0x63, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x54, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x46, + 0x65, + 0x61, + 0x74, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "1982a47676d97873e3abcf4a7c72b03511095fac"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -430,6 +652,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetFeatsPageResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Int32? skip, global::System.Int32? take, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(skip, take); @@ -562,11 +785,8 @@ namespace Foo.Bar.State } public global::System.String Name { get; } - public global::System.Int32 Level { get; } - public global::System.Boolean CanBeLearnedMoreThanOnce { get; } - public global::StrawberryShake.EntityId ActionType { get; } } @@ -597,6 +817,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetFeatsPageResult); + public GetFeatsPageResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -693,9 +914,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.FeatCollectionSegmentData? Feats { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetFeatsPageResultInfo(Feats, _entityIds, version); @@ -897,7 +1118,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.Collections.Generic.IReadOnlyList? Items { get; } } @@ -960,7 +1180,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Create_PeopleSearch_From_ActiveDirectory_Schema.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Create_PeopleSearch_From_ActiveDirectory_Schema.snap index d8253ce169e..b9f1f5bdc15 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Create_PeopleSearch_From_ActiveDirectory_Schema.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Create_PeopleSearch_From_ActiveDirectory_Schema.snap @@ -98,12 +98,10 @@ namespace Foo.Bar } public global::System.Int32 TotalCount { get; } - /// /// Information to aid in pagination. /// public global::Foo.Bar.IPeopleSearch_People_PageInfo PageInfo { get; } - public global::System.Collections.Generic.IReadOnlyList? Items { get; } public virtual global::System.Boolean Equals(PeopleSearch_People_PersonCollectionSegment? other) @@ -183,7 +181,6 @@ namespace Foo.Bar /// Indicates whether more items exist following the set defined by the clients arguments. /// public global::System.Boolean HasNextPage { get; } - /// /// Indicates whether more items exist prior the set defined by the clients arguments. /// @@ -258,37 +255,30 @@ namespace Foo.Bar } public global::System.String Id { get; } - /// /// The database primary key for the person. /// public global::System.Int64 Key { get; } - /// /// The full of the person. /// public global::System.String? DisplayName { get; } - /// /// Indicates if the person is active. /// public global::System.Boolean IsActive { get; } - /// /// The department the person belongs to. /// public global::Foo.Bar.IPeopleSearch_People_Items_Department? Department { get; } - /// /// Url to image, you can specify a size in pixels by appending '?size=300' to get a square image. /// public global::System.Uri? Image { get; } - /// /// The job title of the person. /// public global::System.String Title { get; } - /// /// The person managing the person. /// @@ -379,7 +369,6 @@ namespace Foo.Bar } public global::System.String Id { get; } - /// /// The name of the department. /// @@ -449,12 +438,10 @@ namespace Foo.Bar } public global::System.String Id { get; } - /// /// The database primary key for the person. /// public global::System.Int64 Key { get; } - /// /// The full of the person. /// @@ -532,12 +519,10 @@ namespace Foo.Bar public partial interface IPeopleSearch_People { public global::System.Int32 TotalCount { get; } - /// /// Information to aid in pagination. /// public global::Foo.Bar.IPeopleSearch_People_PageInfo PageInfo { get; } - public global::System.Collections.Generic.IReadOnlyList? Items { get; } } @@ -558,7 +543,6 @@ namespace Foo.Bar /// Indicates whether more items exist following the set defined by the clients arguments. /// public global::System.Boolean HasNextPage { get; } - /// /// Indicates whether more items exist prior the set defined by the clients arguments. /// @@ -579,37 +563,30 @@ namespace Foo.Bar public partial interface IPeopleSearchResult { public global::System.String Id { get; } - /// /// The database primary key for the person. /// public global::System.Int64 Key { get; } - /// /// The full of the person. /// public global::System.String? DisplayName { get; } - /// /// Indicates if the person is active. /// public global::System.Boolean IsActive { get; } - /// /// The department the person belongs to. /// public global::Foo.Bar.IPeopleSearch_People_Items_Department? Department { get; } - /// /// Url to image, you can specify a size in pixels by appending '?size=300' to get a square image. /// public global::System.Uri? Image { get; } - /// /// The job title of the person. /// public global::System.String Title { get; } - /// /// The person managing the person. /// @@ -633,7 +610,6 @@ namespace Foo.Bar public partial interface IPeopleSearch_People_Items_Department { public global::System.String Id { get; } - /// /// The name of the department. /// @@ -651,12 +627,10 @@ namespace Foo.Bar public partial interface IPeopleSearch_People_Items_Manager { public global::System.String Id { get; } - /// /// The database primary key for the person. /// public global::System.Int64 Key { get; } - /// /// The full of the person. /// @@ -728,8 +702,528 @@ namespace Foo.Bar public static PeopleSearchQueryDocument Instance { get; } = new PeopleSearchQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x50, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x28, 0x24, 0x74, 0x65, 0x72, 0x6d, 0x3a, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x21, 0x2c, 0x20, 0x24, 0x73, 0x6b, 0x69, 0x70, 0x3a, 0x20, 0x49, 0x6e, 0x74, 0x2c, 0x20, 0x24, 0x74, 0x61, 0x6b, 0x65, 0x3a, 0x20, 0x49, 0x6e, 0x74, 0x2c, 0x20, 0x24, 0x69, 0x6e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x3a, 0x20, 0x42, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x29, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x3a, 0x20, 0x70, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x28, 0x74, 0x65, 0x72, 0x6d, 0x3a, 0x20, 0x24, 0x74, 0x65, 0x72, 0x6d, 0x2c, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x49, 0x6e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x3a, 0x20, 0x24, 0x69, 0x6e, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x2c, 0x20, 0x73, 0x6b, 0x69, 0x70, 0x3a, 0x20, 0x24, 0x73, 0x6b, 0x69, 0x70, 0x2c, 0x20, 0x74, 0x61, 0x6b, 0x65, 0x3a, 0x20, 0x24, 0x74, 0x61, 0x6b, 0x65, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x70, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x68, 0x61, 0x73, 0x4e, 0x65, 0x78, 0x74, 0x50, 0x61, 0x67, 0x65, 0x20, 0x68, 0x61, 0x73, 0x50, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x50, 0x61, 0x67, 0x65, 0x20, 0x7d, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x50, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x50, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x73, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x20, 0x64, 0x65, 0x70, 0x61, 0x72, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x64, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x65, 0x70, 0x61, 0x72, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x20, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x20, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x64, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x50, + 0x65, + 0x6f, + 0x70, + 0x6c, + 0x65, + 0x53, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x28, + 0x24, + 0x74, + 0x65, + 0x72, + 0x6d, + 0x3a, + 0x20, + 0x53, + 0x74, + 0x72, + 0x69, + 0x6e, + 0x67, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x73, + 0x6b, + 0x69, + 0x70, + 0x3a, + 0x20, + 0x49, + 0x6e, + 0x74, + 0x2c, + 0x20, + 0x24, + 0x74, + 0x61, + 0x6b, + 0x65, + 0x3a, + 0x20, + 0x49, + 0x6e, + 0x74, + 0x2c, + 0x20, + 0x24, + 0x69, + 0x6e, + 0x61, + 0x63, + 0x74, + 0x69, + 0x76, + 0x65, + 0x3a, + 0x20, + 0x42, + 0x6f, + 0x6f, + 0x6c, + 0x65, + 0x61, + 0x6e, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x6f, + 0x70, + 0x6c, + 0x65, + 0x3a, + 0x20, + 0x70, + 0x65, + 0x6f, + 0x70, + 0x6c, + 0x65, + 0x53, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x28, + 0x74, + 0x65, + 0x72, + 0x6d, + 0x3a, + 0x20, + 0x24, + 0x74, + 0x65, + 0x72, + 0x6d, + 0x2c, + 0x20, + 0x69, + 0x6e, + 0x63, + 0x6c, + 0x75, + 0x64, + 0x65, + 0x49, + 0x6e, + 0x61, + 0x63, + 0x74, + 0x69, + 0x76, + 0x65, + 0x3a, + 0x20, + 0x24, + 0x69, + 0x6e, + 0x61, + 0x63, + 0x74, + 0x69, + 0x76, + 0x65, + 0x2c, + 0x20, + 0x73, + 0x6b, + 0x69, + 0x70, + 0x3a, + 0x20, + 0x24, + 0x73, + 0x6b, + 0x69, + 0x70, + 0x2c, + 0x20, + 0x74, + 0x61, + 0x6b, + 0x65, + 0x3a, + 0x20, + 0x24, + 0x74, + 0x61, + 0x6b, + 0x65, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x74, + 0x6f, + 0x74, + 0x61, + 0x6c, + 0x43, + 0x6f, + 0x75, + 0x6e, + 0x74, + 0x20, + 0x70, + 0x61, + 0x67, + 0x65, + 0x49, + 0x6e, + 0x66, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x68, + 0x61, + 0x73, + 0x4e, + 0x65, + 0x78, + 0x74, + 0x50, + 0x61, + 0x67, + 0x65, + 0x20, + 0x68, + 0x61, + 0x73, + 0x50, + 0x72, + 0x65, + 0x76, + 0x69, + 0x6f, + 0x75, + 0x73, + 0x50, + 0x61, + 0x67, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x69, + 0x74, + 0x65, + 0x6d, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x50, + 0x65, + 0x6f, + 0x70, + 0x6c, + 0x65, + 0x53, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x52, + 0x65, + 0x73, + 0x75, + 0x6c, + 0x74, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x50, + 0x65, + 0x6f, + 0x70, + 0x6c, + 0x65, + 0x53, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x52, + 0x65, + 0x73, + 0x75, + 0x6c, + 0x74, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x6b, + 0x65, + 0x79, + 0x20, + 0x64, + 0x69, + 0x73, + 0x70, + 0x6c, + 0x61, + 0x79, + 0x4e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x69, + 0x73, + 0x41, + 0x63, + 0x74, + 0x69, + 0x76, + 0x65, + 0x20, + 0x64, + 0x65, + 0x70, + 0x61, + 0x72, + 0x74, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x69, + 0x64, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x65, + 0x70, + 0x61, + 0x72, + 0x74, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x69, + 0x6d, + 0x61, + 0x67, + 0x65, + 0x20, + 0x74, + 0x69, + 0x74, + 0x6c, + 0x65, + 0x20, + 0x6d, + 0x61, + 0x6e, + 0x61, + 0x67, + 0x65, + 0x72, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x69, + 0x64, + 0x20, + 0x6b, + 0x65, + 0x79, + 0x20, + 0x64, + 0x69, + 0x73, + 0x70, + 0x6c, + 0x61, + 0x79, + 0x4e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x65, + 0x72, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "0aa0e07a3648d30f705916cfe5d398a1c5c4beac"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -806,6 +1300,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IPeopleSearchQueryResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.String term, global::System.Int32? skip, global::System.Int32? take, global::System.Boolean? inactive, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(term, skip, take, inactive); @@ -989,25 +1484,18 @@ namespace Foo.Bar.State } public global::System.String Id { get; } - ///The database primary key for the person. public global::System.Int64 Key { get; } - ///The full of the person. public global::System.String? DisplayName { get; } - ///Indicates if the person is active. public global::System.Boolean IsActive { get; } - ///The department the person belongs to. public global::StrawberryShake.EntityId? Department { get; } - ///Url to image, you can specify a size in pixels by appending '?size=300' to get a square image. public global::System.Uri? Image { get; } - ///The job title of the person. public global::System.String Title { get; } - ///The person managing the person. public global::StrawberryShake.EntityId? Manager { get; } } @@ -1023,7 +1511,6 @@ namespace Foo.Bar.State } public global::System.String Id { get; } - ///The name of the department. public global::System.String Name { get; } } @@ -1045,6 +1532,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IPeopleSearchQueryResult); + public PeopleSearchResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -1174,9 +1662,9 @@ namespace Foo.Bar.State /// Searches for people based on their id, name, and identites. /// public global::Foo.Bar.State.PersonCollectionSegmentData? People { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new PeopleSearchResultInfo(People, _entityIds, version); @@ -1483,12 +1971,9 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.Int32? TotalCount { get; } - ///Information to aid in pagination. public global::Foo.Bar.State.CollectionSegmentInfoData? PageInfo { get; } - public global::System.Collections.Generic.IReadOnlyList? Items { get; } } @@ -1505,10 +1990,8 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - ///Indicates whether more items exist following the set defined by the clients arguments. public global::System.Boolean? HasNextPage { get; } - ///Indicates whether more items exist prior the set defined by the clients arguments. public global::System.Boolean? HasPreviousPage { get; } } @@ -1615,7 +2098,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Create_Query_With_Skip_Take.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Create_Query_With_Skip_Take.snap index f7e17360728..8b66baf801d 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Create_Query_With_Skip_Take.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Create_Query_With_Skip_Take.snap @@ -167,9 +167,7 @@ namespace Foo.Bar } public global::System.String Id { get; } - public global::System.String Title { get; } - public global::System.String Summary { get; } public virtual global::System.Boolean Equals(SearchNewsItems_NewsItems_Items_NewsItem? other) @@ -250,9 +248,7 @@ namespace Foo.Bar public partial interface ISearchNewsItems_NewsItems_Items { public global::System.String Id { get; } - public global::System.String Title { get; } - public global::System.String Summary { get; } } @@ -291,8 +287,204 @@ namespace Foo.Bar public static SearchNewsItemsQueryDocument Instance { get; } = new SearchNewsItemsQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x4e, 0x65, 0x77, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x28, 0x24, 0x71, 0x75, 0x65, 0x72, 0x79, 0x3a, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x21, 0x2c, 0x20, 0x24, 0x73, 0x6b, 0x69, 0x70, 0x3a, 0x20, 0x49, 0x6e, 0x74, 0x2c, 0x20, 0x24, 0x74, 0x61, 0x6b, 0x65, 0x3a, 0x20, 0x49, 0x6e, 0x74, 0x29, 0x20, 0x7b, 0x20, 0x6e, 0x65, 0x77, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x28, 0x73, 0x6b, 0x69, 0x70, 0x3a, 0x20, 0x24, 0x73, 0x6b, 0x69, 0x70, 0x2c, 0x20, 0x74, 0x61, 0x6b, 0x65, 0x3a, 0x20, 0x24, 0x74, 0x61, 0x6b, 0x65, 0x2c, 0x20, 0x71, 0x75, 0x65, 0x72, 0x79, 0x3a, 0x20, 0x24, 0x71, 0x75, 0x65, 0x72, 0x79, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x64, 0x20, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x20, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x4e, 0x65, 0x77, 0x73, 0x49, 0x74, 0x65, 0x6d, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x53, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x4e, + 0x65, + 0x77, + 0x73, + 0x49, + 0x74, + 0x65, + 0x6d, + 0x73, + 0x28, + 0x24, + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x3a, + 0x20, + 0x53, + 0x74, + 0x72, + 0x69, + 0x6e, + 0x67, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x73, + 0x6b, + 0x69, + 0x70, + 0x3a, + 0x20, + 0x49, + 0x6e, + 0x74, + 0x2c, + 0x20, + 0x24, + 0x74, + 0x61, + 0x6b, + 0x65, + 0x3a, + 0x20, + 0x49, + 0x6e, + 0x74, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x65, + 0x77, + 0x73, + 0x49, + 0x74, + 0x65, + 0x6d, + 0x73, + 0x28, + 0x73, + 0x6b, + 0x69, + 0x70, + 0x3a, + 0x20, + 0x24, + 0x73, + 0x6b, + 0x69, + 0x70, + 0x2c, + 0x20, + 0x74, + 0x61, + 0x6b, + 0x65, + 0x3a, + 0x20, + 0x24, + 0x74, + 0x61, + 0x6b, + 0x65, + 0x2c, + 0x20, + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x3a, + 0x20, + 0x24, + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x69, + 0x74, + 0x65, + 0x6d, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x69, + 0x64, + 0x20, + 0x74, + 0x69, + 0x74, + 0x6c, + 0x65, + 0x20, + 0x73, + 0x75, + 0x6d, + 0x6d, + 0x61, + 0x72, + 0x79, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4e, + 0x65, + 0x77, + 0x73, + 0x49, + 0x74, + 0x65, + 0x6d, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "81d29110879bdfdff3d9b0cc85211da657cf052b"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -337,6 +529,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ISearchNewsItemsResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.String query, global::System.Int32? skip, global::System.Int32? take, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(query, skip, take); @@ -472,9 +665,7 @@ namespace Foo.Bar.State } public global::System.String Id { get; } - public global::System.String Title { get; } - public global::System.String Summary { get; } } @@ -491,6 +682,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ISearchNewsItemsResult); + public SearchNewsItemsResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -577,9 +769,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.NewsItemCollectionSegmentData? NewsItems { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new SearchNewsItemsResultInfo(NewsItems, _entityIds, version); @@ -720,7 +912,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.Collections.Generic.IReadOnlyList? Items { get; } } @@ -750,7 +941,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Create_UpdateMembers_Mutation.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Create_UpdateMembers_Mutation.snap index d69bbfa3687..9fe97e04fb8 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Create_UpdateMembers_Mutation.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Create_UpdateMembers_Mutation.snap @@ -253,6 +253,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _guidFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _updateProjectMemberInputFormatter = default !; public global::System.String TypeName => "UpdateProjectMembersInput"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _guidFormatter = serializerResolver.GetInputValueFormatter("Guid"); @@ -395,6 +396,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IUpdateProjectMembersInputInfo.IsIdSet => _set_id; + ///A member of the project public global::System.Collections.Generic.IReadOnlyList Members { @@ -417,6 +419,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _projectUserRoleTypeFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _booleanFormatter = default !; public global::System.String TypeName => "UpdateProjectMemberInput"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _guidFormatter = serializerResolver.GetInputValueFormatter("Guid"); @@ -547,6 +550,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IUpdateProjectMemberInputInfo.IsIdSet => _set_id; + ///The type of role the user has on the project, only used when Remove is false public global::Foo.Bar.ProjectUserRoleType Type { @@ -559,6 +563,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IUpdateProjectMemberInputInfo.IsTypeSet => _set_type; + ///True will remove the member from the project public global::System.Boolean Remove { @@ -587,6 +592,7 @@ namespace Foo.Bar public partial class ProjectUserRoleTypeSerializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "ProjectUserRoleType"; + public ProjectUserRoleType Parse(global::System.String serializedValue) { return serializedValue switch @@ -632,8 +638,154 @@ namespace Foo.Bar public static UpdateMembersMutationDocument Instance { get; } = new UpdateMembersMutationDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Mutation; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x28, 0x24, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x28, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x24, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x6d, + 0x75, + 0x74, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x55, + 0x70, + 0x64, + 0x61, + 0x74, + 0x65, + 0x4d, + 0x65, + 0x6d, + 0x62, + 0x65, + 0x72, + 0x73, + 0x28, + 0x24, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x3a, + 0x20, + 0x55, + 0x70, + 0x64, + 0x61, + 0x74, + 0x65, + 0x50, + 0x72, + 0x6f, + 0x6a, + 0x65, + 0x63, + 0x74, + 0x4d, + 0x65, + 0x6d, + 0x62, + 0x65, + 0x72, + 0x73, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x72, + 0x6f, + 0x6a, + 0x65, + 0x63, + 0x74, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x75, + 0x70, + 0x64, + 0x61, + 0x74, + 0x65, + 0x4d, + 0x65, + 0x6d, + 0x62, + 0x65, + 0x72, + 0x73, + 0x28, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x63, + 0x6f, + 0x72, + 0x72, + 0x65, + 0x6c, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x49, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "8aac78b8e445094164d4bdd50d47e2a919e24dfc"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -671,6 +823,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IUpdateMembersResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::Foo.Bar.UpdateProjectMembersInput input, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(input); @@ -774,6 +927,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IUpdateMembersResult); + public UpdateMembersResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -844,9 +998,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.ProjectMutationsData Project { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new UpdateMembersResultInfo(Project, _entityIds, version); @@ -974,7 +1128,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - ///Update the project members public global::Foo.Bar.State.MutationResultData? UpdateMembers { get; } } @@ -990,7 +1143,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.Guid? CorrelationId { get; } } @@ -999,7 +1151,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.EnumWithUnderscorePrefixedValues.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.EnumWithUnderscorePrefixedValues.snap index 904196a000f..9fe734a4462 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.EnumWithUnderscorePrefixedValues.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.EnumWithUnderscorePrefixedValues.snap @@ -106,6 +106,7 @@ namespace Foo.Bar public partial class Enum1Serializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "Enum1"; + public Enum1 Parse(global::System.String serializedValue) { return serializedValue switch @@ -149,8 +150,37 @@ namespace Foo.Bar public static GetField1QueryDocument Instance { get; } = new GetField1QueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x31, 0x20, 0x7b, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x31, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x46, + 0x69, + 0x65, + 0x6c, + 0x64, + 0x31, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x69, + 0x65, + 0x6c, + 0x64, + 0x31, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "b505bdfe64c0b4ef198f5363575dea66cf6092df"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -180,6 +210,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetField1Result); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -265,6 +296,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetField1Result); + public GetField1Result Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -300,9 +332,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.Enum1? Field1 { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetField1ResultInfo(Field1, _entityIds, version); @@ -358,7 +390,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.FieldsWithUnderlineInName.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.FieldsWithUnderlineInName.snap index b9372b5d3b9..e79be115092 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.FieldsWithUnderlineInName.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.FieldsWithUnderlineInName.snap @@ -183,29 +183,17 @@ namespace Foo.Bar } public global::System.String? InventoryId { get; } - public global::System.String? Area { get; } - public global::System.String? Source { get; } - public global::System.String? Type { get; } - public global::System.String? Name { get; } - public global::System.String? Category { get; } - public global::System.String? Specification { get; } - public global::System.String? Commodity { get; } - public global::Foo.Bar.IGetBwr_TimeSeries_Bwr_TimeSeries_Nodes_Resolution? Resolution { get; } - public global::System.String? Unit { get; } - public global::Foo.Bar.IGetBwr_TimeSeries_Bwr_TimeSeries_Nodes_ValidationCriteria? ValidationCriteria { get; } - public global::Foo.Bar.IGetBwr_TimeSeries_Bwr_TimeSeries_Nodes_ImportSpecification? ImportSpecification { get; } - public global::System.Collections.Generic.IReadOnlyList? _dataPoints { get; } public virtual global::System.Boolean Equals(GetBwr_TimeSeries_Bwr_TimeSeries_Nodes_bwr_TimeSeries? other) @@ -337,7 +325,6 @@ namespace Foo.Bar } public global::Foo.Bar.TimeUnit TimeUnit { get; } - public global::System.Int32 Factor { get; } public virtual global::System.Boolean Equals(GetBwr_TimeSeries_Bwr_TimeSeries_Nodes_Resolution_Resolution? other) @@ -406,13 +393,9 @@ namespace Foo.Bar } public global::System.String? _inventoryItemId { get; } - public global::System.String? Name { get; } - public global::System.String? Completeness { get; } - public global::System.Decimal? LowerBound { get; } - public global::System.Decimal? UpperBound { get; } public virtual global::System.Boolean Equals(GetBwr_TimeSeries_Bwr_TimeSeries_Nodes_ValidationCriteria_bwr_ValidationCriteria? other) @@ -501,7 +484,6 @@ namespace Foo.Bar } public global::System.Int32? FromPeriod { get; } - public global::System.Int32? ToPeriod { get; } public virtual global::System.Boolean Equals(GetBwr_TimeSeries_Bwr_TimeSeries_Nodes_ImportSpecification_bwr_ImportSpecification? other) @@ -576,9 +558,7 @@ namespace Foo.Bar } public global::System.DateTimeOffset Timestamp { get; } - public global::System.Double Value { get; } - public global::Foo.Bar.DataFlag Flag { get; } public virtual global::System.Boolean Equals(GetBwr_TimeSeries_Bwr_TimeSeries_Nodes__dataPoints_DataPoint? other) @@ -668,29 +648,17 @@ namespace Foo.Bar public partial interface IBwr_TimeSeries { public global::System.String? InventoryId { get; } - public global::System.String? Area { get; } - public global::System.String? Source { get; } - public global::System.String? Type { get; } - public global::System.String? Name { get; } - public global::System.String? Category { get; } - public global::System.String? Specification { get; } - public global::System.String? Commodity { get; } - public global::Foo.Bar.IGetBwr_TimeSeries_Bwr_TimeSeries_Nodes_Resolution? Resolution { get; } - public global::System.String? Unit { get; } - public global::Foo.Bar.IGetBwr_TimeSeries_Bwr_TimeSeries_Nodes_ValidationCriteria? ValidationCriteria { get; } - public global::Foo.Bar.IGetBwr_TimeSeries_Bwr_TimeSeries_Nodes_ImportSpecification? ImportSpecification { get; } - public global::System.Collections.Generic.IReadOnlyList? _dataPoints { get; } } @@ -711,7 +679,6 @@ namespace Foo.Bar public partial interface IGetBwr_TimeSeries_Bwr_TimeSeries_Nodes_Resolution { public global::Foo.Bar.TimeUnit TimeUnit { get; } - public global::System.Int32 Factor { get; } } @@ -726,13 +693,9 @@ namespace Foo.Bar public partial interface IBwr_ValidationCriteria { public global::System.String? _inventoryItemId { get; } - public global::System.String? Name { get; } - public global::System.String? Completeness { get; } - public global::System.Decimal? LowerBound { get; } - public global::System.Decimal? UpperBound { get; } } @@ -753,7 +716,6 @@ namespace Foo.Bar public partial interface IGetBwr_TimeSeries_Bwr_TimeSeries_Nodes_ImportSpecification { public global::System.Int32? FromPeriod { get; } - public global::System.Int32? ToPeriod { get; } } @@ -768,9 +730,7 @@ namespace Foo.Bar public partial interface IGetBwr_TimeSeries_Bwr_TimeSeries_Nodes__dataPoints { public global::System.DateTimeOffset Timestamp { get; } - public global::System.Double Value { get; } - public global::Foo.Bar.DataFlag Flag { get; } } @@ -789,6 +749,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _referenceTyOfbwr_TimeSeriesFilterInputFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _listRefFilterTyOfbwr_TimeSeriesFilterInputFormatter = default !; public global::System.String TypeName => "bwr_TimeSeriesFilterInput"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _bwr_TimeSeriesFilterInputFormatter = serializerResolver.GetInputValueFormatter("bwr_TimeSeriesFilterInput"); @@ -1268,6 +1229,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBwr_TimeSeriesFilterInputInfo.IsAndSet => _set_and; + public global::System.Collections.Generic.IReadOnlyList? Or { get => _value_or; @@ -1279,6 +1241,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBwr_TimeSeriesFilterInputInfo.IsOrSet => _set_or; + public global::Foo.Bar.StringOperationFilterInput? Name { get => _value_name; @@ -1290,6 +1253,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBwr_TimeSeriesFilterInputInfo.IsNameSet => _set_name; + public global::Foo.Bar.StringOperationFilterInput? Category { get => _value_category; @@ -1301,6 +1265,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBwr_TimeSeriesFilterInputInfo.IsCategorySet => _set_category; + public global::Foo.Bar.StringOperationFilterInput? Specification { get => _value_specification; @@ -1312,6 +1277,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBwr_TimeSeriesFilterInputInfo.IsSpecificationSet => _set_specification; + public global::Foo.Bar.StringOperationFilterInput? Commodity { get => _value_commodity; @@ -1323,6 +1289,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBwr_TimeSeriesFilterInputInfo.IsCommoditySet => _set_commodity; + public global::Foo.Bar.StringOperationFilterInput? Area { get => _value_area; @@ -1334,6 +1301,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBwr_TimeSeriesFilterInputInfo.IsAreaSet => _set_area; + public global::Foo.Bar.StringOperationFilterInput? Source { get => _value_source; @@ -1345,6 +1313,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBwr_TimeSeriesFilterInputInfo.IsSourceSet => _set_source; + public global::Foo.Bar.StringOperationFilterInput? Type { get => _value_type; @@ -1356,6 +1325,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBwr_TimeSeriesFilterInputInfo.IsTypeSet => _set_type; + public global::Foo.Bar.StringOperationFilterInput? Resolution { get => _value_resolution; @@ -1367,6 +1337,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBwr_TimeSeriesFilterInputInfo.IsResolutionSet => _set_resolution; + public global::Foo.Bar.StringOperationFilterInput? Unit { get => _value_unit; @@ -1378,6 +1349,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBwr_TimeSeriesFilterInputInfo.IsUnitSet => _set_unit; + public global::Foo.Bar.ReferenceTyOfbwr_TimeSeriesFilterInput? ImportSpecification { get => _value_importSpecification; @@ -1389,6 +1361,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBwr_TimeSeriesFilterInputInfo.IsImportSpecificationSet => _set_importSpecification; + public global::Foo.Bar.ReferenceTyOfbwr_TimeSeriesFilterInput? ValidationCriteria { get => _value_validationCriteria; @@ -1400,6 +1373,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBwr_TimeSeriesFilterInputInfo.IsValidationCriteriaSet => _set_validationCriteria; + public global::Foo.Bar.ListRefFilterTyOfbwr_TimeSeriesFilterInput? ValidationResults { get => _value_validationResults; @@ -1411,6 +1385,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IBwr_TimeSeriesFilterInputInfo.IsValidationResultsSet => _set_validationResults; + public global::Foo.Bar.StringOperationFilterInput? _inventoryItemId { get => _value__inventoryItemId; @@ -1431,6 +1406,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _stringOperationFilterInputFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _stringFormatter = default !; public global::System.String TypeName => "StringOperationFilterInput"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _stringOperationFilterInputFormatter = serializerResolver.GetInputValueFormatter("StringOperationFilterInput"); @@ -1874,6 +1850,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IStringOperationFilterInputInfo.IsAndSet => _set_and; + public global::System.Collections.Generic.IReadOnlyList? Or { get => _value_or; @@ -1885,6 +1862,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IStringOperationFilterInputInfo.IsOrSet => _set_or; + public global::System.String? Eq { get => _value_eq; @@ -1896,6 +1874,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IStringOperationFilterInputInfo.IsEqSet => _set_eq; + public global::System.String? Neq { get => _value_neq; @@ -1907,6 +1886,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IStringOperationFilterInputInfo.IsNeqSet => _set_neq; + public global::System.String? Contains { get => _value_contains; @@ -1918,6 +1898,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IStringOperationFilterInputInfo.IsContainsSet => _set_contains; + public global::System.String? Ncontains { get => _value_ncontains; @@ -1929,6 +1910,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IStringOperationFilterInputInfo.IsNcontainsSet => _set_ncontains; + public global::System.Collections.Generic.IReadOnlyList? In { get => _value_in; @@ -1940,6 +1922,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IStringOperationFilterInputInfo.IsInSet => _set_in; + public global::System.Collections.Generic.IReadOnlyList? Nin { get => _value_nin; @@ -1951,6 +1934,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IStringOperationFilterInputInfo.IsNinSet => _set_nin; + public global::System.String? StartsWith { get => _value_startsWith; @@ -1962,6 +1946,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IStringOperationFilterInputInfo.IsStartsWithSet => _set_startsWith; + public global::System.String? NstartsWith { get => _value_nstartsWith; @@ -1973,6 +1958,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IStringOperationFilterInputInfo.IsNstartsWithSet => _set_nstartsWith; + public global::System.String? EndsWith { get => _value_endsWith; @@ -1984,6 +1970,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IStringOperationFilterInputInfo.IsEndsWithSet => _set_endsWith; + public global::System.String? NendsWith { get => _value_nendsWith; @@ -2004,6 +1991,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _referenceTyOfbwr_TimeSeriesFilterInputFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _stringOperationFilterInputFormatter = default !; public global::System.String TypeName => "ReferenceTyOfbwr_TimeSeriesFilterInput"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _referenceTyOfbwr_TimeSeriesFilterInputFormatter = serializerResolver.GetInputValueFormatter("ReferenceTyOfbwr_TimeSeriesFilterInput"); @@ -2193,6 +2181,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IReferenceTyOfbwr_TimeSeriesFilterInputInfo.IsAndSet => _set_and; + public global::System.Collections.Generic.IReadOnlyList? Or { get => _value_or; @@ -2204,6 +2193,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IReferenceTyOfbwr_TimeSeriesFilterInputInfo.IsOrSet => _set_or; + public global::Foo.Bar.StringOperationFilterInput? _inventoryItemId { get => _value__inventoryItemId; @@ -2224,6 +2214,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _referenceTyOfbwr_TimeSeriesFilterInputFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _booleanFormatter = default !; public global::System.String TypeName => "ListRefFilterTyOfbwr_TimeSeriesFilterInput"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _referenceTyOfbwr_TimeSeriesFilterInputFormatter = serializerResolver.GetInputValueFormatter("ReferenceTyOfbwr_TimeSeriesFilterInput"); @@ -2409,6 +2400,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IListRefFilterTyOfbwr_TimeSeriesFilterInputInfo.IsAllSet => _set_all; + public global::Foo.Bar.ReferenceTyOfbwr_TimeSeriesFilterInput? None { get => _value_none; @@ -2420,6 +2412,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IListRefFilterTyOfbwr_TimeSeriesFilterInputInfo.IsNoneSet => _set_none; + public global::Foo.Bar.ReferenceTyOfbwr_TimeSeriesFilterInput? Some { get => _value_some; @@ -2431,6 +2424,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IListRefFilterTyOfbwr_TimeSeriesFilterInputInfo.IsSomeSet => _set_some; + public global::System.Boolean? Any { get => _value_any; @@ -2453,6 +2447,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _resolutionInputFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _aggregationFormatter = default !; public global::System.String TypeName => "ReadDataInput"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _stringFormatter = serializerResolver.GetInputValueFormatter("String"); @@ -2642,6 +2637,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IReadDataInputInfo.IsUnitSet => _set_unit; + public global::System.DateTimeOffset From { get => _value_from; @@ -2653,6 +2649,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IReadDataInputInfo.IsFromSet => _set_from; + public global::System.DateTimeOffset To { get => _value_to; @@ -2664,6 +2661,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IReadDataInputInfo.IsToSet => _set_to; + public global::Foo.Bar.ResolutionInput? Resolution { get => _value_resolution; @@ -2675,6 +2673,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IReadDataInputInfo.IsResolutionSet => _set_resolution; + public global::Foo.Bar.Aggregation? Aggregation { get => _value_aggregation; @@ -2695,6 +2694,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _timeUnitFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _intFormatter = default !; public global::System.String TypeName => "ResolutionInput"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _timeUnitFormatter = serializerResolver.GetInputValueFormatter("TimeUnit"); @@ -2810,6 +2810,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IResolutionInputInfo.IsTimeUnitSet => _set_timeUnit; + public global::System.Int32 Factor { get => _value_factor; @@ -2843,6 +2844,7 @@ namespace Foo.Bar public partial class TimeUnitSerializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "TimeUnit"; + public TimeUnit Parse(global::System.String serializedValue) { return serializedValue switch @@ -2891,6 +2893,7 @@ namespace Foo.Bar public partial class AggregationSerializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "Aggregation"; + public Aggregation Parse(global::System.String serializedValue) { return serializedValue switch @@ -2928,6 +2931,7 @@ namespace Foo.Bar public partial class DataFlagSerializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "DataFlag"; + public DataFlag Parse(global::System.String serializedValue) { return serializedValue switch @@ -3022,8 +3026,778 @@ namespace Foo.Bar public static GetBwr_TimeSeriesQueryDocument Instance { get; } = new GetBwr_TimeSeriesQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x42, 0x77, 0x72, 0x5f, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x28, 0x24, 0x77, 0x68, 0x65, 0x72, 0x65, 0x3a, 0x20, 0x62, 0x77, 0x72, 0x5f, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x2c, 0x20, 0x24, 0x72, 0x65, 0x61, 0x64, 0x44, 0x61, 0x74, 0x61, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x52, 0x65, 0x61, 0x64, 0x44, 0x61, 0x74, 0x61, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x62, 0x77, 0x72, 0x5f, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x28, 0x77, 0x68, 0x65, 0x72, 0x65, 0x3a, 0x20, 0x24, 0x77, 0x68, 0x65, 0x72, 0x65, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x42, 0x77, 0x72, 0x5f, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x62, 0x77, 0x72, 0x5f, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x42, 0x77, 0x72, 0x5f, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x20, 0x6f, 0x6e, 0x20, 0x62, 0x77, 0x72, 0x5f, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x65, 0x72, 0x69, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x69, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x49, 0x64, 0x3a, 0x20, 0x5f, 0x69, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x49, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x20, 0x61, 0x72, 0x65, 0x61, 0x20, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x64, 0x69, 0x74, 0x79, 0x20, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x74, 0x20, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x20, 0x7d, 0x20, 0x75, 0x6e, 0x69, 0x74, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x61, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x42, 0x77, 0x72, 0x5f, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x61, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x62, 0x77, 0x72, 0x5f, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x61, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x20, 0x74, 0x6f, 0x50, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x62, 0x77, 0x72, 0x5f, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x73, 0x28, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x20, 0x24, 0x72, 0x65, 0x61, 0x64, 0x44, 0x61, 0x74, 0x61, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x42, 0x77, 0x72, 0x5f, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x61, 0x20, 0x6f, 0x6e, 0x20, 0x62, 0x77, 0x72, 0x5f, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x61, 0x20, 0x7b, 0x20, 0x5f, 0x69, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x49, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x20, 0x6c, 0x6f, 0x77, 0x65, 0x72, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x75, 0x70, 0x70, 0x65, 0x72, 0x42, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x42, + 0x77, + 0x72, + 0x5f, + 0x54, + 0x69, + 0x6d, + 0x65, + 0x53, + 0x65, + 0x72, + 0x69, + 0x65, + 0x73, + 0x28, + 0x24, + 0x77, + 0x68, + 0x65, + 0x72, + 0x65, + 0x3a, + 0x20, + 0x62, + 0x77, + 0x72, + 0x5f, + 0x54, + 0x69, + 0x6d, + 0x65, + 0x53, + 0x65, + 0x72, + 0x69, + 0x65, + 0x73, + 0x46, + 0x69, + 0x6c, + 0x74, + 0x65, + 0x72, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x2c, + 0x20, + 0x24, + 0x72, + 0x65, + 0x61, + 0x64, + 0x44, + 0x61, + 0x74, + 0x61, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x3a, + 0x20, + 0x52, + 0x65, + 0x61, + 0x64, + 0x44, + 0x61, + 0x74, + 0x61, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x62, + 0x77, + 0x72, + 0x5f, + 0x54, + 0x69, + 0x6d, + 0x65, + 0x53, + 0x65, + 0x72, + 0x69, + 0x65, + 0x73, + 0x28, + 0x77, + 0x68, + 0x65, + 0x72, + 0x65, + 0x3a, + 0x20, + 0x24, + 0x77, + 0x68, + 0x65, + 0x72, + 0x65, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x6f, + 0x64, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x42, + 0x77, + 0x72, + 0x5f, + 0x54, + 0x69, + 0x6d, + 0x65, + 0x53, + 0x65, + 0x72, + 0x69, + 0x65, + 0x73, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x62, + 0x77, + 0x72, + 0x5f, + 0x54, + 0x69, + 0x6d, + 0x65, + 0x53, + 0x65, + 0x72, + 0x69, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x42, + 0x77, + 0x72, + 0x5f, + 0x54, + 0x69, + 0x6d, + 0x65, + 0x53, + 0x65, + 0x72, + 0x69, + 0x65, + 0x73, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x62, + 0x77, + 0x72, + 0x5f, + 0x54, + 0x69, + 0x6d, + 0x65, + 0x53, + 0x65, + 0x72, + 0x69, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x6e, + 0x76, + 0x65, + 0x6e, + 0x74, + 0x6f, + 0x72, + 0x79, + 0x49, + 0x64, + 0x3a, + 0x20, + 0x5f, + 0x69, + 0x6e, + 0x76, + 0x65, + 0x6e, + 0x74, + 0x6f, + 0x72, + 0x79, + 0x49, + 0x74, + 0x65, + 0x6d, + 0x49, + 0x64, + 0x20, + 0x61, + 0x72, + 0x65, + 0x61, + 0x20, + 0x73, + 0x6f, + 0x75, + 0x72, + 0x63, + 0x65, + 0x20, + 0x74, + 0x79, + 0x70, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x63, + 0x61, + 0x74, + 0x65, + 0x67, + 0x6f, + 0x72, + 0x79, + 0x20, + 0x73, + 0x70, + 0x65, + 0x63, + 0x69, + 0x66, + 0x69, + 0x63, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x63, + 0x6f, + 0x6d, + 0x6d, + 0x6f, + 0x64, + 0x69, + 0x74, + 0x79, + 0x20, + 0x72, + 0x65, + 0x73, + 0x6f, + 0x6c, + 0x75, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x74, + 0x69, + 0x6d, + 0x65, + 0x55, + 0x6e, + 0x69, + 0x74, + 0x20, + 0x66, + 0x61, + 0x63, + 0x74, + 0x6f, + 0x72, + 0x20, + 0x7d, + 0x20, + 0x75, + 0x6e, + 0x69, + 0x74, + 0x20, + 0x76, + 0x61, + 0x6c, + 0x69, + 0x64, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x43, + 0x72, + 0x69, + 0x74, + 0x65, + 0x72, + 0x69, + 0x61, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x42, + 0x77, + 0x72, + 0x5f, + 0x56, + 0x61, + 0x6c, + 0x69, + 0x64, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x43, + 0x72, + 0x69, + 0x74, + 0x65, + 0x72, + 0x69, + 0x61, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x62, + 0x77, + 0x72, + 0x5f, + 0x56, + 0x61, + 0x6c, + 0x69, + 0x64, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x43, + 0x72, + 0x69, + 0x74, + 0x65, + 0x72, + 0x69, + 0x61, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x69, + 0x6d, + 0x70, + 0x6f, + 0x72, + 0x74, + 0x53, + 0x70, + 0x65, + 0x63, + 0x69, + 0x66, + 0x69, + 0x63, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x66, + 0x72, + 0x6f, + 0x6d, + 0x50, + 0x65, + 0x72, + 0x69, + 0x6f, + 0x64, + 0x20, + 0x74, + 0x6f, + 0x50, + 0x65, + 0x72, + 0x69, + 0x6f, + 0x64, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x62, + 0x77, + 0x72, + 0x5f, + 0x49, + 0x6d, + 0x70, + 0x6f, + 0x72, + 0x74, + 0x53, + 0x70, + 0x65, + 0x63, + 0x69, + 0x66, + 0x69, + 0x63, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x5f, + 0x64, + 0x61, + 0x74, + 0x61, + 0x50, + 0x6f, + 0x69, + 0x6e, + 0x74, + 0x73, + 0x28, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x3a, + 0x20, + 0x24, + 0x72, + 0x65, + 0x61, + 0x64, + 0x44, + 0x61, + 0x74, + 0x61, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x74, + 0x69, + 0x6d, + 0x65, + 0x73, + 0x74, + 0x61, + 0x6d, + 0x70, + 0x20, + 0x76, + 0x61, + 0x6c, + 0x75, + 0x65, + 0x20, + 0x66, + 0x6c, + 0x61, + 0x67, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x42, + 0x77, + 0x72, + 0x5f, + 0x56, + 0x61, + 0x6c, + 0x69, + 0x64, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x43, + 0x72, + 0x69, + 0x74, + 0x65, + 0x72, + 0x69, + 0x61, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x62, + 0x77, + 0x72, + 0x5f, + 0x56, + 0x61, + 0x6c, + 0x69, + 0x64, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x43, + 0x72, + 0x69, + 0x74, + 0x65, + 0x72, + 0x69, + 0x61, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x69, + 0x6e, + 0x76, + 0x65, + 0x6e, + 0x74, + 0x6f, + 0x72, + 0x79, + 0x49, + 0x74, + 0x65, + 0x6d, + 0x49, + 0x64, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x63, + 0x6f, + 0x6d, + 0x70, + 0x6c, + 0x65, + 0x74, + 0x65, + 0x6e, + 0x65, + 0x73, + 0x73, + 0x20, + 0x6c, + 0x6f, + 0x77, + 0x65, + 0x72, + 0x42, + 0x6f, + 0x75, + 0x6e, + 0x64, + 0x20, + 0x75, + 0x70, + 0x70, + 0x65, + 0x72, + 0x42, + 0x6f, + 0x75, + 0x6e, + 0x64, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "fd97876c1fe57bcb23d5c9cf665e79aff6ded9e3"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -3112,6 +3886,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetBwr_TimeSeriesResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::Foo.Bar.bwr_TimeSeriesFilterInput? @where, global::Foo.Bar.ReadDataInput readDataInput, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(@where, readDataInput); @@ -3288,29 +4063,17 @@ namespace Foo.Bar.State } public global::System.String? InventoryId { get; } - public global::System.String? Area { get; } - public global::System.String? Source { get; } - public global::System.String? Type { get; } - public global::System.String? Name { get; } - public global::System.String? Category { get; } - public global::System.String? Specification { get; } - public global::System.String? Commodity { get; } - public global::Foo.Bar.State.ResolutionData? Resolution { get; } - public global::System.String? Unit { get; } - public global::StrawberryShake.EntityId? ValidationCriteria { get; } - public global::StrawberryShake.EntityId? ImportSpecification { get; } - public global::System.Collections.Generic.IReadOnlyList? _dataPoints { get; } } @@ -3328,13 +4091,9 @@ namespace Foo.Bar.State } public global::System.String? _inventoryItemId { get; } - public global::System.String? Name { get; } - public global::System.String? Completeness { get; } - public global::System.Decimal? LowerBound { get; } - public global::System.Decimal? UpperBound { get; } } @@ -3349,7 +4108,6 @@ namespace Foo.Bar.State } public global::System.Int32? FromPeriod { get; } - public global::System.Int32? ToPeriod { get; } } @@ -3370,6 +4128,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetBwr_TimeSeriesResult); + public GetBwr_TimeSeriesResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -3537,9 +4296,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.Bwr_TimeSeriesConnectionData? Bwr_TimeSeries { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetBwr_TimeSeriesResultInfo(Bwr_TimeSeries, _entityIds, version); @@ -4035,7 +4794,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - ///A flattened list of the nodes. public global::System.Collections.Generic.IReadOnlyList? Nodes { get; } } @@ -4052,9 +4810,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::Foo.Bar.TimeUnit? TimeUnit { get; } - public global::System.Int32? Factor { get; } } @@ -4071,11 +4827,8 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.DateTimeOffset? Timestamp { get; } - public global::System.Double? Value { get; } - public global::Foo.Bar.DataFlag? Flag { get; } } @@ -4232,7 +4985,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Full_Extension_File.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Full_Extension_File.snap index a011051dc2d..43f3ad38d50 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Full_Extension_File.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Full_Extension_File.snap @@ -204,8 +204,135 @@ namespace Foo.Bar public static GetListingsCountQueryDocument Instance { get; } = new GetListingsCountQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x7b, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x4c, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x4c, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x4c, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x7b, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x73, + 0x43, + 0x6f, + 0x75, + 0x6e, + 0x74, + 0x20, + 0x7b, + 0x20, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x73, + 0x50, + 0x61, + 0x79, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x73, + 0x50, + 0x61, + 0x79, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x73, + 0x50, + 0x61, + 0x79, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x63, + 0x6f, + 0x75, + 0x6e, + 0x74, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "c3a13333bd2282e2d7ed111b99934a7610014ba3"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -242,6 +369,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetListingsCountResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -334,6 +462,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetListingsCountResult); + public GetListingsCountResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -387,9 +516,9 @@ namespace Foo.Bar.State /// Vyhledávání mezi inzeráty /// public global::Foo.Bar.State.ListingsPayloadData Listings { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetListingsCountResultInfo(Listings, _entityIds, version); @@ -472,7 +601,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.Int32? Count { get; } } @@ -481,7 +609,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.HasuraMutation.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.HasuraMutation.snap index f93596a52fb..f497fc6ac32 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.HasuraMutation.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.HasuraMutation.snap @@ -200,6 +200,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _stringFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _uuidFormatter = default !; public global::System.String TypeName => "people_insert_input"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _cars_Arr_Rel_Insert_InputFormatter = serializerResolver.GetInputValueFormatter("cars_arr_rel_insert_input"); @@ -387,6 +388,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IPeople_insert_inputInfo.IsCarsSet => _set_cars; + public global::System.String? FirstName { get => _value_firstName; @@ -398,6 +400,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IPeople_insert_inputInfo.IsFirstNameSet => _set_firstName; + public global::System.String? Id { get => _value_id; @@ -409,6 +412,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IPeople_insert_inputInfo.IsIdSet => _set_id; + public global::System.String? LastName { get => _value_lastName; @@ -429,6 +433,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _cars_Insert_InputFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _cars_On_ConflictFormatter = default !; public global::System.String TypeName => "cars_arr_rel_insert_input"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _cars_Insert_InputFormatter = serializerResolver.GetInputValueFormatter("cars_insert_input"); @@ -576,6 +581,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.ICars_arr_rel_insert_inputInfo.IsDataSet => _set_data; + ///on conflict condition public global::Foo.Bar.cars_on_conflict? On_conflict { @@ -598,6 +604,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _uuidFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _people_Obj_Rel_Insert_InputFormatter = default !; public global::System.String TypeName => "cars_insert_input"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _stringFormatter = serializerResolver.GetInputValueFormatter("String"); @@ -785,6 +792,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.ICars_insert_inputInfo.IsCarNameSet => _set_carName; + public global::System.String? Id { get => _value_id; @@ -796,6 +804,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.ICars_insert_inputInfo.IsIdSet => _set_id; + public global::System.String? OwnerId { get => _value_ownerId; @@ -807,6 +816,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.ICars_insert_inputInfo.IsOwnerIdSet => _set_ownerId; + public global::Foo.Bar.people_obj_rel_insert_input? Person { get => _value_person; @@ -827,6 +837,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _people_Insert_InputFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _people_On_ConflictFormatter = default !; public global::System.String TypeName => "people_obj_rel_insert_input"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _people_Insert_InputFormatter = serializerResolver.GetInputValueFormatter("people_insert_input"); @@ -959,6 +970,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IPeople_obj_rel_insert_inputInfo.IsDataSet => _set_data; + ///on conflict condition public global::Foo.Bar.people_on_conflict? On_conflict { @@ -981,6 +993,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _people_Update_ColumnFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _people_Bool_ExpFormatter = default !; public global::System.String TypeName => "people_on_conflict"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _people_ConstraintFormatter = serializerResolver.GetInputValueFormatter("People_constraint"); @@ -1142,6 +1155,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IPeople_on_conflictInfo.IsConstraintSet => _set_constraint; + public global::System.Collections.Generic.IReadOnlyList? Update_columns { get => _value_update_Columns; @@ -1153,6 +1167,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IPeople_on_conflictInfo.IsUpdate_columnsSet => _set_update_Columns; + public global::Foo.Bar.people_bool_exp? Where { get => _value_where; @@ -1175,6 +1190,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _string_Comparison_ExpFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _uuid_Comparison_ExpFormatter = default !; public global::System.String TypeName => "people_bool_exp"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _people_Bool_ExpFormatter = serializerResolver.GetInputValueFormatter("people_bool_exp"); @@ -1463,6 +1479,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IPeople_bool_expInfo.Is_andSet => _set__and; + public global::Foo.Bar.people_bool_exp? _not { get => _value__not; @@ -1474,6 +1491,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IPeople_bool_expInfo.Is_notSet => _set__not; + public global::System.Collections.Generic.IReadOnlyList? _or { get => _value__or; @@ -1485,6 +1503,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IPeople_bool_expInfo.Is_orSet => _set__or; + public global::Foo.Bar.cars_bool_exp? Cars { get => _value_cars; @@ -1496,6 +1515,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IPeople_bool_expInfo.IsCarsSet => _set_cars; + public global::Foo.Bar.String_comparison_exp? FirstName { get => _value_firstName; @@ -1507,6 +1527,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IPeople_bool_expInfo.IsFirstNameSet => _set_firstName; + public global::Foo.Bar.uuid_comparison_exp? Id { get => _value_id; @@ -1518,6 +1539,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IPeople_bool_expInfo.IsIdSet => _set_id; + public global::Foo.Bar.String_comparison_exp? LastName { get => _value_lastName; @@ -1540,6 +1562,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _uuid_Comparison_ExpFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _people_Bool_ExpFormatter = default !; public global::System.String TypeName => "cars_bool_exp"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _cars_Bool_ExpFormatter = serializerResolver.GetInputValueFormatter("cars_bool_exp"); @@ -1828,6 +1851,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.ICars_bool_expInfo.Is_andSet => _set__and; + public global::Foo.Bar.cars_bool_exp? _not { get => _value__not; @@ -1839,6 +1863,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.ICars_bool_expInfo.Is_notSet => _set__not; + public global::System.Collections.Generic.IReadOnlyList? _or { get => _value__or; @@ -1850,6 +1875,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.ICars_bool_expInfo.Is_orSet => _set__or; + public global::Foo.Bar.String_comparison_exp? CarName { get => _value_carName; @@ -1861,6 +1887,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.ICars_bool_expInfo.IsCarNameSet => _set_carName; + public global::Foo.Bar.uuid_comparison_exp? Id { get => _value_id; @@ -1872,6 +1899,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.ICars_bool_expInfo.IsIdSet => _set_id; + public global::Foo.Bar.uuid_comparison_exp? OwnerId { get => _value_ownerId; @@ -1883,6 +1911,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.ICars_bool_expInfo.IsOwnerIdSet => _set_ownerId; + public global::Foo.Bar.people_bool_exp? Person { get => _value_person; @@ -1903,6 +1932,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _stringFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _booleanFormatter = default !; public global::System.String TypeName => "String_comparison_exp"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _stringFormatter = serializerResolver.GetInputValueFormatter("String"); @@ -2477,6 +2507,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IString_comparison_expInfo.Is_eqSet => _set__eq; + public global::System.String? _gt { get => _value__gt; @@ -2488,6 +2519,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IString_comparison_expInfo.Is_gtSet => _set__gt; + public global::System.String? _gte { get => _value__gte; @@ -2499,6 +2531,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IString_comparison_expInfo.Is_gteSet => _set__gte; + ///does the column match the given case-insensitive pattern public global::System.String? _ilike { @@ -2511,6 +2544,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IString_comparison_expInfo.Is_ilikeSet => _set__ilike; + public global::System.Collections.Generic.IReadOnlyList? _in { get => _value__in; @@ -2522,6 +2556,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IString_comparison_expInfo.Is_inSet => _set__in; + ///does the column match the given POSIX regular expression, case insensitive public global::System.String? _iregex { @@ -2534,6 +2569,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IString_comparison_expInfo.Is_iregexSet => _set__iregex; + public global::System.Boolean? _is_null { get => _value__is_null; @@ -2545,6 +2581,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IString_comparison_expInfo.Is_is_nullSet => _set__is_null; + ///does the column match the given pattern public global::System.String? _like { @@ -2557,6 +2594,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IString_comparison_expInfo.Is_likeSet => _set__like; + public global::System.String? _lt { get => _value__lt; @@ -2568,6 +2606,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IString_comparison_expInfo.Is_ltSet => _set__lt; + public global::System.String? _lte { get => _value__lte; @@ -2579,6 +2618,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IString_comparison_expInfo.Is_lteSet => _set__lte; + public global::System.String? _neq { get => _value__neq; @@ -2590,6 +2630,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IString_comparison_expInfo.Is_neqSet => _set__neq; + ///does the column NOT match the given case-insensitive pattern public global::System.String? _nilike { @@ -2602,6 +2643,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IString_comparison_expInfo.Is_nilikeSet => _set__nilike; + public global::System.Collections.Generic.IReadOnlyList? _nin { get => _value__nin; @@ -2613,6 +2655,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IString_comparison_expInfo.Is_ninSet => _set__nin; + ///does the column NOT match the given POSIX regular expression, case insensitive public global::System.String? _niregex { @@ -2625,6 +2668,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IString_comparison_expInfo.Is_niregexSet => _set__niregex; + ///does the column NOT match the given pattern public global::System.String? _nlike { @@ -2637,6 +2681,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IString_comparison_expInfo.Is_nlikeSet => _set__nlike; + ///does the column NOT match the given POSIX regular expression, case sensitive public global::System.String? _nregex { @@ -2649,6 +2694,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IString_comparison_expInfo.Is_nregexSet => _set__nregex; + ///does the column NOT match the given SQL regular expression public global::System.String? _nsimilar { @@ -2661,6 +2707,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IString_comparison_expInfo.Is_nsimilarSet => _set__nsimilar; + ///does the column match the given POSIX regular expression, case sensitive public global::System.String? _regex { @@ -2673,6 +2720,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IString_comparison_expInfo.Is_regexSet => _set__regex; + ///does the column match the given SQL regular expression public global::System.String? _similar { @@ -2694,6 +2742,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _uuidFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _booleanFormatter = default !; public global::System.String TypeName => "uuid_comparison_exp"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _uuidFormatter = serializerResolver.GetInputValueFormatter("uuid"); @@ -3028,6 +3077,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IUuid_comparison_expInfo.Is_eqSet => _set__eq; + public global::System.String? _gt { get => _value__gt; @@ -3039,6 +3089,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IUuid_comparison_expInfo.Is_gtSet => _set__gt; + public global::System.String? _gte { get => _value__gte; @@ -3050,6 +3101,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IUuid_comparison_expInfo.Is_gteSet => _set__gte; + public global::System.Collections.Generic.IReadOnlyList? _in { get => _value__in; @@ -3061,6 +3113,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IUuid_comparison_expInfo.Is_inSet => _set__in; + public global::System.Boolean? _is_null { get => _value__is_null; @@ -3072,6 +3125,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IUuid_comparison_expInfo.Is_is_nullSet => _set__is_null; + public global::System.String? _lt { get => _value__lt; @@ -3083,6 +3137,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IUuid_comparison_expInfo.Is_ltSet => _set__lt; + public global::System.String? _lte { get => _value__lte; @@ -3094,6 +3149,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IUuid_comparison_expInfo.Is_lteSet => _set__lte; + public global::System.String? _neq { get => _value__neq; @@ -3105,6 +3161,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IUuid_comparison_expInfo.Is_neqSet => _set__neq; + public global::System.Collections.Generic.IReadOnlyList? _nin { get => _value__nin; @@ -3126,6 +3183,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _cars_Update_ColumnFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _cars_Bool_ExpFormatter = default !; public global::System.String TypeName => "cars_on_conflict"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _cars_ConstraintFormatter = serializerResolver.GetInputValueFormatter("Cars_constraint"); @@ -3287,6 +3345,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.ICars_on_conflictInfo.IsConstraintSet => _set_constraint; + public global::System.Collections.Generic.IReadOnlyList? Update_columns { get => _value_update_Columns; @@ -3298,6 +3357,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.ICars_on_conflictInfo.IsUpdate_columnsSet => _set_update_Columns; + public global::Foo.Bar.cars_bool_exp? Where { get => _value_where; @@ -3329,6 +3389,7 @@ namespace Foo.Bar public partial class People_constraintSerializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "People_constraint"; + public People_constraint Parse(global::System.String serializedValue) { return serializedValue switch @@ -3372,6 +3433,7 @@ namespace Foo.Bar public partial class People_update_columnSerializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "People_update_column"; + public People_update_column Parse(global::System.String serializedValue) { return serializedValue switch @@ -3411,6 +3473,7 @@ namespace Foo.Bar public partial class Cars_constraintSerializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "Cars_constraint"; + public Cars_constraint Parse(global::System.String serializedValue) { return serializedValue switch @@ -3454,6 +3517,7 @@ namespace Foo.Bar public partial class Cars_update_columnSerializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "Cars_update_column"; + public Cars_update_column Parse(global::System.String serializedValue) { return serializedValue switch @@ -3496,8 +3560,131 @@ namespace Foo.Bar public static InsertPeopleMutationDocument Instance { get; } = new InsertPeopleMutationDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Mutation; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x49, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x50, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x28, 0x24, 0x70, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x3a, 0x20, 0x5b, 0x70, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x5f, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x21, 0x5d, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x5f, 0x70, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x28, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x3a, 0x20, 0x24, 0x70, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x61, 0x66, 0x66, 0x65, 0x63, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x6f, 0x77, 0x73, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x6d, + 0x75, + 0x74, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x49, + 0x6e, + 0x73, + 0x65, + 0x72, + 0x74, + 0x50, + 0x65, + 0x6f, + 0x70, + 0x6c, + 0x65, + 0x28, + 0x24, + 0x70, + 0x65, + 0x6f, + 0x70, + 0x6c, + 0x65, + 0x3a, + 0x20, + 0x5b, + 0x70, + 0x65, + 0x6f, + 0x70, + 0x6c, + 0x65, + 0x5f, + 0x69, + 0x6e, + 0x73, + 0x65, + 0x72, + 0x74, + 0x5f, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x21, + 0x5d, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x6e, + 0x73, + 0x65, + 0x72, + 0x74, + 0x5f, + 0x70, + 0x65, + 0x6f, + 0x70, + 0x6c, + 0x65, + 0x28, + 0x6f, + 0x62, + 0x6a, + 0x65, + 0x63, + 0x74, + 0x73, + 0x3a, + 0x20, + 0x24, + 0x70, + 0x65, + 0x6f, + 0x70, + 0x6c, + 0x65, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x61, + 0x66, + 0x66, + 0x65, + 0x63, + 0x74, + 0x65, + 0x64, + 0x5f, + 0x72, + 0x6f, + 0x77, + 0x73, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "c8b55152dcc4370cf6b30d4d748739527f578d0a"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -3532,6 +3719,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IInsertPeopleResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Collections.Generic.IReadOnlyList people, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(people); @@ -3643,6 +3831,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IInsertPeopleResult); + public InsertPeopleResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -3701,9 +3890,9 @@ namespace Foo.Bar.State /// insert data into the table: "people" /// public global::Foo.Bar.State.people_mutation_responseData? Insert_people { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new InsertPeopleResultInfo(Insert_people, _entityIds, version); @@ -3971,7 +4160,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - ///number of rows affected by the mutation public global::System.Int32? Affected_rows { get; } } @@ -3981,7 +4169,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.IntrospectionQuery.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.IntrospectionQuery.snap index 8cd9f097146..b78f6d9b4d0 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.IntrospectionQuery.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.IntrospectionQuery.snap @@ -102,22 +102,18 @@ namespace Foo.Bar /// The type that query operations will be rooted at. /// public global::Foo.Bar.IIntrospectionQuery___schema_QueryType QueryType { get; } - /// /// If this server supports mutation, the type that mutation operations will be rooted at. /// public global::Foo.Bar.IIntrospectionQuery___schema_MutationType? MutationType { get; } - /// /// If this server support subscription, the type that subscription operations will be rooted at. /// public global::Foo.Bar.IIntrospectionQuery___schema_SubscriptionType? SubscriptionType { get; } - /// /// A list of all types supported by this server. /// public global::System.Collections.Generic.IReadOnlyList Types { get; } - /// /// A list of all directives supported by this server. /// @@ -429,19 +425,12 @@ namespace Foo.Bar } public global::Foo.Bar.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::System.String? Description { get; } - public global::System.Collections.Generic.IReadOnlyList? Fields { get; } - public global::System.Collections.Generic.IReadOnlyList? InputFields { get; } - public global::System.Collections.Generic.IReadOnlyList? Interfaces { get; } - public global::System.Collections.Generic.IReadOnlyList? EnumValues { get; } - public global::System.Collections.Generic.IReadOnlyList? PossibleTypes { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types___Type? other) @@ -565,15 +554,10 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.String? Description { get; } - public global::System.Collections.Generic.IReadOnlyList Args { get; } - public global::System.Boolean OnOperation { get; } - public global::System.Boolean OnFragment { get; } - public global::System.Boolean OnField { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Directives___Directive? other) @@ -658,15 +642,10 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.String? Description { get; } - public global::System.Collections.Generic.IReadOnlyList Args { get; } - public global::Foo.Bar.IIntrospectionQuery___schema_Types_Fields_Type Type { get; } - public global::System.Boolean IsDeprecated { get; } - public global::System.String? DeprecationReason { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_Fields___Field? other) @@ -753,11 +732,8 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.String? Description { get; } - public global::Foo.Bar.IIntrospectionQuery___schema_Types_InputFields_Type Type { get; } - /// /// A GraphQL-formatted string representing the default value for this input value. /// @@ -842,9 +818,7 @@ namespace Foo.Bar } public global::Foo.Bar.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::Foo.Bar.IIntrospectionQuery___schema_Types_Interfaces_OfType? OfType { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_Interfaces___Type? other) @@ -924,11 +898,8 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.String? Description { get; } - public global::System.Boolean IsDeprecated { get; } - public global::System.String? DeprecationReason { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_EnumValues___EnumValue? other) @@ -1010,9 +981,7 @@ namespace Foo.Bar } public global::Foo.Bar.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::Foo.Bar.IIntrospectionQuery___schema_Types_Interfaces_OfType? OfType { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_PossibleTypes___Type? other) @@ -1092,11 +1061,8 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.String? Description { get; } - public global::Foo.Bar.IIntrospectionQuery___schema_Types_InputFields_Type Type { get; } - /// /// A GraphQL-formatted string representing the default value for this input value. /// @@ -1180,11 +1146,8 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.String? Description { get; } - public global::Foo.Bar.IIntrospectionQuery___schema_Types_InputFields_Type Type { get; } - /// /// A GraphQL-formatted string representing the default value for this input value. /// @@ -1269,9 +1232,7 @@ namespace Foo.Bar } public global::Foo.Bar.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::Foo.Bar.IIntrospectionQuery___schema_Types_Interfaces_OfType? OfType { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_Fields_Type___Type? other) @@ -1352,9 +1313,7 @@ namespace Foo.Bar } public global::Foo.Bar.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::Foo.Bar.IIntrospectionQuery___schema_Types_Interfaces_OfType? OfType { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_InputFields_Type___Type? other) @@ -1435,9 +1394,7 @@ namespace Foo.Bar } public global::Foo.Bar.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::Foo.Bar.IIntrospectionQuery___schema_Types_Interfaces_OfType_OfType? OfType { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_Interfaces_OfType___Type? other) @@ -1518,9 +1475,7 @@ namespace Foo.Bar } public global::Foo.Bar.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::Foo.Bar.IIntrospectionQuery___schema_Types_Interfaces_OfType_OfType? OfType { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_PossibleTypes_OfType___Type? other) @@ -1601,9 +1556,7 @@ namespace Foo.Bar } public global::Foo.Bar.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::Foo.Bar.IIntrospectionQuery___schema_Types_Interfaces_OfType? OfType { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Directives_Args_Type___Type? other) @@ -1684,9 +1637,7 @@ namespace Foo.Bar } public global::Foo.Bar.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::Foo.Bar.IIntrospectionQuery___schema_Types_Interfaces_OfType? OfType { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_Fields_Args_Type___Type? other) @@ -1767,9 +1718,7 @@ namespace Foo.Bar } public global::Foo.Bar.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::Foo.Bar.IIntrospectionQuery___schema_Types_Interfaces_OfType_OfType? OfType { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_Fields_Type_OfType___Type? other) @@ -1850,9 +1799,7 @@ namespace Foo.Bar } public global::Foo.Bar.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::Foo.Bar.IIntrospectionQuery___schema_Types_Interfaces_OfType_OfType? OfType { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_InputFields_Type_OfType___Type? other) @@ -1933,9 +1880,7 @@ namespace Foo.Bar } public global::Foo.Bar.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::Foo.Bar.IIntrospectionQuery___schema_Types_Interfaces_OfType_OfType_OfType? OfType { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_Interfaces_OfType_OfType___Type? other) @@ -2015,7 +1960,6 @@ namespace Foo.Bar } public global::Foo.Bar.__TypeKind Kind { get; } - public global::System.String? Name { get; } public virtual global::System.Boolean Equals(IntrospectionQuery___schema_Types_Interfaces_OfType_OfType_OfType___Type? other) @@ -2095,22 +2039,18 @@ namespace Foo.Bar /// The type that query operations will be rooted at. /// public global::Foo.Bar.IIntrospectionQuery___schema_QueryType QueryType { get; } - /// /// If this server supports mutation, the type that mutation operations will be rooted at. /// public global::Foo.Bar.IIntrospectionQuery___schema_MutationType? MutationType { get; } - /// /// If this server support subscription, the type that subscription operations will be rooted at. /// public global::Foo.Bar.IIntrospectionQuery___schema_SubscriptionType? SubscriptionType { get; } - /// /// A list of all types supported by this server. /// public global::System.Collections.Generic.IReadOnlyList Types { get; } - /// /// A list of all directives supported by this server. /// @@ -2205,19 +2145,12 @@ namespace Foo.Bar public partial interface IFullType { public global::Foo.Bar.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::System.String? Description { get; } - public global::System.Collections.Generic.IReadOnlyList? Fields { get; } - public global::System.Collections.Generic.IReadOnlyList? InputFields { get; } - public global::System.Collections.Generic.IReadOnlyList? Interfaces { get; } - public global::System.Collections.Generic.IReadOnlyList? EnumValues { get; } - public global::System.Collections.Generic.IReadOnlyList? PossibleTypes { get; } } @@ -2253,15 +2186,10 @@ namespace Foo.Bar public partial interface IIntrospectionQuery___schema_Directives { public global::System.String Name { get; } - public global::System.String? Description { get; } - public global::System.Collections.Generic.IReadOnlyList Args { get; } - public global::System.Boolean OnOperation { get; } - public global::System.Boolean OnFragment { get; } - public global::System.Boolean OnField { get; } } @@ -2284,15 +2212,10 @@ namespace Foo.Bar public partial interface IIntrospectionQuery___schema_Types_Fields { public global::System.String Name { get; } - public global::System.String? Description { get; } - public global::System.Collections.Generic.IReadOnlyList Args { get; } - public global::Foo.Bar.IIntrospectionQuery___schema_Types_Fields_Type Type { get; } - public global::System.Boolean IsDeprecated { get; } - public global::System.String? DeprecationReason { get; } } @@ -2313,11 +2236,8 @@ namespace Foo.Bar public partial interface IInputValue { public global::System.String Name { get; } - public global::System.String? Description { get; } - public global::Foo.Bar.IIntrospectionQuery___schema_Types_InputFields_Type Type { get; } - /// /// A GraphQL-formatted string representing the default value for this input value. /// @@ -2352,9 +2272,7 @@ namespace Foo.Bar public partial interface ITypeRef { public global::Foo.Bar.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::Foo.Bar.IIntrospectionQuery___schema_Types_Interfaces_OfType? OfType { get; } } @@ -2388,11 +2306,8 @@ namespace Foo.Bar public partial interface IIntrospectionQuery___schema_Types_EnumValues { public global::System.String Name { get; } - public global::System.String? Description { get; } - public global::System.Boolean IsDeprecated { get; } - public global::System.String? DeprecationReason { get; } } @@ -2517,9 +2432,7 @@ namespace Foo.Bar public partial interface IIntrospectionQuery___schema_Types_Interfaces_OfType { public global::Foo.Bar.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::Foo.Bar.IIntrospectionQuery___schema_Types_Interfaces_OfType_OfType? OfType { get; } } @@ -2544,9 +2457,7 @@ namespace Foo.Bar public partial interface IIntrospectionQuery___schema_Types_PossibleTypes_OfType { public global::Foo.Bar.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::Foo.Bar.IIntrospectionQuery___schema_Types_Interfaces_OfType_OfType? OfType { get; } } @@ -2615,9 +2526,7 @@ namespace Foo.Bar public partial interface IIntrospectionQuery___schema_Types_Fields_Type_OfType { public global::Foo.Bar.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::Foo.Bar.IIntrospectionQuery___schema_Types_Interfaces_OfType_OfType? OfType { get; } } @@ -2642,9 +2551,7 @@ namespace Foo.Bar public partial interface IIntrospectionQuery___schema_Types_InputFields_Type_OfType { public global::Foo.Bar.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::Foo.Bar.IIntrospectionQuery___schema_Types_Interfaces_OfType_OfType? OfType { get; } } @@ -2669,9 +2576,7 @@ namespace Foo.Bar public partial interface IIntrospectionQuery___schema_Types_Interfaces_OfType_OfType { public global::Foo.Bar.__TypeKind Kind { get; } - public global::System.String? Name { get; } - public global::Foo.Bar.IIntrospectionQuery___schema_Types_Interfaces_OfType_OfType_OfType? OfType { get; } } @@ -2696,7 +2601,6 @@ namespace Foo.Bar public partial interface IIntrospectionQuery___schema_Types_Interfaces_OfType_OfType_OfType { public global::Foo.Bar.__TypeKind Kind { get; } - public global::System.String? Name { get; } } @@ -2757,6 +2661,7 @@ namespace Foo.Bar public partial class __TypeKindSerializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "__TypeKind"; + public __TypeKind Parse(global::System.String serializedValue) { return serializedValue switch @@ -2906,8 +2811,981 @@ namespace Foo.Bar public static IntrospectionQueryQueryDocument Instance { get; } = new IntrospectionQueryQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x49, 0x6e, 0x74, 0x72, 0x6f, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x71, 0x75, 0x65, 0x72, 0x79, 0x54, 0x79, 0x70, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7d, 0x20, 0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7d, 0x20, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7d, 0x20, 0x74, 0x79, 0x70, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x46, 0x75, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x20, 0x7d, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x72, 0x67, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x7d, 0x20, 0x6f, 0x6e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x6e, 0x46, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6f, 0x6e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x46, 0x75, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x5f, 0x5f, 0x54, 0x79, 0x70, 0x65, 0x20, 0x7b, 0x20, 0x6b, 0x69, 0x6e, 0x64, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x28, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x44, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x3a, 0x20, 0x74, 0x72, 0x75, 0x65, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x61, 0x72, 0x67, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x7d, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x66, 0x20, 0x7d, 0x20, 0x69, 0x73, 0x44, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x20, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x20, 0x7d, 0x20, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x7d, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x66, 0x20, 0x7d, 0x20, 0x65, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x28, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x44, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x3a, 0x20, 0x74, 0x72, 0x75, 0x65, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x73, 0x44, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x20, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x20, 0x7d, 0x20, 0x70, 0x6f, 0x73, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x66, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x5f, 0x5f, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x66, 0x20, 0x7d, 0x20, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x54, 0x79, 0x70, 0x65, 0x52, 0x65, 0x66, 0x20, 0x6f, 0x6e, 0x20, 0x5f, 0x5f, 0x54, 0x79, 0x70, 0x65, 0x20, 0x7b, 0x20, 0x6b, 0x69, 0x6e, 0x64, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6f, 0x66, 0x54, 0x79, 0x70, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6b, 0x69, 0x6e, 0x64, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6f, 0x66, 0x54, 0x79, 0x70, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6b, 0x69, 0x6e, 0x64, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6f, 0x66, 0x54, 0x79, 0x70, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6b, 0x69, 0x6e, 0x64, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x49, + 0x6e, + 0x74, + 0x72, + 0x6f, + 0x73, + 0x70, + 0x65, + 0x63, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x51, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x73, + 0x63, + 0x68, + 0x65, + 0x6d, + 0x61, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x54, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x6d, + 0x75, + 0x74, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x54, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x73, + 0x75, + 0x62, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x54, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x74, + 0x79, + 0x70, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x46, + 0x75, + 0x6c, + 0x6c, + 0x54, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x64, + 0x69, + 0x72, + 0x65, + 0x63, + 0x74, + 0x69, + 0x76, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x64, + 0x65, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x61, + 0x72, + 0x67, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x56, + 0x61, + 0x6c, + 0x75, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x6f, + 0x6e, + 0x4f, + 0x70, + 0x65, + 0x72, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x6f, + 0x6e, + 0x46, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x6f, + 0x6e, + 0x46, + 0x69, + 0x65, + 0x6c, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x46, + 0x75, + 0x6c, + 0x6c, + 0x54, + 0x79, + 0x70, + 0x65, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x5f, + 0x5f, + 0x54, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x6b, + 0x69, + 0x6e, + 0x64, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x64, + 0x65, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x66, + 0x69, + 0x65, + 0x6c, + 0x64, + 0x73, + 0x28, + 0x69, + 0x6e, + 0x63, + 0x6c, + 0x75, + 0x64, + 0x65, + 0x44, + 0x65, + 0x70, + 0x72, + 0x65, + 0x63, + 0x61, + 0x74, + 0x65, + 0x64, + 0x3a, + 0x20, + 0x74, + 0x72, + 0x75, + 0x65, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x64, + 0x65, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x61, + 0x72, + 0x67, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x56, + 0x61, + 0x6c, + 0x75, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x74, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x54, + 0x79, + 0x70, + 0x65, + 0x52, + 0x65, + 0x66, + 0x20, + 0x7d, + 0x20, + 0x69, + 0x73, + 0x44, + 0x65, + 0x70, + 0x72, + 0x65, + 0x63, + 0x61, + 0x74, + 0x65, + 0x64, + 0x20, + 0x64, + 0x65, + 0x70, + 0x72, + 0x65, + 0x63, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x52, + 0x65, + 0x61, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7d, + 0x20, + 0x69, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x46, + 0x69, + 0x65, + 0x6c, + 0x64, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x56, + 0x61, + 0x6c, + 0x75, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x69, + 0x6e, + 0x74, + 0x65, + 0x72, + 0x66, + 0x61, + 0x63, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x54, + 0x79, + 0x70, + 0x65, + 0x52, + 0x65, + 0x66, + 0x20, + 0x7d, + 0x20, + 0x65, + 0x6e, + 0x75, + 0x6d, + 0x56, + 0x61, + 0x6c, + 0x75, + 0x65, + 0x73, + 0x28, + 0x69, + 0x6e, + 0x63, + 0x6c, + 0x75, + 0x64, + 0x65, + 0x44, + 0x65, + 0x70, + 0x72, + 0x65, + 0x63, + 0x61, + 0x74, + 0x65, + 0x64, + 0x3a, + 0x20, + 0x74, + 0x72, + 0x75, + 0x65, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x64, + 0x65, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x69, + 0x73, + 0x44, + 0x65, + 0x70, + 0x72, + 0x65, + 0x63, + 0x61, + 0x74, + 0x65, + 0x64, + 0x20, + 0x64, + 0x65, + 0x70, + 0x72, + 0x65, + 0x63, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x52, + 0x65, + 0x61, + 0x73, + 0x6f, + 0x6e, + 0x20, + 0x7d, + 0x20, + 0x70, + 0x6f, + 0x73, + 0x73, + 0x69, + 0x62, + 0x6c, + 0x65, + 0x54, + 0x79, + 0x70, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x54, + 0x79, + 0x70, + 0x65, + 0x52, + 0x65, + 0x66, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x56, + 0x61, + 0x6c, + 0x75, + 0x65, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x5f, + 0x5f, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x56, + 0x61, + 0x6c, + 0x75, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x64, + 0x65, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x74, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x54, + 0x79, + 0x70, + 0x65, + 0x52, + 0x65, + 0x66, + 0x20, + 0x7d, + 0x20, + 0x64, + 0x65, + 0x66, + 0x61, + 0x75, + 0x6c, + 0x74, + 0x56, + 0x61, + 0x6c, + 0x75, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x54, + 0x79, + 0x70, + 0x65, + 0x52, + 0x65, + 0x66, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x5f, + 0x5f, + 0x54, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x6b, + 0x69, + 0x6e, + 0x64, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6f, + 0x66, + 0x54, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6b, + 0x69, + 0x6e, + 0x64, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6f, + 0x66, + 0x54, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6b, + 0x69, + 0x6e, + 0x64, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6f, + 0x66, + 0x54, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6b, + 0x69, + 0x6e, + 0x64, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "5902e9f9fd92bc2e9785f0821db6ff925e660fde"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -3037,6 +3915,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IIntrospectionQueryResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -3222,6 +4101,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IIntrospectionQueryResult); + public IntrospectionQueryResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -3699,9 +4579,9 @@ namespace Foo.Bar.State /// Access the current type schema of this server. /// public global::Foo.Bar.State.__SchemaData __schema { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new IntrospectionQueryResultInfo(__schema, _entityIds, version); @@ -4384,19 +5264,14 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - ///The type that query operations will be rooted at. public global::Foo.Bar.State.__TypeData? QueryType { get; } - ///If this server supports mutation, the type that mutation operations will be rooted at. public global::Foo.Bar.State.__TypeData? MutationType { get; } - ///If this server support subscription, the type that subscription operations will be rooted at. public global::Foo.Bar.State.__TypeData? SubscriptionType { get; } - ///A list of all types supported by this server. public global::System.Collections.Generic.IReadOnlyList? Types { get; } - ///A list of all directives supported by this server. public global::System.Collections.Generic.IReadOnlyList? Directives { get; } } @@ -4421,23 +5296,14 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::Foo.Bar.__TypeKind? Kind { get; } - public global::System.String? Description { get; } - public global::System.Collections.Generic.IReadOnlyList? Fields { get; } - public global::System.Collections.Generic.IReadOnlyList? InputFields { get; } - public global::System.Collections.Generic.IReadOnlyList? Interfaces { get; } - public global::System.Collections.Generic.IReadOnlyList? EnumValues { get; } - public global::System.Collections.Generic.IReadOnlyList? PossibleTypes { get; } - public global::Foo.Bar.State.__TypeData? OfType { get; } } @@ -4458,17 +5324,11 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::System.String? Description { get; } - public global::System.Collections.Generic.IReadOnlyList? Args { get; } - public global::System.Boolean? OnOperation { get; } - public global::System.Boolean? OnFragment { get; } - public global::System.Boolean? OnField { get; } } @@ -4489,17 +5349,11 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::System.String? Description { get; } - public global::System.Collections.Generic.IReadOnlyList? Args { get; } - public global::Foo.Bar.State.__TypeData? Type { get; } - public global::System.Boolean? IsDeprecated { get; } - public global::System.String? DeprecationReason { get; } } @@ -4518,13 +5372,9 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::System.String? Description { get; } - public global::Foo.Bar.State.__TypeData? Type { get; } - ///A GraphQL-formatted string representing the default value for this input value. public global::System.String? DefaultValue { get; } } @@ -4544,13 +5394,9 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? Name { get; } - public global::System.String? Description { get; } - public global::System.Boolean? IsDeprecated { get; } - public global::System.String? DeprecationReason { get; } } @@ -4559,7 +5405,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.LowerCaseScalarArgument.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.LowerCaseScalarArgument.snap index aefb129a1cd..6504084209d 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.LowerCaseScalarArgument.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.LowerCaseScalarArgument.snap @@ -101,9 +101,7 @@ namespace Foo.Bar } public global::System.String Id { get; } - public global::System.String FirstName { get; } - public global::System.String LastName { get; } public virtual global::System.Boolean Equals(GetPeopleByPk_People_by_pk_people? other) @@ -177,9 +175,7 @@ namespace Foo.Bar public partial interface IGetPeopleByPk_People_by_pk { public global::System.String Id { get; } - public global::System.String FirstName { get; } - public global::System.String LastName { get; } } @@ -218,8 +214,126 @@ namespace Foo.Bar public static GetPeopleByPkQueryDocument Instance { get; } = new GetPeopleByPkQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x50, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x42, 0x79, 0x50, 0x6b, 0x28, 0x24, 0x69, 0x64, 0x3a, 0x20, 0x75, 0x75, 0x69, 0x64, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x70, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x5f, 0x62, 0x79, 0x5f, 0x70, 0x6b, 0x28, 0x69, 0x64, 0x3a, 0x20, 0x24, 0x69, 0x64, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x64, 0x20, 0x66, 0x69, 0x72, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x20, 0x6c, 0x61, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x70, 0x65, 0x6f, 0x70, 0x6c, 0x65, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x50, + 0x65, + 0x6f, + 0x70, + 0x6c, + 0x65, + 0x42, + 0x79, + 0x50, + 0x6b, + 0x28, + 0x24, + 0x69, + 0x64, + 0x3a, + 0x20, + 0x75, + 0x75, + 0x69, + 0x64, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x65, + 0x6f, + 0x70, + 0x6c, + 0x65, + 0x5f, + 0x62, + 0x79, + 0x5f, + 0x70, + 0x6b, + 0x28, + 0x69, + 0x64, + 0x3a, + 0x20, + 0x24, + 0x69, + 0x64, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x69, + 0x64, + 0x20, + 0x66, + 0x69, + 0x72, + 0x73, + 0x74, + 0x4e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6c, + 0x61, + 0x73, + 0x74, + 0x4e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x70, + 0x65, + 0x6f, + 0x70, + 0x6c, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "28da94710b416eca776b3fa8f2e06756a3a5477e"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -259,6 +373,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetPeopleByPkResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.String id, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(id); @@ -366,9 +481,7 @@ namespace Foo.Bar.State } public global::System.String Id { get; } - public global::System.String FirstName { get; } - public global::System.String LastName { get; } } @@ -385,6 +498,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetPeopleByPkResult); + public GetPeopleByPkResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -438,9 +552,9 @@ namespace Foo.Bar.State /// fetch data from the table: "people" using primary key columns /// public global::StrawberryShake.EntityId? People_by_pk { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetPeopleByPkResultInfo(People_by_pk, _entityIds, version); @@ -552,7 +666,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.MultiLineDocumentation.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.MultiLineDocumentation.snap index b44601028fb..4e9031c1097 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.MultiLineDocumentation.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.MultiLineDocumentation.snap @@ -116,8 +116,28 @@ namespace Foo.Bar public static FooQueryDocument Instance { get; } = new FooQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x46, 0x6f, 0x6f, 0x20, 0x7b, 0x20, 0x61, 0x62, 0x63, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x46, + 0x6f, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x61, + 0x62, + 0x63, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "1eb6282f473553c0540a49bffc020a72111807bc"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -147,6 +167,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IFooResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -232,6 +253,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IFooResult); + public FooResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -271,9 +293,9 @@ namespace Foo.Bar.State /// DEF /// public global::System.String? Abc { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new FooResultInfo(Abc, _entityIds, version); @@ -329,7 +351,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.NodeTypenameCollision.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.NodeTypenameCollision.snap index 9490cca8782..a583828a028 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.NodeTypenameCollision.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.NodeTypenameCollision.snap @@ -97,7 +97,6 @@ namespace Foo.Bar /// The name of the current Object type at runtime. /// public global::System.String __typename { get; } - public global::System.String Id { get; } public virtual global::System.Boolean Equals(Nodes_Node_Workspace? other) @@ -164,7 +163,6 @@ namespace Foo.Bar public partial interface INodes_Node { public global::System.String __typename { get; } - public global::System.String Id { get; } } @@ -198,8 +196,92 @@ namespace Foo.Bar public static NodesQueryDocument Instance { get; } = new NodesQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x28, 0x24, 0x69, 0x64, 0x3a, 0x20, 0x49, 0x44, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x28, 0x69, 0x64, 0x3a, 0x20, 0x24, 0x69, 0x64, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x64, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x4e, + 0x6f, + 0x64, + 0x65, + 0x73, + 0x28, + 0x24, + 0x69, + 0x64, + 0x3a, + 0x20, + 0x49, + 0x44, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x6f, + 0x64, + 0x65, + 0x28, + 0x69, + 0x64, + 0x3a, + 0x20, + 0x24, + 0x69, + 0x64, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x69, + 0x64, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x57, + 0x6f, + 0x72, + 0x6b, + 0x73, + 0x70, + 0x61, + 0x63, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "86342fe724e154da3e9ddee8d2b79bcfabc79214"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -237,6 +319,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(INodesResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.String id, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(id); @@ -341,7 +424,6 @@ namespace Foo.Bar.State ///The name of the current Object type at runtime. public global::System.String __typename { get; } - public global::System.String Id { get; } } @@ -358,6 +440,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.INodesResult); + public NodesResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -408,9 +491,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? Node { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new NodesResultInfo(Node, _entityIds, version); @@ -522,7 +605,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.NonNullLists.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.NonNullLists.snap index bd4f1e40a30..6d4a5d9660a 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.NonNullLists.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.NonNullLists.snap @@ -99,17 +99,11 @@ namespace Foo.Bar } public global::System.Collections.Generic.IReadOnlyList Amenities1 { get; } - public global::System.Collections.Generic.IReadOnlyList? Amenities2 { get; } - public global::System.Collections.Generic.IReadOnlyList Amenities3 { get; } - public global::System.Collections.Generic.IReadOnlyList? Amenities4 { get; } - public global::System.Collections.Generic.IReadOnlyList> Amenities5 { get; } - public global::System.Collections.Generic.IReadOnlyList>? Amenities6 { get; } - public global::System.Collections.Generic.IReadOnlyList?> Amenities7 { get; } public virtual global::System.Boolean Equals(GetAll_Listings_Offer? other) @@ -242,17 +236,11 @@ namespace Foo.Bar public partial interface IOffer { public global::System.Collections.Generic.IReadOnlyList Amenities1 { get; } - public global::System.Collections.Generic.IReadOnlyList? Amenities2 { get; } - public global::System.Collections.Generic.IReadOnlyList Amenities3 { get; } - public global::System.Collections.Generic.IReadOnlyList? Amenities4 { get; } - public global::System.Collections.Generic.IReadOnlyList> Amenities5 { get; } - public global::System.Collections.Generic.IReadOnlyList>? Amenities6 { get; } - public global::System.Collections.Generic.IReadOnlyList?> Amenities7 { get; } } @@ -275,6 +263,7 @@ namespace Foo.Bar public partial class AmenitySerializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "Amenity"; + public Amenity Parse(global::System.String serializedValue) { return serializedValue switch @@ -325,8 +314,166 @@ namespace Foo.Bar public static GetAllQueryDocument Instance { get; } = new GetAllQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x20, 0x7b, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x20, 0x6f, 0x6e, 0x20, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x20, 0x7b, 0x20, 0x61, 0x6d, 0x65, 0x6e, 0x69, 0x74, 0x69, 0x65, 0x73, 0x31, 0x20, 0x61, 0x6d, 0x65, 0x6e, 0x69, 0x74, 0x69, 0x65, 0x73, 0x32, 0x20, 0x61, 0x6d, 0x65, 0x6e, 0x69, 0x74, 0x69, 0x65, 0x73, 0x33, 0x20, 0x61, 0x6d, 0x65, 0x6e, 0x69, 0x74, 0x69, 0x65, 0x73, 0x34, 0x20, 0x61, 0x6d, 0x65, 0x6e, 0x69, 0x74, 0x69, 0x65, 0x73, 0x35, 0x20, 0x61, 0x6d, 0x65, 0x6e, 0x69, 0x74, 0x69, 0x65, 0x73, 0x36, 0x20, 0x61, 0x6d, 0x65, 0x6e, 0x69, 0x74, 0x69, 0x65, 0x73, 0x37, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x41, + 0x6c, + 0x6c, + 0x20, + 0x7b, + 0x20, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x4f, + 0x66, + 0x66, + 0x65, + 0x72, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x4f, + 0x66, + 0x66, + 0x65, + 0x72, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4f, + 0x66, + 0x66, + 0x65, + 0x72, + 0x20, + 0x7b, + 0x20, + 0x61, + 0x6d, + 0x65, + 0x6e, + 0x69, + 0x74, + 0x69, + 0x65, + 0x73, + 0x31, + 0x20, + 0x61, + 0x6d, + 0x65, + 0x6e, + 0x69, + 0x74, + 0x69, + 0x65, + 0x73, + 0x32, + 0x20, + 0x61, + 0x6d, + 0x65, + 0x6e, + 0x69, + 0x74, + 0x69, + 0x65, + 0x73, + 0x33, + 0x20, + 0x61, + 0x6d, + 0x65, + 0x6e, + 0x69, + 0x74, + 0x69, + 0x65, + 0x73, + 0x34, + 0x20, + 0x61, + 0x6d, + 0x65, + 0x6e, + 0x69, + 0x74, + 0x69, + 0x65, + 0x73, + 0x35, + 0x20, + 0x61, + 0x6d, + 0x65, + 0x6e, + 0x69, + 0x74, + 0x69, + 0x65, + 0x73, + 0x36, + 0x20, + 0x61, + 0x6d, + 0x65, + 0x6e, + 0x69, + 0x74, + 0x69, + 0x65, + 0x73, + 0x37, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "2cb871ada5a49964bc9fe651d0d97bd18903b601"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -369,6 +516,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetAllResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -467,6 +615,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetAllResult); + public GetAllResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -533,9 +682,9 @@ namespace Foo.Bar.State } public global::System.Collections.Generic.IReadOnlyList Listings { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetAllResultInfo(Listings, _entityIds, version); @@ -814,19 +963,12 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.Collections.Generic.IReadOnlyList? Amenities1 { get; } - public global::System.Collections.Generic.IReadOnlyList? Amenities2 { get; } - public global::System.Collections.Generic.IReadOnlyList? Amenities3 { get; } - public global::System.Collections.Generic.IReadOnlyList? Amenities4 { get; } - public global::System.Collections.Generic.IReadOnlyList>? Amenities5 { get; } - public global::System.Collections.Generic.IReadOnlyList>? Amenities6 { get; } - public global::System.Collections.Generic.IReadOnlyList?>? Amenities7 { get; } } @@ -835,7 +977,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.QueryInterference.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.QueryInterference.snap index adbe6968413..21839145116 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.QueryInterference.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.QueryInterference.snap @@ -94,7 +94,6 @@ namespace Foo.Bar } public global::System.Int32 TotalCount { get; } - public global::System.Collections.Generic.IReadOnlyList? Items { get; } public virtual global::System.Boolean Equals(GetFeatsPage_Feats_FeatCollectionSegment? other) @@ -173,13 +172,9 @@ namespace Foo.Bar } public global::System.Guid Id { get; } - public global::System.String Name { get; } - public global::System.Int32 Level { get; } - public global::System.Boolean CanBeLearnedMoreThanOnce { get; } - public global::System.Collections.Generic.IReadOnlyList Details { get; } public virtual global::System.Boolean Equals(GetFeatsPage_Feats_Items_Feat? other) @@ -315,7 +310,6 @@ namespace Foo.Bar public partial interface IGetFeatsPage_Feats { public global::System.Int32 TotalCount { get; } - public global::System.Collections.Generic.IReadOnlyList? Items { get; } } @@ -330,13 +324,9 @@ namespace Foo.Bar public partial interface IFeatsPage { public global::System.Guid Id { get; } - public global::System.String Name { get; } - public global::System.Int32 Level { get; } - public global::System.Boolean CanBeLearnedMoreThanOnce { get; } - public global::System.Collections.Generic.IReadOnlyList Details { get; } } @@ -519,17 +509,11 @@ namespace Foo.Bar } public global::System.Guid Id { get; } - public global::System.String Name { get; } - public global::System.Int32 Level { get; } - public global::System.String? Special { get; } - public global::System.String? Trigger { get; } - public global::System.Collections.Generic.IReadOnlyList Details { get; } - public global::Foo.Bar.IGetFeatById_Feats_Items_ActionType ActionType { get; } public virtual global::System.Boolean Equals(GetFeatById_Feats_Items_Feat? other) @@ -750,17 +734,11 @@ namespace Foo.Bar public partial interface IFeatById { public global::System.Guid Id { get; } - public global::System.String Name { get; } - public global::System.Int32 Level { get; } - public global::System.String? Special { get; } - public global::System.String? Trigger { get; } - public global::System.Collections.Generic.IReadOnlyList Details { get; } - public global::Foo.Bar.IGetFeatById_Feats_Items_ActionType ActionType { get; } } @@ -812,6 +790,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _frequencySortInputFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _rollableEffectSortInputFormatter = default !; public global::System.String TypeName => "FeatSortInput"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _sortEnumTypeFormatter = serializerResolver.GetInputValueFormatter("SortEnumType"); @@ -1240,6 +1219,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IFeatSortInputInfo.IsNameSet => _set_name; + public global::Foo.Bar.SortEnumType? CanBeLearnedMoreThanOnce { get => _value_canBeLearnedMoreThanOnce; @@ -1251,6 +1231,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IFeatSortInputInfo.IsCanBeLearnedMoreThanOnceSet => _set_canBeLearnedMoreThanOnce; + public global::Foo.Bar.SortEnumType? Special { get => _value_special; @@ -1262,6 +1243,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IFeatSortInputInfo.IsSpecialSet => _set_special; + public global::Foo.Bar.SortEnumType? Trigger { get => _value_trigger; @@ -1273,6 +1255,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IFeatSortInputInfo.IsTriggerSet => _set_trigger; + public global::Foo.Bar.SortEnumType? Level { get => _value_level; @@ -1284,6 +1267,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IFeatSortInputInfo.IsLevelSet => _set_level; + public global::Foo.Bar.SortEnumType? ActionTypeId { get => _value_actionTypeId; @@ -1295,6 +1279,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IFeatSortInputInfo.IsActionTypeIdSet => _set_actionTypeId; + public global::Foo.Bar.ActionTypeSortInput? ActionType { get => _value_actionType; @@ -1306,6 +1291,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IFeatSortInputInfo.IsActionTypeSet => _set_actionType; + public global::Foo.Bar.SortEnumType? FeatTypeId { get => _value_featTypeId; @@ -1317,6 +1303,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IFeatSortInputInfo.IsFeatTypeIdSet => _set_featTypeId; + public global::Foo.Bar.FeatTypeSortInput? FeatType { get => _value_featType; @@ -1328,6 +1315,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IFeatSortInputInfo.IsFeatTypeSet => _set_featType; + public global::Foo.Bar.SortEnumType? FrequencyId { get => _value_frequencyId; @@ -1339,6 +1327,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IFeatSortInputInfo.IsFrequencyIdSet => _set_frequencyId; + public global::Foo.Bar.FrequencySortInput? Frequency { get => _value_frequency; @@ -1350,6 +1339,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IFeatSortInputInfo.IsFrequencySet => _set_frequency; + public global::Foo.Bar.RollableEffectSortInput? RollableEffect { get => _value_rollableEffect; @@ -1361,6 +1351,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IFeatSortInputInfo.IsRollableEffectSet => _set_rollableEffect; + public global::Foo.Bar.SortEnumType? RollableEffectId { get => _value_rollableEffectId; @@ -1372,6 +1363,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IFeatSortInputInfo.IsRollableEffectIdSet => _set_rollableEffectId; + public global::Foo.Bar.SortEnumType? Id { get => _value_id; @@ -1391,6 +1383,7 @@ namespace Foo.Bar { private global::StrawberryShake.Serialization.IInputValueFormatter _sortEnumTypeFormatter = default !; public global::System.String TypeName => "ActionTypeSortInput"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _sortEnumTypeFormatter = serializerResolver.GetInputValueFormatter("SortEnumType"); @@ -1527,6 +1520,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IActionTypeSortInputInfo.IsNameSet => _set_name; + public global::Foo.Bar.SortEnumType? Id { get => _value_id; @@ -1546,6 +1540,7 @@ namespace Foo.Bar { private global::StrawberryShake.Serialization.IInputValueFormatter _sortEnumTypeFormatter = default !; public global::System.String TypeName => "FeatTypeSortInput"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _sortEnumTypeFormatter = serializerResolver.GetInputValueFormatter("SortEnumType"); @@ -1682,6 +1677,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IFeatTypeSortInputInfo.IsNameSet => _set_name; + public global::Foo.Bar.SortEnumType? Id { get => _value_id; @@ -1701,6 +1697,7 @@ namespace Foo.Bar { private global::StrawberryShake.Serialization.IInputValueFormatter _sortEnumTypeFormatter = default !; public global::System.String TypeName => "FrequencySortInput"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _sortEnumTypeFormatter = serializerResolver.GetInputValueFormatter("SortEnumType"); @@ -1885,6 +1882,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IFrequencySortInputInfo.IsNameSet => _set_name; + public global::Foo.Bar.SortEnumType? Amount { get => _value_amount; @@ -1896,6 +1894,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IFrequencySortInputInfo.IsAmountSet => _set_amount; + public global::Foo.Bar.SortEnumType? TimeSpan { get => _value_timeSpan; @@ -1907,6 +1906,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IFrequencySortInputInfo.IsTimeSpanSet => _set_timeSpan; + public global::Foo.Bar.SortEnumType? Id { get => _value_id; @@ -1926,6 +1926,7 @@ namespace Foo.Bar { private global::StrawberryShake.Serialization.IInputValueFormatter _sortEnumTypeFormatter = default !; public global::System.String TypeName => "RollableEffectSortInput"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _sortEnumTypeFormatter = serializerResolver.GetInputValueFormatter("SortEnumType"); @@ -2134,6 +2135,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IRollableEffectSortInputInfo.IsCriticalSuccessSet => _set_criticalSuccess; + public global::Foo.Bar.SortEnumType? Success { get => _value_success; @@ -2145,6 +2147,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IRollableEffectSortInputInfo.IsSuccessSet => _set_success; + public global::Foo.Bar.SortEnumType? Failure { get => _value_failure; @@ -2156,6 +2159,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IRollableEffectSortInputInfo.IsFailureSet => _set_failure; + public global::Foo.Bar.SortEnumType? CriticalFailure { get => _value_criticalFailure; @@ -2167,6 +2171,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IRollableEffectSortInputInfo.IsCriticalFailureSet => _set_criticalFailure; + public global::Foo.Bar.SortEnumType? Id { get => _value_id; @@ -2193,6 +2198,7 @@ namespace Foo.Bar public partial class SortEnumTypeSerializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "SortEnumType"; + public SortEnumType Parse(global::System.String serializedValue) { return serializedValue switch @@ -2254,8 +2260,503 @@ namespace Foo.Bar public static GetFeatsPageQueryDocument Instance { get; } = new GetFeatsPageQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x46, 0x65, 0x61, 0x74, 0x73, 0x50, 0x61, 0x67, 0x65, 0x28, 0x24, 0x73, 0x6b, 0x69, 0x70, 0x3a, 0x20, 0x49, 0x6e, 0x74, 0x21, 0x2c, 0x20, 0x24, 0x74, 0x61, 0x6b, 0x65, 0x3a, 0x20, 0x49, 0x6e, 0x74, 0x21, 0x2c, 0x20, 0x24, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x54, 0x65, 0x72, 0x6d, 0x3a, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x21, 0x20, 0x3d, 0x20, 0x22, 0x22, 0x2c, 0x20, 0x24, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x20, 0x5b, 0x46, 0x65, 0x61, 0x74, 0x53, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x21, 0x5d, 0x20, 0x3d, 0x20, 0x5b, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x20, 0x41, 0x53, 0x43, 0x20, 0x7d, 0x20, 0x5d, 0x29, 0x20, 0x7b, 0x20, 0x66, 0x65, 0x61, 0x74, 0x73, 0x28, 0x73, 0x6b, 0x69, 0x70, 0x3a, 0x20, 0x24, 0x73, 0x6b, 0x69, 0x70, 0x2c, 0x20, 0x74, 0x61, 0x6b, 0x65, 0x3a, 0x20, 0x24, 0x74, 0x61, 0x6b, 0x65, 0x2c, 0x20, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x3a, 0x20, 0x24, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x2c, 0x20, 0x77, 0x68, 0x65, 0x72, 0x65, 0x3a, 0x20, 0x7b, 0x20, 0x6f, 0x72, 0x3a, 0x20, 0x5b, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x20, 0x7b, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x3a, 0x20, 0x24, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x54, 0x65, 0x72, 0x6d, 0x20, 0x7d, 0x20, 0x7d, 0x2c, 0x20, 0x7b, 0x20, 0x74, 0x72, 0x61, 0x69, 0x74, 0x73, 0x3a, 0x20, 0x7b, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x3a, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x20, 0x7b, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x73, 0x3a, 0x20, 0x24, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x54, 0x65, 0x72, 0x6d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x5d, 0x20, 0x7d, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x46, 0x65, 0x61, 0x74, 0x73, 0x50, 0x61, 0x67, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x46, 0x65, 0x61, 0x74, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x46, 0x65, 0x61, 0x74, 0x73, 0x50, 0x61, 0x67, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x46, 0x65, 0x61, 0x74, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x20, 0x63, 0x61, 0x6e, 0x42, 0x65, 0x4c, 0x65, 0x61, 0x72, 0x6e, 0x65, 0x64, 0x4d, 0x6f, 0x72, 0x65, 0x54, 0x68, 0x61, 0x6e, 0x4f, 0x6e, 0x63, 0x65, 0x20, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x46, 0x65, 0x61, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x46, + 0x65, + 0x61, + 0x74, + 0x73, + 0x50, + 0x61, + 0x67, + 0x65, + 0x28, + 0x24, + 0x73, + 0x6b, + 0x69, + 0x70, + 0x3a, + 0x20, + 0x49, + 0x6e, + 0x74, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x74, + 0x61, + 0x6b, + 0x65, + 0x3a, + 0x20, + 0x49, + 0x6e, + 0x74, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x73, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x54, + 0x65, + 0x72, + 0x6d, + 0x3a, + 0x20, + 0x53, + 0x74, + 0x72, + 0x69, + 0x6e, + 0x67, + 0x21, + 0x20, + 0x3d, + 0x20, + 0x22, + 0x22, + 0x2c, + 0x20, + 0x24, + 0x6f, + 0x72, + 0x64, + 0x65, + 0x72, + 0x3a, + 0x20, + 0x5b, + 0x46, + 0x65, + 0x61, + 0x74, + 0x53, + 0x6f, + 0x72, + 0x74, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x21, + 0x5d, + 0x20, + 0x3d, + 0x20, + 0x5b, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x3a, + 0x20, + 0x41, + 0x53, + 0x43, + 0x20, + 0x7d, + 0x20, + 0x5d, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x65, + 0x61, + 0x74, + 0x73, + 0x28, + 0x73, + 0x6b, + 0x69, + 0x70, + 0x3a, + 0x20, + 0x24, + 0x73, + 0x6b, + 0x69, + 0x70, + 0x2c, + 0x20, + 0x74, + 0x61, + 0x6b, + 0x65, + 0x3a, + 0x20, + 0x24, + 0x74, + 0x61, + 0x6b, + 0x65, + 0x2c, + 0x20, + 0x6f, + 0x72, + 0x64, + 0x65, + 0x72, + 0x3a, + 0x20, + 0x24, + 0x6f, + 0x72, + 0x64, + 0x65, + 0x72, + 0x2c, + 0x20, + 0x77, + 0x68, + 0x65, + 0x72, + 0x65, + 0x3a, + 0x20, + 0x7b, + 0x20, + 0x6f, + 0x72, + 0x3a, + 0x20, + 0x5b, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x3a, + 0x20, + 0x7b, + 0x20, + 0x63, + 0x6f, + 0x6e, + 0x74, + 0x61, + 0x69, + 0x6e, + 0x73, + 0x3a, + 0x20, + 0x24, + 0x73, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x54, + 0x65, + 0x72, + 0x6d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x2c, + 0x20, + 0x7b, + 0x20, + 0x74, + 0x72, + 0x61, + 0x69, + 0x74, + 0x73, + 0x3a, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x6f, + 0x6d, + 0x65, + 0x3a, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x3a, + 0x20, + 0x7b, + 0x20, + 0x63, + 0x6f, + 0x6e, + 0x74, + 0x61, + 0x69, + 0x6e, + 0x73, + 0x3a, + 0x20, + 0x24, + 0x73, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x54, + 0x65, + 0x72, + 0x6d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x5d, + 0x20, + 0x7d, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x74, + 0x6f, + 0x74, + 0x61, + 0x6c, + 0x43, + 0x6f, + 0x75, + 0x6e, + 0x74, + 0x20, + 0x69, + 0x74, + 0x65, + 0x6d, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x46, + 0x65, + 0x61, + 0x74, + 0x73, + 0x50, + 0x61, + 0x67, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x46, + 0x65, + 0x61, + 0x74, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x46, + 0x65, + 0x61, + 0x74, + 0x73, + 0x50, + 0x61, + 0x67, + 0x65, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x46, + 0x65, + 0x61, + 0x74, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6c, + 0x65, + 0x76, + 0x65, + 0x6c, + 0x20, + 0x63, + 0x61, + 0x6e, + 0x42, + 0x65, + 0x4c, + 0x65, + 0x61, + 0x72, + 0x6e, + 0x65, + 0x64, + 0x4d, + 0x6f, + 0x72, + 0x65, + 0x54, + 0x68, + 0x61, + 0x6e, + 0x4f, + 0x6e, + 0x63, + 0x65, + 0x20, + 0x64, + 0x65, + 0x74, + 0x61, + 0x69, + 0x6c, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x74, + 0x65, + 0x78, + 0x74, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x46, + 0x65, + 0x61, + 0x74, + 0x44, + 0x65, + 0x74, + 0x61, + 0x69, + 0x6c, + 0x73, + 0x42, + 0x6c, + 0x6f, + 0x63, + 0x6b, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "defcd7f67f31345e43429d1db7aa93bc8ca8af7d"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -2315,6 +2816,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetFeatsPageResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Int32 skip, global::System.Int32 take, global::System.String searchTerm, global::System.Collections.Generic.IReadOnlyList? order, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(skip, take, searchTerm, order); @@ -2480,8 +2982,321 @@ namespace Foo.Bar public static GetFeatByIdQueryDocument Instance { get; } = new GetFeatByIdQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x46, 0x65, 0x61, 0x74, 0x42, 0x79, 0x49, 0x64, 0x28, 0x24, 0x69, 0x64, 0x3a, 0x20, 0x55, 0x55, 0x49, 0x44, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x66, 0x65, 0x61, 0x74, 0x73, 0x28, 0x77, 0x68, 0x65, 0x72, 0x65, 0x3a, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x3a, 0x20, 0x7b, 0x20, 0x65, 0x71, 0x3a, 0x20, 0x24, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x46, 0x65, 0x61, 0x74, 0x42, 0x79, 0x49, 0x64, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x46, 0x65, 0x61, 0x74, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x46, 0x65, 0x61, 0x74, 0x42, 0x79, 0x49, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x46, 0x65, 0x61, 0x74, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x20, 0x73, 0x70, 0x65, 0x63, 0x69, 0x61, 0x6c, 0x20, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x20, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x46, 0x65, 0x61, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x46, + 0x65, + 0x61, + 0x74, + 0x42, + 0x79, + 0x49, + 0x64, + 0x28, + 0x24, + 0x69, + 0x64, + 0x3a, + 0x20, + 0x55, + 0x55, + 0x49, + 0x44, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x66, + 0x65, + 0x61, + 0x74, + 0x73, + 0x28, + 0x77, + 0x68, + 0x65, + 0x72, + 0x65, + 0x3a, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x3a, + 0x20, + 0x7b, + 0x20, + 0x65, + 0x71, + 0x3a, + 0x20, + 0x24, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x69, + 0x74, + 0x65, + 0x6d, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x46, + 0x65, + 0x61, + 0x74, + 0x42, + 0x79, + 0x49, + 0x64, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x46, + 0x65, + 0x61, + 0x74, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x46, + 0x65, + 0x61, + 0x74, + 0x42, + 0x79, + 0x49, + 0x64, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x46, + 0x65, + 0x61, + 0x74, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6c, + 0x65, + 0x76, + 0x65, + 0x6c, + 0x20, + 0x73, + 0x70, + 0x65, + 0x63, + 0x69, + 0x61, + 0x6c, + 0x20, + 0x74, + 0x72, + 0x69, + 0x67, + 0x67, + 0x65, + 0x72, + 0x20, + 0x64, + 0x65, + 0x74, + 0x61, + 0x69, + 0x6c, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x74, + 0x65, + 0x78, + 0x74, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x46, + 0x65, + 0x61, + 0x74, + 0x44, + 0x65, + 0x74, + 0x61, + 0x69, + 0x6c, + 0x73, + 0x42, + 0x6c, + 0x6f, + 0x63, + 0x6b, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x61, + 0x63, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x54, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x41, + 0x63, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x54, + 0x79, + 0x70, + 0x65, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "9ebaff788b63cffe97932e6ad7792fa29e168e73"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -2544,6 +3359,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetFeatByIdResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Guid id, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(id); @@ -2678,19 +3494,12 @@ namespace Foo.Bar.State } public global::System.Guid Id { get; } - public global::System.String Name { get; } - public global::System.Int32 Level { get; } - public global::System.Boolean CanBeLearnedMoreThanOnce { get; } - public global::System.Collections.Generic.IReadOnlyList Details { get; } - public global::System.String? Special { get; } - public global::System.String? Trigger { get; } - public global::StrawberryShake.EntityId ActionType { get; } } @@ -2733,6 +3542,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetFeatsPageResult); + public GetFeatsPageResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -2845,9 +3655,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.FeatCollectionSegmentData? Feats { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetFeatsPageResultInfo(Feats, _entityIds, version); @@ -2871,6 +3681,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetFeatByIdResult); + public GetFeatByIdResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -2993,9 +3804,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.FeatCollectionSegmentData? Feats { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetFeatByIdResultInfo(Feats, _entityIds, version); @@ -3568,9 +4379,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.Int32? TotalCount { get; } - public global::System.Collections.Generic.IReadOnlyList? Items { get; } } @@ -3752,7 +4561,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Query_With_Nested_Fragments.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Query_With_Nested_Fragments.snap index 7cd5e9eb8f0..8899638ec76 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Query_With_Nested_Fragments.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Query_With_Nested_Fragments.snap @@ -156,7 +156,6 @@ namespace Foo.Bar } public global::System.String ListingId { get; } - public global::System.Int32 StartingPrice { get; } public virtual global::System.Boolean Equals(GetAll_Listings_Items_Auction? other) @@ -222,7 +221,6 @@ namespace Foo.Bar } public global::System.String ListingId { get; } - public global::System.Int32 Price { get; } public virtual global::System.Boolean Equals(GetAll_Listings_Items_Offer? other) @@ -384,8 +382,306 @@ namespace Foo.Bar public static GetAllQueryDocument Instance { get; } = new GetAllQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x20, 0x7b, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x4c, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x4c, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x4c, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x7b, 0x20, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x48, 0x61, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x49, 0x64, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x41, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x48, 0x61, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x49, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x4c, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x7b, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x49, 0x64, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x20, 0x6f, 0x6e, 0x20, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x20, 0x7b, 0x20, 0x70, 0x72, 0x69, 0x63, 0x65, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x41, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x6e, 0x20, 0x41, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x7b, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x69, 0x63, 0x65, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x41, + 0x6c, + 0x6c, + 0x20, + 0x7b, + 0x20, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x73, + 0x50, + 0x61, + 0x79, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x73, + 0x50, + 0x61, + 0x79, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x73, + 0x50, + 0x61, + 0x79, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x74, + 0x65, + 0x6d, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x48, + 0x61, + 0x73, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x49, + 0x64, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x4f, + 0x66, + 0x66, + 0x65, + 0x72, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x41, + 0x75, + 0x63, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x48, + 0x61, + 0x73, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x49, + 0x64, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x20, + 0x7b, + 0x20, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x49, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x4f, + 0x66, + 0x66, + 0x65, + 0x72, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4f, + 0x66, + 0x66, + 0x65, + 0x72, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x72, + 0x69, + 0x63, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x41, + 0x75, + 0x63, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x41, + 0x75, + 0x63, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x74, + 0x61, + 0x72, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x50, + 0x72, + 0x69, + 0x63, + 0x65, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "c2ce13cb04932b7d4f0a3b22a6566fa34494b068"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -439,6 +735,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetAllResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -548,6 +845,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetAllResult); + public GetAllResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -643,9 +941,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.ListingsPayloadData Listings { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetAllResultInfo(Listings, _entityIds, version); @@ -792,7 +1090,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.Collections.Generic.IReadOnlyList? Items { get; } } @@ -815,9 +1112,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? ListingId { get; } - public global::System.Int32? StartingPrice { get; } } @@ -833,9 +1128,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.String? ListingId { get; } - public global::System.Int32? Price { get; } } @@ -844,7 +1137,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Schema_With_Spec_Errors.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Schema_With_Spec_Errors.snap index a011051dc2d..43f3ad38d50 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Schema_With_Spec_Errors.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/SchemaGeneratorTests.Schema_With_Spec_Errors.snap @@ -204,8 +204,135 @@ namespace Foo.Bar public static GetListingsCountQueryDocument Instance { get; } = new GetListingsCountQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x7b, 0x20, 0x6c, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x4c, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x4c, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x4c, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x20, 0x7b, 0x20, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x73, + 0x43, + 0x6f, + 0x75, + 0x6e, + 0x74, + 0x20, + 0x7b, + 0x20, + 0x6c, + 0x69, + 0x73, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x73, + 0x50, + 0x61, + 0x79, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x73, + 0x50, + 0x61, + 0x79, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x4c, + 0x69, + 0x73, + 0x74, + 0x69, + 0x6e, + 0x67, + 0x73, + 0x50, + 0x61, + 0x79, + 0x6c, + 0x6f, + 0x61, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x63, + 0x6f, + 0x75, + 0x6e, + 0x74, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "c3a13333bd2282e2d7ed111b99934a7610014ba3"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -242,6 +369,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetListingsCountResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -334,6 +462,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetListingsCountResult); + public GetListingsCountResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -387,9 +516,9 @@ namespace Foo.Bar.State /// Vyhledávání mezi inzeráty /// public global::Foo.Bar.State.ListingsPayloadData Listings { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetListingsCountResultInfo(Listings, _entityIds, version); @@ -472,7 +601,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.Int32? Count { get; } } @@ -481,7 +609,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Generate_Client_With_Internal_Access_Modifier.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Generate_Client_With_Internal_Access_Modifier.snap index 3f530d5621c..ad74b864f60 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Generate_Client_With_Internal_Access_Modifier.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Generate_Client_With_Internal_Access_Modifier.snap @@ -94,7 +94,6 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } public virtual global::System.Boolean Equals(GetHero_Hero_Droid? other) @@ -170,7 +169,6 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } public virtual global::System.Boolean Equals(GetHero_Hero_Human? other) @@ -247,7 +245,6 @@ namespace Foo.Bar internal partial interface IGetHero_Hero { public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } } @@ -277,6 +274,7 @@ namespace Foo.Bar internal partial class EpisodeSerializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "Episode"; + public Episode Parse(global::System.String serializedValue) { return serializedValue switch @@ -326,8 +324,122 @@ namespace Foo.Bar public static GetHeroQueryDocument Instance { get; } = new GetHeroQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x48, 0x65, 0x72, 0x6f, 0x20, 0x7b, 0x20, 0x68, 0x65, 0x72, 0x6f, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x4e, 0x45, 0x57, 0x5f, 0x48, 0x4f, 0x50, 0x45, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72, 0x73, 0x49, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x68, + 0x65, + 0x72, + 0x6f, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x4e, + 0x45, + 0x57, + 0x5f, + 0x48, + 0x4f, + 0x50, + 0x45, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x61, + 0x70, + 0x70, + 0x65, + 0x61, + 0x72, + 0x73, + 0x49, + 0x6e, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "a0ab93285495bb156c6c436ef4b49a3922666647"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -367,6 +479,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetHeroResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -462,7 +575,6 @@ namespace Foo.Bar.State } public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } } @@ -477,7 +589,6 @@ namespace Foo.Bar.State } public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } } @@ -496,6 +607,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetHeroResult); + public GetHeroResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -551,9 +663,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? Hero { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetHeroResultInfo(Hero, _entityIds, version); @@ -736,7 +848,9 @@ namespace Foo.Bar.State internal partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Generate_StarWarsIntegrationTest.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Generate_StarWarsIntegrationTest.snap index 2379ccc9226..3cb225d579f 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Generate_StarWarsIntegrationTest.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Generate_StarWarsIntegrationTest.snap @@ -326,8 +326,148 @@ namespace Foo.Bar public static CreateReviewMutationDocument Instance { get; } = new CreateReviewMutationDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Mutation; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x28, 0x24, 0x73, 0x74, 0x61, 0x72, 0x73, 0x3a, 0x20, 0x49, 0x6e, 0x74, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x45, 0x4d, 0x50, 0x49, 0x52, 0x45, 0x2c, 0x20, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x3a, 0x20, 0x7b, 0x20, 0x73, 0x74, 0x61, 0x72, 0x73, 0x3a, 0x20, 0x24, 0x73, 0x74, 0x61, 0x72, 0x73, 0x2c, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x3a, 0x20, 0x22, 0x67, 0x6f, 0x6f, 0x64, 0x22, 0x20, 0x7d, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x74, 0x61, 0x72, 0x73, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x6d, + 0x75, + 0x74, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x43, + 0x72, + 0x65, + 0x61, + 0x74, + 0x65, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x28, + 0x24, + 0x73, + 0x74, + 0x61, + 0x72, + 0x73, + 0x3a, + 0x20, + 0x49, + 0x6e, + 0x74, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x63, + 0x72, + 0x65, + 0x61, + 0x74, + 0x65, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x45, + 0x4d, + 0x50, + 0x49, + 0x52, + 0x45, + 0x2c, + 0x20, + 0x72, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x3a, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x74, + 0x61, + 0x72, + 0x73, + 0x3a, + 0x20, + 0x24, + 0x73, + 0x74, + 0x61, + 0x72, + 0x73, + 0x2c, + 0x20, + 0x63, + 0x6f, + 0x6d, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x61, + 0x72, + 0x79, + 0x3a, + 0x20, + 0x22, + 0x67, + 0x6f, + 0x6f, + 0x64, + 0x22, + 0x20, + 0x7d, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x73, + 0x74, + 0x61, + 0x72, + 0x73, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "409320ca8b7539016115e55a6bbeceeae0184bdf"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -362,6 +502,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ICreateReviewResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Int32 stars, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(stars); @@ -437,8 +578,83 @@ namespace Foo.Bar public static OnReviewSubscriptionDocument Instance { get; } = new OnReviewSubscriptionDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Subscription; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4f, 0x6e, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x20, 0x7b, 0x20, 0x6f, 0x6e, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x45, 0x4d, 0x50, 0x49, 0x52, 0x45, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x74, 0x61, 0x72, 0x73, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x73, + 0x75, + 0x62, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x4f, + 0x6e, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x20, + 0x7b, + 0x20, + 0x6f, + 0x6e, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x45, + 0x4d, + 0x50, + 0x49, + 0x52, + 0x45, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x73, + 0x74, + 0x61, + 0x72, + 0x73, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "57bd8aa0a7a43840c2b938fb5b484014327cf406"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -471,6 +687,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IOnReviewResult); + public global::System.IObservable> Watch(global::StrawberryShake.ExecutionStrategy? strategy = null) { var request = CreateRequest(); @@ -557,6 +774,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ICreateReviewResult); + public CreateReviewResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -607,9 +825,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.ReviewData CreateReview { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new CreateReviewResultInfo(CreateReview, _entityIds, version); @@ -627,6 +845,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IOnReviewResult); + public OnReviewResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -677,9 +896,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.ReviewData OnReview { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new OnReviewResultInfo(OnReview, _entityIds, version); @@ -827,7 +1046,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.Int32? Stars { get; } } @@ -836,7 +1054,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Interface_With_Default_Names.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Interface_With_Default_Names.snap index 83a9b304cc9..2ff4ceef1fa 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Interface_With_Default_Names.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Interface_With_Default_Names.snap @@ -94,7 +94,6 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } public virtual global::System.Boolean Equals(GetHero_Hero_Droid? other) @@ -170,7 +169,6 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } public virtual global::System.Boolean Equals(GetHero_Hero_Human? other) @@ -247,7 +245,6 @@ namespace Foo.Bar public partial interface IGetHero_Hero { public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } } @@ -277,6 +274,7 @@ namespace Foo.Bar public partial class EpisodeSerializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "Episode"; + public Episode Parse(global::System.String serializedValue) { return serializedValue switch @@ -326,8 +324,122 @@ namespace Foo.Bar public static GetHeroQueryDocument Instance { get; } = new GetHeroQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x48, 0x65, 0x72, 0x6f, 0x20, 0x7b, 0x20, 0x68, 0x65, 0x72, 0x6f, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x4e, 0x45, 0x57, 0x5f, 0x48, 0x4f, 0x50, 0x45, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72, 0x73, 0x49, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x68, + 0x65, + 0x72, + 0x6f, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x4e, + 0x45, + 0x57, + 0x5f, + 0x48, + 0x4f, + 0x50, + 0x45, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x61, + 0x70, + 0x70, + 0x65, + 0x61, + 0x72, + 0x73, + 0x49, + 0x6e, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "a0ab93285495bb156c6c436ef4b49a3922666647"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -367,6 +479,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetHeroResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -462,7 +575,6 @@ namespace Foo.Bar.State } public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } } @@ -477,7 +589,6 @@ namespace Foo.Bar.State } public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } } @@ -496,6 +607,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetHeroResult); + public GetHeroResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -551,9 +663,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? Hero { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetHeroResultInfo(Hero, _entityIds, version); @@ -736,7 +848,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Interface_With_Fragment_Definition_Two_Models.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Interface_With_Fragment_Definition_Two_Models.snap index 505cfb3ccf2..25a856f0b08 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Interface_With_Fragment_Definition_Two_Models.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Interface_With_Fragment_Definition_Two_Models.snap @@ -95,9 +95,7 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.String? PrimaryFunction { get; } - public global::Foo.Bar.IGetHero_Hero_Friends? Friends { get; } public virtual global::System.Boolean Equals(GetHero_Hero_Droid? other) @@ -173,9 +171,7 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.String? HomePlanet { get; } - public global::Foo.Bar.IGetHero_Hero_Friends? Friends { get; } public virtual global::System.Boolean Equals(GetHero_Hero_Human? other) @@ -453,7 +449,6 @@ namespace Foo.Bar public partial interface IHero { public global::System.String Name { get; } - public global::Foo.Bar.IGetHero_Hero_Friends? Friends { get; } } @@ -475,7 +470,6 @@ namespace Foo.Bar public partial interface IHero_Droid : IDroid { public global::System.String Name { get; } - public global::Foo.Bar.IGetHero_Hero_Friends? Friends { get; } } @@ -497,7 +491,6 @@ namespace Foo.Bar public partial interface IHero_Human : IHuman { public global::System.String Name { get; } - public global::Foo.Bar.IGetHero_Hero_Friends? Friends { get; } } @@ -602,8 +595,344 @@ namespace Foo.Bar public static GetHeroQueryDocument Instance { get; } = new GetHeroQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x48, 0x65, 0x72, 0x6f, 0x20, 0x7b, 0x20, 0x68, 0x65, 0x72, 0x6f, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x4e, 0x45, 0x57, 0x5f, 0x48, 0x4f, 0x50, 0x45, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x48, 0x65, 0x72, 0x6f, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x48, 0x65, 0x72, 0x6f, 0x20, 0x6f, 0x6e, 0x20, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x66, 0x72, 0x69, 0x65, 0x6e, 0x64, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x68, 0x6f, 0x6d, 0x65, 0x50, 0x6c, 0x61, 0x6e, 0x65, 0x74, 0x20, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x68, + 0x65, + 0x72, + 0x6f, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x4e, + 0x45, + 0x57, + 0x5f, + 0x48, + 0x4f, + 0x50, + 0x45, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x43, + 0x68, + 0x61, + 0x72, + 0x61, + 0x63, + 0x74, + 0x65, + 0x72, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x66, + 0x72, + 0x69, + 0x65, + 0x6e, + 0x64, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x6f, + 0x64, + 0x65, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x68, + 0x6f, + 0x6d, + 0x65, + 0x50, + 0x6c, + 0x61, + 0x6e, + 0x65, + 0x74, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x70, + 0x72, + 0x69, + 0x6d, + 0x61, + 0x72, + 0x79, + 0x46, + 0x75, + 0x6e, + 0x63, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "180df66e91f5da244f1c1775dfec073d435d8faf"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -669,6 +998,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetHeroResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -791,9 +1121,7 @@ namespace Foo.Bar.State } public global::System.String Name { get; } - public global::System.String? PrimaryFunction { get; } - public global::Foo.Bar.State.FriendsConnectionData? Friends { get; } } @@ -809,9 +1137,7 @@ namespace Foo.Bar.State } public global::System.String Name { get; } - public global::System.String? HomePlanet { get; } - public global::Foo.Bar.State.FriendsConnectionData? Friends { get; } } @@ -830,6 +1156,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetHeroResult); + public GetHeroResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -885,9 +1212,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? Hero { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetHeroResultInfo(Hero, _entityIds, version); @@ -1099,7 +1426,6 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - ///A flattened list of the nodes. public global::System.Collections.Generic.IReadOnlyList? Nodes { get; } } @@ -1313,7 +1639,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Operation_With_Leaf_Argument.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Operation_With_Leaf_Argument.snap index 119ab71febe..6687381835d 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Operation_With_Leaf_Argument.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Operation_With_Leaf_Argument.snap @@ -94,7 +94,6 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } public virtual global::System.Boolean Equals(GetHero_Hero_Droid? other) @@ -170,7 +169,6 @@ namespace Foo.Bar } public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } public virtual global::System.Boolean Equals(GetHero_Hero_Human? other) @@ -247,7 +245,6 @@ namespace Foo.Bar public partial interface IGetHero_Hero { public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } } @@ -277,6 +274,7 @@ namespace Foo.Bar public partial class EpisodeSerializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "Episode"; + public Episode Parse(global::System.String serializedValue) { return serializedValue switch @@ -326,8 +324,141 @@ namespace Foo.Bar public static GetHeroQueryDocument Instance { get; } = new GetHeroQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x48, 0x65, 0x72, 0x6f, 0x28, 0x24, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x45, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x29, 0x20, 0x7b, 0x20, 0x68, 0x65, 0x72, 0x6f, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x24, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, 0x61, 0x72, 0x73, 0x49, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x28, + 0x24, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x45, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x68, + 0x65, + 0x72, + 0x6f, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x24, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x61, + 0x70, + 0x70, + 0x65, + 0x61, + 0x72, + 0x73, + 0x49, + 0x6e, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "d241b83259d20fe706e2aa7c38e6171fe3ccae44"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -369,6 +500,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetHeroResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::Foo.Bar.Episode? episode, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(episode); @@ -478,7 +610,6 @@ namespace Foo.Bar.State } public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } } @@ -493,7 +624,6 @@ namespace Foo.Bar.State } public global::System.String Name { get; } - public global::System.Collections.Generic.IReadOnlyList? AppearsIn { get; } } @@ -512,6 +642,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetHeroResult); + public GetHeroResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -567,9 +698,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? Hero { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetHeroResultInfo(Hero, _entityIds, version); @@ -752,7 +883,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Operation_With_Type_Argument.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Operation_With_Type_Argument.snap index c6b59091960..54c88e2a60a 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Operation_With_Type_Argument.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Operation_With_Type_Argument.snap @@ -90,7 +90,6 @@ namespace Foo.Bar } public global::System.Int32 Stars { get; } - public global::System.String? Commentary { get; } public virtual global::System.Boolean Equals(CreateReviewMut_CreateReview_Review? other) @@ -161,7 +160,6 @@ namespace Foo.Bar public partial interface ICreateReviewMut_CreateReview { public global::System.Int32 Stars { get; } - public global::System.String? Commentary { get; } } @@ -178,6 +176,7 @@ namespace Foo.Bar private global::StrawberryShake.Serialization.IInputValueFormatter _intFormatter = default !; private global::StrawberryShake.Serialization.IInputValueFormatter _stringFormatter = default !; public global::System.String TypeName => "ReviewInput"; + public void Initialize(global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) { _intFormatter = serializerResolver.GetInputValueFormatter("Int"); @@ -304,6 +303,7 @@ namespace Foo.Bar } global::System.Boolean global::Foo.Bar.State.IReviewInputInfo.IsStarsSet => _set_stars; + public global::System.String? Commentary { get => _value_commentary; @@ -331,6 +331,7 @@ namespace Foo.Bar public partial class EpisodeSerializer : global::StrawberryShake.Serialization.IInputValueFormatter, global::StrawberryShake.Serialization.ILeafValueParser { public global::System.String TypeName => "Episode"; + public Episode Parse(global::System.String serializedValue) { return serializedValue switch @@ -374,8 +375,163 @@ namespace Foo.Bar public static CreateReviewMutMutationDocument Instance { get; } = new CreateReviewMutMutationDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Mutation; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x4d, 0x75, 0x74, 0x28, 0x24, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x45, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x21, 0x2c, 0x20, 0x24, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x3a, 0x20, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x21, 0x29, 0x20, 0x7b, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x24, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x2c, 0x20, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x3a, 0x20, 0x24, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x74, 0x61, 0x72, 0x73, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x6d, + 0x75, + 0x74, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x43, + 0x72, + 0x65, + 0x61, + 0x74, + 0x65, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x4d, + 0x75, + 0x74, + 0x28, + 0x24, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x45, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x72, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x3a, + 0x20, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x49, + 0x6e, + 0x70, + 0x75, + 0x74, + 0x21, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x63, + 0x72, + 0x65, + 0x61, + 0x74, + 0x65, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x24, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x2c, + 0x20, + 0x72, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x3a, + 0x20, + 0x24, + 0x72, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x73, + 0x74, + 0x61, + 0x72, + 0x73, + 0x20, + 0x63, + 0x6f, + 0x6d, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x61, + 0x72, + 0x79, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "7b7488dce3bce5700fe4fab0d349728a5121c153"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -413,6 +569,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ICreateReviewMutResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::Foo.Bar.Episode episode, global::Foo.Bar.ReviewInput review, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(episode, review); @@ -520,6 +677,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ICreateReviewMutResult); + public CreateReviewMutResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -570,9 +728,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.ReviewData CreateReview { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new CreateReviewMutResultInfo(CreateReview, _entityIds, version); @@ -684,9 +842,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.Int32? Stars { get; } - public global::System.String? Commentary { get; } } @@ -695,7 +851,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.StarWarsTypeNameOnUnions.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.StarWarsTypeNameOnUnions.snap index 7c934106e58..b482efa3a73 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.StarWarsTypeNameOnUnions.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.StarWarsTypeNameOnUnions.snap @@ -345,8 +345,127 @@ namespace Foo.Bar public static SearchHeroQueryDocument Instance { get; } = new SearchHeroQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x48, 0x65, 0x72, 0x6f, 0x20, 0x7b, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x28, 0x74, 0x65, 0x78, 0x74, 0x3a, 0x20, 0x22, 0x6c, 0x22, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x53, 0x74, 0x61, 0x72, 0x73, 0x68, 0x69, 0x70, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x53, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x28, + 0x74, + 0x65, + 0x78, + 0x74, + 0x3a, + 0x20, + 0x22, + 0x6c, + 0x22, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x53, + 0x74, + 0x61, + 0x72, + 0x73, + 0x68, + 0x69, + 0x70, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "e2347e3fc516d7742122125fa68a1aca286f128c"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -387,6 +506,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ISearchHeroResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -528,6 +648,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ISearchHeroResult); + public SearchHeroResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -604,9 +725,9 @@ namespace Foo.Bar.State } public global::System.Collections.Generic.IReadOnlyList? Search { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new SearchHeroResultInfo(Search, _entityIds, version); @@ -807,7 +928,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.StarWarsUnionList.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.StarWarsUnionList.snap index e6d803a307d..423c405bf4e 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.StarWarsUnionList.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.StarWarsUnionList.snap @@ -339,8 +339,171 @@ namespace Foo.Bar public static SearchHeroQueryDocument Instance { get; } = new SearchHeroQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x48, 0x65, 0x72, 0x6f, 0x20, 0x7b, 0x20, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x28, 0x74, 0x65, 0x78, 0x74, 0x3a, 0x20, 0x22, 0x6c, 0x22, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x53, 0x74, 0x61, 0x72, 0x73, 0x68, 0x69, 0x70, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x48, 0x75, 0x6d, 0x61, 0x6e, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x53, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x65, + 0x61, + 0x72, + 0x63, + 0x68, + 0x28, + 0x74, + 0x65, + 0x78, + 0x74, + 0x3a, + 0x20, + 0x22, + 0x6c, + 0x22, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x53, + 0x74, + 0x61, + 0x72, + 0x73, + 0x68, + 0x69, + 0x70, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "ff8a1467e05c6c19bce649705a6bb53957883a86"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -387,6 +550,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ISearchHeroResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(); @@ -525,6 +689,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ISearchHeroResult); + public SearchHeroResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -601,9 +766,9 @@ namespace Foo.Bar.State } public global::System.Collections.Generic.IReadOnlyList? Search { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new SearchHeroResultInfo(Search, _entityIds, version); @@ -804,7 +969,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Subscription_With_Default_Names.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Subscription_With_Default_Names.snap index d831bac2230..d8181989937 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Subscription_With_Default_Names.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__snapshots__/StarWarsGeneratorTests.Subscription_With_Default_Names.snap @@ -90,7 +90,6 @@ namespace Foo.Bar } public global::System.Int32 Stars { get; } - public global::System.String? Commentary { get; } public virtual global::System.Boolean Equals(OnReviewSub_OnReview_Review? other) @@ -161,7 +160,6 @@ namespace Foo.Bar public partial interface IOnReviewSub_OnReview { public global::System.Int32 Stars { get; } - public global::System.String? Commentary { get; } } @@ -193,8 +191,99 @@ namespace Foo.Bar public static OnReviewSubSubscriptionDocument Instance { get; } = new OnReviewSubSubscriptionDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Subscription; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x4f, 0x6e, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x53, 0x75, 0x62, 0x20, 0x7b, 0x20, 0x6f, 0x6e, 0x52, 0x65, 0x76, 0x69, 0x65, 0x77, 0x28, 0x65, 0x70, 0x69, 0x73, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x4e, 0x45, 0x57, 0x5f, 0x48, 0x4f, 0x50, 0x45, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x73, 0x74, 0x61, 0x72, 0x73, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x73, + 0x75, + 0x62, + 0x73, + 0x63, + 0x72, + 0x69, + 0x70, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x4f, + 0x6e, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x53, + 0x75, + 0x62, + 0x20, + 0x7b, + 0x20, + 0x6f, + 0x6e, + 0x52, + 0x65, + 0x76, + 0x69, + 0x65, + 0x77, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x4e, + 0x45, + 0x57, + 0x5f, + 0x48, + 0x4f, + 0x50, + 0x45, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x73, + 0x74, + 0x61, + 0x72, + 0x73, + 0x20, + 0x63, + 0x6f, + 0x6d, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x61, + 0x72, + 0x79, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "92220fce37342d7ade3d63a2a81342eb1fb14bac"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -228,6 +317,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IOnReviewSubResult); + public global::System.IObservable> Watch(global::StrawberryShake.ExecutionStrategy? strategy = null) { var request = CreateRequest(); @@ -310,6 +400,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IOnReviewSubResult); + public OnReviewSubResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -360,9 +451,9 @@ namespace Foo.Bar.State } public global::Foo.Bar.State.ReviewData OnReview { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new OnReviewSubResultInfo(OnReview, _entityIds, version); @@ -463,9 +554,7 @@ namespace Foo.Bar.State } public global::System.String __typename { get; } - public global::System.Int32? Stars { get; } - public global::System.String? Commentary { get; } } @@ -474,7 +563,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.Razor.Tests/__snapshots__/RazorGeneratorTests.Query_And_Mutation.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.Razor.Tests/__snapshots__/RazorGeneratorTests.Query_And_Mutation.snap index 07a8d37b3bd..56f9e28f11f 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.Razor.Tests/__snapshots__/RazorGeneratorTests.Query_And_Mutation.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.Razor.Tests/__snapshots__/RazorGeneratorTests.Query_And_Mutation.snap @@ -100,7 +100,6 @@ namespace Foo.Bar } public global::System.String Id { get; } - public global::System.String? Name { get; } public virtual global::System.Boolean Equals(GetBars_Bars_Bar? other) @@ -171,7 +170,6 @@ namespace Foo.Bar public partial interface IGetBars_Bars { public global::System.String Id { get; } - public global::System.String? Name { get; } } @@ -258,7 +256,6 @@ namespace Foo.Bar } public global::System.String Id { get; } - public global::System.String? Name { get; } public virtual global::System.Boolean Equals(SaveBars_SaveBar_Bar? other) @@ -329,7 +326,6 @@ namespace Foo.Bar public partial interface ISaveBars_SaveBar { public global::System.String Id { get; } - public global::System.String? Name { get; } } @@ -364,8 +360,113 @@ namespace Foo.Bar public static GetBarsQueryDocument Instance { get; } = new GetBarsQueryDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x71, 0x75, 0x65, 0x72, 0x79, 0x20, 0x47, 0x65, 0x74, 0x42, 0x61, 0x72, 0x73, 0x28, 0x24, 0x61, 0x3a, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x21, 0x2c, 0x20, 0x24, 0x62, 0x3a, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x29, 0x20, 0x7b, 0x20, 0x62, 0x61, 0x72, 0x73, 0x28, 0x61, 0x3a, 0x20, 0x24, 0x61, 0x2c, 0x20, 0x62, 0x3a, 0x20, 0x24, 0x62, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x64, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x72, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x42, + 0x61, + 0x72, + 0x73, + 0x28, + 0x24, + 0x61, + 0x3a, + 0x20, + 0x53, + 0x74, + 0x72, + 0x69, + 0x6e, + 0x67, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x62, + 0x3a, + 0x20, + 0x53, + 0x74, + 0x72, + 0x69, + 0x6e, + 0x67, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x62, + 0x61, + 0x72, + 0x73, + 0x28, + 0x61, + 0x3a, + 0x20, + 0x24, + 0x61, + 0x2c, + 0x20, + 0x62, + 0x3a, + 0x20, + 0x24, + 0x62, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x69, + 0x64, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x72, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "d77fc7854088f9716b48874b752a2d2b8be41aef"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -404,6 +505,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetBarsResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.String a, global::System.String? b, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(a, b); @@ -505,8 +607,120 @@ namespace Foo.Bar public static SaveBarsMutationDocument Instance { get; } = new SaveBarsMutationDocument(); public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Mutation; - public global::System.ReadOnlySpan Body => new global::System.Byte[]{0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x53, 0x61, 0x76, 0x65, 0x42, 0x61, 0x72, 0x73, 0x28, 0x24, 0x61, 0x3a, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x21, 0x2c, 0x20, 0x24, 0x62, 0x3a, 0x20, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x29, 0x20, 0x7b, 0x20, 0x73, 0x61, 0x76, 0x65, 0x42, 0x61, 0x72, 0x28, 0x61, 0x3a, 0x20, 0x24, 0x61, 0x2c, 0x20, 0x62, 0x3a, 0x20, 0x24, 0x62, 0x29, 0x20, 0x7b, 0x20, 0x5f, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x69, 0x64, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0x6f, 0x6e, 0x20, 0x42, 0x61, 0x72, 0x20, 0x7b, 0x20, 0x69, 0x64, 0x20, 0x7d, 0x20, 0x7d, 0x20, 0x7d}; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x6d, + 0x75, + 0x74, + 0x61, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x53, + 0x61, + 0x76, + 0x65, + 0x42, + 0x61, + 0x72, + 0x73, + 0x28, + 0x24, + 0x61, + 0x3a, + 0x20, + 0x53, + 0x74, + 0x72, + 0x69, + 0x6e, + 0x67, + 0x21, + 0x2c, + 0x20, + 0x24, + 0x62, + 0x3a, + 0x20, + 0x53, + 0x74, + 0x72, + 0x69, + 0x6e, + 0x67, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x73, + 0x61, + 0x76, + 0x65, + 0x42, + 0x61, + 0x72, + 0x28, + 0x61, + 0x3a, + 0x20, + 0x24, + 0x61, + 0x2c, + 0x20, + 0x62, + 0x3a, + 0x20, + 0x24, + 0x62, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x69, + 0x64, + 0x20, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x42, + 0x61, + 0x72, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d + }; public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "46ced9034dad969a5b30d75b8a91366be7ad75b5"); + public override global::System.String ToString() { #if NETCOREAPP3_1_OR_GREATER @@ -545,6 +759,7 @@ namespace Foo.Bar } global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(ISaveBarsResult); + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.String a, global::System.String? b, global::System.Threading.CancellationToken cancellationToken = default) { var request = CreateRequest(a, b); @@ -667,7 +882,6 @@ namespace Foo.Bar.State } public global::System.String Id { get; } - public global::System.String? Name { get; } } @@ -684,6 +898,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.IGetBarsResult); + public GetBarsResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -750,9 +965,9 @@ namespace Foo.Bar.State } public global::System.Collections.Generic.IReadOnlyList? Bars { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new GetBarsResultInfo(Bars, _entityIds, version); @@ -772,6 +987,7 @@ namespace Foo.Bar.State } global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::Foo.Bar.ISaveBarsResult); + public SaveBarsResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) { if (snapshot is null) @@ -822,9 +1038,9 @@ namespace Foo.Bar.State } public global::StrawberryShake.EntityId? SaveBar { get; } - public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; public global::System.UInt64 Version => _version; + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) { return new SaveBarsResultInfo(SaveBar, _entityIds, version); @@ -1083,7 +1299,9 @@ namespace Foo.Bar.State public partial class FooClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer { private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() - {Indented = false}; + { + Indented = false + }; public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) { global::System.String __typename = obj.GetProperty("__typename").GetString()!; @@ -1240,8 +1458,10 @@ namespace Foo.Bar.Components { [global::Microsoft.AspNetCore.Components.InjectAttribute] internal global::Foo.Bar.GetBarsQuery Operation { get; set; } = default !; + [global::Microsoft.AspNetCore.Components.ParameterAttribute] public global::System.String A { get; set; } = default !; + [global::Microsoft.AspNetCore.Components.ParameterAttribute] public global::System.String? B { get; set; } diff --git a/src/StrawberryShake/Directory.Build.props b/src/StrawberryShake/Directory.Build.props index 5a4f40302f5..e5829ff34cd 100644 --- a/src/StrawberryShake/Directory.Build.props +++ b/src/StrawberryShake/Directory.Build.props @@ -8,8 +8,8 @@ - net8.0; net7.0; net6.0; netstandard2.1; netstandard2.0 - net8.0; net7.0; net6.0 + net9.0; net8.0; net7.0; net6.0; netstandard2.1; netstandard2.0 + net9.0; net8.0; net7.0; net6.0 From 4b5f44f2e15d0f11d013d6f293d3316999f4dcdf Mon Sep 17 00:00:00 2001 From: Glen Date: Thu, 21 Nov 2024 23:22:47 +0200 Subject: [PATCH 099/154] Improved asynchronous code in Marten tests (#7747) --- .../Data.Marten.Filters.Tests/FilterVisitorTestBase.cs | 7 +++---- .../test/Data.Marten.Sorting.Tests/SortVisitorTestBase.cs | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/FilterVisitorTestBase.cs b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/FilterVisitorTestBase.cs index 81b22b2aee7..ede2ee80c54 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/FilterVisitorTestBase.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/FilterVisitorTestBase.cs @@ -28,7 +28,7 @@ protected async Task CreateSchemaAsync( where T : FilterInputType { var dbName = $"DB_{Guid.NewGuid():N}"; - Container.Resource.CreateDatabaseAsync(dbName).GetAwaiter().GetResult(); + await Container.Resource.CreateDatabaseAsync(dbName); var store = DocumentStore.For(Container.Resource.GetConnectionString(dbName)); var resolver = await BuildResolverAsync(store, entities); @@ -56,7 +56,7 @@ protected async Task CreateSchemaAsync( var schema = builder.Create(); - return new ServiceCollection() + return await new ServiceCollection() .Configure( Schema.DefaultName, o => o.Schema = schema) @@ -79,8 +79,7 @@ protected async Task CreateSchemaAsync( .Services .BuildServiceProvider() .GetRequiredService() - .GetRequestExecutorAsync() - .Result; + .GetRequestExecutorAsync(); } private void ApplyConfigurationToField( diff --git a/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/SortVisitorTestBase.cs b/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/SortVisitorTestBase.cs index 1e320309707..37aadd993ef 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/SortVisitorTestBase.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Sorting.Tests/SortVisitorTestBase.cs @@ -87,7 +87,7 @@ protected async Task CreateSchemaAsync( where T : SortInputType { var dbName = $"DB_{Guid.NewGuid():N}"; - Container.Resource.CreateDatabaseAsync(dbName).GetAwaiter().GetResult(); + await Container.Resource.CreateDatabaseAsync(dbName); var store = DocumentStore.For(Container.Resource.GetConnectionString(dbName)); var resolver = await BuildResolverAsync(store, entities); @@ -113,7 +113,7 @@ protected async Task CreateSchemaAsync( var schema = builder.Create(); - return new ServiceCollection() + return await new ServiceCollection() .Configure( Schema.DefaultName, o => o.Schema = schema) @@ -136,8 +136,7 @@ protected async Task CreateSchemaAsync( .Services .BuildServiceProvider() .GetRequiredService() - .GetRequestExecutorAsync() - .Result; + .GetRequestExecutorAsync(); } private void ApplyConfigurationToField( From 603beee0146cb13af72e65e7fdf8bfb7370b8d20 Mon Sep 17 00:00:00 2001 From: Glen Date: Thu, 21 Nov 2024 23:23:58 +0200 Subject: [PATCH 100/154] Reverted change to restore-keys (#7745) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7243095bca4..75c507274f9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -122,7 +122,7 @@ jobs: website/.cache/yarn key: ${{ runner.os }}-yarn-${{ hashFiles('website/yarn.lock') }} restore-keys: | - ${{ runner.os }}-yarn-${{ hashFiles('website/yarn.lock') }} + ${{ runner.os }}-yarn- - name: Install Packages run: yarn --immutable --network-timeout 100000 From b5640f8bd367af157d1c058fa7f43129351e35bc Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Sun, 17 Nov 2024 22:35:57 +0100 Subject: [PATCH 101/154] Fixed issue when accept:*/* and operation is a subscription. (#7732) --- .../DefaultHttpResponseFormatter.cs | 2 +- .../ServerTestBase.cs | 7 ++ .../GraphQLOverHttpSpecTests.cs | 97 ++++++++++++++++++- .../Types/SubscriptionTypeTests.cs | 53 ++++++++++ 4 files changed, 157 insertions(+), 2 deletions(-) diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore/Serialization/DefaultHttpResponseFormatter.cs b/src/HotChocolate/AspNetCore/src/AspNetCore/Serialization/DefaultHttpResponseFormatter.cs index 0b78420b475..5307bb77879 100644 --- a/src/HotChocolate/AspNetCore/src/AspNetCore/Serialization/DefaultHttpResponseFormatter.cs +++ b/src/HotChocolate/AspNetCore/src/AspNetCore/Serialization/DefaultHttpResponseFormatter.cs @@ -547,7 +547,7 @@ protected virtual void OnWriteResponseHeaders( return _multiPartFormat; } - if (mediaType.Kind is EventStream) + if (mediaType.Kind is EventStream or All) { return _eventStreamFormat; } diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/ServerTestBase.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/ServerTestBase.cs index 7312ade438d..c1e22314497 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/ServerTestBase.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/ServerTestBase.cs @@ -41,6 +41,7 @@ protected virtual TestServer CreateStarWarsServer( .AddStarWarsTypes() .AddTypeExtension() .AddTypeExtension() + .AddType() .AddStarWarsRepositories() .AddInMemorySubscriptions() .UseInstrumentation() @@ -169,4 +170,10 @@ protected virtual TestServer CreateServer( .UseRouting() .UseEndpoints(endpoints => configureConventions?.Invoke(endpoints))); } + + [DirectiveType(DirectiveLocation.Subscription)] + public class Foo + { + public required int Bar { get; set; } + } } diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/GraphQLOverHttpSpecTests.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/GraphQLOverHttpSpecTests.cs index 48d927b0afc..88a93611b15 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/GraphQLOverHttpSpecTests.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/GraphQLOverHttpSpecTests.cs @@ -457,7 +457,102 @@ public async Task EventStream_Sends_KeepAlive() Snapshot .Create() .Add(response) - .MatchInline(""" + .MatchInline( + """ + Headers: + Cache-Control: no-cache + Content-Type: text/event-stream; charset=utf-8 + --------------------------> + Status Code: OK + --------------------------> + event: next + data: {"data":{"delay":"next"}} + + : + + event: next + data: {"data":{"delay":"next"}} + + : + + event: complete + + + """); + } + + [Fact] + public async Task EventStream_When_Accept_Is_All() + { + // arrange + var server = CreateStarWarsServer(); + var client = server.CreateClient(); + client.Timeout = TimeSpan.FromSeconds(30); + + // act + using var request = new HttpRequestMessage(HttpMethod.Post, _url); + request.Content = JsonContent.Create( + new ClientQueryRequest + { + Query = "subscription {delay(count: 2, delay:15000)}", + }); + request.Headers.Add("Accept", "*/*"); + + using var response = await client.SendAsync(request, ResponseHeadersRead); + + // assert + Snapshot + .Create() + .Add(response) + .MatchInline( + """ + Headers: + Cache-Control: no-cache + Content-Type: text/event-stream; charset=utf-8 + --------------------------> + Status Code: OK + --------------------------> + event: next + data: {"data":{"delay":"next"}} + + : + + event: next + data: {"data":{"delay":"next"}} + + : + + event: complete + + + """); + } + + [Fact] + public async Task EventStream_When_Accept_Is_All_And_Subscription_Directive() + { + // arrange + var server = CreateStarWarsServer(); + var client = server.CreateClient(); + client.Timeout = TimeSpan.FromSeconds(30); + + // act + using var request = new HttpRequestMessage(HttpMethod.Post, _url); + request.Content = JsonContent.Create( + new ClientQueryRequest + { + Query = "subscription foo @foo(bar: 1) {delay(count: 2, delay:15000)}", + }); + request.Headers.Add("Accept", "*/*"); + + using var response = await client.SendAsync(request, ResponseHeadersRead); + + // assert + Snapshot + .Create() + .Add(response) + .MatchInline( + """ Headers: Cache-Control: no-cache Content-Type: text/event-stream; charset=utf-8 diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/SubscriptionTypeTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/SubscriptionTypeTests.cs index e9f6ba030d0..35f5dd49747 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/SubscriptionTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Types/SubscriptionTypeTests.cs @@ -6,6 +6,7 @@ #pragma warning disable CS0618 // Type or member is obsolete #nullable enable +using System.Runtime.CompilerServices; using CookieCrumble; using HotChocolate.Execution; using HotChocolate.Subscriptions; @@ -647,6 +648,44 @@ public async Task Arguments_Can_Be_Declared_On_The_Stream() """); } + [Fact] + public async Task Subscription_Directives_Are_Allowed() + { + // arrange + // act + var executor = await new ServiceCollection() + .AddGraphQLServer() + .AddDocumentFromString( + """ + type Subscription { + bookAdded: String! + } + + directive @bug(test: Int!) on SUBSCRIPTION + """) + .BindRuntimeType("Subscription") + .ModifyOptions(o => o.StrictValidation = false) + .BuildRequestExecutorAsync(); + + var result = await executor.ExecuteAsync( + """ + subscription test @bug(test: 123) { + bookAdded + } + """); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "bookAdded": "foo" + } + } + + """); + } + public class TestObservable : IObservable, IDisposable { public bool DisposeRaised { get; private set; } @@ -1047,4 +1086,18 @@ public string OnExplicit( [EventMessage] string message) => message; } + + + public class SubscriptionWithDirective + { + [Subscribe(With = nameof(GetStream))] + public string BookAdded([EventMessage] string foo) => foo; + + private async IAsyncEnumerable GetStream( + [EnumeratorCancellation] CancellationToken ct = default) + { + await Task.Delay(200, ct); + yield return "foo"; + } + } } From 3d6748b801d2cf21985086f6a1a3941ec8550920 Mon Sep 17 00:00:00 2001 From: Tyler Siegrist <19572714+TylerSiegrist@users.noreply.github.com> Date: Wed, 20 Nov 2024 11:01:46 -0800 Subject: [PATCH 102/154] Include net9.0 tools in StrawberryShake package (#7710) --- .../MetaPackages/Blazor/StrawberryShake.Blazor.csproj | 1 + .../MetaPackages/Maui/StrawberryShake.Maui.csproj | 1 + .../MetaPackages/Server/StrawberryShake.Server.csproj | 1 + 3 files changed, 3 insertions(+) diff --git a/src/StrawberryShake/MetaPackages/Blazor/StrawberryShake.Blazor.csproj b/src/StrawberryShake/MetaPackages/Blazor/StrawberryShake.Blazor.csproj index af5ea7560f6..a81c3a73e18 100644 --- a/src/StrawberryShake/MetaPackages/Blazor/StrawberryShake.Blazor.csproj +++ b/src/StrawberryShake/MetaPackages/Blazor/StrawberryShake.Blazor.csproj @@ -27,6 +27,7 @@ + diff --git a/src/StrawberryShake/MetaPackages/Maui/StrawberryShake.Maui.csproj b/src/StrawberryShake/MetaPackages/Maui/StrawberryShake.Maui.csproj index bab3fbdc187..51da496f176 100644 --- a/src/StrawberryShake/MetaPackages/Maui/StrawberryShake.Maui.csproj +++ b/src/StrawberryShake/MetaPackages/Maui/StrawberryShake.Maui.csproj @@ -26,6 +26,7 @@ + diff --git a/src/StrawberryShake/MetaPackages/Server/StrawberryShake.Server.csproj b/src/StrawberryShake/MetaPackages/Server/StrawberryShake.Server.csproj index 4db0fcbd403..163c1341849 100644 --- a/src/StrawberryShake/MetaPackages/Server/StrawberryShake.Server.csproj +++ b/src/StrawberryShake/MetaPackages/Server/StrawberryShake.Server.csproj @@ -26,6 +26,7 @@ + From 9a9d0ca5e201a832023f2445bb7eb570b708a8d6 Mon Sep 17 00:00:00 2001 From: Glen Date: Thu, 21 Nov 2024 23:20:45 +0200 Subject: [PATCH 103/154] Prevented type descriptions from being overwritten while merging (#7751) --- .../Descriptors/Definitions/DefinitionBase.cs | 2 +- .../QueryExtWithDocs1.cs | 10 ++++++ .../QueryExtWithDocs2.cs | 10 ++++++ .../Definitions/DefinitionBaseTests.cs | 34 +++++++++++++++++++ 4 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 src/HotChocolate/Core/test/Types.Tests.Documentation/QueryExtWithDocs1.cs create mode 100644 src/HotChocolate/Core/test/Types.Tests.Documentation/QueryExtWithDocs2.cs create mode 100644 src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/Definitions/DefinitionBaseTests.cs diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/DefinitionBase.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/DefinitionBase.cs index 150908244d1..f249ef83247 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/DefinitionBase.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/DefinitionBase.cs @@ -199,7 +199,7 @@ protected void MergeInto(DefinitionBase target) } } - if (Description is not null) + if (target.Description is null && Description is not null) { target.Description = Description; } diff --git a/src/HotChocolate/Core/test/Types.Tests.Documentation/QueryExtWithDocs1.cs b/src/HotChocolate/Core/test/Types.Tests.Documentation/QueryExtWithDocs1.cs new file mode 100644 index 00000000000..41e06dcdc7b --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests.Documentation/QueryExtWithDocs1.cs @@ -0,0 +1,10 @@ +namespace HotChocolate.Types.Descriptors; + +/// +/// QueryExtWithDocs1 +/// +[QueryType] +public static class QueryExtWithDocs1 +{ + public static int GetFoo1() => 1; +} diff --git a/src/HotChocolate/Core/test/Types.Tests.Documentation/QueryExtWithDocs2.cs b/src/HotChocolate/Core/test/Types.Tests.Documentation/QueryExtWithDocs2.cs new file mode 100644 index 00000000000..c60b27cc0cf --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests.Documentation/QueryExtWithDocs2.cs @@ -0,0 +1,10 @@ +namespace HotChocolate.Types.Descriptors; + +/// +/// QueryExtWithDocs2 +/// +[QueryType] +public static class QueryExtWithDocs2 +{ + public static int GetFoo2() => 1; +} diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/Definitions/DefinitionBaseTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/Definitions/DefinitionBaseTests.cs new file mode 100644 index 00000000000..a69eff3d823 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/Definitions/DefinitionBaseTests.cs @@ -0,0 +1,34 @@ +using CookieCrumble; +using HotChocolate.Execution; +using Microsoft.Extensions.DependencyInjection; + +namespace HotChocolate.Types.Descriptors.Definitions; + +public class DefinitionBaseTests +{ + [Fact] + public async Task MergeInto_MultipleTypeExtensionXmlDescriptions_UsesQueryTypeDescription() + { + // arrange & act + var schema = await new ServiceCollection() + .AddGraphQL() + .AddQueryType(d => d.Description("Query")) + .AddTypeExtension(typeof(QueryExtWithDocs1)) + .AddTypeExtension(typeof(QueryExtWithDocs2)) + .BuildSchemaAsync(); + + // assert + schema.MatchInlineSnapshot( + """ + schema { + query: Query + } + + "Query" + type Query { + foo1: Int! + foo2: Int! + } + """); + } +} From 4ef4e0200c78f2da137989c1697578dcfcd164dd Mon Sep 17 00:00:00 2001 From: Sunghwan Bang Date: Tue, 26 Nov 2024 19:35:59 +0900 Subject: [PATCH 104/154] Fixed websockets message corruption after cancellation (#7768) --- .../Messaging/SynchronizedMessageWriter.cs | 8 ++++---- .../Messaging/SynchronizedMessageWriterTests.cs | 8 ++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/StrawberryShake/Client/src/Transport.WebSockets/Messaging/SynchronizedMessageWriter.cs b/src/StrawberryShake/Client/src/Transport.WebSockets/Messaging/SynchronizedMessageWriter.cs index 75b3fae5e83..36458cfaef7 100644 --- a/src/StrawberryShake/Client/src/Transport.WebSockets/Messaging/SynchronizedMessageWriter.cs +++ b/src/StrawberryShake/Client/src/Transport.WebSockets/Messaging/SynchronizedMessageWriter.cs @@ -31,12 +31,12 @@ public async ValueTask CommitAsync( Action action, CancellationToken cancellationToken) { + await _semaphore + .WaitAsync(cancellationToken) + .ConfigureAwait(false); + try { - await _semaphore - .WaitAsync(cancellationToken) - .ConfigureAwait(false); - _socketMessageWriter.Reset(); action.Invoke(_socketMessageWriter); diff --git a/src/StrawberryShake/Client/test/Transport.WebSocket.Tests/Messaging/SynchronizedMessageWriterTests.cs b/src/StrawberryShake/Client/test/Transport.WebSocket.Tests/Messaging/SynchronizedMessageWriterTests.cs index 2d9485c1eaa..11261b9549f 100644 --- a/src/StrawberryShake/Client/test/Transport.WebSocket.Tests/Messaging/SynchronizedMessageWriterTests.cs +++ b/src/StrawberryShake/Client/test/Transport.WebSocket.Tests/Messaging/SynchronizedMessageWriterTests.cs @@ -30,6 +30,14 @@ public async Task WriteObject_EmptyBuffer_ObjectParallel() await using var writer = new SynchronizedMessageWriter(socketClient); // act + var canceled = writer.CommitAsync(x => + { + x.WriteStartObject(); + x.WriteEndObject(); + }, + new(true)); + Assert.True(canceled.IsCanceled); + List tasks = []; for (var i = 0; i < 10; i++) { From b6c34d7e9c563123525621963c90fee805b1de27 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 27 Nov 2024 11:41:03 +0100 Subject: [PATCH 105/154] Fixed Compile Errors --- .../test/AspNetCore.Tests.Utilities/ServerTestBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/ServerTestBase.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/ServerTestBase.cs index c1e22314497..7806ba06a42 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/ServerTestBase.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests.Utilities/ServerTestBase.cs @@ -174,6 +174,6 @@ protected virtual TestServer CreateServer( [DirectiveType(DirectiveLocation.Subscription)] public class Foo { - public required int Bar { get; set; } + public int Bar { get; set; } } } From 2fb7f1483bf573f5babf382212b7b755266610a5 Mon Sep 17 00:00:00 2001 From: Glen Date: Wed, 27 Nov 2024 16:12:52 +0200 Subject: [PATCH 106/154] Fixed snapshots (#7773) --- .../HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL.snap | 2 ++ .../HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema.md | 6 ++++-- ...areTests.Download_GraphQL_Schema_Slicing_Args_Enabled.md | 6 ++++-- .../IntrospectionClientTests.IntrospectServer.snap | 2 ++ 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL.snap b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL.snap index b2a1cda45f1..30c65b8cacb 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL.snap +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL.snap @@ -125,6 +125,8 @@ directive @cost("The `weight` argument defines what value to add to the overall "The `@defer` directive may be provided for fragment spreads and inline fragments to inform the executor to delay the execution of the current fragment to indicate deprioritization of the current fragment. A query with `@defer` directive will cause the request to potentially return multiple responses, where non-deferred data is delivered in the initial response and data deferred is delivered in a subsequent response. `@include` and `@skip` take precedence over `@defer`." directive @defer("If this argument label has a value other than null, it will be passed on to the result of this defer directive. This label is intended to give client applications a way to identify to which fragment a deferred result belongs to." label: String "Deferred when true." if: Boolean) on FRAGMENT_SPREAD | INLINE_FRAGMENT +directive @foo(bar: Int!) on SUBSCRIPTION + "The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!] "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true) on FIELD_DEFINITION diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema.md b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema.md index d6d500a0681..72badc86f61 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema.md +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema.md @@ -2,12 +2,12 @@ ```text Headers: -ETag: "1-LzuFZZrknIre5etJZHfNRC1e/vj+qW9tAf9pYpS8bQM=" +ETag: "1-LXklYHgmBtUalJ0Ugb0vQzLmP6FuyFt1keIXDn4SE/Y=" Cache-Control: public, must-revalidate, max-age=3600 Content-Type: application/graphql; charset=utf-8 Content-Disposition: attachment; filename="schema.graphql" Last-Modified: Fri, 01 Jan 2021 00:00:00 GMT -Content-Length: 7261 +Content-Length: 7304 --------------------------> Status Code: OK --------------------------> @@ -138,6 +138,8 @@ directive @cost("The `weight` argument defines what value to add to the overall "The `@defer` directive may be provided for fragment spreads and inline fragments to inform the executor to delay the execution of the current fragment to indicate deprioritization of the current fragment. A query with `@defer` directive will cause the request to potentially return multiple responses, where non-deferred data is delivered in the initial response and data deferred is delivered in a subsequent response. `@include` and `@skip` take precedence over `@defer`." directive @defer("If this argument label has a value other than null, it will be passed on to the result of this defer directive. This label is intended to give client applications a way to identify to which fragment a deferred result belongs to." label: String "Deferred when true." if: Boolean) on FRAGMENT_SPREAD | INLINE_FRAGMENT +directive @foo(bar: Int!) on SUBSCRIPTION + "The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!] "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true) on FIELD_DEFINITION diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema_Slicing_Args_Enabled.md b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema_Slicing_Args_Enabled.md index a92a5395df1..e1d4b309a5f 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema_Slicing_Args_Enabled.md +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema_Slicing_Args_Enabled.md @@ -2,12 +2,12 @@ ```text Headers: -ETag: "1-B2t9cwf/BRF8NYIpFDtJE4DLg8FfD5d5HdAF9KObaUc=" +ETag: "1-SGSC/P4ipajfTuy/tz1WcPpecD5c1THPYtIkhxzZoQE=" Cache-Control: public, must-revalidate, max-age=3600 Content-Type: application/graphql; charset=utf-8 Content-Disposition: attachment; filename="schema.graphql" Last-Modified: Fri, 01 Jan 2021 00:00:00 GMT -Content-Length: 7193 +Content-Length: 7236 --------------------------> Status Code: OK --------------------------> @@ -138,6 +138,8 @@ directive @cost("The `weight` argument defines what value to add to the overall "The `@defer` directive may be provided for fragment spreads and inline fragments to inform the executor to delay the execution of the current fragment to indicate deprioritization of the current fragment. A query with `@defer` directive will cause the request to potentially return multiple responses, where non-deferred data is delivered in the initial response and data deferred is delivered in a subsequent response. `@include` and `@skip` take precedence over `@defer`." directive @defer("If this argument label has a value other than null, it will be passed on to the result of this defer directive. This label is intended to give client applications a way to identify to which fragment a deferred result belongs to." label: String "Deferred when true." if: Boolean) on FRAGMENT_SPREAD | INLINE_FRAGMENT +directive @foo(bar: Int!) on SUBSCRIPTION + "The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!] "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true) on FIELD_DEFINITION diff --git a/src/HotChocolate/Utilities/test/Utilities.Introspection.Tests/__snapshots__/IntrospectionClientTests.IntrospectServer.snap b/src/HotChocolate/Utilities/test/Utilities.Introspection.Tests/__snapshots__/IntrospectionClientTests.IntrospectServer.snap index 136fadab945..88333bf5516 100644 --- a/src/HotChocolate/Utilities/test/Utilities.Introspection.Tests/__snapshots__/IntrospectionClientTests.IntrospectServer.snap +++ b/src/HotChocolate/Utilities/test/Utilities.Introspection.Tests/__snapshots__/IntrospectionClientTests.IntrospectServer.snap @@ -129,3 +129,5 @@ directive @cost("The `weight` argument defines what value to add to the overall "The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!] "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true) on FIELD_DEFINITION + +directive @foo(bar: Int!) on SUBSCRIPTION From 0c91a7282e28993cbcaf88f5c21d82215299a499 Mon Sep 17 00:00:00 2001 From: Tobias Tengler <45513122+tobias-tengler@users.noreply.github.com> Date: Mon, 4 Nov 2024 21:34:22 +0100 Subject: [PATCH 107/154] Improve automatic mocking (#7676) --- .../test/Core.Tests/AutomaticMockingTests.cs | 2599 +++++++++++++++-- .../test/Core.Tests/SubgraphErrorTests.cs | 2 +- .../AutomaticMockingTests.ListOfObjects.md | 36 - ...s.ListOfObjects_Property_Error_At_Index.md | 48 - ...ts.ListOfObjects_Property_Null_At_Index.md | 32 - .../AutomaticMockingTests.ListOfScalars.md | 24 - .../AutomaticMockingTests.Object.md | 26 - .../AutomaticMockingTests.Plural_ById.md | 29 - ...AutomaticMockingTests.Plural_ById_Error.md | 36 - ...MockingTests.Plural_ById_Error_At_Index.md | 42 - .../AutomaticMockingTests.Plural_ById_Null.md | 22 - ...cMockingTests.Plural_ById_Null_At_Index.md | 27 - .../AutomaticMockingTests.Singular_ById.md | 24 - ...tomaticMockingTests.Singular_ById_Error.md | 36 - ...utomaticMockingTests.Singular_ById_Null.md | 22 - .../AutomaticMocking/MockFieldMiddleware.cs | 44 +- .../Fusion/test/Shared/TestSubgraph.cs | 6 +- .../test/Shared/TestSubgraphCollection.cs | 8 +- 18 files changed, 2456 insertions(+), 607 deletions(-) delete mode 100644 src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.ListOfObjects.md delete mode 100644 src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.ListOfObjects_Property_Error_At_Index.md delete mode 100644 src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.ListOfObjects_Property_Null_At_Index.md delete mode 100644 src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.ListOfScalars.md delete mode 100644 src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Object.md delete mode 100644 src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Plural_ById.md delete mode 100644 src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Plural_ById_Error.md delete mode 100644 src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Plural_ById_Error_At_Index.md delete mode 100644 src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Plural_ById_Null.md delete mode 100644 src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Plural_ById_Null_At_Index.md delete mode 100644 src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Singular_ById.md delete mode 100644 src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Singular_ById_Error.md delete mode 100644 src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Singular_ById_Null.md diff --git a/src/HotChocolate/Fusion/test/Core.Tests/AutomaticMockingTests.cs b/src/HotChocolate/Fusion/test/Core.Tests/AutomaticMockingTests.cs index 28032d7d2ca..c5ccee5f28d 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/AutomaticMockingTests.cs +++ b/src/HotChocolate/Fusion/test/Core.Tests/AutomaticMockingTests.cs @@ -1,25 +1,19 @@ +using CookieCrumble; using HotChocolate.Execution; using HotChocolate.Fusion.Shared; using Microsoft.Extensions.DependencyInjection; -using static HotChocolate.Fusion.TestHelper; namespace HotChocolate.Fusion; public class AutomaticMockingTests { + #region Objects + [Fact] public async Task Object() { - var request = """ - query { - obj { - id - str - } - } - """; - - var result = await ExecuteRequestAgainstSchemaAsync( + // arrange + var schema = """ type Query { obj: Object @@ -29,323 +23,2548 @@ type Object { id: ID! str: String! } - """, - request); + """; + var request = + """ + query { + obj { + id + str + } + } + """; - MatchMarkdownSnapshot(request, result); + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "obj": { + "id": "1", + "str": "string" + } + } + } + """); } [Fact] - public async Task Singular_ById() + public async Task Object_Null() { - var request = """ - query { - productById(id: "5") { - id - } - } - """; - - var result = await ExecuteRequestAgainstSchemaAsync( + // arrange + var schema = """ type Query { - productById(id: ID!): Product + obj: Object @null } - type Product { + type Object { id: ID! + str: String! } - """, - request); + """; + var request = + """ + query { + obj { + id + str + } + } + """; - MatchMarkdownSnapshot(request, result); + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "obj": null + } + } + """); } [Fact] - public async Task Singular_ById_Error() + public async Task Object_Error() { - var request = """ - query { - productById(id: "5") { - id - } - } - """; - - var result = await ExecuteRequestAgainstSchemaAsync( + // arrange + var schema = """ type Query { - productById(id: ID!): Product @error + obj: Object @error } - type Product { + type Object { id: ID! + str: String! + } + """; + var request = + """ + query { + obj { + id + str + } } - """, - request); + """; - MatchMarkdownSnapshot(request, result); + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "obj" + ] + } + ], + "data": { + "obj": null + } + } + """); } [Fact] - public async Task Singular_ById_Null() + public async Task Object_List() { - var request = """ - query { - productById(id: "5") { - id - } - } - """; - - var result = await ExecuteRequestAgainstSchemaAsync( + // arrange + var schema = """ type Query { - productById(id: ID!): Product @null + objs: [Object!]! } - type Product { + type Object { id: ID! + str: String! } - """, - request); + """; + var request = + """ + query { + objs { + id + str + } + } + """; - MatchMarkdownSnapshot(request, result); + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "objs": [ + { + "id": "1", + "str": "string" + }, + { + "id": "2", + "str": "string" + }, + { + "id": "3", + "str": "string" + } + ] + } + } + """); } [Fact] - public async Task Plural_ById() + public async Task Object_List_NullAtIndex() { - var request = """ - query { - productsById(ids: ["5", "6"]) { - id - } - } - """; - - var result = await ExecuteRequestAgainstSchemaAsync( + // arrange + var schema = """ type Query { - productsById(ids: [ID!]!): [Product!]! + objs: [Object] @null(atIndex: 1) } - type Product { + type Object { id: ID! } - """, - request); + """; + var request = + """ + query { + objs { + id + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); - MatchMarkdownSnapshot(request, result); + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "objs": [ + { + "id": "1" + }, + null, + { + "id": "2" + } + ] + } + } + """); } [Fact] - public async Task Plural_ById_Error() + public async Task Object_List_ErrorAtIndex() { - var request = """ - query { - productsById(ids: ["5", "6"]) { - id - } - } - """; - - var result = await ExecuteRequestAgainstSchemaAsync( + // arrange + var schema = """ type Query { - productsById(ids: [ID!]!): [Product!] @error + objs: [Object] @error(atIndex: 1) } - type Product { + type Object { id: ID! } - """, - request); + """; + var request = + """ + query { + objs { + id + } + } + """; - MatchMarkdownSnapshot(request, result); + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "objs", + 1 + ] + } + ], + "data": { + "objs": [ + { + "id": "1" + }, + null, + { + "id": "2" + } + ] + } + } + """); } [Fact] - public async Task Plural_ById_Error_At_Index() + public async Task Object_List_Property_NullAtIndex() { - var request = """ - query { - productsById(ids: ["5", "6"]) { - id - } - } - """; - - var result = await ExecuteRequestAgainstSchemaAsync( + // arrange + var schema = """ type Query { - productsById(ids: [ID!]!): [Product] @error(atIndex: 1) + objs: [Object!]! } - type Product { - id: ID! + type Object { + str: String @null(atIndex: 1) + } + """; + var request = + """ + query { + objs { + str + } } - """, - request); + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); - MatchMarkdownSnapshot(request, result); + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "objs": [ + { + "str": "string" + }, + { + "str": null + }, + { + "str": "string" + } + ] + } + } + """); } [Fact] - public async Task Plural_ById_Null() + public async Task Object_List_Property_ErrorAtIndex() { - var request = """ - query { - productsById(ids: ["5", "6"]) { - id - } - } - """; + // arrange + var schema = + """ + type Query { + objs: [Object!]! + } + + type Object { + str: String @error(atIndex: 1) + } + """; + var request = + """ + query { + objs { + str + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 3, + "column": 5 + } + ], + "path": [ + "objs", + 1, + "str" + ] + } + ], + "data": { + "objs": [ + { + "str": "string" + }, + { + "str": null + }, + { + "str": "string" + } + ] + } + } + """); + } - var result = await ExecuteRequestAgainstSchemaAsync( + #endregion + + #region Interfaces + + [Fact] + public async Task Interface() + { + // arrange + var schema = """ type Query { - productsById(ids: [ID!]!): [Product!] @null + intrface: Interface } - type Product { + interface Interface { + id: ID! + str: String! + } + + type Object implements Interface { id: ID! + str: String! + num: Int! + } + """; + var request = + """ + query { + intrface { + __typename + id + str + ... on Object { + num + } + } } - """, - request); + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); - MatchMarkdownSnapshot(request, result); + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "intrface": { + "__typename": "Object", + "id": "1", + "str": "string", + "num": 123 + } + } + } + """); } [Fact] - public async Task Plural_ById_Null_At_Index() + public async Task Interface_Null() { - var request = """ - query { - productsById(ids: ["5", "6"]) { - id - } - } - """; + // arrange + var schema = + """ + type Query { + intrface: Interface @null + } + + interface Interface { + id: ID! + str: String! + } + + type Object implements Interface { + id: ID! + str: String! + num: Int! + } + """; + var request = + """ + query { + intrface { + __typename + id + str + ... on Object { + num + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "intrface": null + } + } + """); + } - var result = await ExecuteRequestAgainstSchemaAsync( + [Fact] + public async Task Interface_Error() + { + // arrange + var schema = """ type Query { - productsById(ids: [ID!]!): [Product] @null(atIndex: 1) + intrface: Interface @error } - type Product { + interface Interface { + id: ID! + str: String! + } + + type Object implements Interface { id: ID! + str: String! + num: Int! + } + """; + var request = + """ + query { + intrface { + __typename + id + str + ... on Object { + num + } + } } - """, - request); + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); - MatchMarkdownSnapshot(request, result); + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "intrface" + ] + } + ], + "data": { + "intrface": null + } + } + """); } [Fact] - public async Task ListOfScalars() + public async Task Interface_List() { - var request = """ - query { - scalars - } - """; + // arrange + var schema = + """ + type Query { + interfaces: [Interface] + } + + interface Interface { + id: ID! + str: String! + } + + type Object implements Interface { + id: ID! + str: String! + num: Int! + } + """; + var request = + """ + query { + interfaces { + __typename + id + str + ... on Object { + num + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "interfaces": [ + { + "__typename": "Object", + "id": "1", + "str": "string", + "num": 123 + }, + { + "__typename": "Object", + "id": "2", + "str": "string", + "num": 123 + }, + { + "__typename": "Object", + "id": "3", + "str": "string", + "num": 123 + } + ] + } + } + """); + } - var result = await ExecuteRequestAgainstSchemaAsync( + [Fact] + public async Task Interface_List_Null() + { + // arrange + var schema = """ type Query { - scalars: [String!]! + interfaces: [Interface] @null + } + + interface Interface { + id: ID! + str: String! + } + + type Object implements Interface { + id: ID! + str: String! + num: Int! + } + """; + var request = + """ + query { + interfaces { + __typename + id + str + ... on Object { + num + } + } } - """, - request); + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); - MatchMarkdownSnapshot(request, result); + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "interfaces": null + } + } + """); } [Fact] - public async Task ListOfObjects() + public async Task Interface_List_Error() { - var request = """ - query { - objs { - id - str - } - } - """; + // arrange + var schema = + """ + type Query { + interfaces: [Interface] @error + } + + interface Interface { + id: ID! + str: String! + } + + type Object implements Interface { + id: ID! + str: String! + num: Int! + } + """; + var request = + """ + query { + interfaces { + __typename + id + str + ... on Object { + num + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); - var result = await ExecuteRequestAgainstSchemaAsync( + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "interfaces" + ] + } + ], + "data": { + "interfaces": null + } + } + """); + } + + [Fact] + public async Task Interface_List_NullAtIndex() + { + // arrange + var schema = """ type Query { - objs: [Object!]! + interfaces: [Interface] @null(atIndex: 1) } - type Object { + interface Interface { + id: ID! + str: String! + } + + type Object implements Interface { id: ID! str: String! + num: Int! + } + """; + var request = + """ + query { + interfaces { + __typename + id + str + ... on Object { + num + } + } } - """, - request); + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); - MatchMarkdownSnapshot(request, result); + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "interfaces": [ + { + "__typename": "Object", + "id": "1", + "str": "string", + "num": 123 + }, + null, + { + "__typename": "Object", + "id": "2", + "str": "string", + "num": 123 + } + ] + } + } + """); } [Fact] - public async Task ListOfObjects_Property_Error_At_Index() + public async Task Interface_List_ErrorAtIndex() { - var request = """ - query { - objs { - str - } - } - """; + // arrange + var schema = + """ + type Query { + interfaces: [Interface] @error(atIndex: 1) + } + + interface Interface { + id: ID! + str: String! + } + + type Object implements Interface { + id: ID! + str: String! + num: Int! + } + """; + var request = + """ + query { + interfaces { + __typename + id + str + ... on Object { + num + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "interfaces", + 1 + ] + } + ], + "data": { + "interfaces": [ + { + "__typename": "Object", + "id": "1", + "str": "string", + "num": 123 + }, + null, + { + "__typename": "Object", + "id": "2", + "str": "string", + "num": 123 + } + ] + } + } + """); + } - var result = await ExecuteRequestAgainstSchemaAsync( + [Fact] + public async Task Interface_List_Property_NullAtIndex() + { + // arrange + var schema = """ type Query { - objs: [Object!]! + interfaces: [Interface] } - type Object { - str: String @error(atIndex: 1) + interface Interface { + id: ID! + str: String! } - """, - request); - MatchMarkdownSnapshot(request, result); + type Object implements Interface { + id: ID! + str: String! + num: Int @null(atIndex: 1) + } + """; + var request = + """ + query { + interfaces { + __typename + id + str + ... on Object { + num + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "interfaces": [ + { + "__typename": "Object", + "id": "1", + "str": "string", + "num": 123 + }, + { + "__typename": "Object", + "id": "2", + "str": "string", + "num": null + }, + { + "__typename": "Object", + "id": "3", + "str": "string", + "num": 123 + } + ] + } + } + """); } [Fact] - public async Task ListOfObjects_Property_Null_At_Index() + public async Task Interface_List_Property_ErrorAtIndex() { - var request = """ - query { - objs { - str - } - } - """; + // arrange + var schema = + """ + type Query { + interfaces: [Interface] + } + + interface Interface { + id: ID! + str: String! + } + + type Object implements Interface { + id: ID! + str: String! + num: Int @error(atIndex: 1) + } + """; + var request = + """ + query { + interfaces { + __typename + id + str + ... on Object { + num + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 7, + "column": 7 + } + ], + "path": [ + "interfaces", + 1, + "num" + ] + } + ], + "data": { + "interfaces": [ + { + "__typename": "Object", + "id": "1", + "str": "string", + "num": 123 + }, + { + "__typename": "Object", + "id": "2", + "str": "string", + "num": null + }, + { + "__typename": "Object", + "id": "3", + "str": "string", + "num": 123 + } + ] + } + } + """); + } + + #endregion - var result = await ExecuteRequestAgainstSchemaAsync( + #region Union + + [Fact] + public async Task Union() + { + // arrange + var schema = """ type Query { - objs: [Object!]! + unon: Union } + union Union = Object + type Object { - str: String @null(atIndex: 1) + id: ID! + str: String! + } + """; + var request = + """ + query { + unon { + __typename + ... on Object { + id + str + } + } } - """, - request); + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); - MatchMarkdownSnapshot(request, result); + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "unon": { + "__typename": "Object", + "id": "1", + "str": "string" + } + } + } + """); } + [Fact] + public async Task Union_Null() + { + // arrange + var schema = + """ + type Query { + unon: Union @null + } + + union Union = Object + + type Object { + id: ID! + str: String! + } + """; + var request = + """ + query { + unon { + __typename + ... on Object { + id + str + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "unon": null + } + } + """); + } + + [Fact] + public async Task Union_Error() + { + // arrange + var schema = + """ + type Query { + unon: Union @error + } + + union Union = Object + + type Object { + id: ID! + str: String! + } + """; + var request = + """ + query { + unon { + __typename + ... on Object { + id + str + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "unon" + ] + } + ], + "data": { + "unon": null + } + } + """); + } + + [Fact] + public async Task Union_List() + { + // arrange + var schema = + """ + type Query { + unions: [Union] + } + + union Union = Object + + type Object { + id: ID! + str: String! + } + """; + var request = + """ + query { + unions { + __typename + ... on Object { + id + str + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "unions": [ + { + "__typename": "Object", + "id": "1", + "str": "string" + }, + { + "__typename": "Object", + "id": "2", + "str": "string" + }, + { + "__typename": "Object", + "id": "3", + "str": "string" + } + ] + } + } + """); + } + + [Fact] + public async Task Union_List_Null() + { + // arrange + var schema = + """ + type Query { + unions: [Union] @null + } + + union Union = Object + + type Object { + id: ID! + str: String! + } + """; + var request = + """ + query { + unions { + __typename + ... on Object { + id + str + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "unions": null + } + } + """); + } + + [Fact] + public async Task Union_List_Error() + { + // arrange + var schema = + """ + type Query { + unions: [Union] @error + } + + union Union = Object + + type Object { + id: ID! + str: String! + } + """; + var request = + """ + query { + unions { + __typename + ... on Object { + id + str + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "unions" + ] + } + ], + "data": { + "unions": null + } + } + """); + } + + [Fact] + public async Task Union_List_NullAtIndex() + { + // arrange + var schema = + """ + type Query { + unions: [Union] @null(atIndex: 1) + } + + union Union = Object + + type Object { + id: ID! + str: String! + } + """; + var request = + """ + query { + unions { + __typename + ... on Object { + id + str + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "unions": [ + { + "__typename": "Object", + "id": "1", + "str": "string" + }, + null, + { + "__typename": "Object", + "id": "2", + "str": "string" + } + ] + } + } + """); + } + + [Fact] + public async Task Union_List_ErrorAtIndex() + { + // arrange + var schema = + """ + type Query { + unions: [Union] @error(atIndex: 1) + } + + union Union = Object + + type Object { + id: ID! + str: String! + } + """; + var request = + """ + query { + unions { + __typename + ... on Object { + id + str + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "unions", + 1 + ] + } + ], + "data": { + "unions": [ + { + "__typename": "Object", + "id": "1", + "str": "string" + }, + null, + { + "__typename": "Object", + "id": "2", + "str": "string" + } + ] + } + } + """); + } + + [Fact] + public async Task Union_List_Property_NullAtIndex() + { + // arrange + var schema = + """ + type Query { + unions: [Union] + } + + union Union = Object + + type Object { + id: ID! + str: String @null(atIndex: 1) + } + """; + var request = + """ + query { + unions { + __typename + ... on Object { + id + str + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "unions": [ + { + "__typename": "Object", + "id": "1", + "str": "string" + }, + { + "__typename": "Object", + "id": "2", + "str": null + }, + { + "__typename": "Object", + "id": "3", + "str": "string" + } + ] + } + } + """); + } + + [Fact] + public async Task Union_List_Property_ErrorAtIndex() + { + // arrange + var schema = + """ + type Query { + unions: [Union] + } + + union Union = Object + + type Object { + id: ID! + str: String @error(atIndex: 1) + } + """; + var request = + """ + query { + unions { + __typename + ... on Object { + id + str + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 6, + "column": 7 + } + ], + "path": [ + "unions", + 1, + "str" + ] + } + ], + "data": { + "unions": [ + { + "__typename": "Object", + "id": "1", + "str": "string" + }, + { + "__typename": "Object", + "id": "2", + "str": null + }, + { + "__typename": "Object", + "id": "3", + "str": "string" + } + ] + } + } + """); + } + + #endregion + + #region Scalars + + [Fact] + public async Task Scalar() + { + // arrange + var schema = + """ + type Query { + str: String + } + """; + var request = + """ + query { + str + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "str": "string" + } + } + """); + } + + [Fact] + public async Task Scalar_Null() + { + // arrange + var schema = + """ + type Query { + str: String @null + } + """; + var request = + """ + query { + str + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "str": null + } + } + """); + } + + [Fact] + public async Task Scalar_Error() + { + // arrange + var schema = + """ + type Query { + str: String @error + } + """; + var request = + """ + query { + str + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "str" + ] + } + ], + "data": { + "str": null + } + } + """); + } + + [Fact] + public async Task Scalar_List() + { + // arrange + var schema = + """ + type Query { + scalars: [String!]! + } + """; + var request = + """ + query { + scalars + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "scalars": [ + "string", + "string", + "string" + ] + } + } + """); + } + + [Fact] + public async Task Scalar_List_Null() + { + // arrange + var schema = + """ + type Query { + scalars: [String!] @null + } + """; + var request = + """ + query { + scalars + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "scalars": null + } + } + """); + } + + [Fact] + public async Task Scalar_List_Error() + { + // arrange + var schema = + """ + type Query { + scalars: [String!] @error + } + """; + var request = + """ + query { + scalars + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "scalars" + ] + } + ], + "data": { + "scalars": null + } + } + """); + } + + [Fact] + public async Task Scalar_List_NullAtIndex() + { + // arrange + var schema = + """ + type Query { + scalars: [String] @null(atIndex: 1) + } + """; + var request = + """ + query { + scalars + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "scalars": [ + "string", + null, + "string" + ] + } + } + """); + } + + [Fact] + public async Task Scalar_List_ErrorAtIndex() + { + // arrange + var schema = + """ + type Query { + scalars: [String] @error(atIndex: 1) + } + """; + var request = + """ + query { + scalars + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "scalars", + 1 + ] + } + ], + "data": { + "scalars": [ + "string", + null, + "string" + ] + } + } + """); + } + + #endregion + + #region Enums + + [Fact] + public async Task Enum() + { + // arrange + var schema = + """ + type Query { + enm: MyEnum + } + + enum MyEnum { + VALUE + } + """; + var request = + """ + query { + enm + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "enm": "VALUE" + } + } + """); + } + + [Fact] + public async Task Enum_Null() + { + // arrange + var schema = + """ + type Query { + enm: MyEnum @null + } + + enum MyEnum { + VALUE + } + """; + var request = + """ + query { + enm + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "enm": null + } + } + """); + } + + [Fact] + public async Task Enum_Error() + { + // arrange + var schema = + """ + type Query { + enm: MyEnum @error + } + + enum MyEnum { + VALUE + } + """; + var request = + """ + query { + enm + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "enm" + ] + } + ], + "data": { + "enm": null + } + } + """); + } + + [Fact] + public async Task Enum_List() + { + // arrange + var schema = + """ + type Query { + enums: [MyEnum] + } + + enum MyEnum { + VALUE + } + """; + var request = + """ + query { + enums + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "enums": [ + "VALUE", + "VALUE", + "VALUE" + ] + } + } + """); + } + + [Fact] + public async Task Enum_List_NullAtIndex() + { + // arrange + var schema = + """ + type Query { + enums: [MyEnum] @null(atIndex: 1) + } + + enum MyEnum { + VALUE + } + """; + var request = + """ + query { + enums + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "enums": [ + "VALUE", + null, + "VALUE" + ] + } + } + """); + } + + [Fact] + public async Task Enum_List_ErrorAtIndex() + { + // arrange + var schema = + """ + type Query { + enums: [MyEnum] @error(atIndex: 1) + } + + enum MyEnum { + VALUE + } + """; + var request = + """ + query { + enums + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "enums", + 1 + ] + } + ], + "data": { + "enums": [ + "VALUE", + null, + "VALUE" + ] + } + } + """); + } + + #endregion + + #region byId + + [Fact] + public async Task ById() + { + // arrange + var schema = + """ + type Query { + productById(id: ID!): Product + } + + type Product { + id: ID! + } + """; + var request = + """ + query { + productById(id: "5") { + id + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "productById": { + "id": "5" + } + } + } + """); + } + + [Fact] + public async Task ById_Null() + { + // arrange + var schema = + """ + type Query { + productById(id: ID!): Product @null + } + + type Product { + id: ID! + } + """; + var request = + """ + query { + productById(id: "5") { + id + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "productById": null + } + } + """); + } + + [Fact] + public async Task ById_Error() + { + // arrange + var schema = + """ + type Query { + productById(id: ID!): Product @error + } + + type Product { + id: ID! + } + """; + var request = + """ + query { + productById(id: "5") { + id + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "productById" + ] + } + ], + "data": { + "productById": null + } + } + """); + } + + #endregion + + #region byIds + + [Fact] + public async Task ByIds() + { + // arrange + var schema = + """ + type Query { + productsById(ids: [ID!]!): [Product!]! + } + + type Product { + id: ID! + } + """; + var request = + """ + query { + productsById(ids: ["5", "6"]) { + id + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "productsById": [ + { + "id": "5" + }, + { + "id": "6" + } + ] + } + } + """); + } + + [Fact] + public async Task ByIds_Null() + { + // arrange + var schema = + """ + type Query { + productsById(ids: [ID!]!): [Product!] @null + } + + type Product { + id: ID! + } + """; + var request = + """ + query { + productsById(ids: ["5", "6"]) { + id + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "productsById": null + } + } + """); + } + + [Fact] + public async Task ByIds_Error() + { + // arrange + var schema = + """ + type Query { + productsById(ids: [ID!]!): [Product!] @error + } + + type Product { + id: ID! + } + """; + var request = + """ + query { + productsById(ids: ["5", "6"]) { + id + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "productsById" + ] + } + ], + "data": { + "productsById": null + } + } + """); + } + + [Fact] + public async Task ByIds_NullAtIndex() + { + // arrange + var schema = + """ + type Query { + productsById(ids: [ID!]!): [Product] @null(atIndex: 1) + } + + type Product { + id: ID! + } + """; + var request = + """ + query { + productsById(ids: ["5", "6"]) { + id + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "productsById": [ + { + "id": "5" + }, + null + ] + } + } + """); + } + + [Fact] + public async Task ByIds_ErrorAtIndex() + { + // arrange + var schema = + """ + type Query { + productsById(ids: [ID!]!): [Product] @error(atIndex: 1) + } + + type Product { + id: ID! + } + """; + var request = + """ + query { + productsById(ids: ["5", "6"]) { + id + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "productsById", + 1 + ] + } + ], + "data": { + "productsById": [ + { + "id": "5" + }, + null + ] + } + } + """); + } + + #endregion + private static async Task ExecuteRequestAgainstSchemaAsync( - string schemaText, - string request) + string request, + string schemaText) { var executor = await new ServiceCollection() .AddGraphQL() diff --git a/src/HotChocolate/Fusion/test/Core.Tests/SubgraphErrorTests.cs b/src/HotChocolate/Fusion/test/Core.Tests/SubgraphErrorTests.cs index fbd994fe52a..02e28226469 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/SubgraphErrorTests.cs +++ b/src/HotChocolate/Fusion/test/Core.Tests/SubgraphErrorTests.cs @@ -3094,7 +3094,7 @@ type Query { using var subgraphs = new TestSubgraphCollection(output, [subgraph]); var executor = await subgraphs.GetExecutorAsync( - configureBuilder: builder => + configure: builder => builder.AddErrorFilter(error => error.WithMessage("REPLACED MESSAGE").WithCode("CUSTOM_CODE"))); var request = """ query { diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.ListOfObjects.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.ListOfObjects.md deleted file mode 100644 index 5fc24ecde57..00000000000 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.ListOfObjects.md +++ /dev/null @@ -1,36 +0,0 @@ -# ListOfObjects - -## Result - -```json -{ - "data": { - "objs": [ - { - "id": "1", - "str": "string" - }, - { - "id": "2", - "str": "string" - }, - { - "id": "3", - "str": "string" - } - ] - } -} -``` - -## Request - -```graphql -{ - objs { - id - str - } -} -``` - diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.ListOfObjects_Property_Error_At_Index.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.ListOfObjects_Property_Error_At_Index.md deleted file mode 100644 index 1592a58a9a4..00000000000 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.ListOfObjects_Property_Error_At_Index.md +++ /dev/null @@ -1,48 +0,0 @@ -# ListOfObjects_Property_Error_At_Index - -## Result - -```json -{ - "errors": [ - { - "message": "Unexpected Execution Error", - "locations": [ - { - "line": 3, - "column": 5 - } - ], - "path": [ - "objs", - 1, - "str" - ] - } - ], - "data": { - "objs": [ - { - "str": "string" - }, - { - "str": null - }, - { - "str": "string" - } - ] - } -} -``` - -## Request - -```graphql -{ - objs { - str - } -} -``` - diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.ListOfObjects_Property_Null_At_Index.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.ListOfObjects_Property_Null_At_Index.md deleted file mode 100644 index 4f558a6f36e..00000000000 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.ListOfObjects_Property_Null_At_Index.md +++ /dev/null @@ -1,32 +0,0 @@ -# ListOfObjects_Property_Null_At_Index - -## Result - -```json -{ - "data": { - "objs": [ - { - "str": "string" - }, - { - "str": null - }, - { - "str": "string" - } - ] - } -} -``` - -## Request - -```graphql -{ - objs { - str - } -} -``` - diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.ListOfScalars.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.ListOfScalars.md deleted file mode 100644 index 4f96b4f8e1c..00000000000 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.ListOfScalars.md +++ /dev/null @@ -1,24 +0,0 @@ -# ListOfScalars - -## Result - -```json -{ - "data": { - "scalars": [ - "string", - "string", - "string" - ] - } -} -``` - -## Request - -```graphql -{ - scalars -} -``` - diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Object.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Object.md deleted file mode 100644 index a43dea186c2..00000000000 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Object.md +++ /dev/null @@ -1,26 +0,0 @@ -# Object - -## Result - -```json -{ - "data": { - "obj": { - "id": "1", - "str": "string" - } - } -} -``` - -## Request - -```graphql -{ - obj { - id - str - } -} -``` - diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Plural_ById.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Plural_ById.md deleted file mode 100644 index f2cc900662b..00000000000 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Plural_ById.md +++ /dev/null @@ -1,29 +0,0 @@ -# Plural_ById - -## Result - -```json -{ - "data": { - "productsById": [ - { - "id": "5" - }, - { - "id": "6" - } - ] - } -} -``` - -## Request - -```graphql -{ - productsById(ids: [ "5", "6" ]) { - id - } -} -``` - diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Plural_ById_Error.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Plural_ById_Error.md deleted file mode 100644 index 39e73d2f24c..00000000000 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Plural_ById_Error.md +++ /dev/null @@ -1,36 +0,0 @@ -# Plural_ById_Error - -## Result - -```json -{ - "errors": [ - { - "message": "Unexpected Execution Error", - "locations": [ - { - "line": 2, - "column": 3 - } - ], - "path": [ - "productsById" - ] - } - ], - "data": { - "productsById": null - } -} -``` - -## Request - -```graphql -{ - productsById(ids: [ "5", "6" ]) { - id - } -} -``` - diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Plural_ById_Error_At_Index.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Plural_ById_Error_At_Index.md deleted file mode 100644 index f7f39976a46..00000000000 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Plural_ById_Error_At_Index.md +++ /dev/null @@ -1,42 +0,0 @@ -# Plural_ById_Error_At_Index - -## Result - -```json -{ - "errors": [ - { - "message": "Unexpected Execution Error", - "locations": [ - { - "line": 2, - "column": 3 - } - ], - "path": [ - "productsById", - 1 - ] - } - ], - "data": { - "productsById": [ - { - "id": "5" - }, - null - ] - } -} -``` - -## Request - -```graphql -{ - productsById(ids: [ "5", "6" ]) { - id - } -} -``` - diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Plural_ById_Null.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Plural_ById_Null.md deleted file mode 100644 index d7e2df9b9cb..00000000000 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Plural_ById_Null.md +++ /dev/null @@ -1,22 +0,0 @@ -# Plural_ById_Null - -## Result - -```json -{ - "data": { - "productsById": null - } -} -``` - -## Request - -```graphql -{ - productsById(ids: [ "5", "6" ]) { - id - } -} -``` - diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Plural_ById_Null_At_Index.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Plural_ById_Null_At_Index.md deleted file mode 100644 index a212820065c..00000000000 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Plural_ById_Null_At_Index.md +++ /dev/null @@ -1,27 +0,0 @@ -# Plural_ById_Null_At_Index - -## Result - -```json -{ - "data": { - "productsById": [ - { - "id": "5" - }, - null - ] - } -} -``` - -## Request - -```graphql -{ - productsById(ids: [ "5", "6" ]) { - id - } -} -``` - diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Singular_ById.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Singular_ById.md deleted file mode 100644 index 4208d21ed24..00000000000 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Singular_ById.md +++ /dev/null @@ -1,24 +0,0 @@ -# Singular_ById - -## Result - -```json -{ - "data": { - "productById": { - "id": "5" - } - } -} -``` - -## Request - -```graphql -{ - productById(id: "5") { - id - } -} -``` - diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Singular_ById_Error.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Singular_ById_Error.md deleted file mode 100644 index 78fd5eed27f..00000000000 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Singular_ById_Error.md +++ /dev/null @@ -1,36 +0,0 @@ -# Singular_ById_Error - -## Result - -```json -{ - "errors": [ - { - "message": "Unexpected Execution Error", - "locations": [ - { - "line": 2, - "column": 3 - } - ], - "path": [ - "productById" - ] - } - ], - "data": { - "productById": null - } -} -``` - -## Request - -```graphql -{ - productById(id: "5") { - id - } -} -``` - diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Singular_ById_Null.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Singular_ById_Null.md deleted file mode 100644 index 330c90f56d2..00000000000 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/AutomaticMockingTests.Singular_ById_Null.md +++ /dev/null @@ -1,22 +0,0 @@ -# Singular_ById_Null - -## Result - -```json -{ - "data": { - "productById": null - } -} -``` - -## Request - -```graphql -{ - productById(id: "5") { - id - } -} -``` - diff --git a/src/HotChocolate/Fusion/test/Shared/AutomaticMocking/MockFieldMiddleware.cs b/src/HotChocolate/Fusion/test/Shared/AutomaticMocking/MockFieldMiddleware.cs index 14547b2e6c7..3caae3590a5 100644 --- a/src/HotChocolate/Fusion/test/Shared/AutomaticMocking/MockFieldMiddleware.cs +++ b/src/HotChocolate/Fusion/test/Shared/AutomaticMocking/MockFieldMiddleware.cs @@ -32,9 +32,9 @@ public ValueTask InvokeAsync(IMiddlewareContext context) { context.ReportError(CreateError(context, nullIndex)); } - else if (namedFieldType.IsScalarType()) + else if (namedFieldType.IsScalarType() || namedFieldType.IsEnumType()) { - var currentListIndex = context.Parent().Index; + var currentListIndex = context.Parent()?.Index; if (currentListIndex.HasValue && currentListIndex == nullIndex) { throw new GraphQLException(CreateError(context)); @@ -52,9 +52,9 @@ public ValueTask InvokeAsync(IMiddlewareContext context) { nullIndex = nullDirective.AtIndex; - if (namedFieldType.IsScalarType()) + if (namedFieldType.IsScalarType() || namedFieldType.IsEnumType()) { - var currentListIndex = context.Parent().Index; + var currentListIndex = context.Parent()?.Index; if (currentListIndex.HasValue && currentListIndex == nullIndex) { context.Result = null; @@ -113,17 +113,39 @@ public ValueTask InvokeAsync(IMiddlewareContext context) { context.Result = CreateObject(); } + else if (fieldType.IsInterfaceType() || fieldType.IsUnionType()) + { + var possibleTypes = context.Schema.GetPossibleTypes(namedFieldType); + + context.ValueType = possibleTypes.First(); + context.Result = CreateObject(); + } else if (fieldType.IsListType()) { if (namedFieldType.IsObjectType()) { context.Result = CreateListOfObjects(null, nullIndex); } + else if (namedFieldType.IsInterfaceType() || namedFieldType.IsUnionType()) + { + var possibleTypes = context.Schema.GetPossibleTypes(namedFieldType); + + context.ValueType = possibleTypes.First(); + context.Result = CreateListOfObjects(null, nullIndex); + } + else if(namedFieldType is EnumType enumType) + { + context.Result = CreateListOfEnums(enumType, nullIndex); + } else { context.Result = CreateListOfScalars(namedFieldType, nullIndex); } } + else if(namedFieldType is EnumType enumType) + { + context.Result = CreateEnumValue(enumType); + } else { context.Result = CreateScalarValue(namedFieldType); @@ -145,6 +167,11 @@ public ValueTask InvokeAsync(IMiddlewareContext context) }; } + private object? CreateEnumValue(EnumType enumType) + { + return enumType.Values.FirstOrDefault()?.Value; + } + private object CreateObject(object? id = null, int? index = null) { var finalId = id ?? ++_idCounter; @@ -159,6 +186,13 @@ private object CreateObject(object? id = null, int? index = null) .ToArray(); } + private object?[] CreateListOfEnums(EnumType enumType, int? nullIndex) + { + return Enumerable.Range(0, DefaultListSize) + .Select(index => nullIndex == index ? null : CreateEnumValue(enumType)) + .ToArray(); + } + private object?[] CreateListOfObjects(object[]? ids, int? nullIndex) { if (ids is not null) @@ -169,7 +203,7 @@ private object CreateObject(object? id = null, int? index = null) } return Enumerable.Range(0, DefaultListSize) - .Select(index => CreateObject(null, index)) + .Select(index => nullIndex == index ? null : CreateObject(null, index)) .ToArray(); } diff --git a/src/HotChocolate/Fusion/test/Shared/TestSubgraph.cs b/src/HotChocolate/Fusion/test/Shared/TestSubgraph.cs index cd84c8bdcd9..262d57a39cb 100644 --- a/src/HotChocolate/Fusion/test/Shared/TestSubgraph.cs +++ b/src/HotChocolate/Fusion/test/Shared/TestSubgraph.cs @@ -18,14 +18,14 @@ public static Task CreateAsync( string schemaText, bool isOffline = false) => CreateAsync( - configureBuilder: builder => builder + configure: builder => builder .AddDocumentFromString(schemaText) .AddResolverMocking() .AddTestDirectives(), isOffline: isOffline); public static async Task CreateAsync( - Action configureBuilder, + Action configure, string extensions = "", bool isOffline = false) { @@ -39,7 +39,7 @@ public static async Task CreateAsync( .AddRouting() .AddGraphQLServer(disableDefaultSecurity: true); - configureBuilder(builder); + configure(builder); }, app => { diff --git a/src/HotChocolate/Fusion/test/Shared/TestSubgraphCollection.cs b/src/HotChocolate/Fusion/test/Shared/TestSubgraphCollection.cs index da9f8cd6229..621c2ce9167 100644 --- a/src/HotChocolate/Fusion/test/Shared/TestSubgraphCollection.cs +++ b/src/HotChocolate/Fusion/test/Shared/TestSubgraphCollection.cs @@ -21,11 +21,11 @@ public IHttpClientFactory GetHttpClientFactory() public async Task GetExecutorAsync( FusionFeatureCollection? features = null, - Action? configureBuilder = null) + Action? configure = null) { var fusionGraph = await GetFusionGraphAsync(features); - return await GetExecutorAsync(fusionGraph, configureBuilder); + return await GetExecutorAsync(fusionGraph, configure); } public async Task GetFusionGraphAsync(FusionFeatureCollection? features = null) @@ -60,7 +60,7 @@ public void Dispose() private async Task GetExecutorAsync( Skimmed.SchemaDefinition fusionGraph, - Action? configureBuilder = null) + Action? configure = null) { var httpClientFactory = GetHttpClientFactory(); @@ -69,7 +69,7 @@ private async Task GetExecutorAsync( .AddFusionGatewayServer() .ConfigureFromDocument(SchemaFormatter.FormatAsDocument(fusionGraph)); - configureBuilder?.Invoke(builder); + configure?.Invoke(builder); return await builder.BuildRequestExecutorAsync(); } From 0fa9b52414d9172d9c11319cf92d0be70f18ee7d Mon Sep 17 00:00:00 2001 From: Tobias Tengler <45513122+tobias-tengler@users.noreply.github.com> Date: Sat, 9 Nov 2024 16:06:57 +0100 Subject: [PATCH 108/154] Only cache authorization policies if permitted by policy provider (#7705) --- .../AuthorizationPolicyCache.cs | 41 +----- .../DefaultAuthorizationHandler.cs | 75 +++++++++- .../AuthorizationPolicyProviderTess.cs | 137 ++++++++++++++++++ 3 files changed, 211 insertions(+), 42 deletions(-) create mode 100644 src/HotChocolate/AspNetCore/test/AspNetCore.Authorization.Tests/AuthorizationPolicyProviderTess.cs diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore.Authorization/AuthorizationPolicyCache.cs b/src/HotChocolate/AspNetCore/src/AspNetCore.Authorization/AuthorizationPolicyCache.cs index 258288c701d..591c326aa1b 100644 --- a/src/HotChocolate/AspNetCore/src/AspNetCore.Authorization/AuthorizationPolicyCache.cs +++ b/src/HotChocolate/AspNetCore/src/AspNetCore.Authorization/AuthorizationPolicyCache.cs @@ -4,48 +4,21 @@ namespace HotChocolate.AspNetCore.Authorization; -internal sealed class AuthorizationPolicyCache(IAuthorizationPolicyProvider policyProvider) +internal sealed class AuthorizationPolicyCache { - private readonly ConcurrentDictionary> _cache = new(); + private readonly ConcurrentDictionary _cache = new(); - public Task GetOrCreatePolicyAsync(AuthorizeDirective directive) + public AuthorizationPolicy? LookupPolicy(AuthorizeDirective directive) { var cacheKey = directive.GetPolicyCacheKey(); - return _cache.GetOrAdd(cacheKey, _ => BuildAuthorizationPolicy(directive.Policy, directive.Roles)); + return _cache.GetValueOrDefault(cacheKey); } - private async Task BuildAuthorizationPolicy( - string? policyName, - IReadOnlyList? roles) + public void CachePolicy(AuthorizeDirective directive, AuthorizationPolicy policy) { - var policyBuilder = new AuthorizationPolicyBuilder(); - - if (!string.IsNullOrWhiteSpace(policyName)) - { - var policy = await policyProvider.GetPolicyAsync(policyName).ConfigureAwait(false); - - if (policy is not null) - { - policyBuilder = policyBuilder.Combine(policy); - } - else - { - throw new MissingAuthorizationPolicyException(policyName); - } - } - else - { - var defaultPolicy = await policyProvider.GetDefaultPolicyAsync().ConfigureAwait(false); - - policyBuilder = policyBuilder.Combine(defaultPolicy); - } - - if (roles is not null) - { - policyBuilder = policyBuilder.RequireRole(roles); - } + var cacheKey = directive.GetPolicyCacheKey(); - return policyBuilder.Build(); + _cache.TryAdd(cacheKey, policy); } } diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore.Authorization/DefaultAuthorizationHandler.cs b/src/HotChocolate/AspNetCore/src/AspNetCore.Authorization/DefaultAuthorizationHandler.cs index c36ae4fb598..bcc15389477 100644 --- a/src/HotChocolate/AspNetCore/src/AspNetCore.Authorization/DefaultAuthorizationHandler.cs +++ b/src/HotChocolate/AspNetCore/src/AspNetCore.Authorization/DefaultAuthorizationHandler.cs @@ -12,7 +12,9 @@ namespace HotChocolate.AspNetCore.Authorization; internal sealed class DefaultAuthorizationHandler : IAuthorizationHandler { private readonly IAuthorizationService _authSvc; - private readonly AuthorizationPolicyCache _policyCache; + private readonly IAuthorizationPolicyProvider _authorizationPolicyProvider; + private readonly AuthorizationPolicyCache _authorizationPolicyCache; + private readonly bool _canCachePolicies; /// /// Initializes a new instance . @@ -20,21 +22,29 @@ internal sealed class DefaultAuthorizationHandler : IAuthorizationHandler /// /// The authorization service. /// - /// + /// + /// The authorization policy provider. + /// + /// /// The authorization policy cache. /// /// /// is null. - /// is null. + /// is null. /// public DefaultAuthorizationHandler( IAuthorizationService authorizationService, - AuthorizationPolicyCache policyCache) + IAuthorizationPolicyProvider authorizationPolicyProvider, + AuthorizationPolicyCache authorizationPolicyCache) { _authSvc = authorizationService ?? throw new ArgumentNullException(nameof(authorizationService)); - _policyCache = policyCache ?? - throw new ArgumentNullException(nameof(policyCache)); + _authorizationPolicyProvider = authorizationPolicyProvider ?? + throw new ArgumentNullException(nameof(authorizationPolicyProvider)); + _authorizationPolicyCache = authorizationPolicyCache ?? + throw new ArgumentNullException(nameof(authorizationPolicyCache)); + + _canCachePolicies = _authorizationPolicyProvider.AllowsCachingPolicies; } /// @@ -123,9 +133,24 @@ private async ValueTask AuthorizeAsync( { try { - var combinedPolicy = await _policyCache.GetOrCreatePolicyAsync(directive); + AuthorizationPolicy? authorizationPolicy = null; + + if (_canCachePolicies) + { + authorizationPolicy = _authorizationPolicyCache.LookupPolicy(directive); + } + + if (authorizationPolicy is null) + { + authorizationPolicy = await BuildAuthorizationPolicy(directive.Policy, directive.Roles); - var result = await _authSvc.AuthorizeAsync(user, context, combinedPolicy).ConfigureAwait(false); + if (_canCachePolicies) + { + _authorizationPolicyCache.CachePolicy(directive, authorizationPolicy); + } + } + + var result = await _authSvc.AuthorizeAsync(user, context, authorizationPolicy).ConfigureAwait(false); return result.Succeeded ? AuthorizeResult.Allowed @@ -137,6 +162,40 @@ private async ValueTask AuthorizeAsync( } } + private async Task BuildAuthorizationPolicy( + string? policyName, + IReadOnlyList? roles) + { + var policyBuilder = new AuthorizationPolicyBuilder(); + + if (!string.IsNullOrWhiteSpace(policyName)) + { + var policy = await _authorizationPolicyProvider.GetPolicyAsync(policyName).ConfigureAwait(false); + + if (policy is not null) + { + policyBuilder = policyBuilder.Combine(policy); + } + else + { + throw new MissingAuthorizationPolicyException(policyName); + } + } + else + { + var defaultPolicy = await _authorizationPolicyProvider.GetDefaultPolicyAsync().ConfigureAwait(false); + + policyBuilder = policyBuilder.Combine(defaultPolicy); + } + + if (roles is not null) + { + policyBuilder = policyBuilder.RequireRole(roles); + } + + return policyBuilder.Build(); + } + private static UserState GetUserState(IDictionary contextData) { if (contextData.TryGetValue(WellKnownContextData.UserState, out var value) && diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Authorization.Tests/AuthorizationPolicyProviderTess.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Authorization.Tests/AuthorizationPolicyProviderTess.cs new file mode 100644 index 00000000000..230b8ca1e21 --- /dev/null +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Authorization.Tests/AuthorizationPolicyProviderTess.cs @@ -0,0 +1,137 @@ +using System.Net; +using System.Security.Claims; +using HotChocolate.AspNetCore.Tests.Utilities; +using HotChocolate.Execution.Configuration; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authorization.Infrastructure; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; + +namespace HotChocolate.AspNetCore.Authorization; + +public class AuthorizationPolicyProviderTess(TestServerFactory serverFactory) : ServerTestBase(serverFactory) +{ + [Fact] + public async Task Policies_Are_Cached_If_PolicyProvider_Allows_Caching() + { + // arrange + var policyProvider = new CustomAuthorizationPolicyProvider(allowsCaching: true); + + var server = CreateTestServer( + builder => + { + builder.Services.AddSingleton(_ => policyProvider); + + builder + .AddQueryType() + .AddAuthorization(); + }, + context => + { + var identity = new ClaimsIdentity("testauth"); + context.User = new ClaimsPrincipal(identity); + }); + + // act + var result1 = + await server.PostAsync(new ClientQueryRequest { Query = "{ bar }", }); + var result2 = + await server.PostAsync(new ClientQueryRequest { Query = "{ bar }", }); + + // assert + Assert.Equal(HttpStatusCode.OK, result1.StatusCode); + Assert.Null(result1.Errors); + Assert.Equal(HttpStatusCode.OK, result2.StatusCode); + Assert.Null(result2.Errors); + Assert.Equal(1, policyProvider.InvocationsOfGetPolicyAsync); + } + + [Fact] + public async Task Policies_Are_Not_Cached_If_PolicyProvider_Disallows_Caching() + { + // arrange + var policyProvider = new CustomAuthorizationPolicyProvider(allowsCaching: false); + + var server = CreateTestServer( + builder => + { + builder.Services.AddSingleton(_ => policyProvider); + + builder + .AddQueryType() + .AddAuthorization(); + }, + context => + { + var identity = new ClaimsIdentity("testauth"); + context.User = new ClaimsPrincipal(identity); + }); + + // act + var result1 = + await server.PostAsync(new ClientQueryRequest { Query = "{ bar }", }); + var result2 = + await server.PostAsync(new ClientQueryRequest { Query = "{ bar }", }); + + // assert + Assert.Equal(HttpStatusCode.OK, result1.StatusCode); + Assert.Null(result1.Errors); + Assert.Equal(HttpStatusCode.OK, result2.StatusCode); + Assert.Null(result2.Errors); + Assert.Equal(2, policyProvider.InvocationsOfGetPolicyAsync); + } + + public class Query + { + [HotChocolate.Authorization.Authorize(Policy = "policy")] + public string Bar() => "bar"; + } + + private TestServer CreateTestServer( + Action build, + Action configureUser) + { + return ServerFactory.Create( + services => + { + build(services + .AddRouting() + .AddGraphQLServer() + .AddHttpRequestInterceptor( + (context, requestExecutor, requestBuilder, cancellationToken) => + { + configureUser(context); + return default; + })); + }, + app => + { + app.UseRouting(); + app.UseEndpoints(b => b.MapGraphQL()); + }); + } + + public class CustomAuthorizationPolicyProvider(bool allowsCaching) : IAuthorizationPolicyProvider + { + public int InvocationsOfGetPolicyAsync { get; private set; } + + public Task GetPolicyAsync(string policyName) + { + InvocationsOfGetPolicyAsync++; + + var policy = new AuthorizationPolicyBuilder() + .AddRequirements(new DenyAnonymousAuthorizationRequirement()) + .Build(); + + return Task.FromResult(policy); + } + + public Task GetDefaultPolicyAsync() => throw new NotImplementedException(); + + public Task GetFallbackPolicyAsync() => throw new NotImplementedException(); + + public virtual bool AllowsCachingPolicies => allowsCaching; + } +} From 220ec23578c76d9fd2ede3a1dd94bc4fd5e25c00 Mon Sep 17 00:00:00 2001 From: Tobias Tengler <45513122+tobias-tengler@users.noreply.github.com> Date: Tue, 12 Nov 2024 07:59:55 +0100 Subject: [PATCH 109/154] [Fusion] Fixed duplicated selections (#7677) --- .../Nodes/ResolverNodeBase.Config.cs | 5 + .../DefaultRequestDocumentFormatter.cs | 7 +- .../NodeRequestDocumentFormatter.cs | 4 +- .../src/Core/Utilities/SelectionRewriter.cs | 169 +++ .../test/Core.Tests/RequestPlannerTests.cs | 992 +++++++++++++++++- ...stPlannerTests.Fragment_Deduplication_1.md | 72 ++ ...stPlannerTests.Fragment_Deduplication_2.md | 45 + ...stPlannerTests.Fragment_Deduplication_3.md | 312 ++++++ ...stPlannerTests.Fragment_Deduplication_4.md | 44 + ...stPlannerTests.Fragment_Deduplication_5.md | 44 + ...stPlannerTests.Fragment_Deduplication_6.md | 44 + .../Fusion/test/Shared/TestSubgraph.cs | 7 +- 12 files changed, 1734 insertions(+), 11 deletions(-) create mode 100644 src/HotChocolate/Fusion/src/Core/Utilities/SelectionRewriter.cs create mode 100644 src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Fragment_Deduplication_1.md create mode 100644 src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Fragment_Deduplication_2.md create mode 100644 src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Fragment_Deduplication_3.md create mode 100644 src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Fragment_Deduplication_4.md create mode 100644 src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Fragment_Deduplication_5.md create mode 100644 src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Fragment_Deduplication_6.md diff --git a/src/HotChocolate/Fusion/src/Core/Execution/Nodes/ResolverNodeBase.Config.cs b/src/HotChocolate/Fusion/src/Core/Execution/Nodes/ResolverNodeBase.Config.cs index daa6ebe1295..0c2d16c4906 100644 --- a/src/HotChocolate/Fusion/src/Core/Execution/Nodes/ResolverNodeBase.Config.cs +++ b/src/HotChocolate/Fusion/src/Core/Execution/Nodes/ResolverNodeBase.Config.cs @@ -62,6 +62,11 @@ public Config( IReadOnlyList path, TransportFeatures transportFeatures) { + // This is a temporary solution to selections being duplicated during request planning. + // It should be properly fixed with new planner in the future. + var rewriter = new SelectionRewriter(); + document = rewriter.RewriteDocument(document, null); + string[]? buffer = null; var usedCapacity = 0; diff --git a/src/HotChocolate/Fusion/src/Core/Planning/RequestFormatters/DefaultRequestDocumentFormatter.cs b/src/HotChocolate/Fusion/src/Core/Planning/RequestFormatters/DefaultRequestDocumentFormatter.cs index cf7568f45f5..ca49fb75684 100644 --- a/src/HotChocolate/Fusion/src/Core/Planning/RequestFormatters/DefaultRequestDocumentFormatter.cs +++ b/src/HotChocolate/Fusion/src/Core/Planning/RequestFormatters/DefaultRequestDocumentFormatter.cs @@ -2,7 +2,6 @@ namespace HotChocolate.Fusion.Planning; -internal sealed class DefaultRequestDocumentFormatter(FusionGraphConfiguration configuration) - : RequestDocumentFormatter(configuration) -{ -} +internal sealed class DefaultRequestDocumentFormatter( + FusionGraphConfiguration configuration) + : RequestDocumentFormatter(configuration); diff --git a/src/HotChocolate/Fusion/src/Core/Planning/RequestFormatters/NodeRequestDocumentFormatter.cs b/src/HotChocolate/Fusion/src/Core/Planning/RequestFormatters/NodeRequestDocumentFormatter.cs index 63ad94a0760..c684fc663b9 100644 --- a/src/HotChocolate/Fusion/src/Core/Planning/RequestFormatters/NodeRequestDocumentFormatter.cs +++ b/src/HotChocolate/Fusion/src/Core/Planning/RequestFormatters/NodeRequestDocumentFormatter.cs @@ -15,8 +15,6 @@ internal sealed class NodeRequestDocumentFormatter( ISchema schema) : RequestDocumentFormatter(configuration) { - private readonly ISchema _schema = schema; - internal RequestDocument CreateRequestDocument( QueryPlanContext context, SelectionExecutionStep executionStep, @@ -113,7 +111,7 @@ private SelectionSetNode CreateSelectionSetNode( { var selectionNodes = new List(); var typeSelectionNodes = new List(); - var entityType = _schema.GetType(entityTypeName); + var entityType = schema.GetType(entityTypeName); var selectionSet = (SelectionSet)context.Operation.GetSelectionSet(parentSelection, entityType); CreateSelectionNodes( diff --git a/src/HotChocolate/Fusion/src/Core/Utilities/SelectionRewriter.cs b/src/HotChocolate/Fusion/src/Core/Utilities/SelectionRewriter.cs new file mode 100644 index 00000000000..9d1c88ee951 --- /dev/null +++ b/src/HotChocolate/Fusion/src/Core/Utilities/SelectionRewriter.cs @@ -0,0 +1,169 @@ +using System.Collections.Immutable; +using HotChocolate.Execution.Processing; +using HotChocolate.Language; + +namespace HotChocolate.Fusion.Utilities; + +public sealed class SelectionRewriter +{ + public DocumentNode RewriteDocument(DocumentNode document, string? operationName) + { + var operation = document.GetOperation(operationName); + var context = new Context(); + + RewriteFields(operation.SelectionSet, context); + + var newSelectionSet = new SelectionSetNode( + null, + context.Selections.ToImmutable()); + + var newOperation = new OperationDefinitionNode( + null, + operation.Name, + operation.Operation, + operation.VariableDefinitions, + RewriteDirectives(operation.Directives), + newSelectionSet); + + return new DocumentNode(ImmutableArray.Empty.Add(newOperation)); + } + + private void RewriteFields(SelectionSetNode selectionSet, Context context) + { + foreach (var selection in selectionSet.Selections) + { + switch (selection) + { + case FieldNode field: + RewriteField(field, context); + break; + + case InlineFragmentNode inlineFragment: + RewriteInlineFragment(inlineFragment, context); + break; + + case FragmentSpreadNode fragmentSpread: + context.Selections.Add(fragmentSpread); + break; + } + } + } + + private void RewriteField(FieldNode fieldNode, Context context) + { + if (fieldNode.SelectionSet is null) + { + var node = fieldNode.WithLocation(null); + + if (context.Visited.Add(node)) + { + context.Selections.Add(node); + } + } + else + { + var fieldContext = new Context(); + + RewriteFields(fieldNode.SelectionSet, fieldContext); + + var newSelectionSetNode = new SelectionSetNode( + null, + fieldContext.Selections.ToImmutable()); + + var newFieldNode = new FieldNode( + null, + fieldNode.Name, + fieldNode.Alias, + RewriteDirectives(fieldNode.Directives), + RewriteArguments(fieldNode.Arguments), + newSelectionSetNode); + + if (context.Visited.Add(newFieldNode)) + { + context.Selections.Add(newFieldNode); + } + } + } + + private void RewriteInlineFragment(InlineFragmentNode inlineFragment, Context context) + { + if ((inlineFragment.TypeCondition is null || + inlineFragment.TypeCondition.Name.Value.Equals(context.Type, StringComparison.Ordinal)) && + inlineFragment.Directives.Count == 0) + { + RewriteFields(inlineFragment.SelectionSet, context); + return; + } + + var inlineFragmentContext = new Context(inlineFragment.TypeCondition?.Name.Value); + + RewriteFields(inlineFragment.SelectionSet, inlineFragmentContext); + + var newSelectionSetNode = new SelectionSetNode( + null, + inlineFragmentContext.Selections.ToImmutable()); + + var newInlineFragment = new InlineFragmentNode( + null, + inlineFragment.TypeCondition, + RewriteDirectives(inlineFragment.Directives), + newSelectionSetNode); + + context.Selections.Add(newInlineFragment); + } + + private IReadOnlyList RewriteDirectives(IReadOnlyList directives) + { + if (directives.Count == 0) + { + return directives; + } + + if (directives.Count == 1) + { + var directive = directives[0]; + var newDirective = new DirectiveNode(directive.Name.Value, RewriteArguments(directive.Arguments)); + return ImmutableArray.Empty.Add(newDirective); + } + + var buffer = new DirectiveNode[directives.Count]; + for (var i = 0; i < buffer.Length; i++) + { + var directive = directives[i]; + buffer[i] = new DirectiveNode(directive.Name.Value, RewriteArguments(directive.Arguments)); + } + + return ImmutableArray.Create(buffer); + } + + private IReadOnlyList RewriteArguments(IReadOnlyList arguments) + { + if (arguments.Count == 0) + { + return arguments; + } + + if (arguments.Count == 1) + { + return ImmutableArray.Empty.Add(arguments[0].WithLocation(null)); + } + + var buffer = new ArgumentNode[arguments.Count]; + for (var i = 0; i < buffer.Length; i++) + { + buffer[i] = arguments[i].WithLocation(null); + } + + return ImmutableArray.Create(buffer); + } + + private class Context(string? typeName = null) + { + public string? Type => typeName; + + public ImmutableArray.Builder Selections { get; } = + ImmutableArray.CreateBuilder(); + + public HashSet Visited { get; } = new(SyntaxComparer.BySyntax); + } +} diff --git a/src/HotChocolate/Fusion/test/Core.Tests/RequestPlannerTests.cs b/src/HotChocolate/Fusion/test/Core.Tests/RequestPlannerTests.cs index eb894f47e35..63676a5730b 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/RequestPlannerTests.cs +++ b/src/HotChocolate/Fusion/test/Core.Tests/RequestPlannerTests.cs @@ -10,14 +10,1004 @@ using HotChocolate.Language; using HotChocolate.Skimmed.Serialization; using Microsoft.Extensions.DependencyInjection; +using Xunit.Abstractions; using static HotChocolate.Fusion.Shared.DemoProjectSchemaExtensions; using static HotChocolate.Language.Utf8GraphQLParser; using HttpClientConfiguration = HotChocolate.Fusion.Composition.HttpClientConfiguration; namespace HotChocolate.Fusion; -public class RequestPlannerTests +public class RequestPlannerTests(ITestOutputHelper output) { + [Fact] + public async Task Fragment_Deduplication_1() + { + // arrange + var subgraph = await TestSubgraph.CreateAsync( + """ + type Query { + entry: SomeObject! + } + + type SomeObject { + id: ID! + string: String! + other: AnotherObject! + } + + type AnotherObject { + id: ID! + number: Int! + } + """); + + using var subgraphs = new TestSubgraphCollection(output, [subgraph]); + var fusionGraph = await subgraphs.GetFusionGraphAsync(); + + // act + var result = await CreateQueryPlanAsync( + fusionGraph, + """ + query { + entry { + id + string + other { + __typename + ...frag4 + } + ...frag1 + ...frag2 + ...frag3 + } + } + + fragment frag1 on SomeObject { + id + string + other { + number + } + } + + fragment frag2 on SomeObject { + id + other { + id + } + } + + fragment frag3 on SomeObject { + id + other { + __typename + } + } + + fragment frag4 on AnotherObject { + id + number + } + """); + + // assert + var snapshot = new Snapshot(); + snapshot.Add(result.UserRequest, nameof(result.UserRequest)); + snapshot.Add(result.QueryPlan, nameof(result.QueryPlan)); + await snapshot.MatchMarkdownAsync(); + } + + [Fact] + public async Task Fragment_Deduplication_2() + { + // arrange + var subgraph = await TestSubgraph.CreateAsync( + """ + type Query { + viewer: Viewer! + } + + type Viewer { + unionField: SomeUnion! + } + + union SomeUnion = Object1 | Object2 + + type Object1 { + someField: String + } + + type Object2 { + otherField: Int + } + """); + + using var subgraphs = new TestSubgraphCollection(output, [subgraph]); + var fusionGraph = await subgraphs.GetFusionGraphAsync(); + + // act + var result = await CreateQueryPlanAsync( + fusionGraph, + """ + query { + viewer { + unionField { + ... on Object1 { + __typename + someField + } + } + unionField { + __typename + } + } + } + """); + + // assert + var snapshot = new Snapshot(); + snapshot.Add(result.UserRequest, nameof(result.UserRequest)); + snapshot.Add(result.QueryPlan, nameof(result.QueryPlan)); + await snapshot.MatchMarkdownAsync(); + } + + [Fact] + public async Task Fragment_Deduplication_3() + { + // arrange + var subgraph = await TestSubgraph.CreateAsync( + """ + schema { + query: Query + } + + "The node interface is implemented by entities that have a global unique identifier." + interface Node { + id: ID! + } + + interface ProductFilter { + identifier: String! + title: String! + tooltip: FilterTooltip + } + + type AlternativeQuerySuggestion { + queryString: String! + productCount: Int! + productPreviewImageUrls: [URL!]! + } + + type BlogPage implements Node { + id: ID! + } + + type Brand implements Node { + products(sortOrder: ProductListSortOrder! = RELEVANCE filters: [FilterInput!] "Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): BrandProductsConnection + id: ID! + databaseId: Int! @deprecated(reason: "This is only meant for backwards compatibility.") + } + + "A connection to a list of items." + type BrandProductFiltersConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [BrandProductFiltersEdge!] + "A flattened list of the nodes." + nodes: [ProductFilter!] + } + + "An edge in a connection." + type BrandProductFiltersEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: ProductFilter! + } + + "A connection to a list of items." + type BrandProductsConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [BrandProductsEdge!] + "A flattened list of the nodes." + nodes: [Product] + "Identifies the total count of items in the connection." + totalCount: Int! + productFilters("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): BrandProductFiltersConnection + } + + "An edge in a connection." + type BrandProductsEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: Product + } + + type CheckboxFilter implements ProductFilter { + identifier: String! + title: String! + tooltip: FilterTooltip + pinnedOptions: [CheckboxFilterOption!]! + commonOptions: [CheckboxFilterOption!]! + } + + type CheckboxFilterOption { + optionIdentifier: String! + title: String! + count: Int! + tooltip: FilterTooltip + } + + type CommunityDiscussion implements Node { + id: ID! + databaseId: Int! @deprecated(reason: "This is only meant for backwards compatibility.") + } + + type FilterTooltip { + text: String + absoluteUrl: URL + } + + type GalaxusReferral { + productCount: Int! + portalUrl: URL! + products: [Product!] + } + + type NavigationItem implements Node { + products(sortOrder: ProductListSortOrder! = RELEVANCE filters: [FilterInput!] "Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): NavigationItemProductsConnection + id: ID! + } + + "A connection to a list of items." + type NavigationItemProductFiltersConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [NavigationItemProductFiltersEdge!] + "A flattened list of the nodes." + nodes: [ProductFilter!] + } + + "An edge in a connection." + type NavigationItemProductFiltersEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: ProductFilter! + } + + "A connection to a list of items." + type NavigationItemProductsConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [NavigationItemProductsEdge!] + "A flattened list of the nodes." + nodes: [Product] + "Identifies the total count of items in the connection." + totalCount: Int! + productFilters("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): NavigationItemProductFiltersConnection + quickFilters("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): NavigationItemQuickFiltersConnection + } + + "An edge in a connection." + type NavigationItemProductsEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: Product + } + + "A connection to a list of items." + type NavigationItemQuickFiltersConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [NavigationItemQuickFiltersEdge!] + "A flattened list of the nodes." + nodes: [QuickFilter!] + } + + "An edge in a connection." + type NavigationItemQuickFiltersEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: QuickFilter! + } + + "Information about pagination in a connection." + type PageInfo { + "Indicates whether more edges exist following the set defined by the clients arguments." + hasNextPage: Boolean! + "Indicates whether more edges exist prior the set defined by the clients arguments." + hasPreviousPage: Boolean! + "When paginating backwards, the cursor to continue." + startCursor: String + "When paginating forwards, the cursor to continue." + endCursor: String + } + + type Product implements Node { + id: ID! + } + + type ProductQuestion implements Node { + id: ID! + databaseId: Int! @deprecated(reason: "This is only meant for backwards compatibility.") + } + + type ProductReview implements Node { + id: ID! + databaseId: Int! @deprecated(reason: "This is only meant for backwards compatibility.") + } + + type ProductType implements Node { + id: ID! + databaseId: Int! @deprecated(reason: "This is only meant for backwards compatibility.") + } + + type Query { + "Fetches an object given its ID." + node("ID of the object." id: ID!): Node + "Lookup nodes by a list of IDs." + nodes("The list of node IDs." ids: [ID!]!): [Node]! + productById(id: ID!): Product + blogPageById(id: ID!): BlogPage + brandById(id: ID!): Brand + productTypeById(id: ID!): ProductType + discussionById(id: ID!): CommunityDiscussion + questionById(id: ID!): ProductQuestion + ratingById(id: ID!): ProductReview + navigationItemById(id: ID!): NavigationItem + shopSearch(query: String! filters: [FilterInput!] searchQueryConfig: ShopSearchConfigInput): ShopSearchResult! + } + + type QuickFilter { + filterIdentifier: String! + optionIdentifier: String! + optionTitle: String! + filterTitle: String! + disabled: Boolean! + } + + type RangeFilter implements ProductFilter { + identifier: String! + title: String! + tooltip: FilterTooltip + topOutliersMerged: Boolean! + min: Decimal! + max: Decimal! + step: Float! + unitName: String + unitId: Int + dataPoints: [RangeFilterDataPoint!]! + } + + type RangeFilterDataPoint { + count: Int! + value: Decimal! + } + + type ShopSearchAdditionalQueryInfo { + correctedQuery: String + didYouMeanQuery: String + lastProductSearchPass: String + executedSearchTerm: String + testGroup: String + isManagedQuery: Boolean! + isRerankedQuery: Boolean! + } + + type ShopSearchResult implements Node { + products(sortOrder: ShopSearchSortOrder! = RELEVANCE filters: [FilterInput!] "Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): ShopSearchResultProductsConnection + id: ID! + productFilters("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): ShopSearchResultProductFiltersConnection + quickFilters("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): ShopSearchResultQuickFiltersConnection + productTypes("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): ShopSearchResultProductTypesConnection + brands("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): ShopSearchResultBrandsConnection + blogPages("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): ShopSearchResultBlogPagesConnection + communityItems("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): ShopSearchResultCommunityItemsConnection + additionalQueryInfo: ShopSearchAdditionalQueryInfo + alternativeQuerySuggestions: [AlternativeQuerySuggestion!] + "Certain queries lead to a redirection instead of a search result. A common case is a redirect to a brand page. Others are also possible." + redirectionUrl: URL + galaxusReferral: GalaxusReferral + } + + "A connection to a list of items." + type ShopSearchResultBlogPagesConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [ShopSearchResultBlogPagesEdge!] + "A flattened list of the nodes." + nodes: [BlogPage!] + } + + "An edge in a connection." + type ShopSearchResultBlogPagesEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: BlogPage! + } + + "A connection to a list of items." + type ShopSearchResultBrandsConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [ShopSearchResultBrandsEdge!] + "A flattened list of the nodes." + nodes: [Brand!] + } + + "An edge in a connection." + type ShopSearchResultBrandsEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: Brand! + } + + "A connection to a list of items." + type ShopSearchResultCommunityItemsConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [ShopSearchResultCommunityItemsEdge!] + "A flattened list of the nodes." + nodes: [CommunitySearchResult!] + } + + "An edge in a connection." + type ShopSearchResultCommunityItemsEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: CommunitySearchResult! + } + + "A connection to a list of items." + type ShopSearchResultProductFiltersConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [ShopSearchResultProductFiltersEdge!] + "A flattened list of the nodes." + nodes: [ProductFilter!] + } + + "An edge in a connection." + type ShopSearchResultProductFiltersEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: ProductFilter! + } + + "A connection to a list of items." + type ShopSearchResultProductTypesConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [ShopSearchResultProductTypesEdge!] + "A flattened list of the nodes." + nodes: [ProductType!] + } + + "An edge in a connection." + type ShopSearchResultProductTypesEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: ProductType! + } + + "A connection to a list of items." + type ShopSearchResultProductsConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [ShopSearchResultProductsEdge!] + "A flattened list of the nodes." + nodes: [Product] + "Identifies the total count of items in the connection." + totalCount: Int! + productFilters("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): ShopSearchResultProductFiltersConnection + quickFilters("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): ShopSearchResultQuickFiltersConnection + } + + "An edge in a connection." + type ShopSearchResultProductsEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: Product + } + + "A connection to a list of items." + type ShopSearchResultQuickFiltersConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [ShopSearchResultQuickFiltersEdge!] + "A flattened list of the nodes." + nodes: [QuickFilter!] + } + + "An edge in a connection." + type ShopSearchResultQuickFiltersEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: QuickFilter! + } + + union CommunitySearchResult = ProductReview | ProductQuestion | CommunityDiscussion + + input FilterInput { + filterIdentifier: String! + optionIdentifiers: [String!] + min: Decimal + max: Decimal + unitId: Int + } + + input ShopSearchConfigInput { + searchQueryId: String + ltrEnabled: Boolean + rewriters: [String!] + testGroup: String + } + + enum ApplyPolicy { + BEFORE_RESOLVER + AFTER_RESOLVER + VALIDATION + } + + enum ProductListSortOrder { + HIGHEST_PRICE + LOWEST_PRICE + NUMBER_OF_SALES + RELEVANCE + REBATE + AVAILABILITY + RATING + NEWEST + } + + enum ShopSearchSortOrder { + HIGHEST_PRICE + LOWEST_PRICE + NUMBER_OF_SALES + RELEVANCE + REBATE + AVAILABILITY + RATING + NEWEST + } + + "The built-in `Decimal` scalar type." + scalar Decimal + + "The `Long` scalar type represents non-fractional signed whole 64-bit numeric values. Long can represent values between -(2^63) and 2^63 - 1." + scalar Long + + scalar URL @specifiedBy(url: "https:\/\/tools.ietf.org\/html\/rfc3986") + """); + + using var subgraphs = new TestSubgraphCollection(output, [subgraph]); + var fusionGraph = await subgraphs.GetFusionGraphAsync(); + + // act + var result = await CreateQueryPlanAsync( + fusionGraph, + """ + query searchQuery( + $query: String! + $filters: [FilterInput!] + $sortOrder: ShopSearchSortOrder! + $searchQueryConfig: ShopSearchConfigInput! + ) { + shopSearch(query: $query, searchQueryConfig: $searchQueryConfig) { + ...searchEngineResultsPage + ...searchEngineResultsPageProducts_1rZpll + products(first: 48, filters: $filters, sortOrder: $sortOrder) { + productFilters { + edges { + node { + __typename + identifier + } + } + } + } + id + } + } + + fragment alternativeQuerySuggestions on ShopSearchResult { + alternativeQuerySuggestions { + productCount + productPreviewImageUrls + queryString + } + } + + fragment galaxusReferral on ShopSearchResult { + galaxusReferral { + portalUrl + productCount + products { + id + } + } + } + + fragment searchEngineBlogTeasersSection on ShopSearchResult { + blogPages { + nodes { + id + } + } + } + + fragment searchEngineCommunitySection on ShopSearchResult { + communityItems(first: 3) { + nodes { + __typename + ... on CommunityDiscussion { + databaseId + } + ... on ProductQuestion { + databaseId + } + ... on ProductReview { + databaseId + } + ... on Node { + __isNode: __typename + id + } + } + } + } + + fragment searchEngineProductsSection on ShopSearchResultProductsConnection { + edges { + node { + id + } + } + productFilters { + edges { + node { + __typename + ... on CheckboxFilter { + __typename + identifier + title + } + ... on RangeFilter { + __typename + identifier + title + } + } + } + } + totalCount + pageInfo { + hasNextPage + endCursor + } + } + + fragment searchEngineResultsPage on ShopSearchResult { + ...searchEngineTitleSection + ...useSerpTrackingShopSearchResult + ...searchEngineBlogTeasersSection + ...searchEngineCommunitySection + ...zeroResults + additionalQueryInfo { + lastProductSearchPass + } + alternativeQuerySuggestions { + __typename + } + galaxusReferral { + __typename + } + redirectionUrl + } + + fragment searchEngineResultsPageProducts_1rZpll on ShopSearchResult { + products(first: 48, filters: $filters, sortOrder: $sortOrder) { + edges { + node { + id + __typename + } + cursor + } + ...searchEngineProductsSection + ...vectorProductsSection + ...useSerpTrackingProducts + productFilters { + ...searchFiltersProductFilters + } + quickFilters { + ...searchFiltersQuickFilters + } + pageInfo { + endCursor + hasNextPage + } + } + id + } + + fragment searchEngineTitleSection on ShopSearchResult { + additionalQueryInfo { + correctedQuery + didYouMeanQuery + lastProductSearchPass + } + } + + fragment searchFiltersProductFilters on ShopSearchResultProductFiltersConnection { + edges { + node { + __typename + ... on CheckboxFilter { + __typename + identifier + title + commonOptions { + count + optionIdentifier + title + tooltip { + absoluteUrl + text + } + } + pinnedOptions { + count + optionIdentifier + title + tooltip { + absoluteUrl + text + } + } + tooltip { + absoluteUrl + text + } + } + ... on RangeFilter { + __typename + identifier + title + min + max + step + dataPoints { + count + value + } + topOutliersMerged + unitId + unitName + tooltip { + absoluteUrl + text + } + } + } + } + } + + fragment searchFiltersQuickFilters on ShopSearchResultQuickFiltersConnection { + edges { + node { + disabled + filterIdentifier + optionIdentifier + optionTitle + filterTitle + } + } + } + + fragment useSerpTrackingProducts on ShopSearchResultProductsConnection { + totalCount + } + + fragment useSerpTrackingShopSearchResult on ShopSearchResult { + brands { + edges { + node { + id + } + } + } + productTypes { + edges { + node { + id + } + } + } + redirectionUrl + galaxusReferral { + __typename + } + alternativeQuerySuggestions { + queryString + } + additionalQueryInfo { + correctedQuery + didYouMeanQuery + isRerankedQuery + lastProductSearchPass + testGroup + } + } + + fragment vectorProductsSection on ShopSearchResultProductsConnection { + edges { + node { + id + } + } + pageInfo { + hasNextPage + endCursor + } + totalCount + } + + fragment zeroResults on ShopSearchResult { + ...alternativeQuerySuggestions + ...galaxusReferral + } + """); + + // assert + var snapshot = new Snapshot(); + snapshot.Add(result.UserRequest, nameof(result.UserRequest)); + snapshot.Add(result.QueryPlan, nameof(result.QueryPlan)); + await snapshot.MatchMarkdownAsync(); + } + + [Fact] + public async Task Fragment_Deduplication_4() + { + // arrange + var subgraph = await TestSubgraph.CreateAsync( + """ + type Query { + field1: String! + field2: String! + } + """); + + using var subgraphs = new TestSubgraphCollection(output, [subgraph]); + var fusionGraph = await subgraphs.GetFusionGraphAsync(); + + // act + var result = await CreateQueryPlanAsync( + fusionGraph, + """ + query { + field1 + ... on Query { + field2 + } + ...query + } + + fragment query on Query { + field1 + field2 + } + """); + + // assert + var snapshot = new Snapshot(); + snapshot.Add(result.UserRequest, nameof(result.UserRequest)); + snapshot.Add(result.QueryPlan, nameof(result.QueryPlan)); + await snapshot.MatchMarkdownAsync(); + } + + [Fact] + public async Task Fragment_Deduplication_5() + { + // arrange + var subgraph = await TestSubgraph.CreateAsync( + """ + type Query { + field1: String! + field2: String! + } + """); + + using var subgraphs = new TestSubgraphCollection(output, [subgraph]); + var fusionGraph = await subgraphs.GetFusionGraphAsync(); + + // act + var result = await CreateQueryPlanAsync( + fusionGraph, + """ + query test($skip: Boolean!) { + field1 + ... @skip(if: $skip) { + field2 + } + ...query + } + + fragment query on Query { + field1 + } + """); + + // assert + var snapshot = new Snapshot(); + snapshot.Add(result.UserRequest, nameof(result.UserRequest)); + snapshot.Add(result.QueryPlan, nameof(result.QueryPlan)); + await snapshot.MatchMarkdownAsync(); + } + + [Fact] + public async Task Fragment_Deduplication_6() + { + // arrange + var subgraph = await TestSubgraph.CreateAsync( + """ + type Query { + field1: String! + field2: String! + } + """); + + using var subgraphs = new TestSubgraphCollection(output, [subgraph]); + var fusionGraph = await subgraphs.GetFusionGraphAsync(); + + // act + var result = await CreateQueryPlanAsync( + fusionGraph, + """ + query test($skip: Boolean!) { + field1 + ... on Query @skip(if: $skip) { + field2 + } + ...query + } + + fragment query on Query { + field1 + } + """); + + // assert + var snapshot = new Snapshot(); + snapshot.Add(result.UserRequest, nameof(result.UserRequest)); + snapshot.Add(result.QueryPlan, nameof(result.QueryPlan)); + await snapshot.MatchMarkdownAsync(); + } + [Fact] public async Task Query_Plan_01() { diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Fragment_Deduplication_1.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Fragment_Deduplication_1.md new file mode 100644 index 00000000000..18aaab6df35 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Fragment_Deduplication_1.md @@ -0,0 +1,72 @@ +# Fragment_Deduplication_1 + +## UserRequest + +```graphql +{ + entry { + id + string + other { + __typename + ... frag4 + } + ... frag1 + ... frag2 + ... frag3 + } +} + +fragment frag1 on SomeObject { + id + string + other { + number + } +} + +fragment frag2 on SomeObject { + id + other { + id + } +} + +fragment frag3 on SomeObject { + id + other { + __typename + } +} + +fragment frag4 on AnotherObject { + id + number +} +``` + +## QueryPlan + +```json +{ + "document": "{ entry { id string other { __typename ... frag4 } ... frag1 ... frag2 ... frag3 } } fragment frag1 on SomeObject { id string other { number } } fragment frag2 on SomeObject { id other { id } } fragment frag3 on SomeObject { id other { __typename } } fragment frag4 on AnotherObject { id number }", + "rootNode": { + "type": "Sequence", + "nodes": [ + { + "type": "Resolve", + "subgraph": "Subgraph_1", + "document": "query fetch_entry_1 { entry { id string other { __typename id number } } }", + "selectionSetId": 0 + }, + { + "type": "Compose", + "selectionSetIds": [ + 0 + ] + } + ] + } +} +``` + diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Fragment_Deduplication_2.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Fragment_Deduplication_2.md new file mode 100644 index 00000000000..c3900eef8ff --- /dev/null +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Fragment_Deduplication_2.md @@ -0,0 +1,45 @@ +# Fragment_Deduplication_2 + +## UserRequest + +```graphql +{ + viewer { + unionField { + ... on Object1 { + __typename + someField + } + } + unionField { + __typename + } + } +} +``` + +## QueryPlan + +```json +{ + "document": "{ viewer { unionField { ... on Object1 { __typename someField } } unionField { __typename } } }", + "rootNode": { + "type": "Sequence", + "nodes": [ + { + "type": "Resolve", + "subgraph": "Subgraph_1", + "document": "query fetch_viewer_1 { viewer { unionField { __typename ... on Object2 { __typename } ... on Object1 { __typename someField } } } }", + "selectionSetId": 0 + }, + { + "type": "Compose", + "selectionSetIds": [ + 0 + ] + } + ] + } +} +``` + diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Fragment_Deduplication_3.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Fragment_Deduplication_3.md new file mode 100644 index 00000000000..6a7577ac89a --- /dev/null +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Fragment_Deduplication_3.md @@ -0,0 +1,312 @@ +# Fragment_Deduplication_3 + +## UserRequest + +```graphql +query searchQuery($query: String!, $filters: [FilterInput!], $sortOrder: ShopSearchSortOrder!, $searchQueryConfig: ShopSearchConfigInput!) { + shopSearch(query: $query, searchQueryConfig: $searchQueryConfig) { + ... searchEngineResultsPage + ... searchEngineResultsPageProducts_1rZpll + products(first: 48, filters: $filters, sortOrder: $sortOrder) { + productFilters { + edges { + node { + __typename + identifier + } + } + } + } + id + } +} + +fragment alternativeQuerySuggestions on ShopSearchResult { + alternativeQuerySuggestions { + productCount + productPreviewImageUrls + queryString + } +} + +fragment galaxusReferral on ShopSearchResult { + galaxusReferral { + portalUrl + productCount + products { + id + } + } +} + +fragment searchEngineBlogTeasersSection on ShopSearchResult { + blogPages { + nodes { + id + } + } +} + +fragment searchEngineCommunitySection on ShopSearchResult { + communityItems(first: 3) { + nodes { + __typename + ... on CommunityDiscussion { + databaseId + } + ... on ProductQuestion { + databaseId + } + ... on ProductReview { + databaseId + } + ... on Node { + __isNode: __typename + id + } + } + } +} + +fragment searchEngineProductsSection on ShopSearchResultProductsConnection { + edges { + node { + id + } + } + productFilters { + edges { + node { + __typename + ... on CheckboxFilter { + __typename + identifier + title + } + ... on RangeFilter { + __typename + identifier + title + } + } + } + } + totalCount + pageInfo { + hasNextPage + endCursor + } +} + +fragment searchEngineResultsPage on ShopSearchResult { + ... searchEngineTitleSection + ... useSerpTrackingShopSearchResult + ... searchEngineBlogTeasersSection + ... searchEngineCommunitySection + ... zeroResults + additionalQueryInfo { + lastProductSearchPass + } + alternativeQuerySuggestions { + __typename + } + galaxusReferral { + __typename + } + redirectionUrl +} + +fragment searchEngineResultsPageProducts_1rZpll on ShopSearchResult { + products(first: 48, filters: $filters, sortOrder: $sortOrder) { + edges { + node { + id + __typename + } + cursor + } + ... searchEngineProductsSection + ... vectorProductsSection + ... useSerpTrackingProducts + productFilters { + ... searchFiltersProductFilters + } + quickFilters { + ... searchFiltersQuickFilters + } + pageInfo { + endCursor + hasNextPage + } + } + id +} + +fragment searchEngineTitleSection on ShopSearchResult { + additionalQueryInfo { + correctedQuery + didYouMeanQuery + lastProductSearchPass + } +} + +fragment searchFiltersProductFilters on ShopSearchResultProductFiltersConnection { + edges { + node { + __typename + ... on CheckboxFilter { + __typename + identifier + title + commonOptions { + count + optionIdentifier + title + tooltip { + absoluteUrl + text + } + } + pinnedOptions { + count + optionIdentifier + title + tooltip { + absoluteUrl + text + } + } + tooltip { + absoluteUrl + text + } + } + ... on RangeFilter { + __typename + identifier + title + min + max + step + dataPoints { + count + value + } + topOutliersMerged + unitId + unitName + tooltip { + absoluteUrl + text + } + } + } + } +} + +fragment searchFiltersQuickFilters on ShopSearchResultQuickFiltersConnection { + edges { + node { + disabled + filterIdentifier + optionIdentifier + optionTitle + filterTitle + } + } +} + +fragment useSerpTrackingProducts on ShopSearchResultProductsConnection { + totalCount +} + +fragment useSerpTrackingShopSearchResult on ShopSearchResult { + brands { + edges { + node { + id + } + } + } + productTypes { + edges { + node { + id + } + } + } + redirectionUrl + galaxusReferral { + __typename + } + alternativeQuerySuggestions { + queryString + } + additionalQueryInfo { + correctedQuery + didYouMeanQuery + isRerankedQuery + lastProductSearchPass + testGroup + } +} + +fragment vectorProductsSection on ShopSearchResultProductsConnection { + edges { + node { + id + } + } + pageInfo { + hasNextPage + endCursor + } + totalCount +} + +fragment zeroResults on ShopSearchResult { + ... alternativeQuerySuggestions + ... galaxusReferral +} +``` + +## QueryPlan + +```json +{ + "document": "query searchQuery($query: String!, $filters: [FilterInput!], $sortOrder: ShopSearchSortOrder!, $searchQueryConfig: ShopSearchConfigInput!) { shopSearch(query: $query, searchQueryConfig: $searchQueryConfig) { ... searchEngineResultsPage ... searchEngineResultsPageProducts_1rZpll products(first: 48, filters: $filters, sortOrder: $sortOrder) { productFilters { edges { node { __typename identifier } } } } id } } fragment alternativeQuerySuggestions on ShopSearchResult { alternativeQuerySuggestions { productCount productPreviewImageUrls queryString } } fragment galaxusReferral on ShopSearchResult { galaxusReferral { portalUrl productCount products { id } } } fragment searchEngineBlogTeasersSection on ShopSearchResult { blogPages { nodes { id } } } fragment searchEngineCommunitySection on ShopSearchResult { communityItems(first: 3) { nodes { __typename ... on CommunityDiscussion { databaseId } ... on ProductQuestion { databaseId } ... on ProductReview { databaseId } ... on Node { __isNode: __typename id } } } } fragment searchEngineProductsSection on ShopSearchResultProductsConnection { edges { node { id } } productFilters { edges { node { __typename ... on CheckboxFilter { __typename identifier title } ... on RangeFilter { __typename identifier title } } } } totalCount pageInfo { hasNextPage endCursor } } fragment searchEngineResultsPage on ShopSearchResult { ... searchEngineTitleSection ... useSerpTrackingShopSearchResult ... searchEngineBlogTeasersSection ... searchEngineCommunitySection ... zeroResults additionalQueryInfo { lastProductSearchPass } alternativeQuerySuggestions { __typename } galaxusReferral { __typename } redirectionUrl } fragment searchEngineResultsPageProducts_1rZpll on ShopSearchResult { products(first: 48, filters: $filters, sortOrder: $sortOrder) { edges { node { id __typename } cursor } ... searchEngineProductsSection ... vectorProductsSection ... useSerpTrackingProducts productFilters { ... searchFiltersProductFilters } quickFilters { ... searchFiltersQuickFilters } pageInfo { endCursor hasNextPage } } id } fragment searchEngineTitleSection on ShopSearchResult { additionalQueryInfo { correctedQuery didYouMeanQuery lastProductSearchPass } } fragment searchFiltersProductFilters on ShopSearchResultProductFiltersConnection { edges { node { __typename ... on CheckboxFilter { __typename identifier title commonOptions { count optionIdentifier title tooltip { absoluteUrl text } } pinnedOptions { count optionIdentifier title tooltip { absoluteUrl text } } tooltip { absoluteUrl text } } ... on RangeFilter { __typename identifier title min max step dataPoints { count value } topOutliersMerged unitId unitName tooltip { absoluteUrl text } } } } } fragment searchFiltersQuickFilters on ShopSearchResultQuickFiltersConnection { edges { node { disabled filterIdentifier optionIdentifier optionTitle filterTitle } } } fragment useSerpTrackingProducts on ShopSearchResultProductsConnection { totalCount } fragment useSerpTrackingShopSearchResult on ShopSearchResult { brands { edges { node { id } } } productTypes { edges { node { id } } } redirectionUrl galaxusReferral { __typename } alternativeQuerySuggestions { queryString } additionalQueryInfo { correctedQuery didYouMeanQuery isRerankedQuery lastProductSearchPass testGroup } } fragment vectorProductsSection on ShopSearchResultProductsConnection { edges { node { id } } pageInfo { hasNextPage endCursor } totalCount } fragment zeroResults on ShopSearchResult { ... alternativeQuerySuggestions ... galaxusReferral }", + "operation": "searchQuery", + "rootNode": { + "type": "Sequence", + "nodes": [ + { + "type": "Resolve", + "subgraph": "Subgraph_1", + "document": "query searchQuery_1($filters: [FilterInput!], $sortOrder: ShopSearchSortOrder!, $query: String!, $searchQueryConfig: ShopSearchConfigInput) { shopSearch(query: $query, searchQueryConfig: $searchQueryConfig) { additionalQueryInfo { correctedQuery didYouMeanQuery lastProductSearchPass isRerankedQuery testGroup } brands { edges { node { id } } } productTypes { edges { node { id } } } redirectionUrl galaxusReferral { __typename portalUrl productCount products { id } } alternativeQuerySuggestions { queryString productCount productPreviewImageUrls __typename } blogPages { nodes { id } } communityItems(first: 3) { nodes { __typename ... on CommunityDiscussion { __typename databaseId __isNode: __typename id } ... on ProductQuestion { __typename databaseId __isNode: __typename id } ... on ProductReview { __typename databaseId __isNode: __typename id } } } products(first: 48, filters: $filters, sortOrder: $sortOrder) { edges { node { id __typename } cursor } productFilters { edges { node { __typename ... on RangeFilter { __typename identifier title min max step dataPoints { count value } topOutliersMerged unitId unitName tooltip { absoluteUrl text } } ... on CheckboxFilter { __typename identifier title commonOptions { count optionIdentifier title tooltip { absoluteUrl text } } pinnedOptions { count optionIdentifier title tooltip { absoluteUrl text } } tooltip { absoluteUrl text } } } } } totalCount pageInfo { hasNextPage endCursor } quickFilters { edges { node { disabled filterIdentifier optionIdentifier optionTitle filterTitle } } } } id } }", + "selectionSetId": 0, + "forwardedVariables": [ + { + "variable": "filters" + }, + { + "variable": "sortOrder" + }, + { + "variable": "query" + }, + { + "variable": "searchQueryConfig" + } + ] + }, + { + "type": "Compose", + "selectionSetIds": [ + 0 + ] + } + ] + } +} +``` + diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Fragment_Deduplication_4.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Fragment_Deduplication_4.md new file mode 100644 index 00000000000..0206c3a4170 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Fragment_Deduplication_4.md @@ -0,0 +1,44 @@ +# Fragment_Deduplication_4 + +## UserRequest + +```graphql +{ + field1 + ... on Query { + field2 + } + ... query +} + +fragment query on Query { + field1 + field2 +} +``` + +## QueryPlan + +```json +{ + "document": "{ field1 ... on Query { field2 } ... query } fragment query on Query { field1 field2 }", + "rootNode": { + "type": "Sequence", + "nodes": [ + { + "type": "Resolve", + "subgraph": "Subgraph_1", + "document": "query fetch_field1_field2_1 { field1 field2 }", + "selectionSetId": 0 + }, + { + "type": "Compose", + "selectionSetIds": [ + 0 + ] + } + ] + } +} +``` + diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Fragment_Deduplication_5.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Fragment_Deduplication_5.md new file mode 100644 index 00000000000..21aca5905e7 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Fragment_Deduplication_5.md @@ -0,0 +1,44 @@ +# Fragment_Deduplication_5 + +## UserRequest + +```graphql +query test($skip: Boolean!) { + field1 + ... @skip(if: $skip) { + field2 + } + ... query +} + +fragment query on Query { + field1 +} +``` + +## QueryPlan + +```json +{ + "document": "query test($skip: Boolean!) { field1 ... @skip(if: $skip) { field2 } ... query } fragment query on Query { field1 }", + "operation": "test", + "rootNode": { + "type": "Sequence", + "nodes": [ + { + "type": "Resolve", + "subgraph": "Subgraph_1", + "document": "query test_1 { field1 field2 }", + "selectionSetId": 0 + }, + { + "type": "Compose", + "selectionSetIds": [ + 0 + ] + } + ] + } +} +``` + diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Fragment_Deduplication_6.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Fragment_Deduplication_6.md new file mode 100644 index 00000000000..fee31341e51 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Fragment_Deduplication_6.md @@ -0,0 +1,44 @@ +# Fragment_Deduplication_6 + +## UserRequest + +```graphql +query test($skip: Boolean!) { + field1 + ... on Query @skip(if: $skip) { + field2 + } + ... query +} + +fragment query on Query { + field1 +} +``` + +## QueryPlan + +```json +{ + "document": "query test($skip: Boolean!) { field1 ... on Query @skip(if: $skip) { field2 } ... query } fragment query on Query { field1 }", + "operation": "test", + "rootNode": { + "type": "Sequence", + "nodes": [ + { + "type": "Resolve", + "subgraph": "Subgraph_1", + "document": "query test_1 { field1 field2 }", + "selectionSetId": 0 + }, + { + "type": "Compose", + "selectionSetIds": [ + 0 + ] + } + ] + } +} +``` + diff --git a/src/HotChocolate/Fusion/test/Shared/TestSubgraph.cs b/src/HotChocolate/Fusion/test/Shared/TestSubgraph.cs index 262d57a39cb..f23d4b0f40b 100644 --- a/src/HotChocolate/Fusion/test/Shared/TestSubgraph.cs +++ b/src/HotChocolate/Fusion/test/Shared/TestSubgraph.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using HotChocolate.AspNetCore.Tests.Utilities; using HotChocolate.Execution; using HotChocolate.Execution.Configuration; @@ -11,11 +12,11 @@ public record TestSubgraph( TestServer TestServer, ISchema Schema, SubgraphTestContext Context, - string SchemaExtensions = "", + [StringSyntax("graphql")] string SchemaExtensions = "", bool IsOffline = false) { public static Task CreateAsync( - string schemaText, + [StringSyntax("graphql")] string schemaText, bool isOffline = false) => CreateAsync( configure: builder => builder @@ -26,7 +27,7 @@ public static Task CreateAsync( public static async Task CreateAsync( Action configure, - string extensions = "", + [StringSyntax("graphql")] string extensions = "", bool isOffline = false) { var testServerFactory = new TestServerFactory(); From af4afaf9aa21d5d3855bae9a3adab779da74ef0f Mon Sep 17 00:00:00 2001 From: Tobias Tengler <45513122+tobias-tengler@users.noreply.github.com> Date: Sun, 17 Nov 2024 22:37:59 +0100 Subject: [PATCH 110/154] Support nodes field for automatic mocking (#7728) --- .../test/Core.Tests/AutomaticMockingTests.cs | 531 +++++++++++++++++- ...solve_Sequence_Skip_On_EntryField_False.md | 2 +- ...Fragment_EntryField_Selected_Separately.md | 2 +- ...uence_Skip_On_EntryField_Fragment_False.md | 2 +- ...eld_Fragment_Other_Field_Selected_False.md | 2 +- ...n_EntryField_Other_Field_Selected_False.md | 2 +- ...esolve_Sequence_Skip_On_RootField_False.md | 2 +- ...quence_Skip_On_RootField_Fragment_False.md | 2 +- ...Fragment_Other_RootField_Selected_False.md | 2 +- ...ootField_Other_RootField_Selected_False.md | 2 +- ...Resolve_Sequence_Skip_On_SubField_False.md | 2 +- ...equence_Skip_On_SubField_Fragment_False.md | 2 +- ...eld_Fragment_Other_Field_Selected_False.md | 2 +- ...d_Fragment_SubField_Selected_Separately.md | 2 +- ..._On_SubField_Other_Field_Selected_False.md | 2 +- ...p_On_SubField_Other_Field_Selected_True.md | 2 +- ..._Nullable_One_Service_Errors_EntryField.md | 2 +- ...nt_Nullable_One_Service_Errors_SubField.md | 2 +- ...ice_Returns_TopLevel_Error_Without_Data.md | 2 +- ...fline_SubField_Nullable_Parent_Nullable.md | 2 +- ...fline_SubField_Nullable_Parent_Nullable.md | 2 +- .../AutomaticMocking/MockFieldMiddleware.cs | 90 +-- 22 files changed, 594 insertions(+), 67 deletions(-) diff --git a/src/HotChocolate/Fusion/test/Core.Tests/AutomaticMockingTests.cs b/src/HotChocolate/Fusion/test/Core.Tests/AutomaticMockingTests.cs index c5ccee5f28d..560e79b4a04 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/AutomaticMockingTests.cs +++ b/src/HotChocolate/Fusion/test/Core.Tests/AutomaticMockingTests.cs @@ -195,6 +195,77 @@ type Object { """); } + [Fact] + public async Task Object_List_Twice() + { + // arrange + var schema = + """ + type Query { + objsA: [Object!]! + objsB: [Object!]! + } + + type Object { + id: ID! + str: String! + } + """; + var request = + """ + query { + objsA { + id + str + } + objsB { + id + str + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "objsA": [ + { + "id": "1", + "str": "string" + }, + { + "id": "2", + "str": "string" + }, + { + "id": "3", + "str": "string" + } + ], + "objsB": [ + { + "id": "4", + "str": "string" + }, + { + "id": "5", + "str": "string" + }, + { + "id": "6", + "str": "string" + } + ] + } + } + """); + } + [Fact] public async Task Object_List_NullAtIndex() { @@ -232,7 +303,7 @@ type Object { }, null, { - "id": "2" + "id": "3" } ] } @@ -292,7 +363,7 @@ type Object { }, null, { - "id": "2" + "id": "3" } ] } @@ -813,7 +884,7 @@ ... on Object { null, { "__typename": "Object", - "id": "2", + "id": "3", "str": "string", "num": 123 } @@ -891,7 +962,7 @@ ... on Object { null, { "__typename": "Object", - "id": "2", + "id": "3", "str": "string", "num": 123 } @@ -1416,7 +1487,7 @@ ... on Object { null, { "__typename": "Object", - "id": "2", + "id": "3", "str": "string" } ] @@ -1487,7 +1558,7 @@ ... on Object { null, { "__typename": "Object", - "id": "2", + "id": "3", "str": "string" } ] @@ -2562,6 +2633,454 @@ type Product { #endregion + #region node + + [Fact] + public async Task NodeField() + { + // arrange + var schema = + """ + type Query { + node(id: ID!): Node + } + + interface Node { + id: ID! + } + + type Product implements Node { + id: ID! + name: String! + } + """; + var request = + """ + query { + node(id: "5") { + id + ... on Product { + name + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "node": { + "id": "5", + "name": "string" + } + } + } + """); + } + + [Fact] + public async Task NodeField_Null() + { + // arrange + var schema = + """ + type Query { + node(id: ID!): Node @null + } + + interface Node { + id: ID! + } + + type Product implements Node { + id: ID! + name: String! + } + """; + var request = + """ + query { + node(id: "5") { + id + ... on Product { + name + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "node": null + } + } + """); + } + + [Fact] + public async Task NodeField_Error() + { + // arrange + var schema = + """ + type Query { + node(id: ID!): Node @error + } + + interface Node { + id: ID! + } + + type Product implements Node { + id: ID! + name: String! + } + """; + var request = + """ + query { + node(id: "5") { + id + ... on Product { + name + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "node" + ] + } + ], + "data": { + "node": null + } + } + """); + } + + #endregion + + #region nodes + + [Fact] + public async Task NodesField() + { + // arrange + var schema = + """ + type Query { + nodes(ids: [ID!]!): [Node]! + } + + interface Node { + id: ID! + } + + type Product implements Node { + id: ID! + name: String! + } + """; + var request = + """ + query { + nodes(ids: ["5", "6"]) { + id + ... on Product { + name + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "nodes": [ + { + "id": "5", + "name": "string" + }, + { + "id": "6", + "name": "string" + } + ] + } + } + """); + } + + [Fact] + public async Task NodesField_Null() + { + // arrange + var schema = + """ + type Query { + nodes(ids: [ID!]!): [Node]! @null + } + + interface Node { + id: ID! + } + + type Product implements Node { + id: ID! + name: String! + } + """; + var request = + """ + query { + nodes(ids: ["5", "6"]) { + id + ... on Product { + name + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Cannot return null for non-nullable field.", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "nodes" + ], + "extensions": { + "code": "HC0018" + } + } + ], + "data": null + } + """); + } + + [Fact] + public async Task NodesField_Error() + { + // arrange + var schema = + """ + type Query { + nodes(ids: [ID!]!): [Node]! @error + } + + interface Node { + id: ID! + } + + type Product implements Node { + id: ID! + name: String! + } + """; + var request = + """ + query { + nodes(ids: ["5", "6"]) { + id + ... on Product { + name + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "nodes" + ] + } + ], + "data": null + } + """); + } + + [Fact] + public async Task NodesField_NullAtIndex() + { + // arrange + var schema = + """ + type Query { + nodes(ids: [ID!]!): [Node]! @null(atIndex: 1) + } + + interface Node { + id: ID! + } + + type Product implements Node { + id: ID! + name: String! + } + """; + var request = + """ + query { + nodes(ids: ["5", "6"]) { + id + ... on Product { + name + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "data": { + "nodes": [ + { + "id": "5", + "name": "string" + }, + null + ] + } + } + """); + } + + [Fact] + public async Task NodesField_ErrorAtIndex() + { + // arrange + var schema = + """ + type Query { + nodes(ids: [ID!]!): [Node]! @error(atIndex: 1) + } + + interface Node { + id: ID! + } + + type Product implements Node { + id: ID! + name: String! + } + """; + var request = + """ + query { + nodes(ids: ["5", "6"]) { + id + ... on Product { + name + } + } + } + """; + + // act + var result = await ExecuteRequestAgainstSchemaAsync(request, schema); + + // assert + result.MatchInlineSnapshot( + """ + { + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "nodes", + 1 + ] + } + ], + "data": { + "nodes": [ + { + "id": "5", + "name": "string" + }, + null + ] + } + } + """); + } + + #endregion + private static async Task ExecuteRequestAgainstSchemaAsync( string request, string schemaText) diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_False.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_False.md index a870f2c0e6e..033fc66519f 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_False.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_False.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string" } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Fragment_EntryField_Selected_Separately.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Fragment_EntryField_Selected_Separately.md index dad3e26b060..2483d68825e 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Fragment_EntryField_Selected_Separately.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Fragment_EntryField_Selected_Separately.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string" } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Fragment_False.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Fragment_False.md index 2b7ba7d9219..dbd64781d7e 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Fragment_False.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Fragment_False.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string" } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Fragment_Other_Field_Selected_False.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Fragment_Other_Field_Selected_False.md index e7a8c628f70..130c690c005 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Fragment_Other_Field_Selected_False.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Fragment_Other_Field_Selected_False.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string" }, "other": "string" diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Other_Field_Selected_False.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Other_Field_Selected_False.md index 59342fe350c..fbca62a4ad9 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Other_Field_Selected_False.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_EntryField_Other_Field_Selected_False.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string" }, "other": "string" diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_False.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_False.md index 59c6fc0d72a..49766b70a88 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_False.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_False.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string" } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_Fragment_False.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_Fragment_False.md index aa0c42c676c..05d3668e254 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_Fragment_False.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_Fragment_False.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string" } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_Fragment_Other_RootField_Selected_False.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_Fragment_Other_RootField_Selected_False.md index b7d5aba46f0..3d301d5aed3 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_Fragment_Other_RootField_Selected_False.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_Fragment_Other_RootField_Selected_False.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string" } }, diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_Other_RootField_Selected_False.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_Other_RootField_Selected_False.md index 431149bad3a..0bd42e7c758 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_Other_RootField_Selected_False.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_RootField_Other_RootField_Selected_False.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string" } }, diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_False.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_False.md index 807675840eb..57c8142f6c6 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_False.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_False.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string" } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Fragment_False.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Fragment_False.md index 246ed7a3ac1..e7cc0553f77 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Fragment_False.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Fragment_False.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string" } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Fragment_Other_Field_Selected_False.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Fragment_Other_Field_Selected_False.md index e0269b4c432..04923bb4e45 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Fragment_Other_Field_Selected_False.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Fragment_Other_Field_Selected_False.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string", "other": "string" } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Fragment_SubField_Selected_Separately.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Fragment_SubField_Selected_Separately.md index 57bdc2d984f..39be711a557 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Fragment_SubField_Selected_Separately.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Fragment_SubField_Selected_Separately.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string" } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Other_Field_Selected_False.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Other_Field_Selected_False.md index d63f4d334c4..487880d07d4 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Other_Field_Selected_False.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Other_Field_Selected_False.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": "string", "other": "string" } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Other_Field_Selected_True.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Other_Field_Selected_True.md index eb405b3b327..01cc70364d6 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Other_Field_Selected_True.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SkipTests.Resolve_Sequence_Skip_On_SubField_Other_Field_Selected_True.md @@ -8,7 +8,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "other": "string" } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_One_Service_Errors_EntryField.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_One_Service_Errors_EntryField.md index 1c4df83d44f..5f25db81835 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_One_Service_Errors_EntryField.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_One_Service_Errors_EntryField.md @@ -23,7 +23,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": null } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_One_Service_Errors_SubField.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_One_Service_Errors_SubField.md index 06f0976a7e9..c51b0a924c9 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_One_Service_Errors_SubField.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_One_Service_Errors_SubField.md @@ -24,7 +24,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": null } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_Second_Service_Returns_TopLevel_Error_Without_Data.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_Second_Service_Returns_TopLevel_Error_Without_Data.md index db0f7f7f4d2..9cee8f08ad0 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_Second_Service_Returns_TopLevel_Error_Without_Data.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/SubgraphErrorTests.Resolve_Sequence_SubField_Nullable_Parent_Nullable_Second_Service_Returns_TopLevel_Error_Without_Data.md @@ -13,7 +13,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": null } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/TransportErrorTests.Resolve_Sequence_Node_Second_Service_Offline_SubField_Nullable_Parent_Nullable.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/TransportErrorTests.Resolve_Sequence_Node_Second_Service_Offline_SubField_Nullable_Parent_Nullable.md index cb16b1cd5fe..4231d73351f 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/TransportErrorTests.Resolve_Sequence_Node_Second_Service_Offline_SubField_Nullable_Parent_Nullable.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/TransportErrorTests.Resolve_Sequence_Node_Second_Service_Offline_SubField_Nullable_Parent_Nullable.md @@ -24,7 +24,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": null } } diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/TransportErrorTests.Resolve_Sequence_Second_Service_Offline_SubField_Nullable_Parent_Nullable.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/TransportErrorTests.Resolve_Sequence_Second_Service_Offline_SubField_Nullable_Parent_Nullable.md index 5029f85a0de..2e35f9f740f 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/TransportErrorTests.Resolve_Sequence_Second_Service_Offline_SubField_Nullable_Parent_Nullable.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/TransportErrorTests.Resolve_Sequence_Second_Service_Offline_SubField_Nullable_Parent_Nullable.md @@ -24,7 +24,7 @@ "product": { "id": "1", "brand": { - "id": "1", + "id": "2", "name": null } } diff --git a/src/HotChocolate/Fusion/test/Shared/AutomaticMocking/MockFieldMiddleware.cs b/src/HotChocolate/Fusion/test/Shared/AutomaticMocking/MockFieldMiddleware.cs index 3caae3590a5..54ac054ac8f 100644 --- a/src/HotChocolate/Fusion/test/Shared/AutomaticMocking/MockFieldMiddleware.cs +++ b/src/HotChocolate/Fusion/test/Shared/AutomaticMocking/MockFieldMiddleware.cs @@ -1,3 +1,4 @@ +using HotChocolate.Language; using HotChocolate.Resolvers; using HotChocolate.Types; using HotChocolate.Utilities; @@ -9,10 +10,15 @@ internal sealed class MockFieldMiddleware { private const int DefaultListSize = 3; - private int _idCounter; - public ValueTask InvokeAsync(IMiddlewareContext context) { + var mockingContext = context.GetGlobalStateOrDefault(nameof(AutomaticMockingContext)); + if (mockingContext is null) + { + mockingContext = new AutomaticMockingContext(); + context.SetGlobalState(nameof(AutomaticMockingContext), mockingContext); + } + var field = context.Selection.Field; var fieldName = field.Name; var fieldType = field.Type; @@ -69,13 +75,16 @@ public ValueTask InvokeAsync(IMiddlewareContext context) } } - if (fieldName.EndsWith("ById")) + if (fieldName.EndsWith("ById") || fieldName is "node" or "nodes") { if (context.Selection.Arguments.ContainsName("id")) { var id = context.ArgumentValue("id"); - if (namedFieldType.IsObjectType()) + if (namedFieldType.IsCompositeType()) { + var possibleTypes = context.Schema.GetPossibleTypes(namedFieldType); + + context.ValueType = possibleTypes.First(); context.Result = CreateObject(id); return ValueTask.CompletedTask; } @@ -91,10 +100,16 @@ public ValueTask InvokeAsync(IMiddlewareContext context) nullableType = fieldType.InnerType(); } - if (nullableType.IsListType() && namedFieldType.IsObjectType()) + if (nullableType.IsListType()) { - context.Result = CreateListOfObjects(ids, nullIndex); - return ValueTask.CompletedTask; + if (namedFieldType.IsCompositeType()) + { + var possibleTypes = context.Schema.GetPossibleTypes(namedFieldType); + + context.ValueType = possibleTypes.First(); + context.Result = CreateListOfObjects(ids, nullIndex); + return ValueTask.CompletedTask; + } } } } @@ -109,56 +124,53 @@ public ValueTask InvokeAsync(IMiddlewareContext context) } } - if (fieldType.IsObjectType()) - { - context.Result = CreateObject(); - } - else if (fieldType.IsInterfaceType() || fieldType.IsUnionType()) + var hasIdFieldSelection = context.Select().IsSelected("id"); + + if (fieldType.IsCompositeType()) { + int? id = hasIdFieldSelection ? ++mockingContext.IdCounter : null; var possibleTypes = context.Schema.GetPossibleTypes(namedFieldType); context.ValueType = possibleTypes.First(); - context.Result = CreateObject(); + context.Result = CreateObject(id); } else if (fieldType.IsListType()) { - if (namedFieldType.IsObjectType()) - { - context.Result = CreateListOfObjects(null, nullIndex); - } - else if (namedFieldType.IsInterfaceType() || namedFieldType.IsUnionType()) + if (namedFieldType.IsCompositeType()) { + var ids = Enumerable.Range(0, DefaultListSize) + .Select(_ => (object?)(hasIdFieldSelection ? ++mockingContext.IdCounter : null)).ToArray(); var possibleTypes = context.Schema.GetPossibleTypes(namedFieldType); context.ValueType = possibleTypes.First(); - context.Result = CreateListOfObjects(null, nullIndex); + context.Result = CreateListOfObjects(ids, nullIndex); } - else if(namedFieldType is EnumType enumType) + else if (namedFieldType is EnumType enumType) { context.Result = CreateListOfEnums(enumType, nullIndex); } else { - context.Result = CreateListOfScalars(namedFieldType, nullIndex); + context.Result = CreateListOfScalars(namedFieldType, nullIndex, mockingContext); } } - else if(namedFieldType is EnumType enumType) + else if (namedFieldType is EnumType enumType) { context.Result = CreateEnumValue(enumType); } else { - context.Result = CreateScalarValue(namedFieldType); + context.Result = CreateScalarValue(namedFieldType, mockingContext); } return ValueTask.CompletedTask; } - private object? CreateScalarValue(INamedType scalarType) + private object? CreateScalarValue(INamedType scalarType, AutomaticMockingContext mockingContext) { return scalarType switch { - IdType => ++_idCounter, + IdType => ++mockingContext.IdCounter, StringType => "string", IntType => 123, FloatType => 123.456, @@ -172,17 +184,15 @@ public ValueTask InvokeAsync(IMiddlewareContext context) return enumType.Values.FirstOrDefault()?.Value; } - private object CreateObject(object? id = null, int? index = null) + private object CreateObject(object? id, int? index = null) { - var finalId = id ?? ++_idCounter; - - return new ObjectTypeInst(finalId, index); + return new ObjectTypeInst(id, index); } - private object?[] CreateListOfScalars(INamedType scalarType, int? nullIndex) + private object?[] CreateListOfScalars(INamedType scalarType, int? nullIndex, AutomaticMockingContext mockingContext) { return Enumerable.Range(0, DefaultListSize) - .Select(index => nullIndex == index ? null : CreateScalarValue(scalarType)) + .Select(index => nullIndex == index ? null : CreateScalarValue(scalarType, mockingContext)) .ToArray(); } @@ -193,17 +203,10 @@ private object CreateObject(object? id = null, int? index = null) .ToArray(); } - private object?[] CreateListOfObjects(object[]? ids, int? nullIndex) + private object?[] CreateListOfObjects(object?[] ids, int? nullIndex) { - if (ids is not null) - { - return ids - .Select((itemId, index) => nullIndex == index ? null : CreateObject(itemId, index)) - .ToArray(); - } - - return Enumerable.Range(0, DefaultListSize) - .Select(index => nullIndex == index ? null : CreateObject(null, index)) + return ids + .Select((itemId, index) => nullIndex == index ? null : CreateObject(itemId, index)) .ToArray(); } @@ -224,4 +227,9 @@ private static IError CreateError(IResolverContext context, int? index = null) } private record ObjectTypeInst(object? Id = null, int? Index = null); + + private class AutomaticMockingContext + { + public int IdCounter { get; set; } + } } From 341b113fa83a62693c2750a04e3bb5c84cb64bcb Mon Sep 17 00:00:00 2001 From: Tobias Tengler <45513122+tobias-tengler@users.noreply.github.com> Date: Sun, 17 Nov 2024 22:38:47 +0100 Subject: [PATCH 111/154] [Fusion] Add tests for @remove (#7727) --- .../test/Composition.Tests/RemoveTests.cs | 81 ++++++++++++++++ ...d_That_Is_Present_In_Another_Subgraph.snap | 11 +++ ...moves_A_Field_Exclusively_Owned_By_It.snap | 21 +++++ .../test/Core.Tests/RequestPlannerTests.cs | 94 +++++++++++++++++++ ...e_Field_On_Two_Subgraphs_One_Removes_It.md | 93 ++++++++++++++++++ .../Fusion/test/Shared/TestSubgraph.cs | 2 + .../test/Shared/TestSubgraphCollection.cs | 16 ++-- 7 files changed, 310 insertions(+), 8 deletions(-) create mode 100644 src/HotChocolate/Fusion/test/Composition.Tests/RemoveTests.cs create mode 100644 src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/RemoveTests.One_Subgraph_Removes_Field_That_Is_Present_In_Another_Subgraph.snap create mode 100644 src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/RemoveTests.Subgraph_Removes_A_Field_Exclusively_Owned_By_It.snap create mode 100644 src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Same_Field_On_Two_Subgraphs_One_Removes_It.md diff --git a/src/HotChocolate/Fusion/test/Composition.Tests/RemoveTests.cs b/src/HotChocolate/Fusion/test/Composition.Tests/RemoveTests.cs new file mode 100644 index 00000000000..f4c79eca20f --- /dev/null +++ b/src/HotChocolate/Fusion/test/Composition.Tests/RemoveTests.cs @@ -0,0 +1,81 @@ +using HotChocolate.Fusion.Shared; +using Xunit.Abstractions; + +namespace HotChocolate.Fusion.Composition; + +public class RemoveTests(ITestOutputHelper output) +{ + [Fact] + public async Task One_Subgraph_Removes_Field_That_Is_Present_In_Another_Subgraph() + { + // arrange + var subgraphA = await TestSubgraph.CreateAsync( + """ + type Query { + field: String! + } + """, + """ + schema @remove(coordinate: "Query.field") { + } + """ + ); + + var subgraphB = await TestSubgraph.CreateAsync( + """ + type Query { + field: String! + } + """ + ); + + using var subgraphs = new TestSubgraphCollection(output, [subgraphA, subgraphB]); + + // act + var fusionGraph = await subgraphs.GetFusionGraphAsync(); + + // assert + fusionGraph.MatchSnapshot(); + } + + [Fact] + public async Task Subgraph_Removes_A_Field_Exclusively_Owned_By_It() + { + // arrange + var subgraphA = await TestSubgraph.CreateAsync( + """ + type Query { + someField: SomeObject! + } + + type SomeObject { + property: String! + } + """, + """ + schema @remove(coordinate: "Query.someField") { + } + """ + ); + + var subgraphB = await TestSubgraph.CreateAsync( + """ + type Query { + otherField: AnotherObject! + } + + type AnotherObject { + property: String! + } + """ + ); + + using var subgraphs = new TestSubgraphCollection(output, [subgraphA, subgraphB]); + + // act + var fusionGraph = await subgraphs.GetFusionGraphAsync(); + + // assert + fusionGraph.MatchSnapshot(); + } +} diff --git a/src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/RemoveTests.One_Subgraph_Removes_Field_That_Is_Present_In_Another_Subgraph.snap b/src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/RemoveTests.One_Subgraph_Removes_Field_That_Is_Present_In_Another_Subgraph.snap new file mode 100644 index 00000000000..bdf5637dd43 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/RemoveTests.One_Subgraph_Removes_Field_That_Is_Present_In_Another_Subgraph.snap @@ -0,0 +1,11 @@ +schema + @fusion(version: 1) + @transport(subgraph: "Subgraph_1", location: "http:\/\/localhost:5000\/graphql", kind: "HTTP") + @transport(subgraph: "Subgraph_2", location: "http:\/\/localhost:5000\/graphql", kind: "HTTP") { + query: Query +} + +type Query { + field: String! + @resolver(subgraph: "Subgraph_2", select: "{ field }") +} diff --git a/src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/RemoveTests.Subgraph_Removes_A_Field_Exclusively_Owned_By_It.snap b/src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/RemoveTests.Subgraph_Removes_A_Field_Exclusively_Owned_By_It.snap new file mode 100644 index 00000000000..b8532d1bfc1 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/RemoveTests.Subgraph_Removes_A_Field_Exclusively_Owned_By_It.snap @@ -0,0 +1,21 @@ +schema + @fusion(version: 1) + @transport(subgraph: "Subgraph_1", location: "http:\/\/localhost:5000\/graphql", kind: "HTTP") + @transport(subgraph: "Subgraph_2", location: "http:\/\/localhost:5000\/graphql", kind: "HTTP") { + query: Query +} + +type Query { + otherField: AnotherObject! + @resolver(subgraph: "Subgraph_2", select: "{ otherField }") +} + +type AnotherObject { + property: String! + @source(subgraph: "Subgraph_2") +} + +type SomeObject { + property: String! + @source(subgraph: "Subgraph_1") +} diff --git a/src/HotChocolate/Fusion/test/Core.Tests/RequestPlannerTests.cs b/src/HotChocolate/Fusion/test/Core.Tests/RequestPlannerTests.cs index 63676a5730b..9a77a56837d 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/RequestPlannerTests.cs +++ b/src/HotChocolate/Fusion/test/Core.Tests/RequestPlannerTests.cs @@ -19,6 +19,100 @@ namespace HotChocolate.Fusion; public class RequestPlannerTests(ITestOutputHelper output) { + [Fact] + public async Task Same_Field_On_Two_Subgraphs_One_Removes_It() + { + // arrange + var subgraphA = await TestSubgraph.CreateAsync( + """ + interface Node { + id: ID! + } + + type BlogAuthor { + fullName: String! + } + + type Query { + node(id: ID!): Node + } + + type User implements Node { + followedBlogAuthors(first: Int!): [BlogAuthor]! + someField: String! + otherField: Int! + anotherField: Float! + id: ID! + } + """, + """ + schema @remove(coordinate: "User.followedBlogAuthors") { + } + """ + ); + + var subgraphB = await TestSubgraph.CreateAsync( + """ + interface Node { + id: ID! + } + + type BlogAuthor { + fullName: String! + } + + type Query { + node(id: ID!): Node + } + + type User implements Node { + followedBlogAuthors(first: Int!): [BlogAuthor]! + id: ID! + } + """ + ); + + var subgraphC = await TestSubgraph.CreateAsync( + """ + type Query { + userBySlug(slug: String!): User + } + + type User { + id: ID! + } + """); + + using var subgraphs = new TestSubgraphCollection(output, [subgraphA, subgraphB, subgraphC]); + var fusionGraph = await subgraphs.GetFusionGraphAsync(); + + // act + var result = await CreateQueryPlanAsync( + fusionGraph, + """ + query { + userBySlug(slug: "me") { + ...likedAuthors + } + } + + fragment likedAuthors on User { + someField + otherField + anotherField + followedBlogAuthors(first: 3) { + fullName + } + } + """); + + // assert + var snapshot = new Snapshot(); + snapshot.Add(result.UserRequest, nameof(result.UserRequest)); + snapshot.Add(result.QueryPlan, nameof(result.QueryPlan)); + await snapshot.MatchMarkdownAsync(); + } + [Fact] public async Task Fragment_Deduplication_1() { diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Same_Field_On_Two_Subgraphs_One_Removes_It.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Same_Field_On_Two_Subgraphs_One_Removes_It.md new file mode 100644 index 00000000000..ed7311e16c1 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/RequestPlannerTests.Same_Field_On_Two_Subgraphs_One_Removes_It.md @@ -0,0 +1,93 @@ +# Same_Field_On_Two_Subgraphs_One_Removes_It + +## UserRequest + +```graphql +{ + userBySlug(slug: "me") { + ... likedAuthors + } +} + +fragment likedAuthors on User { + someField + otherField + anotherField + followedBlogAuthors(first: 3) { + fullName + } +} +``` + +## QueryPlan + +```json +{ + "document": "{ userBySlug(slug: \u0022me\u0022) { ... likedAuthors } } fragment likedAuthors on User { someField otherField anotherField followedBlogAuthors(first: 3) { fullName } }", + "rootNode": { + "type": "Sequence", + "nodes": [ + { + "type": "Resolve", + "subgraph": "Subgraph_3", + "document": "query fetch_userBySlug_1 { userBySlug(slug: \u0022me\u0022) { __fusion_exports__1: id } }", + "selectionSetId": 0, + "provides": [ + { + "variable": "__fusion_exports__1" + } + ] + }, + { + "type": "Compose", + "selectionSetIds": [ + 0 + ] + }, + { + "type": "Parallel", + "nodes": [ + { + "type": "Resolve", + "subgraph": "Subgraph_1", + "document": "query fetch_userBySlug_2($__fusion_exports__1: ID!) { node(id: $__fusion_exports__1) { ... on User { someField otherField anotherField } } }", + "selectionSetId": 1, + "path": [ + "node" + ], + "requires": [ + { + "variable": "__fusion_exports__1" + } + ] + }, + { + "type": "Resolve", + "subgraph": "Subgraph_2", + "document": "query fetch_userBySlug_3($__fusion_exports__1: ID!) { node(id: $__fusion_exports__1) { ... on User { followedBlogAuthors(first: 3) { fullName } } } }", + "selectionSetId": 1, + "path": [ + "node" + ], + "requires": [ + { + "variable": "__fusion_exports__1" + } + ] + } + ] + }, + { + "type": "Compose", + "selectionSetIds": [ + 1 + ] + } + ] + }, + "state": { + "__fusion_exports__1": "User_id" + } +} +``` + diff --git a/src/HotChocolate/Fusion/test/Shared/TestSubgraph.cs b/src/HotChocolate/Fusion/test/Shared/TestSubgraph.cs index f23d4b0f40b..f2800c068a1 100644 --- a/src/HotChocolate/Fusion/test/Shared/TestSubgraph.cs +++ b/src/HotChocolate/Fusion/test/Shared/TestSubgraph.cs @@ -17,12 +17,14 @@ public record TestSubgraph( { public static Task CreateAsync( [StringSyntax("graphql")] string schemaText, + [StringSyntax("graphql")] string extensions = "", bool isOffline = false) => CreateAsync( configure: builder => builder .AddDocumentFromString(schemaText) .AddResolverMocking() .AddTestDirectives(), + extensions: extensions, isOffline: isOffline); public static async Task CreateAsync( diff --git a/src/HotChocolate/Fusion/test/Shared/TestSubgraphCollection.cs b/src/HotChocolate/Fusion/test/Shared/TestSubgraphCollection.cs index 621c2ce9167..f846609b961 100644 --- a/src/HotChocolate/Fusion/test/Shared/TestSubgraphCollection.cs +++ b/src/HotChocolate/Fusion/test/Shared/TestSubgraphCollection.cs @@ -11,14 +11,6 @@ namespace HotChocolate.Fusion.Shared; public class TestSubgraphCollection(ITestOutputHelper outputHelper, TestSubgraph[] subgraphs) : IDisposable { - public IHttpClientFactory GetHttpClientFactory() - { - var subgraphsDictionary = GetSubgraphs() - .ToDictionary(s => s.SubgraphName, s => s.Subgraph); - - return new TestSubgraphCollectionHttpClientFactory(subgraphsDictionary); - } - public async Task GetExecutorAsync( FusionFeatureCollection? features = null, Action? configure = null) @@ -74,6 +66,14 @@ private async Task GetExecutorAsync( return await builder.BuildRequestExecutorAsync(); } + private IHttpClientFactory GetHttpClientFactory() + { + var subgraphsDictionary = GetSubgraphs() + .ToDictionary(s => s.SubgraphName, s => s.Subgraph); + + return new TestSubgraphCollectionHttpClientFactory(subgraphsDictionary); + } + private IEnumerable<(string SubgraphName, TestSubgraph Subgraph)> GetSubgraphs() => subgraphs.Select((s, i) => ($"Subgraph_{++i}", s)); From b9aaab440026b7c0d41b22a1b28f11c4df90fda3 Mon Sep 17 00:00:00 2001 From: Tobias Tengler <45513122+tobias-tengler@users.noreply.github.com> Date: Sun, 17 Nov 2024 22:40:26 +0100 Subject: [PATCH 112/154] @semanticNonNull support (#7681) --- .../Core/src/Abstractions/ErrorCodes.cs | 1 + .../src/Abstractions/WellKnownDirectives.cs | 10 + .../src/Abstractions/WellKnownMiddleware.cs | 5 + .../Core/src/Types/IReadOnlySchemaOptions.cs | 7 + .../Core/src/Types/SchemaBuilder.cs | 1 + .../Core/src/Types/SchemaOptions.cs | 162 +-- .../Types/SemanticNonNullTypeInterceptor.cs | 376 ++++++ .../src/Types/Types/Directives/Directives.cs | 6 + .../Directives/SemanticNonNullDirective.cs | 11 + .../Execution.Tests/SemanticNonNullTests.cs | 1057 +++++++++++++++++ ...s_Null_Should_Null_Item_Without_Error.snap | 13 + ...eturns_Null_Should_Null_Without_Error.snap | 5 + ...eturns_Null_Should_Null_Without_Error.snap | 5 + ...s_Null_Should_Null_Item_Without_Error.snap | 9 + ...eturns_Null_Should_Null_Without_Error.snap | 5 + ...eturns_Null_Should_Null_Without_Error.snap | 5 + ...t_Item_Returns_Null_Should_Error_Item.snap | 31 + ...m_Throwing_Should_Null_And_Error_Item.snap | 44 + ...Object_List_Returns_Null_Should_Error.snap | 22 + ..._List_Throwing_Should_Null_FAnd_Error.snap | 19 + ...sync_Object_Returns_Null_Should_Error.snap | 22 + ...Object_Throwing_Should_Null_And_Error.snap | 19 + ...t_Item_Returns_Null_Should_Error_Item.snap | 27 + ...m_Throwing_Should_Null_And_Error_Item.snap | 40 + ...Scalar_List_Returns_Null_Should_Error.snap | 22 + ...r_List_Throwing_Should_Null_And_Error.snap | 19 + ...sync_Scalar_Returns_Null_Should_Error.snap | 22 + ...Scalar_Throwing_Should_Null_And_Error.snap | 19 + ...sts.Mutation_With_MutationConventions.snap | 7 + ...s_Null_Should_Null_Item_Without_Error.snap | 13 + ...eturns_Null_Should_Null_Without_Error.snap | 5 + ...eturns_Null_Should_Null_Without_Error.snap | 5 + ...s_Null_Should_Null_Item_Without_Error.snap | 9 + ...eturns_Null_Should_Null_Without_Error.snap | 5 + ...eturns_Null_Should_Null_Without_Error.snap | 5 + ...t_Item_Returns_Null_Should_Error_Item.snap | 31 + ...m_Throwing_Should_Null_And_Error_Item.snap | 44 + ...Object_List_Returns_Null_Should_Error.snap | 22 + ..._List_Throwing_Should_Null_FAnd_Error.snap | 19 + ...Pure_Object_Returns_Null_Should_Error.snap | 22 + ...Object_Throwing_Should_Null_And_Error.snap | 19 + ...ner_Return_Null_Should_Null_And_Error.snap | 53 + ...le_Returns_Null_Should_Null_And_Error.snap | 35 + ...t_Item_Returns_Null_Should_Error_Item.snap | 27 + ...m_Throwing_Should_Null_And_Error_Item.snap | 40 + ...Scalar_List_Returns_Null_Should_Error.snap | 22 + ...r_List_Throwing_Should_Null_And_Error.snap | 19 + ...Pure_Scalar_Returns_Null_Should_Error.snap | 22 + ...Scalar_Throwing_Should_Null_And_Error.snap | 19 + ...ticNonNullTests.Query_With_Connection.snap | 34 + ...ts.Query_With_NullableConnectionNodes.snap | 17 + .../test/Types.Tests/SemanticNonNullTests.cs | 334 ++++++ ....Apply_SemanticNonNull_To_SchemaFirst.snap | 27 + ...Derive_SemanticNonNull_From_CodeFirst.snap | 27 + ...anticNonNull_From_ImplementationFirst.snap | 27 + ...ationFirst_With_GraphQLType_As_String.snap | 27 + ...ntationFirst_With_GraphQLType_As_Type.snap | 27 + ...anticNonNullTests.MutationConventions.snap | 24 + ...NonNullTests.Object_Implementing_Node.snap | 22 + .../SemanticNonNullTests.Pagination.snap | 56 + 60 files changed, 2921 insertions(+), 128 deletions(-) create mode 100644 src/HotChocolate/Core/src/Types/SemanticNonNullTypeInterceptor.cs create mode 100644 src/HotChocolate/Core/src/Types/Types/Directives/SemanticNonNullDirective.cs create mode 100644 src/HotChocolate/Core/test/Execution.Tests/SemanticNonNullTests.cs create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Nullable_Object_List_Item_Returns_Null_Should_Null_Item_Without_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Nullable_Object_List_Returns_Null_Should_Null_Without_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Nullable_Object_Returns_Null_Should_Null_Without_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Nullable_Scalar_List_Item_Returns_Null_Should_Null_Item_Without_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Nullable_Scalar_List_Returns_Null_Should_Null_Without_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Nullable_Scalar_Returns_Null_Should_Null_Without_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Item_Returns_Null_Should_Error_Item.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Item_Throwing_Should_Null_And_Error_Item.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Returns_Null_Should_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Throwing_Should_Null_FAnd_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_Returns_Null_Should_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_Throwing_Should_Null_And_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Item_Returns_Null_Should_Error_Item.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Item_Throwing_Should_Null_And_Error_Item.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Returns_Null_Should_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Throwing_Should_Null_And_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_Returns_Null_Should_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_Throwing_Should_Null_And_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Mutation_With_MutationConventions.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Nullable_Object_List_Item_Returns_Null_Should_Null_Item_Without_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Nullable_Object_List_Returns_Null_Should_Null_Without_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Nullable_Object_Returns_Null_Should_Null_Without_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Nullable_Scalar_List_Item_Returns_Null_Should_Null_Item_Without_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Nullable_Scalar_List_Returns_Null_Should_Null_Without_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Nullable_Scalar_Returns_Null_Should_Null_Without_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Item_Returns_Null_Should_Error_Item.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Item_Throwing_Should_Null_And_Error_Item.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Returns_Null_Should_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Throwing_Should_Null_FAnd_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_Returns_Null_Should_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_Throwing_Should_Null_And_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_ListOfList_Nullable_Middle_Item_Outer_And_Inner_Return_Null_Should_Null_And_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_ListOfList_Nullable_Outer_And_Inner_Middle_Returns_Null_Should_Null_And_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Item_Returns_Null_Should_Error_Item.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Item_Throwing_Should_Null_And_Error_Item.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Returns_Null_Should_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Throwing_Should_Null_And_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_Returns_Null_Should_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_Throwing_Should_Null_And_Error.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Query_With_Connection.snap create mode 100644 src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Query_With_NullableConnectionNodes.snap create mode 100644 src/HotChocolate/Core/test/Types.Tests/SemanticNonNullTests.cs create mode 100644 src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Apply_SemanticNonNull_To_SchemaFirst.snap create mode 100644 src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Derive_SemanticNonNull_From_CodeFirst.snap create mode 100644 src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Derive_SemanticNonNull_From_ImplementationFirst.snap create mode 100644 src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Derive_SemanticNonNull_From_ImplementationFirst_With_GraphQLType_As_String.snap create mode 100644 src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Derive_SemanticNonNull_From_ImplementationFirst_With_GraphQLType_As_Type.snap create mode 100644 src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.MutationConventions.snap create mode 100644 src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Object_Implementing_Node.snap create mode 100644 src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Pagination.snap diff --git a/src/HotChocolate/Core/src/Abstractions/ErrorCodes.cs b/src/HotChocolate/Core/src/Abstractions/ErrorCodes.cs index dad7fd68e68..d18b87831d1 100644 --- a/src/HotChocolate/Core/src/Abstractions/ErrorCodes.cs +++ b/src/HotChocolate/Core/src/Abstractions/ErrorCodes.cs @@ -49,6 +49,7 @@ public static class Execution public const string OneSlicingArgumentRequired = "HC0082"; public const string NonNullViolation = "HC0018"; + public const string SemanticNonNullViolation = "HC0088"; public const string MustBeInputType = "HC0017"; public const string InvalidType = "HC0016"; public const string QueryNotFound = "HC0015"; diff --git a/src/HotChocolate/Core/src/Abstractions/WellKnownDirectives.cs b/src/HotChocolate/Core/src/Abstractions/WellKnownDirectives.cs index 9126408eb4b..ed741877439 100644 --- a/src/HotChocolate/Core/src/Abstractions/WellKnownDirectives.cs +++ b/src/HotChocolate/Core/src/Abstractions/WellKnownDirectives.cs @@ -69,4 +69,14 @@ public static class WellKnownDirectives /// The name of the @tag argument name. /// public const string Name = "name"; + + /// + /// The name of the @semanticNonNull directive. + /// + public const string SemanticNonNull = "semanticNonNull"; + + /// + /// The name of the @semanticNonNull argument levels. + /// + public const string Levels = "levels"; } diff --git a/src/HotChocolate/Core/src/Abstractions/WellKnownMiddleware.cs b/src/HotChocolate/Core/src/Abstractions/WellKnownMiddleware.cs index d7d31265e9d..be6fa2bb050 100644 --- a/src/HotChocolate/Core/src/Abstractions/WellKnownMiddleware.cs +++ b/src/HotChocolate/Core/src/Abstractions/WellKnownMiddleware.cs @@ -90,4 +90,9 @@ public static class WellKnownMiddleware /// The key identifies the authorization middleware. /// public const string Authorization = "HotChocolate.Authorization"; + + /// + /// This key identifies the semantic-non-null middleware. + /// + public const string SemanticNonNull = "HotChocolate.Types.SemanticNonNull"; } diff --git a/src/HotChocolate/Core/src/Types/IReadOnlySchemaOptions.cs b/src/HotChocolate/Core/src/Types/IReadOnlySchemaOptions.cs index 78b8062ffe9..6509c7e4614 100644 --- a/src/HotChocolate/Core/src/Types/IReadOnlySchemaOptions.cs +++ b/src/HotChocolate/Core/src/Types/IReadOnlySchemaOptions.cs @@ -165,6 +165,13 @@ public interface IReadOnlySchemaOptions /// bool EnableStream { get; } + /// + /// Enables the @semanticNonNull directive and rewrites Non-Null types to nullable types + /// with this directive attached to indicate semantic non-nullability. + /// This feature is experimental and might be changed or removed in the future. + /// + bool EnableSemanticNonNull { get; } + /// /// Specifies the maximum allowed nodes that can be fetched at once through the nodes field. /// diff --git a/src/HotChocolate/Core/src/Types/SchemaBuilder.cs b/src/HotChocolate/Core/src/Types/SchemaBuilder.cs index e538d629c02..b34a54db89b 100644 --- a/src/HotChocolate/Core/src/Types/SchemaBuilder.cs +++ b/src/HotChocolate/Core/src/Types/SchemaBuilder.cs @@ -37,6 +37,7 @@ public partial class SchemaBuilder : ISchemaBuilder typeof(InterfaceCompletionTypeInterceptor), typeof(MiddlewareValidationTypeInterceptor), typeof(EnableTrueNullabilityTypeInterceptor), + typeof(SemanticNonNullTypeInterceptor), ]; private SchemaOptions _options = new(); diff --git a/src/HotChocolate/Core/src/Types/SchemaOptions.cs b/src/HotChocolate/Core/src/Types/SchemaOptions.cs index 800c9e0f827..eb4b277f829 100644 --- a/src/HotChocolate/Core/src/Types/SchemaOptions.cs +++ b/src/HotChocolate/Core/src/Types/SchemaOptions.cs @@ -15,58 +15,34 @@ public class SchemaOptions : IReadOnlySchemaOptions private BindingBehavior _defaultBindingBehavior = BindingBehavior.Implicit; private FieldBindingFlags _defaultFieldBindingFlags = FieldBindingFlags.Instance; - /// - /// Gets or sets the name of the query type. - /// + /// public string? QueryTypeName { get; set; } - /// - /// Gets or sets the name of the mutation type. - /// + /// public string? MutationTypeName { get; set; } - /// - /// Gets or sets the name of the subscription type. - /// + /// public string? SubscriptionTypeName { get; set; } - /// - /// Defines if the schema allows the query type to be omitted. - /// + /// public bool StrictValidation { get; set; } = true; - /// - /// Defines if the CSharp XML documentation shall be integrated. - /// + /// public bool UseXmlDocumentation { get; set; } = true; - /// - /// A delegate which defines the name of the XML documentation file to be read. - /// Only used if is true. - /// + /// public Func? ResolveXmlDocumentationFileName { get; set; } - /// - /// Defines if fields shall be sorted by name. - /// Default: false - /// + /// public bool SortFieldsByName { get; set; } - /// - /// Defines if types shall be removed from the schema that are - /// unreachable from the root types. - /// + /// public bool RemoveUnreachableTypes { get; set; } - /// - /// Defines if unused type system directives shall - /// be removed from the schema. - /// + /// public bool RemoveUnusedTypeSystemDirectives { get; set; } = true; - /// - /// Defines the default binding behavior. - /// + /// public BindingBehavior DefaultBindingBehavior { get => _defaultBindingBehavior; @@ -81,10 +57,7 @@ public BindingBehavior DefaultBindingBehavior } } - /// - /// Defines which members shall be by default inferred as GraphQL fields. - /// This default applies to and . - /// + /// public FieldBindingFlags DefaultFieldBindingFlags { get => _defaultFieldBindingFlags; @@ -99,137 +72,69 @@ public FieldBindingFlags DefaultFieldBindingFlags } } - /// - /// Defines on which fields a middleware pipeline can be applied on. - /// + /// public FieldMiddlewareApplication FieldMiddleware { get; set; } = FieldMiddlewareApplication.UserDefinedFields; - /// - /// Defines if the experimental directive introspection feature shall be enabled. - /// + /// public bool EnableDirectiveIntrospection { get; set; } - /// - /// The default directive visibility when directive introspection is enabled. - /// + /// public DirectiveVisibility DefaultDirectiveVisibility { get; set; } = DirectiveVisibility.Public; - /// - /// Defines that the default resolver execution strategy. - /// + /// public ExecutionStrategy DefaultResolverStrategy { get; set; } = ExecutionStrategy.Parallel; - /// - /// Defines if the order of important middleware components shall be validated. - /// + /// public bool ValidatePipelineOrder { get; set; } = true; - /// - /// Defines if the runtime types of types shall be validated. - /// + /// public bool StrictRuntimeTypeValidation { get; set; } - /// - /// Defines a delegate that determines if a runtime - /// is an instance of an . - /// + /// public IsOfTypeFallback? DefaultIsOfTypeCheck { get; set; } - /// - /// Defines if the OneOf spec RFC is enabled. This feature is experimental. - /// + /// public bool EnableOneOf { get; set; } - /// - /// Defines if the schema building process shall validate that all nodes are resolvable through `node`. - /// + /// public bool EnsureAllNodesCanBeResolved { get; set; } = true; - /// - /// Defines if flag enums should be inferred as object value nodes - /// - /// - /// Given the following enum - ///
- /// - /// [Flags] - /// public enum Example { First, Second, Third } - /// - /// public class Query { public Example Loopback(Example input) => input; - /// - ///
- /// The following schema is produced - ///
- /// - /// type Query { - /// loopback(input: ExampleFlagsInput!): ExampleFlags - /// } - /// - /// type ExampleFlags { - /// isFirst: Boolean! - /// isSecond: Boolean! - /// isThird: Boolean! - /// } - /// - /// input ExampleFlagsInput { - /// isFirst: Boolean - /// isSecond: Boolean - /// isThird: Boolean - /// } - /// - ///
+ /// public bool EnableFlagEnums { get; set; } - /// - /// Enables the @defer directive. - /// Defer and stream both are at the moment preview features. - /// + /// public bool EnableDefer { get; set; } - /// - /// Enables the @stream directive. - /// Defer and stream both are at the moment preview features. - /// + /// public bool EnableStream { get; set; } - /// - /// Specifies the maximum allowed nodes that can be fetched at once through the nodes field. - /// + /// + public bool EnableSemanticNonNull { get; set; } + + /// public int MaxAllowedNodeBatchSize { get; set; } = 50; - /// - /// Specified if the leading I shall be stripped from the interface name. - /// + /// public bool StripLeadingIFromInterface { get; set; } - /// - /// Specifies that the true nullability proto type shall be enabled. - /// + /// public bool EnableTrueNullability { get; set; } - /// - /// Specifies that the @tag directive shall be registered with the type system. - /// + /// public bool EnableTag { get; set; } = true; - /// - /// Defines the default dependency injection scope for query fields. - /// + /// public DependencyInjectionScope DefaultQueryDependencyInjectionScope { get; set; } = DependencyInjectionScope.Resolver; - /// - /// Defines the default dependency injection scope for mutation fields. - /// + /// public DependencyInjectionScope DefaultMutationDependencyInjectionScope { get; set; } = DependencyInjectionScope.Request; - /// - /// Defines if the root field pages shall be published to the promise cache. - /// + /// public bool PublishRootFieldPagesToPromiseCache { get; set; } = true; /// @@ -263,6 +168,7 @@ public static SchemaOptions FromOptions(IReadOnlySchemaOptions options) EnableFlagEnums = options.EnableFlagEnums, EnableDefer = options.EnableDefer, EnableStream = options.EnableStream, + EnableSemanticNonNull = options.EnableSemanticNonNull, DefaultFieldBindingFlags = options.DefaultFieldBindingFlags, MaxAllowedNodeBatchSize = options.MaxAllowedNodeBatchSize, StripLeadingIFromInterface = options.StripLeadingIFromInterface, diff --git a/src/HotChocolate/Core/src/Types/SemanticNonNullTypeInterceptor.cs b/src/HotChocolate/Core/src/Types/SemanticNonNullTypeInterceptor.cs new file mode 100644 index 00000000000..1be7f69f7bf --- /dev/null +++ b/src/HotChocolate/Core/src/Types/SemanticNonNullTypeInterceptor.cs @@ -0,0 +1,376 @@ +#nullable enable + +using System.Collections; +using HotChocolate.Configuration; +using HotChocolate.Language; +using HotChocolate.Resolvers; +using HotChocolate.Types; +using HotChocolate.Types.Descriptors; +using HotChocolate.Types.Descriptors.Definitions; +using HotChocolate.Types.Helpers; +using HotChocolate.Types.Relay; + +namespace HotChocolate; + +internal sealed class SemanticNonNullTypeInterceptor : TypeInterceptor +{ + private ITypeInspector _typeInspector = null!; + private ExtendedTypeReference _nodeTypeReference = null!; + + internal override bool IsEnabled(IDescriptorContext context) + => context.Options.EnableSemanticNonNull; + + internal override void InitializeContext( + IDescriptorContext context, + TypeInitializer typeInitializer, + TypeRegistry typeRegistry, + TypeLookup typeLookup, + TypeReferenceResolver typeReferenceResolver) + { + _typeInspector = context.TypeInspector; + + _nodeTypeReference = _typeInspector.GetTypeRef(typeof(NodeType)); + } + + /// + /// After the root types have been resolved, we go through all the fields of the mutation type + /// and undo semantic non-nullability. This is because mutations can be chained and we want to retain + /// the null-bubbling so execution is aborted once one non-null mutation field produces an error. + /// We have to do this in a different hook because the mutation type is not yet fully resolved in the + /// hook. + /// + public override void OnAfterResolveRootType( + ITypeCompletionContext completionContext, + ObjectTypeDefinition definition, + OperationType operationType) + { + if (operationType == OperationType.Mutation) + { + foreach (var field in definition.Fields) + { + if (field.IsIntrospectionField) + { + continue; + } + + if (!field.HasDirectives) + { + continue; + } + + var semanticNonNullDirective = + field.Directives.FirstOrDefault(d => d.Value is SemanticNonNullDirective); + + if (semanticNonNullDirective is not null) + { + field.Directives.Remove(semanticNonNullDirective); + } + + var semanticNonNullFormatterDefinition = + field.FormatterDefinitions.FirstOrDefault(fd => fd.Key == WellKnownMiddleware.SemanticNonNull); + + if (semanticNonNullFormatterDefinition is not null) + { + field.FormatterDefinitions.Remove(semanticNonNullFormatterDefinition); + } + } + } + } + + public override void OnAfterCompleteName(ITypeCompletionContext completionContext, DefinitionBase definition) + { + if (completionContext.IsIntrospectionType) + { + return; + } + + if (definition is ObjectTypeDefinition objectDef) + { + if (objectDef.Name is "CollectionSegmentInfo" or "PageInfo") + { + return; + } + + var implementsNode = objectDef.Interfaces.Any(i => i.Equals(_nodeTypeReference)); + + foreach (var field in objectDef.Fields) + { + if (field.IsIntrospectionField) + { + continue; + } + + if (implementsNode && field.Name == "id") + { + continue; + } + + if (field.Type is null) + { + continue; + } + + var levels = GetSemanticNonNullLevels(field.Type); + + if (levels.Count < 1) + { + continue; + } + + ApplySemanticNonNullDirective(field, completionContext, levels); + + field.FormatterDefinitions.Add(CreateSemanticNonNullResultFormatterDefinition(levels)); + } + } + else if (definition is InterfaceTypeDefinition interfaceDef) + { + if (interfaceDef.Name == "Node") + { + // The Node interface is well defined, so we don't want to go and change the type of its fields. + return; + } + + foreach (var field in interfaceDef.Fields) + { + if (field.Type is null) + { + continue; + } + + var levels = GetSemanticNonNullLevels(field.Type); + + if (levels.Count < 1) + { + continue; + } + + ApplySemanticNonNullDirective(field, completionContext, levels); + } + } + } + + private void ApplySemanticNonNullDirective( + OutputFieldDefinitionBase field, + ITypeCompletionContext completionContext, + HashSet levels) + { + var directiveDependency = new TypeDependency( + _typeInspector.GetTypeRef(typeof(SemanticNonNullDirective)), + TypeDependencyFulfilled.Completed); + + ((RegisteredType)completionContext).Dependencies.Add(directiveDependency); + + field.AddDirective(new SemanticNonNullDirective(levels.ToList()), _typeInspector); + + field.Type = BuildNullableTypeStructure(field.Type!, _typeInspector); + } + + private static HashSet GetSemanticNonNullLevels(TypeReference typeReference) + { + if (typeReference is ExtendedTypeReference extendedTypeReference) + { + return GetSemanticNonNullLevelsFromReference(extendedTypeReference); + } + + if (typeReference is SchemaTypeReference schemaRef) + { + return GetSemanticNonNullLevelsFromReference(schemaRef); + } + + if (typeReference is SyntaxTypeReference syntaxRef) + { + return GetSemanticNonNullLevelsFromReference(syntaxRef); + } + + return []; + } + + private static HashSet GetSemanticNonNullLevelsFromReference(ExtendedTypeReference typeReference) + { + var levels = new HashSet(); + + var currentType = typeReference.Type; + var index = 0; + + do + { + if (currentType.IsArrayOrList) + { + if (!currentType.IsNullable) + { + levels.Add(index); + } + + index++; + currentType = currentType.ElementType; + } + else if (!currentType.IsNullable) + { + levels.Add(index); + break; + } + else + { + break; + } + } while (currentType is not null); + + return levels; + } + + private static HashSet GetSemanticNonNullLevelsFromReference(SchemaTypeReference typeReference) + { + var levels = new HashSet(); + + var currentType = typeReference.Type; + var index = 0; + + while (true) + { + if (currentType is ListType listType) + { + index++; + currentType = listType.ElementType; + } + else if (currentType is NonNullType nonNullType) + { + levels.Add(index); + currentType = nonNullType.Type; + } + else + { + break; + } + } + + return levels; + } + + private static HashSet GetSemanticNonNullLevelsFromReference(SyntaxTypeReference typeReference) + { + var levels = new HashSet(); + + var currentType = typeReference.Type; + var index = 0; + + while (true) + { + if (currentType is ListTypeNode listType) + { + index++; + currentType = listType.Type; + } + else if (currentType is NonNullTypeNode nonNullType) + { + levels.Add(index); + currentType = nonNullType.Type; + } + else + { + break; + } + } + + return levels; + } + + private static readonly bool?[] _fullNullablePattern = Enumerable.Range(0, 32).Select(_ => (bool?)true).ToArray(); + + private static TypeReference BuildNullableTypeStructure( + TypeReference typeReference, + ITypeInspector typeInspector) + { + if (typeReference is ExtendedTypeReference extendedTypeRef) + { + return extendedTypeRef.WithType(typeInspector.ChangeNullability(extendedTypeRef.Type, + _fullNullablePattern)); + } + + if (typeReference is SchemaTypeReference schemaRef) + { + return schemaRef.WithType(BuildNullableTypeStructure(schemaRef.Type)); + } + + if (typeReference is SyntaxTypeReference syntaxRef) + { + return syntaxRef.WithType(BuildNullableTypeStructure(syntaxRef.Type)); + } + + throw new NotSupportedException(); + } + + private static IType BuildNullableTypeStructure(ITypeSystemMember typeSystemMember) + { + if (typeSystemMember is ListType listType) + { + return new ListType(BuildNullableTypeStructure(listType.ElementType)); + } + + if (typeSystemMember is NonNullType nonNullType) + { + return BuildNullableTypeStructure(nonNullType.Type); + } + + return (IType)typeSystemMember; + } + + private static ITypeNode BuildNullableTypeStructure(ITypeNode typeNode) + { + if (typeNode is ListTypeNode listType) + { + return new ListTypeNode(BuildNullableTypeStructure(listType.Type)); + } + + if (typeNode is NonNullTypeNode nonNullType) + { + return BuildNullableTypeStructure(nonNullType.Type); + } + + return typeNode; + } + + private static ResultFormatterDefinition CreateSemanticNonNullResultFormatterDefinition(HashSet levels) + => new((context, result) => + { + CheckResultForSemanticNonNullViolations(result, context, context.Path, levels, 0); + + return result; + }, + key: WellKnownMiddleware.SemanticNonNull, + isRepeatable: false); + + private static void CheckResultForSemanticNonNullViolations(object? result, IResolverContext context, Path path, + HashSet levels, + int currentLevel) + { + if (result is null && levels.Contains(currentLevel)) + { + context.ReportError(CreateSemanticNonNullViolationError(path)); + return; + } + + if (result is IEnumerable enumerable) + { + if (currentLevel >= 32) + { + // We bail if we're at a depth of 32 as this would mean that we're dealing with an AnyType or another structure. + return; + } + + var index = 0; + foreach (var item in enumerable) + { + CheckResultForSemanticNonNullViolations(item, context, path.Append(index), levels, currentLevel + 1); + + index++; + } + } + } + + private static IError CreateSemanticNonNullViolationError(Path path) + => ErrorBuilder.New() + .SetMessage("Cannot return null for semantic non-null field.") + .SetCode(ErrorCodes.Execution.SemanticNonNullViolation) + .SetPath(path) + .Build(); +} diff --git a/src/HotChocolate/Core/src/Types/Types/Directives/Directives.cs b/src/HotChocolate/Core/src/Types/Types/Directives/Directives.cs index 716b0259114..362a883a4d1 100644 --- a/src/HotChocolate/Core/src/Types/Types/Directives/Directives.cs +++ b/src/HotChocolate/Core/src/Types/Types/Directives/Directives.cs @@ -15,6 +15,7 @@ public static class Directives WellKnownDirectives.Stream, WellKnownDirectives.Defer, WellKnownDirectives.OneOf, + WellKnownDirectives.SemanticNonNull ]; internal static IReadOnlyList CreateReferences( @@ -38,6 +39,11 @@ internal static IReadOnlyList CreateReferences( directiveTypes.Add(typeInspector.GetTypeRef(typeof(StreamDirectiveType))); } + if (descriptorContext.Options.EnableSemanticNonNull) + { + directiveTypes.Add(typeInspector.GetTypeRef(typeof(SemanticNonNullDirective))); + } + if (descriptorContext.Options.EnableTag) { directiveTypes.Add(typeInspector.GetTypeRef(typeof(Tag))); diff --git a/src/HotChocolate/Core/src/Types/Types/Directives/SemanticNonNullDirective.cs b/src/HotChocolate/Core/src/Types/Types/Directives/SemanticNonNullDirective.cs new file mode 100644 index 00000000000..1f64e212a95 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Types/Directives/SemanticNonNullDirective.cs @@ -0,0 +1,11 @@ +#nullable enable + +namespace HotChocolate.Types; + +[DirectiveType(WellKnownDirectives.SemanticNonNull, DirectiveLocation.FieldDefinition, IsRepeatable = false)] +public sealed class SemanticNonNullDirective(IReadOnlyList levels) +{ + [GraphQLType>>] + [DefaultValueSyntax("[0]")] + public IReadOnlyList? Levels { get; } = levels; +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/SemanticNonNullTests.cs b/src/HotChocolate/Core/test/Execution.Tests/SemanticNonNullTests.cs new file mode 100644 index 00000000000..48088a0729f --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/SemanticNonNullTests.cs @@ -0,0 +1,1057 @@ +using CookieCrumble; +using HotChocolate.Resolvers; +using HotChocolate.Types; +using Microsoft.Extensions.DependencyInjection; + +namespace HotChocolate.Execution; + +public class SemanticNonNullTests +{ + #region Scalar + + [Fact] + public async Task Async_Scalar_Returns_Null_Should_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + scalarReturningNull + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Async_Scalar_Throwing_Should_Null_And_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + scalarThrowingError + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Async_Nullable_Scalar_Returns_Null_Should_Null_Without_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + nullableScalarReturningNull + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Pure_Scalar_Returns_Null_Should_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + pureScalarReturningNull + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Pure_Scalar_Throwing_Should_Null_And_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + pureScalarThrowingError + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Pure_Nullable_Scalar_Returns_Null_Should_Null_Without_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + pureNullableScalarReturningNull + } + """); + + result.MatchSnapshot(); + } + + #endregion + + #region Scalar List + + [Fact] + public async Task Async_Scalar_List_Returns_Null_Should_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + scalarListReturningNull + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Async_Scalar_List_Throwing_Should_Null_And_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + scalarListThrowingError + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Async_Nullable_Scalar_List_Returns_Null_Should_Null_Without_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + nullableScalarListReturningNull + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Pure_Scalar_List_Returns_Null_Should_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + pureScalarListReturningNull + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Pure_Scalar_List_Throwing_Should_Null_And_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + pureScalarListThrowingError + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Pure_Nullable_Scalar_List_Returns_Null_Should_Null_Without_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + pureNullableScalarListReturningNull + } + """); + + result.MatchSnapshot(); + } + + #endregion + + #region Scalar List Item + + [Fact] + public async Task Async_Scalar_List_Item_Returns_Null_Should_Error_Item() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + scalarListItemReturningNull + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Async_Scalar_List_Item_Throwing_Should_Null_And_Error_Item() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + scalarListItemThrowingError + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Async_Nullable_Scalar_List_Item_Returns_Null_Should_Null_Item_Without_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + nullableScalarListItemReturningNull + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Pure_Scalar_List_Item_Returns_Null_Should_Error_Item() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + pureScalarListItemReturningNull + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Pure_Scalar_List_Item_Throwing_Should_Null_And_Error_Item() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + pureScalarListItemThrowingError + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Pure_Nullable_Scalar_List_Item_Returns_Null_Should_Null_Item_Without_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + pureNullableScalarListItemReturningNull + } + """); + + result.MatchSnapshot(); + } + + #endregion + + #region Object + + [Fact] + public async Task Async_Object_Returns_Null_Should_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + objectReturningNull { + property + } + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Async_Object_Throwing_Should_Null_And_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + objectThrowingError { + property + } + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Async_Nullable_Object_Returns_Null_Should_Null_Without_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + nullableObjectReturningNull { + property + } + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Pure_Object_Returns_Null_Should_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + pureObjectReturningNull { + property + } + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Pure_Object_Throwing_Should_Null_And_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + pureObjectThrowingError { + property + } + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Pure_Nullable_Object_Returns_Null_Should_Null_Without_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + pureNullableObjectReturningNull { + property + } + } + """); + + result.MatchSnapshot(); + } + + #endregion + + #region Object List + + [Fact] + public async Task Async_Object_List_Returns_Null_Should_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + objectListReturningNull { + property + } + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Async_Object_List_Throwing_Should_Null_FAnd_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + objectListThrowingError { + property + } + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Async_Nullable_Object_List_Returns_Null_Should_Null_Without_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + nullableObjectListReturningNull { + property + } + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Pure_Object_List_Returns_Null_Should_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + pureObjectListReturningNull { + property + } + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Pure_Object_List_Throwing_Should_Null_FAnd_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + pureObjectListThrowingError { + property + } + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Pure_Nullable_Object_List_Returns_Null_Should_Null_Without_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + pureNullableObjectListReturningNull { + property + } + } + """); + + result.MatchSnapshot(); + } + + #endregion + + #region Object List Item + + [Fact] + public async Task Async_Object_List_Item_Returns_Null_Should_Error_Item() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + objectListItemReturningNull { + property + } + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Async_Object_List_Item_Throwing_Should_Null_And_Error_Item() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + objectListItemThrowingError { + property + } + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Async_Nullable_Object_List_Item_Returns_Null_Should_Null_Item_Without_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + nullableObjectListItemReturningNull { + property + } + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Pure_Object_List_Item_Returns_Null_Should_Error_Item() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + pureObjectListItemReturningNull { + property + } + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Pure_Object_List_Item_Throwing_Should_Null_And_Error_Item() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + pureObjectListItemThrowingError { + property + } + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Pure_Nullable_Object_List_Item_Returns_Null_Should_Null_Item_Without_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .ExecuteRequestAsync(""" + { + pureNullableObjectListItemReturningNull { + property + } + } + """); + + result.MatchSnapshot(); + } + + #endregion + + [Fact] + public async Task Mutation_With_MutationConventions() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.StrictValidation = false; + o.EnableSemanticNonNull = true; + }) + .AddMutationConventions() + .AddMutationType() + .ExecuteRequestAsync(""" + mutation { + someMutationReturningNull { + scalarReturningNull + } + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Query_With_Connection() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddMutationConventions() + .AddQueryType() + .ExecuteRequestAsync(""" + { + scalarConnection { + edges { + node + } + } + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Query_With_NullableConnectionNodes() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddMutationConventions() + .AddQueryType() + .ExecuteRequestAsync(""" + { + nullableScalarConnection { + edges { + node + } + } + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Pure_Scalar_ListOfList_Nullable_Outer_And_Inner_Middle_Returns_Null_Should_Null_And_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddMutationConventions() + .AddQueryType() + .ExecuteRequestAsync(""" + { + nestedScalarArrayNullableOuterItems + } + """); + + result.MatchSnapshot(); + } + + [Fact] + public async Task Pure_Scalar_ListOfList_Nullable_Middle_Item_Outer_And_Inner_Return_Null_Should_Null_And_Error() + { + var result = await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddMutationConventions() + .AddQueryType() + .ExecuteRequestAsync(""" + { + nestedScalarArrayNullableMiddleItem + } + """); + + result.MatchSnapshot(); + } + + public class Query + { + #region Scalar + + public Task ScalarReturningNull() + { + return Task.FromResult(null!); + } + + public Task ScalarThrowingError() + { + throw new Exception("Something went wrong"); + } + + public Task NullableScalarReturningNull() + { + return Task.FromResult(null); + } + + public string PureScalarReturningNull => null!; + + public string PureScalarThrowingError => throw new Exception("Somethin went wrong"); + + public string? PureNullableScalarReturningNull => null; + + #endregion + + #region Scalar List + + public Task ScalarListReturningNull() + { + return Task.FromResult(null!); + } + + public Task ScalarListThrowingError() + { + throw new Exception("Something went wrong"); + } + + public Task NullableScalarListReturningNull() + { + return Task.FromResult(null); + } + + public string[] PureScalarListReturningNull => null!; + + public string[] PureScalarListThrowingError => throw new Exception("Somethin went wrong"); + + public string[]? PureNullableScalarListReturningNull => null; + + #endregion + + #region Scalar List Item + + public Task ScalarListItemReturningNull() + { + return Task.FromResult(["a", null!, "c"]); + } + + public Task ScalarListItemThrowingError(IResolverContext context) + { + // TODO: How can you create a terminating error for a single item? + context.ReportError(ErrorBuilder.New().SetMessage("Another error").SetPath(context.Path.Append(1)).Build()); + return Task.FromResult(["a", null!, "c"]); + } + + public Task NullableScalarListItemReturningNull() + { + return Task.FromResult(["a", null, "c"]); + } + + public string[] PureScalarListItemReturningNull => ["a", null!, "c"]; + + // TODO: This is no longer a pure resolver as soon as it access the IResolverContext, right? + public string[] PureScalarListItemThrowingError(IResolverContext context) + { + // TODO: How can you create a terminating error for a single item? + context.ReportError(ErrorBuilder.New().SetMessage("Another error").SetPath(context.Path.Append(1)).Build()); + return ["a", null!, "c"]; + } + + public string?[] PureNullableScalarListItemReturningNull => ["a", null, "c"]; + + #endregion + + #region Object + + public Task ObjectReturningNull() + { + return Task.FromResult(null!); + } + + public Task ObjectThrowingError() + { + throw new Exception("Something went wrong"); + } + + public Task NullableObjectReturningNull() + { + return Task.FromResult(null); + } + + public SomeObject PureObjectReturningNull => null!; + + public SomeObject PureObjectThrowingError => throw new Exception("Somethin went wrong"); + + public SomeObject? PureNullableObjectReturningNull => null; + + #endregion + + #region Object List + + public Task ObjectListReturningNull() + { + return Task.FromResult(null!); + } + + public Task ObjectListThrowingError() + { + throw new Exception("Something went wrong"); + } + + public Task NullableObjectListReturningNull() + { + return Task.FromResult(null); + } + + public SomeObject[] PureObjectListReturningNull => null!; + + public SomeObject[] PureObjectListThrowingError => throw new Exception("Somethin went wrong"); + + public SomeObject[]? PureNullableObjectListReturningNull => null; + + #endregion + + #region Object List Item + + public Task ObjectListItemReturningNull() + { + return Task.FromResult([new("a"), null!, new("c")]); + } + + public Task ObjectListItemThrowingError(IResolverContext context) + { + context.ReportError(ErrorBuilder.New().SetMessage("Another error").SetPath(context.Path.Append(1)).Build()); + return Task.FromResult([new("a"), null!, new("c")]); + } + + public Task NullableObjectListItemReturningNull() + { + return Task.FromResult([new("a"), null, new("c")]); + } + + public SomeObject[] PureObjectListItemReturningNull => [new("a"), null!, new("c")]; + + // TODO: This is no longer a pure resolver as soon as it access the IResolverContext, right? + public SomeObject[] PureObjectListItemThrowingError(IResolverContext context) + { + context.ReportError(ErrorBuilder.New().SetMessage("Another error").SetPath(context.Path.Append(1)).Build()); + return [new("a"), null!, new("c")];; + } + + public SomeObject?[] PureNullableObjectListItemReturningNull => [new("a"), null, new("c")]; + + #endregion + + #region Nested Array + public string?[][]? NestedScalarArrayNullableOuterItems() + { + return [["a1", null!, "c1"], null!, ["a2", null!, "c2"]]; + } + + public string[]?[] NestedScalarArrayNullableMiddleItem() + { + return [["a1", null!, "c1"], null!, ["a2", null!, "c2"]]; + } + #endregion + + [UsePaging] + public string[] ScalarConnection() => new[] { "a", null!, "c" }; + + [UsePaging] + public string?[] NullableScalarConnection() => new[] { "a", null, "c" }; + } + + public record SomeObject(string Property); + + public class Mutation + { + [UseMutationConvention(PayloadFieldName = "scalarReturningNull")] + public Task SomeMutationReturningNull() => Task.FromResult(null!); + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Nullable_Object_List_Item_Returns_Null_Should_Null_Item_Without_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Nullable_Object_List_Item_Returns_Null_Should_Null_Item_Without_Error.snap new file mode 100644 index 00000000000..5bf14273af3 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Nullable_Object_List_Item_Returns_Null_Should_Null_Item_Without_Error.snap @@ -0,0 +1,13 @@ +{ + "data": { + "nullableObjectListItemReturningNull": [ + { + "property": "a" + }, + null, + { + "property": "c" + } + ] + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Nullable_Object_List_Returns_Null_Should_Null_Without_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Nullable_Object_List_Returns_Null_Should_Null_Without_Error.snap new file mode 100644 index 00000000000..80512055ffc --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Nullable_Object_List_Returns_Null_Should_Null_Without_Error.snap @@ -0,0 +1,5 @@ +{ + "data": { + "nullableObjectListReturningNull": null + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Nullable_Object_Returns_Null_Should_Null_Without_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Nullable_Object_Returns_Null_Should_Null_Without_Error.snap new file mode 100644 index 00000000000..d6ed49ca517 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Nullable_Object_Returns_Null_Should_Null_Without_Error.snap @@ -0,0 +1,5 @@ +{ + "data": { + "nullableObjectReturningNull": null + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Nullable_Scalar_List_Item_Returns_Null_Should_Null_Item_Without_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Nullable_Scalar_List_Item_Returns_Null_Should_Null_Item_Without_Error.snap new file mode 100644 index 00000000000..b3c51911a4c --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Nullable_Scalar_List_Item_Returns_Null_Should_Null_Item_Without_Error.snap @@ -0,0 +1,9 @@ +{ + "data": { + "nullableScalarListItemReturningNull": [ + "a", + null, + "c" + ] + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Nullable_Scalar_List_Returns_Null_Should_Null_Without_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Nullable_Scalar_List_Returns_Null_Should_Null_Without_Error.snap new file mode 100644 index 00000000000..68f0bbd3b50 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Nullable_Scalar_List_Returns_Null_Should_Null_Without_Error.snap @@ -0,0 +1,5 @@ +{ + "data": { + "nullableScalarListReturningNull": null + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Nullable_Scalar_Returns_Null_Should_Null_Without_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Nullable_Scalar_Returns_Null_Should_Null_Without_Error.snap new file mode 100644 index 00000000000..161eb3e36df --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Nullable_Scalar_Returns_Null_Should_Null_Without_Error.snap @@ -0,0 +1,5 @@ +{ + "data": { + "nullableScalarReturningNull": null + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Item_Returns_Null_Should_Error_Item.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Item_Returns_Null_Should_Error_Item.snap new file mode 100644 index 00000000000..cef5849184d --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Item_Returns_Null_Should_Error_Item.snap @@ -0,0 +1,31 @@ +{ + "errors": [ + { + "message": "Cannot return null for semantic non-null field.", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "objectListItemReturningNull", + 1 + ], + "extensions": { + "code": "HC0088" + } + } + ], + "data": { + "objectListItemReturningNull": [ + { + "property": "a" + }, + null, + { + "property": "c" + } + ] + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Item_Throwing_Should_Null_And_Error_Item.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Item_Throwing_Should_Null_And_Error_Item.snap new file mode 100644 index 00000000000..c136aaacabf --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Item_Throwing_Should_Null_And_Error_Item.snap @@ -0,0 +1,44 @@ +{ + "errors": [ + { + "message": "Another error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "objectListItemThrowingError", + 1 + ] + }, + { + "message": "Cannot return null for semantic non-null field.", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "objectListItemThrowingError", + 1 + ], + "extensions": { + "code": "HC0088" + } + } + ], + "data": { + "objectListItemThrowingError": [ + { + "property": "a" + }, + null, + { + "property": "c" + } + ] + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Returns_Null_Should_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Returns_Null_Should_Error.snap new file mode 100644 index 00000000000..5272b1193ac --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Returns_Null_Should_Error.snap @@ -0,0 +1,22 @@ +{ + "errors": [ + { + "message": "Cannot return null for semantic non-null field.", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "objectListReturningNull" + ], + "extensions": { + "code": "HC0088" + } + } + ], + "data": { + "objectListReturningNull": null + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Throwing_Should_Null_FAnd_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Throwing_Should_Null_FAnd_Error.snap new file mode 100644 index 00000000000..584bc579f4d --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_List_Throwing_Should_Null_FAnd_Error.snap @@ -0,0 +1,19 @@ +{ + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "objectListThrowingError" + ] + } + ], + "data": { + "objectListThrowingError": null + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_Returns_Null_Should_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_Returns_Null_Should_Error.snap new file mode 100644 index 00000000000..fa19729099c --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_Returns_Null_Should_Error.snap @@ -0,0 +1,22 @@ +{ + "errors": [ + { + "message": "Cannot return null for semantic non-null field.", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "objectReturningNull" + ], + "extensions": { + "code": "HC0088" + } + } + ], + "data": { + "objectReturningNull": null + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_Throwing_Should_Null_And_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_Throwing_Should_Null_And_Error.snap new file mode 100644 index 00000000000..de64fcf0f25 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Object_Throwing_Should_Null_And_Error.snap @@ -0,0 +1,19 @@ +{ + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "objectThrowingError" + ] + } + ], + "data": { + "objectThrowingError": null + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Item_Returns_Null_Should_Error_Item.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Item_Returns_Null_Should_Error_Item.snap new file mode 100644 index 00000000000..6e3b5178b29 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Item_Returns_Null_Should_Error_Item.snap @@ -0,0 +1,27 @@ +{ + "errors": [ + { + "message": "Cannot return null for semantic non-null field.", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "scalarListItemReturningNull", + 1 + ], + "extensions": { + "code": "HC0088" + } + } + ], + "data": { + "scalarListItemReturningNull": [ + "a", + null, + "c" + ] + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Item_Throwing_Should_Null_And_Error_Item.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Item_Throwing_Should_Null_And_Error_Item.snap new file mode 100644 index 00000000000..87f194c0037 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Item_Throwing_Should_Null_And_Error_Item.snap @@ -0,0 +1,40 @@ +{ + "errors": [ + { + "message": "Another error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "scalarListItemThrowingError", + 1 + ] + }, + { + "message": "Cannot return null for semantic non-null field.", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "scalarListItemThrowingError", + 1 + ], + "extensions": { + "code": "HC0088" + } + } + ], + "data": { + "scalarListItemThrowingError": [ + "a", + null, + "c" + ] + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Returns_Null_Should_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Returns_Null_Should_Error.snap new file mode 100644 index 00000000000..136f20e321e --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Returns_Null_Should_Error.snap @@ -0,0 +1,22 @@ +{ + "errors": [ + { + "message": "Cannot return null for semantic non-null field.", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "scalarListReturningNull" + ], + "extensions": { + "code": "HC0088" + } + } + ], + "data": { + "scalarListReturningNull": null + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Throwing_Should_Null_And_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Throwing_Should_Null_And_Error.snap new file mode 100644 index 00000000000..fe169a3cc33 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_List_Throwing_Should_Null_And_Error.snap @@ -0,0 +1,19 @@ +{ + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "scalarListThrowingError" + ] + } + ], + "data": { + "scalarListThrowingError": null + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_Returns_Null_Should_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_Returns_Null_Should_Error.snap new file mode 100644 index 00000000000..d6f046b374e --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_Returns_Null_Should_Error.snap @@ -0,0 +1,22 @@ +{ + "errors": [ + { + "message": "Cannot return null for semantic non-null field.", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "scalarReturningNull" + ], + "extensions": { + "code": "HC0088" + } + } + ], + "data": { + "scalarReturningNull": null + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_Throwing_Should_Null_And_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_Throwing_Should_Null_And_Error.snap new file mode 100644 index 00000000000..f992ef3496b --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Async_Scalar_Throwing_Should_Null_And_Error.snap @@ -0,0 +1,19 @@ +{ + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "scalarThrowingError" + ] + } + ], + "data": { + "scalarThrowingError": null + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Mutation_With_MutationConventions.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Mutation_With_MutationConventions.snap new file mode 100644 index 00000000000..bd13441af00 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Mutation_With_MutationConventions.snap @@ -0,0 +1,7 @@ +{ + "data": { + "someMutationReturningNull": { + "scalarReturningNull": null + } + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Nullable_Object_List_Item_Returns_Null_Should_Null_Item_Without_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Nullable_Object_List_Item_Returns_Null_Should_Null_Item_Without_Error.snap new file mode 100644 index 00000000000..bc867086e8a --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Nullable_Object_List_Item_Returns_Null_Should_Null_Item_Without_Error.snap @@ -0,0 +1,13 @@ +{ + "data": { + "pureNullableObjectListItemReturningNull": [ + { + "property": "a" + }, + null, + { + "property": "c" + } + ] + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Nullable_Object_List_Returns_Null_Should_Null_Without_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Nullable_Object_List_Returns_Null_Should_Null_Without_Error.snap new file mode 100644 index 00000000000..2abd30023ac --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Nullable_Object_List_Returns_Null_Should_Null_Without_Error.snap @@ -0,0 +1,5 @@ +{ + "data": { + "pureNullableObjectListReturningNull": null + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Nullable_Object_Returns_Null_Should_Null_Without_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Nullable_Object_Returns_Null_Should_Null_Without_Error.snap new file mode 100644 index 00000000000..472da5015a4 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Nullable_Object_Returns_Null_Should_Null_Without_Error.snap @@ -0,0 +1,5 @@ +{ + "data": { + "pureNullableObjectReturningNull": null + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Nullable_Scalar_List_Item_Returns_Null_Should_Null_Item_Without_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Nullable_Scalar_List_Item_Returns_Null_Should_Null_Item_Without_Error.snap new file mode 100644 index 00000000000..65e0c7452b1 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Nullable_Scalar_List_Item_Returns_Null_Should_Null_Item_Without_Error.snap @@ -0,0 +1,9 @@ +{ + "data": { + "pureNullableScalarListItemReturningNull": [ + "a", + null, + "c" + ] + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Nullable_Scalar_List_Returns_Null_Should_Null_Without_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Nullable_Scalar_List_Returns_Null_Should_Null_Without_Error.snap new file mode 100644 index 00000000000..859a7fd908e --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Nullable_Scalar_List_Returns_Null_Should_Null_Without_Error.snap @@ -0,0 +1,5 @@ +{ + "data": { + "pureNullableScalarListReturningNull": null + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Nullable_Scalar_Returns_Null_Should_Null_Without_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Nullable_Scalar_Returns_Null_Should_Null_Without_Error.snap new file mode 100644 index 00000000000..e05006cc33f --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Nullable_Scalar_Returns_Null_Should_Null_Without_Error.snap @@ -0,0 +1,5 @@ +{ + "data": { + "pureNullableScalarReturningNull": null + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Item_Returns_Null_Should_Error_Item.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Item_Returns_Null_Should_Error_Item.snap new file mode 100644 index 00000000000..20741ef4898 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Item_Returns_Null_Should_Error_Item.snap @@ -0,0 +1,31 @@ +{ + "errors": [ + { + "message": "Cannot return null for semantic non-null field.", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "pureObjectListItemReturningNull", + 1 + ], + "extensions": { + "code": "HC0088" + } + } + ], + "data": { + "pureObjectListItemReturningNull": [ + { + "property": "a" + }, + null, + { + "property": "c" + } + ] + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Item_Throwing_Should_Null_And_Error_Item.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Item_Throwing_Should_Null_And_Error_Item.snap new file mode 100644 index 00000000000..338ea110d60 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Item_Throwing_Should_Null_And_Error_Item.snap @@ -0,0 +1,44 @@ +{ + "errors": [ + { + "message": "Another error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "pureObjectListItemThrowingError", + 1 + ] + }, + { + "message": "Cannot return null for semantic non-null field.", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "pureObjectListItemThrowingError", + 1 + ], + "extensions": { + "code": "HC0088" + } + } + ], + "data": { + "pureObjectListItemThrowingError": [ + { + "property": "a" + }, + null, + { + "property": "c" + } + ] + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Returns_Null_Should_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Returns_Null_Should_Error.snap new file mode 100644 index 00000000000..dd9c523f560 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Returns_Null_Should_Error.snap @@ -0,0 +1,22 @@ +{ + "errors": [ + { + "message": "Cannot return null for semantic non-null field.", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "pureObjectListReturningNull" + ], + "extensions": { + "code": "HC0088" + } + } + ], + "data": { + "pureObjectListReturningNull": null + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Throwing_Should_Null_FAnd_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Throwing_Should_Null_FAnd_Error.snap new file mode 100644 index 00000000000..89587618619 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_List_Throwing_Should_Null_FAnd_Error.snap @@ -0,0 +1,19 @@ +{ + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "pureObjectListThrowingError" + ] + } + ], + "data": { + "pureObjectListThrowingError": null + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_Returns_Null_Should_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_Returns_Null_Should_Error.snap new file mode 100644 index 00000000000..81b0b6ca595 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_Returns_Null_Should_Error.snap @@ -0,0 +1,22 @@ +{ + "errors": [ + { + "message": "Cannot return null for semantic non-null field.", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "pureObjectReturningNull" + ], + "extensions": { + "code": "HC0088" + } + } + ], + "data": { + "pureObjectReturningNull": null + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_Throwing_Should_Null_And_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_Throwing_Should_Null_And_Error.snap new file mode 100644 index 00000000000..76230f78282 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Object_Throwing_Should_Null_And_Error.snap @@ -0,0 +1,19 @@ +{ + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "pureObjectThrowingError" + ] + } + ], + "data": { + "pureObjectThrowingError": null + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_ListOfList_Nullable_Middle_Item_Outer_And_Inner_Return_Null_Should_Null_And_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_ListOfList_Nullable_Middle_Item_Outer_And_Inner_Return_Null_Should_Null_And_Error.snap new file mode 100644 index 00000000000..7c42ba3a2a3 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_ListOfList_Nullable_Middle_Item_Outer_And_Inner_Return_Null_Should_Null_And_Error.snap @@ -0,0 +1,53 @@ +{ + "errors": [ + { + "message": "Cannot return null for semantic non-null field.", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "nestedScalarArrayNullableMiddleItem", + 0, + 1 + ], + "extensions": { + "code": "HC0088" + } + }, + { + "message": "Cannot return null for semantic non-null field.", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "nestedScalarArrayNullableMiddleItem", + 2, + 1 + ], + "extensions": { + "code": "HC0088" + } + } + ], + "data": { + "nestedScalarArrayNullableMiddleItem": [ + [ + "a1", + null, + "c1" + ], + null, + [ + "a2", + null, + "c2" + ] + ] + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_ListOfList_Nullable_Outer_And_Inner_Middle_Returns_Null_Should_Null_And_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_ListOfList_Nullable_Outer_And_Inner_Middle_Returns_Null_Should_Null_And_Error.snap new file mode 100644 index 00000000000..5b68ea3fafb --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_ListOfList_Nullable_Outer_And_Inner_Middle_Returns_Null_Should_Null_And_Error.snap @@ -0,0 +1,35 @@ +{ + "errors": [ + { + "message": "Cannot return null for semantic non-null field.", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "nestedScalarArrayNullableOuterItems", + 1 + ], + "extensions": { + "code": "HC0088" + } + } + ], + "data": { + "nestedScalarArrayNullableOuterItems": [ + [ + "a1", + null, + "c1" + ], + null, + [ + "a2", + null, + "c2" + ] + ] + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Item_Returns_Null_Should_Error_Item.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Item_Returns_Null_Should_Error_Item.snap new file mode 100644 index 00000000000..f3a0b0bb1ca --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Item_Returns_Null_Should_Error_Item.snap @@ -0,0 +1,27 @@ +{ + "errors": [ + { + "message": "Cannot return null for semantic non-null field.", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "pureScalarListItemReturningNull", + 1 + ], + "extensions": { + "code": "HC0088" + } + } + ], + "data": { + "pureScalarListItemReturningNull": [ + "a", + null, + "c" + ] + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Item_Throwing_Should_Null_And_Error_Item.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Item_Throwing_Should_Null_And_Error_Item.snap new file mode 100644 index 00000000000..e5ecb022752 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Item_Throwing_Should_Null_And_Error_Item.snap @@ -0,0 +1,40 @@ +{ + "errors": [ + { + "message": "Another error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "pureScalarListItemThrowingError", + 1 + ] + }, + { + "message": "Cannot return null for semantic non-null field.", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "pureScalarListItemThrowingError", + 1 + ], + "extensions": { + "code": "HC0088" + } + } + ], + "data": { + "pureScalarListItemThrowingError": [ + "a", + null, + "c" + ] + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Returns_Null_Should_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Returns_Null_Should_Error.snap new file mode 100644 index 00000000000..e6c89302539 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Returns_Null_Should_Error.snap @@ -0,0 +1,22 @@ +{ + "errors": [ + { + "message": "Cannot return null for semantic non-null field.", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "pureScalarListReturningNull" + ], + "extensions": { + "code": "HC0088" + } + } + ], + "data": { + "pureScalarListReturningNull": null + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Throwing_Should_Null_And_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Throwing_Should_Null_And_Error.snap new file mode 100644 index 00000000000..87eb04d1690 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_List_Throwing_Should_Null_And_Error.snap @@ -0,0 +1,19 @@ +{ + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "pureScalarListThrowingError" + ] + } + ], + "data": { + "pureScalarListThrowingError": null + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_Returns_Null_Should_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_Returns_Null_Should_Error.snap new file mode 100644 index 00000000000..4ddf157a011 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_Returns_Null_Should_Error.snap @@ -0,0 +1,22 @@ +{ + "errors": [ + { + "message": "Cannot return null for semantic non-null field.", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "pureScalarReturningNull" + ], + "extensions": { + "code": "HC0088" + } + } + ], + "data": { + "pureScalarReturningNull": null + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_Throwing_Should_Null_And_Error.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_Throwing_Should_Null_And_Error.snap new file mode 100644 index 00000000000..a1980169f92 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Pure_Scalar_Throwing_Should_Null_And_Error.snap @@ -0,0 +1,19 @@ +{ + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "pureScalarThrowingError" + ] + } + ], + "data": { + "pureScalarThrowingError": null + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Query_With_Connection.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Query_With_Connection.snap new file mode 100644 index 00000000000..af087216eda --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Query_With_Connection.snap @@ -0,0 +1,34 @@ +{ + "errors": [ + { + "message": "Unexpected Execution Error", + "locations": [ + { + "line": 4, + "column": 7 + } + ], + "path": [ + "scalarConnection", + "edges", + 1, + "node" + ] + } + ], + "data": { + "scalarConnection": { + "edges": [ + { + "node": "a" + }, + { + "node": null + }, + { + "node": "c" + } + ] + } + } +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Query_With_NullableConnectionNodes.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Query_With_NullableConnectionNodes.snap new file mode 100644 index 00000000000..ba3ef427b57 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/SemanticNonNullTests.Query_With_NullableConnectionNodes.snap @@ -0,0 +1,17 @@ +{ + "data": { + "nullableScalarConnection": { + "edges": [ + { + "node": "a" + }, + { + "node": null + }, + { + "node": "c" + } + ] + } + } +} diff --git a/src/HotChocolate/Core/test/Types.Tests/SemanticNonNullTests.cs b/src/HotChocolate/Core/test/Types.Tests/SemanticNonNullTests.cs new file mode 100644 index 00000000000..f20ad4eaa9e --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests/SemanticNonNullTests.cs @@ -0,0 +1,334 @@ +#nullable enable + +using HotChocolate.Execution; +using HotChocolate.Tests; +using HotChocolate.Types; +using HotChocolate.Types.Relay; +using Microsoft.Extensions.DependencyInjection; + +namespace HotChocolate; + +public class SemanticNonNullTests +{ + [Fact] + public async Task Object_Implementing_Node() + { + await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + o.EnsureAllNodesCanBeResolved = false; + }) + .AddQueryType() + .AddGlobalObjectIdentification() + .BuildSchemaAsync() + .MatchSnapshotAsync(); + } + + [Fact] + public async Task MutationConventions() + { + await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.StrictValidation = false; + o.EnableSemanticNonNull = true; + }) + .AddMutationConventions() + .AddMutationType() + .BuildSchemaAsync() + .MatchSnapshotAsync(); + } + + [Fact] + public async Task Pagination() + { + await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => + { + o.EnableSemanticNonNull = true; + }) + .AddQueryType() + .BuildSchemaAsync() + .MatchSnapshotAsync(); + } + + [Fact] + public async Task Derive_SemanticNonNull_From_ImplementationFirst() + { + await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => o.EnableSemanticNonNull = true) + .AddQueryType() + .BuildSchemaAsync() + .MatchSnapshotAsync(); + } + + [Fact] + public async Task Derive_SemanticNonNull_From_ImplementationFirst_With_GraphQLType_As_Type() + { + await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => o.EnableSemanticNonNull = true) + .AddQueryType() + .BuildSchemaAsync() + .MatchSnapshotAsync(); + } + + [Fact] + public async Task Derive_SemanticNonNull_From_ImplementationFirst_With_GraphQLType_As_String() + { + await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => o.EnableSemanticNonNull = true) + .AddType() + .AddQueryType() + .BuildSchemaAsync() + .MatchSnapshotAsync(); + } + + [Fact] + public async Task Derive_SemanticNonNull_From_CodeFirst() + { + await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => o.EnableSemanticNonNull = true) + .AddQueryType() + .UseField(_ => _ => default) + .BuildSchemaAsync() + .MatchSnapshotAsync(); + } + + [Fact] + public async Task Apply_SemanticNonNull_To_SchemaFirst() + { + await new ServiceCollection() + .AddGraphQL() + .ModifyOptions(o => o.EnableSemanticNonNull = true) + .AddDocumentFromString( + """ + type Query { + scalar: String + nonNulScalar: String! + scalarArray: [String] + nonNullScalarArray: [String!]! + outerNonNullScalarArray: [String]! + scalarNestedArray: [[String]] + nonNullScalarNestedArray: [[String!]!]! + innerNonNullScalarNestedArray: [[String!]]! + object: Foo + nonNullObject: Foo! + objectArray: [Foo] + nonNullObjectArray: [Foo!]! + objectNestedArray: [[Foo]] + nonNullObjectNestedArray: [[Foo!]!]! + innerNonNullObjectNestedArray: [[Foo!]]! + } + + type Foo { + bar: String! + } + """) + .UseField(_ => _ => default) + .BuildSchemaAsync() + .MatchSnapshotAsync(); + } + + public class QueryType : ObjectType + { + protected override void Configure(IObjectTypeDescriptor descriptor) + { + descriptor.Name("Query"); + descriptor.Field("scalar").Type(); + descriptor.Field("nonNulScalar").Type>(); + descriptor.Field("scalarArray").Type>(); + descriptor.Field("nonNullScalarArray").Type>>>(); + descriptor.Field("outerNonNullScalarArray").Type>>(); + descriptor.Field("scalarNestedArray").Type>>(); + descriptor.Field("nonNullScalarNestedArray").Type>>>>>(); + descriptor.Field("innerNonNullScalarNestedArray").Type>>>>(); + descriptor.Field("object").Type(); + descriptor.Field("nonNullObject").Type>(); + descriptor.Field("objectArray").Type>(); + descriptor.Field("nonNullObjectArray").Type>>>(); + descriptor.Field("objectNestedArray").Type>>(); + descriptor.Field("nonNullObjectNestedArray").Type>>>>>(); + descriptor.Field("innerNonNullObjectNestedArray").Type>>>>(); + } + } + + public class FooType : ObjectType + { + protected override void Configure(IObjectTypeDescriptor descriptor) + { + descriptor.Name("Foo"); + descriptor.Field("bar").Type>(); + } + } + + public class Query + { + public string? Scalar { get; } + + public string NonNulScalar { get; } = null!; + + public string?[]? ScalarArray { get; } + + public string[] NonNullScalarArray { get; } = null!; + + public string?[] OuterNonNullScalarArray { get; } = null!; + + public string?[]?[]? ScalarNestedArray { get; } + + public string[][] NonNullScalarNestedArray { get; } = null!; + + public string[]?[] InnerNonNullScalarNestedArray { get; } = null!; + + public Foo? Object { get; } + + public Foo NonNullObject { get; } = null!; + + public Foo?[]? ObjectArray { get; } + + public Foo[] NonNullObjectArray { get; } = null!; + + public Foo?[]?[]? ObjectNestedArray { get; } + + public Foo[][] NonNullObjectNestedArray { get; } = null!; + + public Foo[]?[] InnerNonNullObjectNestedArray { get; } = null!; + } + + [ObjectType("Query")] + public class QueryWithTypeAttribute + { + [GraphQLType] + public string? Scalar { get; } + + [GraphQLType>] + public string NonNulScalar { get; } = null!; + + [GraphQLType>] + public string?[]? ScalarArray { get; } + + [GraphQLType>>>] + public string[] NonNullScalarArray { get; } = null!; + + [GraphQLType>>] + public string?[] OuterNonNullScalarArray { get; } = null!; + + [GraphQLType>>] + public string?[]?[]? ScalarNestedArray { get; } + + [GraphQLType>>>>>] + public string[][] NonNullScalarNestedArray { get; } = null!; + + [GraphQLType>>>>] + public string[]?[] InnerNonNullScalarNestedArray { get; } = null!; + + [GraphQLType] + public Foo? Object { get; } + + [GraphQLType>] + public Foo NonNullObject { get; } = null!; + + [GraphQLType>] + public Foo?[]? ObjectArray { get; } + + [GraphQLType>>>] + public Foo[] NonNullObjectArray { get; } = null!; + + [GraphQLType>>] + public Foo?[]?[]? ObjectNestedArray { get; } + + [GraphQLType>>>>>] + public Foo[][] NonNullObjectNestedArray { get; } = null!; + + [GraphQLType>>>>] + public Foo[]?[] InnerNonNullObjectNestedArray { get; } = null!; + } + + [ObjectType("Query")] + public class QueryWithTypeAttributeAsString + { + [GraphQLType("String")] + public string? Scalar { get; } + + [GraphQLType("String!")] + public string NonNulScalar { get; } = null!; + + [GraphQLType("[String]")] + public string?[]? ScalarArray { get; } + + [GraphQLType("[String!]!")] + public string[] NonNullScalarArray { get; } = null!; + + [GraphQLType("[String]!")] + public string?[] OuterNonNullScalarArray { get; } = null!; + + [GraphQLType("[[String]]")] + public string?[]?[]? ScalarNestedArray { get; } + + [GraphQLType("[[String!]!]!")] + public string[][] NonNullScalarNestedArray { get; } = null!; + + [GraphQLType("[[String!]]!")] + public string[]?[] InnerNonNullScalarNestedArray { get; } = null!; + + [GraphQLType("Foo")] + public Foo? Object { get; } + + [GraphQLType("Foo!")] + public Foo NonNullObject { get; } = null!; + + [GraphQLType("[Foo]")] + public Foo?[]? ObjectArray { get; } + + [GraphQLType("[Foo!]!")] + public Foo[] NonNullObjectArray { get; } = null!; + + [GraphQLType("[[Foo]]")] + public Foo?[]?[]? ObjectNestedArray { get; } + + [GraphQLType("[[Foo!]!]!")] + public Foo[][] NonNullObjectNestedArray { get; } = null!; + + [GraphQLType("[[Foo!]]!")] + public Foo[]?[] InnerNonNullObjectNestedArray { get; } = null!; + } + + public class Foo + { + public string Bar { get; } = default!; + } + + [ObjectType("Query")] + public class QueryWithNode + { + public MyNode GetMyNode() => new(1); + } + + [Node] + public record MyNode([property: ID] int Id); + + public class Mutation + { + [UseMutationConvention] + [Error] + public bool DoSomething() => true; + } + + public class MyException : Exception; + + public class QueryWithPagination + { + [UsePaging] + public string[] GetCursorPagination() => []; + + [UseOffsetPaging] + public string[] GetOffsetPagination() => []; + } +} diff --git a/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Apply_SemanticNonNull_To_SchemaFirst.snap b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Apply_SemanticNonNull_To_SchemaFirst.snap new file mode 100644 index 00000000000..08e902a8348 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Apply_SemanticNonNull_To_SchemaFirst.snap @@ -0,0 +1,27 @@ +schema { + query: Query +} + +type Foo { + bar: String @semanticNonNull +} + +type Query { + scalar: String + nonNulScalar: String @semanticNonNull + scalarArray: [String] + nonNullScalarArray: [String] @semanticNonNull(levels: [ 0, 1 ]) + outerNonNullScalarArray: [String] @semanticNonNull + scalarNestedArray: [[String]] + nonNullScalarNestedArray: [[String]] @semanticNonNull(levels: [ 0, 1, 2 ]) + innerNonNullScalarNestedArray: [[String]] @semanticNonNull(levels: [ 0, 2 ]) + object: Foo + nonNullObject: Foo @semanticNonNull + objectArray: [Foo] + nonNullObjectArray: [Foo] @semanticNonNull(levels: [ 0, 1 ]) + objectNestedArray: [[Foo]] + nonNullObjectNestedArray: [[Foo]] @semanticNonNull(levels: [ 0, 1, 2 ]) + innerNonNullObjectNestedArray: [[Foo]] @semanticNonNull(levels: [ 0, 2 ]) +} + +directive @semanticNonNull(levels: [Int!] = [ 0 ]) on FIELD_DEFINITION diff --git a/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Derive_SemanticNonNull_From_CodeFirst.snap b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Derive_SemanticNonNull_From_CodeFirst.snap new file mode 100644 index 00000000000..08e902a8348 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Derive_SemanticNonNull_From_CodeFirst.snap @@ -0,0 +1,27 @@ +schema { + query: Query +} + +type Foo { + bar: String @semanticNonNull +} + +type Query { + scalar: String + nonNulScalar: String @semanticNonNull + scalarArray: [String] + nonNullScalarArray: [String] @semanticNonNull(levels: [ 0, 1 ]) + outerNonNullScalarArray: [String] @semanticNonNull + scalarNestedArray: [[String]] + nonNullScalarNestedArray: [[String]] @semanticNonNull(levels: [ 0, 1, 2 ]) + innerNonNullScalarNestedArray: [[String]] @semanticNonNull(levels: [ 0, 2 ]) + object: Foo + nonNullObject: Foo @semanticNonNull + objectArray: [Foo] + nonNullObjectArray: [Foo] @semanticNonNull(levels: [ 0, 1 ]) + objectNestedArray: [[Foo]] + nonNullObjectNestedArray: [[Foo]] @semanticNonNull(levels: [ 0, 1, 2 ]) + innerNonNullObjectNestedArray: [[Foo]] @semanticNonNull(levels: [ 0, 2 ]) +} + +directive @semanticNonNull(levels: [Int!] = [ 0 ]) on FIELD_DEFINITION diff --git a/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Derive_SemanticNonNull_From_ImplementationFirst.snap b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Derive_SemanticNonNull_From_ImplementationFirst.snap new file mode 100644 index 00000000000..08e902a8348 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Derive_SemanticNonNull_From_ImplementationFirst.snap @@ -0,0 +1,27 @@ +schema { + query: Query +} + +type Foo { + bar: String @semanticNonNull +} + +type Query { + scalar: String + nonNulScalar: String @semanticNonNull + scalarArray: [String] + nonNullScalarArray: [String] @semanticNonNull(levels: [ 0, 1 ]) + outerNonNullScalarArray: [String] @semanticNonNull + scalarNestedArray: [[String]] + nonNullScalarNestedArray: [[String]] @semanticNonNull(levels: [ 0, 1, 2 ]) + innerNonNullScalarNestedArray: [[String]] @semanticNonNull(levels: [ 0, 2 ]) + object: Foo + nonNullObject: Foo @semanticNonNull + objectArray: [Foo] + nonNullObjectArray: [Foo] @semanticNonNull(levels: [ 0, 1 ]) + objectNestedArray: [[Foo]] + nonNullObjectNestedArray: [[Foo]] @semanticNonNull(levels: [ 0, 1, 2 ]) + innerNonNullObjectNestedArray: [[Foo]] @semanticNonNull(levels: [ 0, 2 ]) +} + +directive @semanticNonNull(levels: [Int!] = [ 0 ]) on FIELD_DEFINITION diff --git a/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Derive_SemanticNonNull_From_ImplementationFirst_With_GraphQLType_As_String.snap b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Derive_SemanticNonNull_From_ImplementationFirst_With_GraphQLType_As_String.snap new file mode 100644 index 00000000000..08e902a8348 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Derive_SemanticNonNull_From_ImplementationFirst_With_GraphQLType_As_String.snap @@ -0,0 +1,27 @@ +schema { + query: Query +} + +type Foo { + bar: String @semanticNonNull +} + +type Query { + scalar: String + nonNulScalar: String @semanticNonNull + scalarArray: [String] + nonNullScalarArray: [String] @semanticNonNull(levels: [ 0, 1 ]) + outerNonNullScalarArray: [String] @semanticNonNull + scalarNestedArray: [[String]] + nonNullScalarNestedArray: [[String]] @semanticNonNull(levels: [ 0, 1, 2 ]) + innerNonNullScalarNestedArray: [[String]] @semanticNonNull(levels: [ 0, 2 ]) + object: Foo + nonNullObject: Foo @semanticNonNull + objectArray: [Foo] + nonNullObjectArray: [Foo] @semanticNonNull(levels: [ 0, 1 ]) + objectNestedArray: [[Foo]] + nonNullObjectNestedArray: [[Foo]] @semanticNonNull(levels: [ 0, 1, 2 ]) + innerNonNullObjectNestedArray: [[Foo]] @semanticNonNull(levels: [ 0, 2 ]) +} + +directive @semanticNonNull(levels: [Int!] = [ 0 ]) on FIELD_DEFINITION diff --git a/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Derive_SemanticNonNull_From_ImplementationFirst_With_GraphQLType_As_Type.snap b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Derive_SemanticNonNull_From_ImplementationFirst_With_GraphQLType_As_Type.snap new file mode 100644 index 00000000000..08e902a8348 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Derive_SemanticNonNull_From_ImplementationFirst_With_GraphQLType_As_Type.snap @@ -0,0 +1,27 @@ +schema { + query: Query +} + +type Foo { + bar: String @semanticNonNull +} + +type Query { + scalar: String + nonNulScalar: String @semanticNonNull + scalarArray: [String] + nonNullScalarArray: [String] @semanticNonNull(levels: [ 0, 1 ]) + outerNonNullScalarArray: [String] @semanticNonNull + scalarNestedArray: [[String]] + nonNullScalarNestedArray: [[String]] @semanticNonNull(levels: [ 0, 1, 2 ]) + innerNonNullScalarNestedArray: [[String]] @semanticNonNull(levels: [ 0, 2 ]) + object: Foo + nonNullObject: Foo @semanticNonNull + objectArray: [Foo] + nonNullObjectArray: [Foo] @semanticNonNull(levels: [ 0, 1 ]) + objectNestedArray: [[Foo]] + nonNullObjectNestedArray: [[Foo]] @semanticNonNull(levels: [ 0, 1, 2 ]) + innerNonNullObjectNestedArray: [[Foo]] @semanticNonNull(levels: [ 0, 2 ]) +} + +directive @semanticNonNull(levels: [Int!] = [ 0 ]) on FIELD_DEFINITION diff --git a/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.MutationConventions.snap b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.MutationConventions.snap new file mode 100644 index 00000000000..9441a12814c --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.MutationConventions.snap @@ -0,0 +1,24 @@ +schema { + mutation: Mutation +} + +interface Error { + message: String @semanticNonNull +} + +type DoSomethingPayload { + boolean: Boolean + errors: [DoSomethingError] @semanticNonNull(levels: [ 1 ]) +} + +type Mutation { + doSomething: DoSomethingPayload! +} + +type MyError implements Error { + message: String @semanticNonNull +} + +union DoSomethingError = MyError + +directive @semanticNonNull(levels: [Int!] = [ 0 ]) on FIELD_DEFINITION diff --git a/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Object_Implementing_Node.snap b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Object_Implementing_Node.snap new file mode 100644 index 00000000000..454e9932455 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Object_Implementing_Node.snap @@ -0,0 +1,22 @@ +schema { + query: Query +} + +"The node interface is implemented by entities that have a global unique identifier." +interface Node { + id: ID! +} + +type MyNode implements Node { + id: ID! +} + +type Query { + "Fetches an object given its ID." + node("ID of the object." id: ID!): Node + "Lookup nodes by a list of IDs." + nodes("The list of node IDs." ids: [ID!]!): [Node]! + myNode: MyNode @semanticNonNull +} + +directive @semanticNonNull(levels: [Int!] = [ 0 ]) on FIELD_DEFINITION diff --git a/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Pagination.snap b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Pagination.snap new file mode 100644 index 00000000000..146dc8ea148 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SemanticNonNullTests.Pagination.snap @@ -0,0 +1,56 @@ +schema { + query: QueryWithPagination +} + +"Information about the offset pagination." +type CollectionSegmentInfo { + "Indicates whether more items exist following the set defined by the clients arguments." + hasNextPage: Boolean! + "Indicates whether more items exist prior the set defined by the clients arguments." + hasPreviousPage: Boolean! +} + +"A connection to a list of items." +type CursorPaginationConnection { + "Information to aid in pagination." + pageInfo: PageInfo @semanticNonNull + "A list of edges." + edges: [CursorPaginationEdge] @semanticNonNull(levels: [ 1 ]) + "A flattened list of the nodes." + nodes: [String] @semanticNonNull(levels: [ 1 ]) +} + +"An edge in a connection." +type CursorPaginationEdge { + "A cursor for use in pagination." + cursor: String @semanticNonNull + "The item at the end of the edge." + node: String @semanticNonNull +} + +"A segment of a collection." +type OffsetPaginationCollectionSegment { + "Information to aid in pagination." + pageInfo: CollectionSegmentInfo @semanticNonNull + "A flattened list of the items." + items: [String] @semanticNonNull(levels: [ 1 ]) +} + +"Information about pagination in a connection." +type PageInfo { + "Indicates whether more edges exist following the set defined by the clients arguments." + hasNextPage: Boolean! + "Indicates whether more edges exist prior the set defined by the clients arguments." + hasPreviousPage: Boolean! + "When paginating backwards, the cursor to continue." + startCursor: String + "When paginating forwards, the cursor to continue." + endCursor: String +} + +type QueryWithPagination { + cursorPagination("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): CursorPaginationConnection + offsetPagination(skip: Int take: Int): OffsetPaginationCollectionSegment +} + +directive @semanticNonNull(levels: [Int!] = [ 0 ]) on FIELD_DEFINITION From 2ae0bbc5aed9e7dea4abc849a4b8779ee2c2f0aa Mon Sep 17 00:00:00 2001 From: Glen Date: Fri, 29 Nov 2024 10:27:04 +0200 Subject: [PATCH 113/154] Updated Strawberry Shake targets (#7772) --- .../MetaPackages/Common/MSBuild/StrawberryShake.targets | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/StrawberryShake/MetaPackages/Common/MSBuild/StrawberryShake.targets b/src/StrawberryShake/MetaPackages/Common/MSBuild/StrawberryShake.targets index f688ecb8098..5bc09160921 100644 --- a/src/StrawberryShake/MetaPackages/Common/MSBuild/StrawberryShake.targets +++ b/src/StrawberryShake/MetaPackages/Common/MSBuild/StrawberryShake.targets @@ -42,17 +42,19 @@ 6 7 8 + 9 + Text="The Strawberry Shake code generation requires .NET SDK 6, .NET SDK 7, .NET SDK 8, or .NET SDK 9 to work." + Condition="'$(DotNetMajor)' != '6' AND '$(DotNetMajor)' != '7' AND '$(DotNetMajor)' != '8' AND '$(DotNetMajor)' != '9'" /> $(MSBuildProjectDirectory)\$(GraphQLPersistedOperationOutput.TrimEnd('/').TrimEnd('\')) $([System.IO.Path]::Combine($(MSBuildThisFileDirectory), "..", "tools", "net6", "dotnet-graphql.dll")) $([System.IO.Path]::Combine($(MSBuildThisFileDirectory), "..", "tools", "net7", "dotnet-graphql.dll")) $([System.IO.Path]::Combine($(MSBuildThisFileDirectory), "..", "tools", "net8", "dotnet-graphql.dll")) + $([System.IO.Path]::Combine($(MSBuildThisFileDirectory), "..", "tools", "net9", "dotnet-graphql.dll")) From ae1557d333af1b3760853320370e7fa9ef607611 Mon Sep 17 00:00:00 2001 From: Glen Date: Sun, 10 Nov 2024 14:39:58 +0200 Subject: [PATCH 114/154] Fixed parsing issue in Utf8GraphQLRequestParser#ParseStringOrNull (#7703) --- .../Utf8GraphQLRequestParser.Utilities.cs | 1 + .../Parser/GraphQLRequestParserTests.cs | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/HotChocolate/Language/src/Language.Web/Utf8GraphQLRequestParser.Utilities.cs b/src/HotChocolate/Language/src/Language.Web/Utf8GraphQLRequestParser.Utilities.cs index 12aacc3d0cd..6bc79ecaf7c 100644 --- a/src/HotChocolate/Language/src/Language.Web/Utf8GraphQLRequestParser.Utilities.cs +++ b/src/HotChocolate/Language/src/Language.Web/Utf8GraphQLRequestParser.Utilities.cs @@ -12,6 +12,7 @@ public ref partial struct Utf8GraphQLRequestParser case TokenKind.String: if(_reader.Value.Length == 0) { + _reader.MoveNext(); return null; } diff --git a/src/HotChocolate/Language/test/Language.Tests/Parser/GraphQLRequestParserTests.cs b/src/HotChocolate/Language/test/Language.Tests/Parser/GraphQLRequestParserTests.cs index 7e56b68475e..dc6973f8e67 100644 --- a/src/HotChocolate/Language/test/Language.Tests/Parser/GraphQLRequestParserTests.cs +++ b/src/HotChocolate/Language/test/Language.Tests/Parser/GraphQLRequestParserTests.cs @@ -661,6 +661,32 @@ public void Parse_Invalid_Query() }); } + [Fact] + public void Parse_Empty_OperationName() + { + // arrange + var source = Encoding.UTF8.GetBytes( + """ + { + "operationName": "", + "query": "{}" + } + """.NormalizeLineBreaks()); + var parserOptions = new ParserOptions(); + var requestParser = new Utf8GraphQLRequestParser( + source, + parserOptions, + new DocumentCache(), + new Sha256DocumentHashProvider()); + + // act + var batch = requestParser.Parse(); + + // assert + var request = Assert.Single(batch); + Assert.Null(request.OperationName); + } + [Fact] public void Parse_Empty_Json() { From a390e461ae9756495e6a5c0ae22f428e42c18b0d Mon Sep 17 00:00:00 2001 From: Glen Date: Tue, 12 Nov 2024 08:59:17 +0200 Subject: [PATCH 115/154] Fixed issue with non-deterministic order of automatic type registrations (#7708) --- .../Generators/TypeModuleSyntaxGenerator.cs | 2 +- .../Models/DataLoaderDefaultsInfo.cs | 2 + .../Types.Analyzers/Models/DataLoaderInfo.cs | 2 + .../Models/DataLoaderModuleInfo.cs | 2 + .../Models/InterfaceTypeExtensionInfo.cs | 2 + .../src/Types.Analyzers/Models/ModuleInfo.cs | 2 + .../Models/ObjectTypeExtensionInfo.cs | 2 + .../Types.Analyzers/Models/OperationInfo.cs | 2 + .../Models/OperationRegistrationInfo.cs | 2 + .../Models/RegisterDataLoaderInfo.cs | 2 + .../Models/RequestMiddlewareInfo.cs | 2 + .../src/Types.Analyzers/Models/SyntaxInfo.cs | 2 + .../Models/TypeExtensionInfo.cs | 2 + .../src/Types.Analyzers/Models/TypeInfo.cs | 2 + .../test/Types.Analyzers.Tests/TestHelper.cs | 8 +- .../TypeModuleSyntaxGeneratorTests.cs | 160 ++++++++++ ...urce_TypeModuleOrdering_MatchesSnapshot.md | 276 ++++++++++++++++++ 17 files changed, 468 insertions(+), 4 deletions(-) create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/TypeModuleSyntaxGeneratorTests.cs create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/TypeModuleSyntaxGeneratorTests.GenerateSource_TypeModuleOrdering_MatchesSnapshot.md diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Generators/TypeModuleSyntaxGenerator.cs b/src/HotChocolate/Core/src/Types.Analyzers/Generators/TypeModuleSyntaxGenerator.cs index 44128fc1927..1e263527958 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Generators/TypeModuleSyntaxGenerator.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Generators/TypeModuleSyntaxGenerator.cs @@ -63,7 +63,7 @@ private static void WriteConfiguration( var hasInterfaceTypes = false; var hasConfigurations = false; - foreach (var syntaxInfo in syntaxInfos) + foreach (var syntaxInfo in syntaxInfos.OrderBy(s => s.OrderByKey)) { if(syntaxInfo.Diagnostics.Length > 0) { diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderDefaultsInfo.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderDefaultsInfo.cs index b20d2b6f4a4..e03bac44820 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderDefaultsInfo.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderDefaultsInfo.cs @@ -18,6 +18,8 @@ public sealed class DataLoaderDefaultsInfo( public bool GenerateInterfaces { get; } = generateInterfaces; + public override string OrderByKey => string.Empty; + public override bool Equals(object? obj) => obj is DataLoaderDefaultsInfo other && Equals(other); diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderInfo.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderInfo.cs index a7b0ca2e071..17f1287d808 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderInfo.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderInfo.cs @@ -78,6 +78,8 @@ public DataLoaderInfo( public ImmutableArray Parameters { get; } + public override string OrderByKey => FullName; + public ImmutableArray GetLookups(ITypeSymbol keyType, ITypeSymbol valueType) { if (_lookups.Length > 0) diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderModuleInfo.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderModuleInfo.cs index f4e591c801e..2aa450888c3 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderModuleInfo.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderModuleInfo.cs @@ -4,6 +4,8 @@ public sealed class DataLoaderModuleInfo(string moduleName) : SyntaxInfo { public string ModuleName { get; } = moduleName; + public override string OrderByKey => ModuleName; + public override bool Equals(object? obj) => obj is DataLoaderModuleInfo other && Equals(other); diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/InterfaceTypeExtensionInfo.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/InterfaceTypeExtensionInfo.cs index 1e02710b2f3..fd6cf1481d7 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/InterfaceTypeExtensionInfo.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/InterfaceTypeExtensionInfo.cs @@ -23,6 +23,8 @@ public sealed class InterfaceTypeExtensionInfo( public ImmutableArray Resolvers { get; } = resolvers; + public override string OrderByKey => Name; + public override bool Equals(object? obj) => obj is ObjectTypeExtensionInfo other && Equals(other); diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/ModuleInfo.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/ModuleInfo.cs index 8f3e84c110d..d4b210b691d 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/ModuleInfo.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/ModuleInfo.cs @@ -6,6 +6,8 @@ public sealed class ModuleInfo(string moduleName, ModuleOptions options) : Synta public ModuleOptions Options { get; } = options; + public override string OrderByKey => ModuleName; + public override bool Equals(object? obj) => obj is ModuleInfo other && Equals(other); diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/ObjectTypeExtensionInfo.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/ObjectTypeExtensionInfo.cs index 48476007c07..d9d6e532541 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/ObjectTypeExtensionInfo.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/ObjectTypeExtensionInfo.cs @@ -26,6 +26,8 @@ public sealed class ObjectTypeExtensionInfo( public ImmutableArray Resolvers { get; } = resolvers; + public override string OrderByKey => Name; + public override bool Equals(object? obj) => obj is ObjectTypeExtensionInfo other && Equals(other); diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/OperationInfo.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/OperationInfo.cs index 80d8a8370ab..0e5667f650c 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/OperationInfo.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/OperationInfo.cs @@ -8,6 +8,8 @@ public sealed class OperationInfo(OperationType type, string typeName, string me public string MethodName { get; } = methodName; + public override string OrderByKey => TypeName; + public override bool Equals(object? obj) => obj is OperationInfo other && Equals(other); diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/OperationRegistrationInfo.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/OperationRegistrationInfo.cs index eaacce16c7f..183f3410829 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/OperationRegistrationInfo.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/OperationRegistrationInfo.cs @@ -6,6 +6,8 @@ public sealed class OperationRegistrationInfo(OperationType type, string typeNam public string TypeName { get; } = typeName; + public override string OrderByKey => TypeName; + public override bool Equals(object? obj) => obj is OperationRegistrationInfo other && Equals(other); diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/RegisterDataLoaderInfo.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/RegisterDataLoaderInfo.cs index 3b5fabaaf15..5ea029d6610 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/RegisterDataLoaderInfo.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/RegisterDataLoaderInfo.cs @@ -4,6 +4,8 @@ public sealed class RegisterDataLoaderInfo(string name) : SyntaxInfo { public string Name { get; } = name; + public override string OrderByKey => Name; + public override bool Equals(object? obj) => obj is RegisterDataLoaderInfo other && Equals(other); diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/RequestMiddlewareInfo.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/RequestMiddlewareInfo.cs index 103339d961b..a0d6dfbc21c 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/RequestMiddlewareInfo.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/RequestMiddlewareInfo.cs @@ -21,6 +21,8 @@ public sealed class RequestMiddlewareInfo( public List InvokeParameters { get; } = invokeParameters; + public override string OrderByKey => Name; + public override bool Equals(object? obj) => obj is RequestMiddlewareInfo other && Equals(other); diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/SyntaxInfo.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/SyntaxInfo.cs index b3b73f33602..43fecf1f506 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/SyntaxInfo.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/SyntaxInfo.cs @@ -5,6 +5,8 @@ namespace HotChocolate.Types.Analyzers.Models; public abstract class SyntaxInfo : IEquatable { + public abstract string OrderByKey { get; } + public ImmutableArray Diagnostics { get; private set; } = ImmutableArray.Empty; public void AddDiagnostic(Diagnostic diagnostic) diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/TypeExtensionInfo.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/TypeExtensionInfo.cs index 8d593a7c6a7..c622bbb245f 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/TypeExtensionInfo.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/TypeExtensionInfo.cs @@ -8,6 +8,8 @@ public sealed class TypeExtensionInfo(string name, bool isStatic, OperationType public OperationType Type { get; } = type; + public override string OrderByKey => Name; + public override bool Equals(object? obj) => obj is TypeExtensionInfo other && Equals(other); diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/TypeInfo.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/TypeInfo.cs index 18fcbccdf30..37dfa3fd5ad 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/TypeInfo.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/TypeInfo.cs @@ -4,6 +4,8 @@ public sealed class TypeInfo(string name) : SyntaxInfo { public string Name { get; } = name; + public override string OrderByKey => Name; + public override bool Equals(object? obj) => obj is TypeInfo other && Equals(other); diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs index 1ecd1bb24f7..b8df05d5b73 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs @@ -21,9 +21,11 @@ internal static partial class TestHelper public static Snapshot GetGeneratedSourceSnapshot([StringSyntax("csharp")] string sourceText) { - // Parse the provided string into a C# syntax tree. - var syntaxTree = CSharpSyntaxTree.ParseText(sourceText); + return GetGeneratedSourceSnapshot([sourceText]); + } + public static Snapshot GetGeneratedSourceSnapshot(string[] sourceTexts) + { IEnumerable references = [ #if NET8_0 @@ -48,7 +50,7 @@ public static Snapshot GetGeneratedSourceSnapshot([StringSyntax("csharp")] strin // Create a Roslyn compilation for the syntax tree. var compilation = CSharpCompilation.Create( assemblyName: "Tests", - syntaxTrees: [syntaxTree], + syntaxTrees: sourceTexts.Select(s => CSharpSyntaxTree.ParseText(s)), references); // Create an instance of our GraphQLServerGenerator incremental source generator. diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/TypeModuleSyntaxGeneratorTests.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/TypeModuleSyntaxGeneratorTests.cs new file mode 100644 index 00000000000..58923ba704b --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/TypeModuleSyntaxGeneratorTests.cs @@ -0,0 +1,160 @@ +namespace HotChocolate.Types; + +public class TypeModuleSyntaxGeneratorTests +{ + [Fact] + public async Task GenerateSource_TypeModuleOrdering_MatchesSnapshot() + { + await TestHelper.GetGeneratedSourceSnapshot( + [ + """ + using HotChocolate.Types; + + namespace TestNamespace; + + internal class ATestBType: ObjectType; + internal record ATestB(int Id); + """, + """ + using HotChocolate.Types; + + namespace TestNamespace; + + internal class ATestAType : ObjectType; + internal record ATestA(int Id); + """, + """ + using HotChocolate.Types; + + namespace TestNamespace; + + [ObjectType] + internal static partial class ATestBAttrType; + internal record ATestBAttr(int Id); + """, + """ + using HotChocolate.Types; + + namespace TestNamespace; + + [ObjectType] + internal static partial class ATestAAttrType; + internal record ATestAAttr(int Id); + """, + """ + using HotChocolate.Types; + + namespace TestNamespace; + + internal class ATestBExtType : ObjectTypeExtension; + internal record ATestBExt(int Id); + """, + """ + using HotChocolate.Types; + + namespace TestNamespace; + + internal class ATestAExtType : ObjectTypeExtension; + internal record ATestAExt(int Id); + """, + """ + using HotChocolate.Types; + + namespace TestNamespace; + + [ExtendObjectType] + internal class ATestBExtAttrType; + internal record ATestBExtAttr(int Id); + """, + """ + using HotChocolate.Types; + + namespace TestNamespace; + + [ExtendObjectType] + internal class ATestAExtAttrType; + internal record ATestAExtAttr(int Id); + """, + """ + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using GreenDonut; + + namespace TestNamespace; + + internal class TestBDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options) + : BatchDataLoader(batchScheduler, options) + { + protected override async Task> LoadBatchAsync( + IReadOnlyList ids, + CancellationToken cancellationToken) + { + return await Task.FromResult(new Dictionary()); + } + } + """, + """ + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using GreenDonut; + + namespace TestNamespace; + + internal class TestADataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options) + : BatchDataLoader(batchScheduler, options) + { + protected override async Task> LoadBatchAsync( + IReadOnlyList ids, + CancellationToken cancellationToken) + { + return await Task.FromResult(new Dictionary()); + } + } + """, + """ + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using GreenDonut; + + namespace TestNamespace; + + internal static class TestBDataLoaderAttr + { + [DataLoader] + public static async Task> GetObjectByIdBAsync( + IReadOnlyList ids, + CancellationToken cancellationToken) + { + return await Task.FromResult(new Dictionary()); + } + } + """, + """ + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using GreenDonut; + + namespace TestNamespace; + + internal static class TestADataLoaderAttr + { + [DataLoader] + public static async Task> GetObjectByIdAAsync( + IReadOnlyList ids, + CancellationToken cancellationToken) + { + return await Task.FromResult(new Dictionary()); + } + } + """ + ]).MatchMarkdownAsync(); + } +} diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/TypeModuleSyntaxGeneratorTests.GenerateSource_TypeModuleOrdering_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/TypeModuleSyntaxGeneratorTests.GenerateSource_TypeModuleOrdering_MatchesSnapshot.md new file mode 100644 index 00000000000..30114ad4044 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/TypeModuleSyntaxGeneratorTests.GenerateSource_TypeModuleOrdering_MatchesSnapshot.md @@ -0,0 +1,276 @@ +# GenerateSource_TypeModuleOrdering_MatchesSnapshot + +## GreenDonutDataLoader.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using Microsoft.Extensions.DependencyInjection; +using GreenDonut; + +namespace TestNamespace +{ + public interface IObjectByIdBDataLoader + : global::GreenDonut.IDataLoader + { + } + + public sealed partial class ObjectByIdBDataLoader + : global::GreenDonut.DataLoaderBase + , IObjectByIdBDataLoader + { + private readonly global::System.IServiceProvider _services; + + public ObjectByIdBDataLoader( + global::System.IServiceProvider services, + global::GreenDonut.IBatchScheduler batchScheduler, + global::GreenDonut.DataLoaderOptions options) + : base(batchScheduler, options) + { + _services = services ?? + throw new global::System.ArgumentNullException(nameof(services)); + } + + protected override async global::System.Threading.Tasks.ValueTask FetchAsync( + global::System.Collections.Generic.IReadOnlyList keys, + global::System.Memory> results, + global::GreenDonut.DataLoaderFetchContext context, + global::System.Threading.CancellationToken ct) + { + var temp = await global::TestNamespace.TestBDataLoaderAttr.GetObjectByIdBAsync(keys, ct).ConfigureAwait(false); + CopyResults(keys, results.Span, temp); + } + + private void CopyResults( + global::System.Collections.Generic.IReadOnlyList keys, + global::System.Span> results, + global::System.Collections.Generic.IReadOnlyDictionary resultMap) + { + for (var i = 0; i < keys.Count; i++) + { + var key = keys[i]; + if (resultMap.TryGetValue(key, out var value)) + { + results[i] = global::GreenDonut.Result.Resolve(value); + } + else + { + results[i] = global::GreenDonut.Result.Resolve(default(object)); + } + } + } + } + + public interface IObjectByIdADataLoader + : global::GreenDonut.IDataLoader + { + } + + public sealed partial class ObjectByIdADataLoader + : global::GreenDonut.DataLoaderBase + , IObjectByIdADataLoader + { + private readonly global::System.IServiceProvider _services; + + public ObjectByIdADataLoader( + global::System.IServiceProvider services, + global::GreenDonut.IBatchScheduler batchScheduler, + global::GreenDonut.DataLoaderOptions options) + : base(batchScheduler, options) + { + _services = services ?? + throw new global::System.ArgumentNullException(nameof(services)); + } + + protected override async global::System.Threading.Tasks.ValueTask FetchAsync( + global::System.Collections.Generic.IReadOnlyList keys, + global::System.Memory> results, + global::GreenDonut.DataLoaderFetchContext context, + global::System.Threading.CancellationToken ct) + { + var temp = await global::TestNamespace.TestADataLoaderAttr.GetObjectByIdAAsync(keys, ct).ConfigureAwait(false); + CopyResults(keys, results.Span, temp); + } + + private void CopyResults( + global::System.Collections.Generic.IReadOnlyList keys, + global::System.Span> results, + global::System.Collections.Generic.IReadOnlyDictionary resultMap) + { + for (var i = 0; i < keys.Count; i++) + { + var key = keys[i]; + if (resultMap.TryGetValue(key, out var value)) + { + results[i] = global::GreenDonut.Result.Resolve(value); + } + else + { + results[i] = global::GreenDonut.Result.Resolve(default(object)); + } + } + } + } +} + + +``` + +## HotChocolateResolvers.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using HotChocolate.Internal; + +namespace TestNamespace +{ + internal static class ATestBAttrTypeResolvers + { + private static bool _bindingsInitialized; + public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver) + { + } + } + + internal static class ATestAAttrTypeResolvers + { + private static bool _bindingsInitialized; + public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver) + { + } + } +} + + +``` + +## HotChocolateTypeModule.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + AddObjectTypeExtension_8734371(builder, global::TestNamespace.ATestAAttrType.Initialize); + AddObjectTypeExtension_8734371(builder, global::TestNamespace.ATestBAttrType.Initialize); + builder.AddTypeExtension(); + builder.AddTypeExtension(); + builder.AddType(); + builder.AddTypeExtension(); + builder.AddTypeExtension(); + builder.AddType(); + builder.AddDataLoader(); + builder.AddDataLoader(); + builder.AddDataLoader(); + builder.AddDataLoader(); + return builder; + } + + private static void AddObjectTypeExtension_8734371( + global::HotChocolate.Execution.Configuration.IRequestExecutorBuilder builder, + Action> initialize) + { + builder.ConfigureSchema(sb => + { + string typeName = typeof(T).FullName!; + string typeKey = $"8734371_Type_ObjectType<{typeName}>"; + string hooksKey = $"8734371_Hooks_ObjectType<{typeName}>"; + + if (!sb.ContextData.ContainsKey(typeKey)) + { + sb.AddObjectType( + descriptor => + { + var hooks = (global::System.Collections.Generic.List>>)descriptor.Extend().Context.ContextData[hooksKey]!; + foreach (var configure in hooks) + { + configure(descriptor); + }; + }); + sb.ContextData.Add(typeKey, null); + } + + if (!sb.ContextData.TryGetValue(hooksKey, out var value)) + { + value = new System.Collections.Generic.List>>(); + sb.ContextData.Add(hooksKey, value); + } + + ((System.Collections.Generic.List>>)value!).Add(initialize); + }); + } + } +} + +``` + +## HotChocolateTypes.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace TestNamespace +{ +public static partial class ATestBAttrType +{ + internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor descriptor) + { + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); +} + +public static partial class ATestAAttrType +{ + internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor descriptor) + { + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); +} +} + + +``` + From 09211fff4f8eafeff003aa6bd216a770daab77f8 Mon Sep 17 00:00:00 2001 From: danielreynolds1 <55194784+danielreynolds1@users.noreply.github.com> Date: Thu, 7 Nov 2024 10:07:45 +0000 Subject: [PATCH 116/154] Added "AddDataLoader" overload that takes a factory (#7674) --- .../DataLoaderServiceCollectionExtensions.cs | 13 +++++ ...aLoaderServiceCollectionExtensionsTests.cs | 56 +++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 src/GreenDonut/test/Core.Tests/DependencyInjection/DataLoaderServiceCollectionExtensionsTests.cs diff --git a/src/GreenDonut/src/Core/DependencyInjection/DataLoaderServiceCollectionExtensions.cs b/src/GreenDonut/src/Core/DependencyInjection/DataLoaderServiceCollectionExtensions.cs index 3e6f24daa58..337cf21ddb7 100644 --- a/src/GreenDonut/src/Core/DependencyInjection/DataLoaderServiceCollectionExtensions.cs +++ b/src/GreenDonut/src/Core/DependencyInjection/DataLoaderServiceCollectionExtensions.cs @@ -41,6 +41,19 @@ public static IServiceCollection AddDataLoader( return services; } + public static IServiceCollection AddDataLoader( + this IServiceCollection services, + Func factory) + where TService : class, IDataLoader + where TImplementation : class, TService + { + services.TryAddDataLoaderCore(); + services.AddSingleton(new DataLoaderRegistration(typeof(TService), typeof(TImplementation), sp => factory(sp))); + services.TryAddScoped(sp => sp.GetDataLoader()); + services.TryAddScoped(sp => sp.GetDataLoader()); + return services; + } + public static IServiceCollection TryAddDataLoaderCore( this IServiceCollection services) { diff --git a/src/GreenDonut/test/Core.Tests/DependencyInjection/DataLoaderServiceCollectionExtensionsTests.cs b/src/GreenDonut/test/Core.Tests/DependencyInjection/DataLoaderServiceCollectionExtensionsTests.cs new file mode 100644 index 00000000000..42bc1672d34 --- /dev/null +++ b/src/GreenDonut/test/Core.Tests/DependencyInjection/DataLoaderServiceCollectionExtensionsTests.cs @@ -0,0 +1,56 @@ +using GreenDonut; +using Xunit; +using static GreenDonut.TestHelpers; + +namespace Microsoft.Extensions.DependencyInjection; + +public class DataLoaderServiceCollectionExtensionsTests +{ + [Fact] + public void ImplFactoryIsCalledWhenServiceIsResolved() + { + // arrange + var factoryCalled = false; + var fetch = CreateFetch(); + var services = new ServiceCollection() + .AddScoped() + .AddDataLoader(sp => + { + factoryCalled = true; + return new DataLoader(fetch, sp.GetRequiredService()); + }); + var scope = services.BuildServiceProvider().CreateScope(); + + // act + var dataLoader = scope.ServiceProvider.GetRequiredService>(); + + // assert + Assert.NotNull(dataLoader); + Assert.True(factoryCalled); + } + + [Fact] + public void InterfaceImplFactoryIsCalledWhenServiceIsResolved() + { + // arrange + var factoryCalled = false; + var fetch = CreateFetch(); + var services = new ServiceCollection() + .AddScoped() + .AddDataLoader, DataLoader>(sp => + { + factoryCalled = true; + return new DataLoader(fetch, sp.GetRequiredService()); + }); + var scope = services.BuildServiceProvider().CreateScope(); + + // act + var dataLoader = scope.ServiceProvider.GetRequiredService>(); + var asInterface = scope.ServiceProvider.GetRequiredService>(); + + // assert + Assert.NotNull(dataLoader); + Assert.NotNull(asInterface); + Assert.True(factoryCalled); + } +} From cde43b5b118c5cf5de8b60013f22fb83664a1c56 Mon Sep 17 00:00:00 2001 From: Shaun Becker Date: Wed, 27 Nov 2024 06:52:47 -0500 Subject: [PATCH 117/154] Fix xml method description when dictionary args present (#7765) --- .../Conventions/XmlDocumentationProvider.cs | 3 ++- .../WithDictionaryArgs.cs | 11 +++++++++++ .../Conventions/XmlDocumentationProviderTests.cs | 16 ++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 src/HotChocolate/Core/test/Types.Tests.Documentation/WithDictionaryArgs.cs diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/XmlDocumentationProvider.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/XmlDocumentationProvider.cs index e8bf71564a1..0aa7b82a01a 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/XmlDocumentationProvider.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/XmlDocumentationProvider.cs @@ -405,7 +405,8 @@ private static MemberName GetMemberElementName(MemberInfo member) "(`[0-9]+)|(, .*?PublicKeyToken=[0-9a-z]*)", string.Empty) .Replace("[[", "{") - .Replace("]]", "}")) + .Replace("]]", "}") + .Replace("],[", ",")) .ToArray()); if (!string.IsNullOrEmpty(paramTypesList)) diff --git a/src/HotChocolate/Core/test/Types.Tests.Documentation/WithDictionaryArgs.cs b/src/HotChocolate/Core/test/Types.Tests.Documentation/WithDictionaryArgs.cs new file mode 100644 index 00000000000..31b24e49656 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests.Documentation/WithDictionaryArgs.cs @@ -0,0 +1,11 @@ +namespace HotChocolate.Types.Descriptors; + +public class WithDictionaryArgs +{ + /// + /// This is a method description + /// + /// Args description + /// + public string Method(Dictionary? args = null) => string.Empty; +} diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/Conventions/XmlDocumentationProviderTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/Conventions/XmlDocumentationProviderTests.cs index 8f8788622dd..7b9fbc52f91 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/Conventions/XmlDocumentationProviderTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Descriptors/Conventions/XmlDocumentationProviderTests.cs @@ -414,4 +414,20 @@ public void When_method_has_no_returns_then_it_is_ignored() // assert methodDescription.MatchSnapshot(); } + + [Fact] + public void When_method_has_dictionary_args_then_it_is_found() + { + // arrange + var documentationProvider = new XmlDocumentationProvider( + new XmlDocumentationFileResolver(), + new NoOpStringBuilderPool()); + + // act + var methodDescription = documentationProvider.GetDescription( + typeof(WithDictionaryArgs).GetMethod(nameof(WithDictionaryArgs.Method))!); + + // assert + Assert.Equal("This is a method description", methodDescription); + } } From 59379f3c11255e4a515dd0f8a6b00129f4acc43e Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Fri, 29 Nov 2024 17:55:47 +0100 Subject: [PATCH 118/154] Fixed .NET compatibility of ported features. (#7782) --- .../AspNetCore.Authorization/DefaultAuthorizationHandler.cs | 3 ++- .../src/Types/Types/Directives/SemanticNonNullDirective.cs | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore.Authorization/DefaultAuthorizationHandler.cs b/src/HotChocolate/AspNetCore/src/AspNetCore.Authorization/DefaultAuthorizationHandler.cs index bcc15389477..4101cb2a9e8 100644 --- a/src/HotChocolate/AspNetCore/src/AspNetCore.Authorization/DefaultAuthorizationHandler.cs +++ b/src/HotChocolate/AspNetCore/src/AspNetCore.Authorization/DefaultAuthorizationHandler.cs @@ -43,8 +43,9 @@ public DefaultAuthorizationHandler( throw new ArgumentNullException(nameof(authorizationPolicyProvider)); _authorizationPolicyCache = authorizationPolicyCache ?? throw new ArgumentNullException(nameof(authorizationPolicyCache)); - +#if NET7_0_OR_GREATER _canCachePolicies = _authorizationPolicyProvider.AllowsCachingPolicies; +#endif } /// diff --git a/src/HotChocolate/Core/src/Types/Types/Directives/SemanticNonNullDirective.cs b/src/HotChocolate/Core/src/Types/Types/Directives/SemanticNonNullDirective.cs index 1f64e212a95..d6f47c4e8ac 100644 --- a/src/HotChocolate/Core/src/Types/Types/Directives/SemanticNonNullDirective.cs +++ b/src/HotChocolate/Core/src/Types/Types/Directives/SemanticNonNullDirective.cs @@ -5,7 +5,11 @@ namespace HotChocolate.Types; [DirectiveType(WellKnownDirectives.SemanticNonNull, DirectiveLocation.FieldDefinition, IsRepeatable = false)] public sealed class SemanticNonNullDirective(IReadOnlyList levels) { +#if NETSTANDARD2_0 + [GraphQLType(typeof(ListType>))] +#else [GraphQLType>>] +#endif [DefaultValueSyntax("[0]")] public IReadOnlyList? Levels { get; } = levels; } From a8933042de20acd77b9d88cbf14e64ebe8920ab8 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Fri, 29 Nov 2024 18:09:24 +0100 Subject: [PATCH 119/154] Fixed variable warning. --- .../src/AspNetCore.Authorization/DefaultAuthorizationHandler.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore.Authorization/DefaultAuthorizationHandler.cs b/src/HotChocolate/AspNetCore/src/AspNetCore.Authorization/DefaultAuthorizationHandler.cs index 4101cb2a9e8..2bd17af5b7f 100644 --- a/src/HotChocolate/AspNetCore/src/AspNetCore.Authorization/DefaultAuthorizationHandler.cs +++ b/src/HotChocolate/AspNetCore/src/AspNetCore.Authorization/DefaultAuthorizationHandler.cs @@ -45,6 +45,8 @@ public DefaultAuthorizationHandler( throw new ArgumentNullException(nameof(authorizationPolicyCache)); #if NET7_0_OR_GREATER _canCachePolicies = _authorizationPolicyProvider.AllowsCachingPolicies; +#else + _canCachePolicies = true; #endif } From 8e942899796df6de4a0f391e4b3cdc693fc4cb01 Mon Sep 17 00:00:00 2001 From: danielreynolds1 <55194784+danielreynolds1@users.noreply.github.com> Date: Tue, 3 Dec 2024 10:33:29 +0000 Subject: [PATCH 120/154] Added "AddDataLoader" executor overload that takes a factory (#7787) --- ...estExecutorBuilderExtensions.DataLoader.cs | 12 ++++ .../Integration/DataLoader/DataLoaderTests.cs | 61 +++++++++++++++++++ ..._From_DependencyInjection_Using_Factory.md | 55 +++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 src/HotChocolate/Core/test/Execution.Tests/Integration/DataLoader/__snapshots__/DataLoaderTests.ClassDataLoader_Resolve_From_DependencyInjection_Using_Factory.md diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.DataLoader.cs b/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.DataLoader.cs index a93cb3dd994..dc7cd587228 100644 --- a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.DataLoader.cs +++ b/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.DataLoader.cs @@ -37,6 +37,18 @@ public static IRequestExecutorBuilder AddDataLoader( builder.Services.TryAddScoped(sp => sp.GetDataLoader()); return builder; } + + public static IRequestExecutorBuilder AddDataLoader( + this IRequestExecutorBuilder builder, + Func factory) + where TService : class, IDataLoader + where TImplementation : class, TService + { + builder.Services.AddSingleton(new DataLoaderRegistration(typeof(TService), typeof(TImplementation), sp => factory(sp))); + builder.Services.TryAddScoped(sp => sp.GetDataLoader()); + builder.Services.TryAddScoped(sp => sp.GetDataLoader()); + return builder; + } } file static class DataLoaderServiceProviderExtensions diff --git a/src/HotChocolate/Core/test/Execution.Tests/Integration/DataLoader/DataLoaderTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Integration/DataLoader/DataLoaderTests.cs index 79c48dad239..1a43ca5ac86 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Integration/DataLoader/DataLoaderTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Integration/DataLoader/DataLoaderTests.cs @@ -472,6 +472,67 @@ await executor.ExecuteAsync( await snapshot.MatchMarkdownAsync(); } + [Fact] + public async Task ClassDataLoader_Resolve_From_DependencyInjection_Using_Factory() + { + var snapshot = new Snapshot(); + + // arrange + var executor = await CreateExecutorAsync( + c => c + .AddQueryType() + .AddDataLoader(sp => + new TestDataLoader( + sp.GetRequiredService(), + new DataLoaderOptions())) + .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) + .UseRequest( + next => async context => + { + await next(context); + + var dataLoader = (TestDataLoader)context.Services.GetRequiredService(); + + context.Result = OperationResultBuilder + .FromResult(((IOperationResult)context.Result!)) + .AddExtension("loads", dataLoader.Loads) + .Build(); + }) + .UseDefaultPipeline()); + + // act + snapshot.Add( + await executor.ExecuteAsync( + OperationRequestBuilder.New() + .SetDocument( + @"{ + a: dataLoaderWithInterface(key: ""a"") + b: dataLoaderWithInterface(key: ""b"") + }") + .Build())); + + snapshot.Add( + await executor.ExecuteAsync( + OperationRequestBuilder.New() + .SetDocument( + @"{ + a: dataLoaderWithInterface(key: ""a"") + }") + .Build())); + + snapshot.Add( + await executor.ExecuteAsync( + OperationRequestBuilder.New() + .SetDocument( + @"{ + c: dataLoaderWithInterface(key: ""c"") + }") + .Build())); + + // assert + await snapshot.MatchMarkdownAsync(); + } + [LocalFact] public async Task NestedDataLoader() { diff --git a/src/HotChocolate/Core/test/Execution.Tests/Integration/DataLoader/__snapshots__/DataLoaderTests.ClassDataLoader_Resolve_From_DependencyInjection_Using_Factory.md b/src/HotChocolate/Core/test/Execution.Tests/Integration/DataLoader/__snapshots__/DataLoaderTests.ClassDataLoader_Resolve_From_DependencyInjection_Using_Factory.md new file mode 100644 index 00000000000..5782eeba4de --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/Integration/DataLoader/__snapshots__/DataLoaderTests.ClassDataLoader_Resolve_From_DependencyInjection_Using_Factory.md @@ -0,0 +1,55 @@ +# ClassDataLoader_Resolve_From_DependencyInjection_Using_Factory + +## Result 1 + +```json +{ + "data": { + "a": "a", + "b": "b" + }, + "extensions": { + "loads": [ + [ + "a", + "b" + ] + ] + } +} +``` + +## Result 2 + +```json +{ + "data": { + "a": "a" + }, + "extensions": { + "loads": [ + [ + "a" + ] + ] + } +} +``` + +## Result 3 + +```json +{ + "data": { + "c": "c" + }, + "extensions": { + "loads": [ + [ + "c" + ] + ] + } +} +``` + From 68240995e325c069c221b267fe4acad7f329155c Mon Sep 17 00:00:00 2001 From: Tobias Tengler <45513122+tobias-tengler@users.noreply.github.com> Date: Tue, 3 Dec 2024 11:42:01 +0100 Subject: [PATCH 121/154] Fixed parsing issue in Utf8GraphQLRequestParser#ParseStringOrNull (#7703) --- ...ryCacheRequestExecutorBuilderExtensions.cs | 1 + .../Execution/ExecutionResultKind.cs | 5 + .../OperationRequestBuilderExtensions.cs | 7 + .../Execution/WarmupExecutionResult.cs | 8 + .../src/Abstractions/WellKnownContextData.cs | 5 + ...estExecutorBuilderExtensions.UseRequest.cs | 7 + .../WarmupRequestContextExtensions.cs | 7 + ...nlyPersistedOperationsAllowedMiddleware.cs | 4 +- .../Pipeline/SkipWarmupExecutionMiddleware.cs | 22 ++ .../Execution.Tests/WarmupRequestTests.cs | 100 ++++++ .../FusionRequestExecutorBuilderExtensions.cs | 3 + website/src/docs/docs.json | 332 ++++++++++++++++++ .../docs/hotchocolate/v15/server/warmup.md | 101 ++++++ 13 files changed, 600 insertions(+), 2 deletions(-) create mode 100644 src/HotChocolate/Core/src/Abstractions/Execution/WarmupExecutionResult.cs create mode 100644 src/HotChocolate/Core/src/Execution/Extensions/WarmupRequestContextExtensions.cs create mode 100644 src/HotChocolate/Core/src/Execution/Pipeline/SkipWarmupExecutionMiddleware.cs create mode 100644 src/HotChocolate/Core/test/Execution.Tests/WarmupRequestTests.cs create mode 100644 website/src/docs/hotchocolate/v15/server/warmup.md diff --git a/src/HotChocolate/Caching/src/Caching/Extensions/QueryCacheRequestExecutorBuilderExtensions.cs b/src/HotChocolate/Caching/src/Caching/Extensions/QueryCacheRequestExecutorBuilderExtensions.cs index c798cd71d98..9747fba206e 100644 --- a/src/HotChocolate/Caching/src/Caching/Extensions/QueryCacheRequestExecutorBuilderExtensions.cs +++ b/src/HotChocolate/Caching/src/Caching/Extensions/QueryCacheRequestExecutorBuilderExtensions.cs @@ -42,6 +42,7 @@ public static IRequestExecutorBuilder UseQueryCachePipeline( .UseDocumentValidation() .UseOperationCache() .UseOperationResolver() + .UseSkipWarmupExecution() .UseOperationVariableCoercion() .UseOperationExecution(); } diff --git a/src/HotChocolate/Core/src/Abstractions/Execution/ExecutionResultKind.cs b/src/HotChocolate/Core/src/Abstractions/Execution/ExecutionResultKind.cs index 6892f01cd68..0af580428ce 100644 --- a/src/HotChocolate/Core/src/Abstractions/Execution/ExecutionResultKind.cs +++ b/src/HotChocolate/Core/src/Abstractions/Execution/ExecutionResultKind.cs @@ -24,4 +24,9 @@ public enum ExecutionResultKind /// A subscription response stream. /// SubscriptionResult, + + /// + /// A no-op result for warmup requests. + /// + WarmupResult, } diff --git a/src/HotChocolate/Core/src/Abstractions/Execution/OperationRequestBuilderExtensions.cs b/src/HotChocolate/Core/src/Abstractions/Execution/OperationRequestBuilderExtensions.cs index caabc4720ff..61d599c764e 100644 --- a/src/HotChocolate/Core/src/Abstractions/Execution/OperationRequestBuilderExtensions.cs +++ b/src/HotChocolate/Core/src/Abstractions/Execution/OperationRequestBuilderExtensions.cs @@ -74,4 +74,11 @@ public static OperationRequestBuilder SetUser( this OperationRequestBuilder builder, ClaimsPrincipal claimsPrincipal) => builder.SetGlobalState(nameof(ClaimsPrincipal), claimsPrincipal); + + /// + /// Marks this request as a warmup request that will bypass security measures and skip execution. + /// + public static OperationRequestBuilder MarkAsWarmupRequest( + this OperationRequestBuilder builder) + => builder.SetGlobalState(WellKnownContextData.IsWarmupRequest, true); } diff --git a/src/HotChocolate/Core/src/Abstractions/Execution/WarmupExecutionResult.cs b/src/HotChocolate/Core/src/Abstractions/Execution/WarmupExecutionResult.cs new file mode 100644 index 00000000000..d4b864c9fc8 --- /dev/null +++ b/src/HotChocolate/Core/src/Abstractions/Execution/WarmupExecutionResult.cs @@ -0,0 +1,8 @@ +namespace HotChocolate.Execution; + +public sealed class WarmupExecutionResult : ExecutionResult +{ + public override ExecutionResultKind Kind => ExecutionResultKind.WarmupResult; + + public override IReadOnlyDictionary? ContextData => null; +} diff --git a/src/HotChocolate/Core/src/Abstractions/WellKnownContextData.cs b/src/HotChocolate/Core/src/Abstractions/WellKnownContextData.cs index 21da2c72034..efd00842378 100644 --- a/src/HotChocolate/Core/src/Abstractions/WellKnownContextData.cs +++ b/src/HotChocolate/Core/src/Abstractions/WellKnownContextData.cs @@ -328,4 +328,9 @@ public static class WellKnownContextData /// The key to access the compiled requirements. /// public const string FieldRequirements = "HotChocolate.Types.ObjectField.Requirements"; + + /// + /// The key to determine whether the request is a warmup request. + /// + public const string IsWarmupRequest = "HotChocolate.AspNetCore.Warmup.IsWarmupRequest"; } diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.UseRequest.cs b/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.UseRequest.cs index 768df411ddf..193ff3ab283 100644 --- a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.UseRequest.cs +++ b/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.UseRequest.cs @@ -120,6 +120,10 @@ public static IRequestExecutorBuilder UseOperationVariableCoercion( this IRequestExecutorBuilder builder) => builder.UseRequest(OperationVariableCoercionMiddleware.Create()); + public static IRequestExecutorBuilder UseSkipWarmupExecution( + this IRequestExecutorBuilder builder) => + builder.UseRequest(SkipWarmupExecutionMiddleware.Create()); + public static IRequestExecutorBuilder UseReadPersistedOperation( this IRequestExecutorBuilder builder) => builder.UseRequest(ReadPersistedOperationMiddleware.Create()); @@ -191,6 +195,7 @@ public static IRequestExecutorBuilder UsePersistedOperationPipeline( .UseDocumentValidation() .UseOperationCache() .UseOperationResolver() + .UseSkipWarmupExecution() .UseOperationVariableCoercion() .UseOperationExecution(); } @@ -215,6 +220,7 @@ public static IRequestExecutorBuilder UseAutomaticPersistedOperationPipeline( .UseDocumentValidation() .UseOperationCache() .UseOperationResolver() + .UseSkipWarmupExecution() .UseOperationVariableCoercion() .UseOperationExecution(); } @@ -229,6 +235,7 @@ internal static void AddDefaultPipeline(this IList pipeli pipeline.Add(DocumentValidationMiddleware.Create()); pipeline.Add(OperationCacheMiddleware.Create()); pipeline.Add(OperationResolverMiddleware.Create()); + pipeline.Add(SkipWarmupExecutionMiddleware.Create()); pipeline.Add(OperationVariableCoercionMiddleware.Create()); pipeline.Add(OperationExecutionMiddleware.Create()); } diff --git a/src/HotChocolate/Core/src/Execution/Extensions/WarmupRequestContextExtensions.cs b/src/HotChocolate/Core/src/Execution/Extensions/WarmupRequestContextExtensions.cs new file mode 100644 index 00000000000..21eab8a7994 --- /dev/null +++ b/src/HotChocolate/Core/src/Execution/Extensions/WarmupRequestContextExtensions.cs @@ -0,0 +1,7 @@ +namespace HotChocolate.Execution; + +public static class WarmupRequestExecutorExtensions +{ + public static bool IsWarmupRequest(this IRequestContext requestContext) + => requestContext.ContextData.ContainsKey(WellKnownContextData.IsWarmupRequest); +} diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/OnlyPersistedOperationsAllowedMiddleware.cs b/src/HotChocolate/Core/src/Execution/Pipeline/OnlyPersistedOperationsAllowedMiddleware.cs index 64508627949..6cab50e07b1 100644 --- a/src/HotChocolate/Core/src/Execution/Pipeline/OnlyPersistedOperationsAllowedMiddleware.cs +++ b/src/HotChocolate/Core/src/Execution/Pipeline/OnlyPersistedOperationsAllowedMiddleware.cs @@ -38,8 +38,8 @@ private OnlyPersistedOperationsAllowedMiddleware( public ValueTask InvokeAsync(IRequestContext context) { - // if all operations are allowed we can skip this middleware. - if(!_options.OnlyAllowPersistedDocuments) + // if all operations are allowed or the request is a warmup request, we can skip this middleware. + if(!_options.OnlyAllowPersistedDocuments || context.IsWarmupRequest()) { return _next(context); } diff --git a/src/HotChocolate/Core/src/Execution/Pipeline/SkipWarmupExecutionMiddleware.cs b/src/HotChocolate/Core/src/Execution/Pipeline/SkipWarmupExecutionMiddleware.cs new file mode 100644 index 00000000000..9a97cfbcc74 --- /dev/null +++ b/src/HotChocolate/Core/src/Execution/Pipeline/SkipWarmupExecutionMiddleware.cs @@ -0,0 +1,22 @@ +namespace HotChocolate.Execution.Pipeline; + +internal sealed class SkipWarmupExecutionMiddleware(RequestDelegate next) +{ + public async ValueTask InvokeAsync(IRequestContext context) + { + if (context.IsWarmupRequest()) + { + context.Result = new WarmupExecutionResult(); + return; + } + + await next(context).ConfigureAwait(false); + } + + public static RequestCoreMiddleware Create() + => (_, next) => + { + var middleware = new SkipWarmupExecutionMiddleware(next); + return context => middleware.InvokeAsync(context); + }; +} diff --git a/src/HotChocolate/Core/test/Execution.Tests/WarmupRequestTests.cs b/src/HotChocolate/Core/test/Execution.Tests/WarmupRequestTests.cs new file mode 100644 index 00000000000..fe04071361d --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/WarmupRequestTests.cs @@ -0,0 +1,100 @@ +using HotChocolate.Execution.Caching; +using HotChocolate.Language; +using Microsoft.Extensions.DependencyInjection; +using Moq; + +namespace HotChocolate.Execution; + +public class WarmupRequestTests +{ + [Fact] + public async Task Warmup_Request_Warms_Up_Caches() + { + // arrange + var executor = await new ServiceCollection() + .AddGraphQL() + .AddQueryType() + .BuildRequestExecutorAsync(); + + var documentId = "f614e9a2ed367399e87751d41ca09105"; + var warmupRequest = OperationRequestBuilder.New() + .SetDocument("query test($name: String!) { greeting(name: $name) }") + .SetDocumentId(documentId) + .MarkAsWarmupRequest() + .Build(); + + var regularRequest = OperationRequestBuilder.New() + .SetDocumentId(documentId) + .SetVariableValues(new Dictionary { ["name"] = "Foo" }) + .Build(); + + // act 1 + var warmupResult = await executor.ExecuteAsync(warmupRequest); + + // assert 1 + Assert.IsType(warmupResult); + + var provider = executor.Services.GetCombinedServices(); + var documentCache = provider.GetRequiredService(); + var operationCache = provider.GetRequiredService(); + + Assert.True(documentCache.TryGetDocument(documentId, out _)); + Assert.Equal(1, operationCache.Count); + + // act 2 + var regularResult = await executor.ExecuteAsync(regularRequest); + var regularOperationResult = regularResult.ExpectOperationResult(); + + // assert 2 + Assert.Null(regularOperationResult.Errors); + Assert.NotNull(regularOperationResult.Data); + Assert.NotEmpty(regularOperationResult.Data); + + Assert.True(documentCache.TryGetDocument(documentId, out _)); + Assert.Equal(1, operationCache.Count); + } + + [Fact] + public async Task Warmup_Request_Can_Skip_Persisted_Operation_Check() + { + // arrange + var executor = await new ServiceCollection() + .AddGraphQL() + .ConfigureSchemaServices(services => + { + services.AddSingleton(_ => new Mock().Object); + }) + .AddQueryType() + .ModifyRequestOptions(options => + { + options.PersistedOperations.OnlyAllowPersistedDocuments = true; + }) + .UsePersistedOperationPipeline() + .BuildRequestExecutorAsync(); + + var documentId = "f614e9a2ed367399e87751d41ca09105"; + var warmupRequest = OperationRequestBuilder.New() + .SetDocument("query test($name: String!) { greeting(name: $name) }") + .SetDocumentId(documentId) + .MarkAsWarmupRequest() + .Build(); + + // act + var warmupResult = await executor.ExecuteAsync(warmupRequest); + + // assert + Assert.IsType(warmupResult); + + var provider = executor.Services.GetCombinedServices(); + var documentCache = provider.GetRequiredService(); + var operationCache = provider.GetRequiredService(); + + Assert.True(documentCache.TryGetDocument(documentId, out _)); + Assert.Equal(1, operationCache.Count); + } + + public class Query + { + public string Greeting(string name) => $"Hello {name}"; + } +} diff --git a/src/HotChocolate/Fusion/src/Core/DependencyInjection/FusionRequestExecutorBuilderExtensions.cs b/src/HotChocolate/Fusion/src/Core/DependencyInjection/FusionRequestExecutorBuilderExtensions.cs index 39fd20bca4e..12ba1e5490a 100644 --- a/src/HotChocolate/Fusion/src/Core/DependencyInjection/FusionRequestExecutorBuilderExtensions.cs +++ b/src/HotChocolate/Fusion/src/Core/DependencyInjection/FusionRequestExecutorBuilderExtensions.cs @@ -564,6 +564,7 @@ private static IRequestExecutorBuilder UseFusionDefaultPipeline( .UseDocumentValidation() .UseOperationCache() .UseOperationResolver() + .UseSkipWarmupExecution() .UseOperationVariableCoercion() .UseDistributedOperationExecution(); } @@ -588,6 +589,7 @@ private static IRequestExecutorBuilder UseFusionPersistedOperationPipeline( .UseDocumentValidation() .UseOperationCache() .UseOperationResolver() + .UseSkipWarmupExecution() .UseOperationVariableCoercion() .UseDistributedOperationExecution(); } @@ -612,6 +614,7 @@ private static IRequestExecutorBuilder UseFusionAutomaticPersistedOperationPipel .UseDocumentValidation() .UseOperationCache() .UseOperationResolver() + .UseSkipWarmupExecution() .UseOperationVariableCoercion() .UseDistributedOperationExecution(); } diff --git a/website/src/docs/docs.json b/website/src/docs/docs.json index 45ab2522ad2..f42840a2487 100644 --- a/website/src/docs/docs.json +++ b/website/src/docs/docs.json @@ -121,6 +121,338 @@ "metaDescription": "Hot Chocolate is the most efficient, feature-rich, open-source GraphQL server in the .NET ecosystem, that helps developers to build powerful APIs.", "latestStableVersion": "v13", "versions": [ + { + "path": "v15", + "title": "v15", + "items": [ + { + "path": "index", + "title": "Introduction" + }, + { + "path": "get-started-with-graphql-in-net-core", + "title": "Getting Started" + }, + { + "path": "defining-a-schema", + "title": "Defining a schema", + "items": [ + { + "path": "index", + "title": "Overview" + }, + { + "path": "queries", + "title": "Queries" + }, + { + "path": "mutations", + "title": "Mutations" + }, + { + "path": "subscriptions", + "title": "Subscriptions" + }, + { + "path": "object-types", + "title": "Object Types" + }, + { + "path": "scalars", + "title": "Scalars" + }, + { + "path": "arguments", + "title": "Arguments" + }, + { + "path": "input-object-types", + "title": "Input Object Types" + }, + { + "path": "lists", + "title": "Lists" + }, + { + "path": "non-null", + "title": "Non-Null" + }, + { + "path": "enums", + "title": "Enums" + }, + { + "path": "interfaces", + "title": "Interfaces" + }, + { + "path": "unions", + "title": "Unions" + }, + { + "path": "extending-types", + "title": "Extending Types" + }, + { + "path": "directives", + "title": "Directives" + }, + { + "path": "documentation", + "title": "Documentation" + }, + { + "path": "versioning", + "title": "Versioning" + }, + { + "path": "relay", + "title": "Relay" + }, + { + "path": "dynamic-schemas", + "title": "Dynamic Schemas" + } + ] + }, + { + "path": "fetching-data", + "title": "Fetching data", + "items": [ + { + "path": "index", + "title": "Overview" + }, + { + "path": "resolvers", + "title": "Resolvers" + }, + { + "path": "fetching-from-databases", + "title": "Fetching from Databases" + }, + { + "path": "fetching-from-rest", + "title": "Fetching from REST" + }, + { + "path": "dataloader", + "title": "DataLoader" + }, + { + "path": "pagination", + "title": "Pagination" + }, + { + "path": "filtering", + "title": "Filtering" + }, + { + "path": "sorting", + "title": "Sorting" + }, + { + "path": "projections", + "title": "Projections" + } + ] + }, + { + "path": "execution-engine", + "title": "Execution Engine", + "items": [ + { + "path": "index", + "title": "Overview" + }, + { + "path": "field-middleware", + "title": "Field middleware" + } + ] + }, + { + "path": "integrations", + "title": "Integrations", + "items": [ + { + "path": "index", + "title": "Overview" + }, + { + "path": "entity-framework", + "title": "Entity Framework" + }, + { + "path": "mongodb", + "title": "MongoDB" + }, + { + "path": "spatial-data", + "title": "Spatial Data" + }, + { + "path": "marten", + "title": "Marten" + } + ] + }, + { + "path": "server", + "title": "Server", + "items": [ + { + "path": "index", + "title": "Overview" + }, + { + "path": "endpoints", + "title": "Endpoints" + }, + { + "path": "http-transport", + "title": "HTTP transport" + }, + { + "path": "interceptors", + "title": "Interceptors" + }, + { + "path": "dependency-injection", + "title": "Dependency injection" + }, + { + "path": "warmup", + "title": "Warmup" + }, + { + "path": "global-state", + "title": "Global State" + }, + { + "path": "introspection", + "title": "Introspection" + }, + { + "path": "files", + "title": "Files" + }, + { + "path": "instrumentation", + "title": "Instrumentation" + }, + { + "path": "batching", + "title": "Batching" + }, + { + "path": "command-line", + "title": "Command Line" + } + ] + }, + { + "path": "performance", + "title": "Performance", + "items": [ + { + "path": "index", + "title": "Overview" + }, + { + "path": "persisted-operations", + "title": "Persisted operations" + }, + { + "path": "automatic-persisted-operations", + "title": "Automatic persisted operations" + } + ] + }, + { + "path": "security", + "title": "Security", + "items": [ + { + "path": "index", + "title": "Overview" + }, + { + "path": "authentication", + "title": "Authentication" + }, + { + "path": "authorization", + "title": "Authorization" + }, + { + "path": "cost-analysis", + "title": "Cost Analysis" + } + ] + }, + { + "path": "api-reference", + "title": "API Reference", + "items": [ + { + "path": "custom-attributes", + "title": "Custom Attributes" + }, + { + "path": "errors", + "title": "Errors" + }, + { + "path": "language", + "title": "Language" + }, + { + "path": "extending-filtering", + "title": "Extending Filtering" + }, + { + "path": "visitors", + "title": "Visitors" + }, + { + "path": "apollo-federation", + "title": "Apollo Federation" + }, + { + "path": "executable", + "title": "Executable" + } + ] + }, + { + "path": "migrating", + "title": "Migrating", + "items": [ + { + "path": "migrate-from-14-to-15", + "title": "Migrate from 14 to 15" + }, + { + "path": "migrate-from-13-to-14", + "title": "Migrate from 13 to 14" + }, + { + "path": "migrate-from-12-to-13", + "title": "Migrate from 12 to 13" + }, + { + "path": "migrate-from-11-to-12", + "title": "Migrate from 11 to 12" + }, + { + "path": "migrate-from-10-to-11", + "title": "Migrate from 10 to 11" + } + ] + } + ] + }, { "path": "v14", "title": "v14", diff --git a/website/src/docs/hotchocolate/v15/server/warmup.md b/website/src/docs/hotchocolate/v15/server/warmup.md new file mode 100644 index 00000000000..6bf74375564 --- /dev/null +++ b/website/src/docs/hotchocolate/v15/server/warmup.md @@ -0,0 +1,101 @@ +--- +title: Warmup +--- + +By default the creation of Hot Chocolate's schema is lazy. If a request is about to be executed against the schema or the schema is otherwise needed, it will be constructed on the fly. + +Depending on the size of your schema this might be undesired, since it will cause initial requests to run longer than they would, if the schema was already constructed. + +In an environment with a load balancer, you might also want to utilize something like a Readiness Probe to determine when your server is ready (meaning fully initialized) to handle requests. + +# Initializing the schema on startup + +If you want the schema creation process to happen at server startup, rather than lazily, you can chain in a call to `InitializeOnStartup()` on the `IRequestExecutorBuilder`. + +```csharp +builder.Services + .AddGraphQLServer() + .InitializeOnStartup() +``` + +This will cause a hosted service to be executed as part of the server startup process, taking care of the schema creation. This process is blocking, meaning Kestrel won't answer requests until the construction of the schema is done. If you're using standard ASP.NET Core health checks, this will already suffice to implement a simple Readiness Probe. + +This also has the added benefit that schema misconfigurations will cause errors at startup, tightening the feedback loop while developing. + +# Warming up the executor + +Creating the schema at startup is already a big win for the performance of initial requests. Though, you might want to go one step further and already initialize in-memory caches like the document and operation cache, before serving any requests. + +For this the `InitializeOnStartup()` method contains an argument called `warmup` that allows you to pass a callback where you can execute requests against the newly created schema. + +```csharp +builder.Services + .AddGraphQLServer() + .InitializeOnStartup( + warmup: async (executor, cancellationToken) => { + await executor.ExecuteAsync("{ __typename }"); + }); +``` + +The warmup process is also blocking, meaning the server won't start answering requests until both the schema creation and the warmup process is finished. + +Since the execution of an operation could have side-effects, you might want to only warmup the executor, but skip the actual execution of the request. For this you can mark an operation as a warmup request. + +```csharp +var request = OperationRequestBuilder.New() + .SetDocument("{ __typename }") + .MarkAsWarmupRequest() + .Build(); + +await executor.ExecuteAsync(request); +``` + +Requests marked as warmup requests will be able to skip security measures like persisted operations and will finish without actually executing the specified operation. + +Keep in mind that the operation name is part of the operation cache. If your client is sending an operation name, you also want to include that operation name in the warmup request, or the actual request will miss the cache. + +```csharp +var request = OperationRequestBuilder.New() + .SetDocument("query testQuery { __typename }") + .SetOperationName("testQuery") + .MarkAsWarmupRequest() + .Build(); +``` + +## Skipping reporting + +If you've implemented a custom diagnostic event listener as described [here](/docs/hotchocolate/v15/server/instrumentation#execution-events) you might want to skip reporting certain events in the case of a warmup request. + +You can use the `IRequestContext.IsWarmupRequest()` method to determine whether a request is a warmup request or not. + +```csharp +public class MyExecutionEventListener : ExecutionDiagnosticEventListener +{ + public override void RequestError(IRequestContext context, + Exception exception) + { + if (context.IsWarmupRequest()) + { + return; + } + + // Reporting + } +} + +``` + +## Keeping the executor warm + +By default the warmup only takes place at server startup. If you're using [dynamic schemas](/docs/hotchocolate/v15/defining-a-schema/dynamic-schemas) for instance, your schema might change throughout the lifetime of the server. +In this case the warmup will not apply to subsequent schema changes, unless you set the `keepWarm` argument to `true`. + +```csharp +builder.Services + .AddGraphQLServer() + .InitializeOnStartup( + keepWarm: true, + warmup: /* ... */); +``` + +If set to `true`, the schema and its warmup task will be executed in the background, while requests are still handled by the old schema. Once the warmup is finished requests will be served by the new and already warmed up schema. From 7cafe32a471f8c1e2e81b56ee35b2d2bd7c774eb Mon Sep 17 00:00:00 2001 From: Sunghwan Bang Date: Tue, 3 Dec 2024 19:40:52 +0900 Subject: [PATCH 122/154] Added support for between both cursors (#7800) --- .../Extensions/PagingQueryableExtensions.cs | 4 +- .../PagingHelperTests.cs | 44 +++++++++++++++++++ ...HelperTests.Fetch_First_2_Items_Between.md | 36 +++++++++++++++ ...gHelperTests.Fetch_Last_2_Items_Between.md | 36 +++++++++++++++ 4 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/PagingHelperTests.Fetch_First_2_Items_Between.md create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/PagingHelperTests.Fetch_Last_2_Items_Between.md diff --git a/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs index 9c692438b9b..9fadcc6bfe6 100644 --- a/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs +++ b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs @@ -99,13 +99,13 @@ public static async ValueTask> ToPageAsync( if (arguments.After is not null) { var cursor = CursorParser.Parse(arguments.After, keys); - source = source.Where(BuildWhereExpression(keys, cursor, forward)); + source = source.Where(BuildWhereExpression(keys, cursor, true)); } if (arguments.Before is not null) { var cursor = CursorParser.Parse(arguments.Before, keys); - source = source.Where(BuildWhereExpression(keys, cursor, forward)); + source = source.Where(BuildWhereExpression(keys, cursor, false)); } if (arguments.First is not null) diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperTests.cs b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperTests.cs index dbd46bb9e16..ae0cfe98548 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperTests.cs +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperTests.cs @@ -76,6 +76,27 @@ public async Task Fetch_First_2_Items_Third_Page() page.MatchMarkdownSnapshot(); } + [Fact] + public async Task Fetch_First_2_Items_Between() + { + // Arrange + var connectionString = CreateConnectionString(); + await SeedAsync(connectionString); + + // .. get first page + var arguments = new PagingArguments(4); + await using var context = new CatalogContext(connectionString); + var page = await context.Products.OrderBy(t => t.Name).ThenBy(t => t.Id) + .ToPageAsync(arguments); + + // Act + arguments = new PagingArguments(2, after: page.CreateCursor(page.First!), before: page.CreateCursor(page.Last!)); + page = await context.Products.OrderBy(t => t.Name).ThenBy(t => t.Id).ToPageAsync(arguments); + + // Assert + page.MatchMarkdownSnapshot(); + } + [Fact] public async Task Fetch_Last_2_Items() { @@ -118,6 +139,29 @@ public async Task Fetch_Last_2_Items_Before_Last_Page() page.MatchMarkdownSnapshot(); } + [Fact] + public async Task Fetch_Last_2_Items_Between() + { + // Arrange + var connectionString = CreateConnectionString(); + await SeedAsync(connectionString); + + // .. get last page + var arguments = new PagingArguments(last: 4); + await using var context = new CatalogContext(connectionString); + var page = await context.Products + .OrderBy(t => t.Name) + .ThenBy(t => t.Id) + .ToPageAsync(arguments); + + // Act + arguments = new PagingArguments(after: page.CreateCursor(page.First!), last: 2, before: page.CreateCursor(page.Last!)); + page = await context.Products.OrderBy(t => t.Name).ThenBy(t => t.Id).ToPageAsync(arguments); + + // Assert + page.MatchMarkdownSnapshot(); + } + [Fact] public async Task Batch_Fetch_First_2_Items() { diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/PagingHelperTests.Fetch_First_2_Items_Between.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/PagingHelperTests.Fetch_First_2_Items_Between.md new file mode 100644 index 00000000000..d2fa29aa0d6 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/PagingHelperTests.Fetch_First_2_Items_Between.md @@ -0,0 +1,36 @@ +# Fetch_First_2_Items_Between + +```json +[ + { + "Id": 2, + "Name": "Product 0-1", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 1, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + }, + { + "Id": 11, + "Name": "Product 0-10", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 1, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + } +] +``` diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/PagingHelperTests.Fetch_Last_2_Items_Between.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/PagingHelperTests.Fetch_Last_2_Items_Between.md new file mode 100644 index 00000000000..5134dd84b75 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/PagingHelperTests.Fetch_Last_2_Items_Between.md @@ -0,0 +1,36 @@ +# Fetch_Last_2_Items_Between + +```json +[ + { + "Id": 9998, + "Name": "Product 99-97", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 100, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + }, + { + "Id": 9999, + "Name": "Product 99-98", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 100, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + } +] +``` From b0c4e1c3ead26d7df721ef5c4c92cdb64a0e974f Mon Sep 17 00:00:00 2001 From: Sunghwan Bang Date: Tue, 3 Dec 2024 21:11:04 +0900 Subject: [PATCH 123/154] Fixed client complete abort websocket that is sending (#7793) --- .../src/AspNetCore/Subscriptions/OperationSession.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore/Subscriptions/OperationSession.cs b/src/HotChocolate/AspNetCore/src/AspNetCore/Subscriptions/OperationSession.cs index b421740a3cf..6bc08aa6582 100644 --- a/src/HotChocolate/AspNetCore/src/AspNetCore/Subscriptions/OperationSession.cs +++ b/src/HotChocolate/AspNetCore/src/AspNetCore/Subscriptions/OperationSession.cs @@ -74,7 +74,8 @@ await _session.Protocol.SendErrorMessageAsync( { try { - await SendResultMessageAsync(item, ct); + // use original cancellation token here to keep the websocket open for other streams. + await SendResultMessageAsync(item, cancellationToken); } finally { From a570145ac6fad83c3eff801cb5d0ff7942369446 Mon Sep 17 00:00:00 2001 From: Daniel Rebouta <34064225+CronKz@users.noreply.github.com> Date: Fri, 6 Dec 2024 10:12:53 +0100 Subject: [PATCH 124/154] Added support for taking between both cursors in ef (#7806) --- .../EfQueryableCursorPagingHandler.cs | 4 +- .../IntegrationTests.cs | 72 +++++++++++++++++++ ...ests.Paging_Fetch_First_2_Items_Between.md | 27 +++++++ ...Tests.Paging_Fetch_Last_2_Items_Between.md | 27 +++++++ 4 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/__snapshots__/IntegrationTests.Paging_Fetch_First_2_Items_Between.md create mode 100644 src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/__snapshots__/IntegrationTests.Paging_Fetch_Last_2_Items_Between.md diff --git a/src/HotChocolate/Data/src/EntityFramework/Pagination/EfQueryableCursorPagingHandler.cs b/src/HotChocolate/Data/src/EntityFramework/Pagination/EfQueryableCursorPagingHandler.cs index e15e027f651..bdcb80b4eb5 100644 --- a/src/HotChocolate/Data/src/EntityFramework/Pagination/EfQueryableCursorPagingHandler.cs +++ b/src/HotChocolate/Data/src/EntityFramework/Pagination/EfQueryableCursorPagingHandler.cs @@ -45,13 +45,13 @@ private async ValueTask SliceAsync( if (arguments.After is not null) { var cursor = CursorParser.Parse(arguments.After, keys); - query = query.Where(ExpressionHelpers.BuildWhereExpression(keys, cursor, forward)); + query = query.Where(ExpressionHelpers.BuildWhereExpression(keys, cursor, true)); } if (arguments.Before is not null) { var cursor = CursorParser.Parse(arguments.Before, keys); - query = query.Where(ExpressionHelpers.BuildWhereExpression(keys, cursor, forward)); + query = query.Where(ExpressionHelpers.BuildWhereExpression(keys, cursor, false)); } if (arguments.First is not null) diff --git a/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/IntegrationTests.cs b/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/IntegrationTests.cs index a236f7fb47c..f4bd8261252 100644 --- a/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/IntegrationTests.cs +++ b/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/IntegrationTests.cs @@ -316,6 +316,78 @@ public async Task Paging_Last_10_With_Default_Sorting_HasPreviousPage() result.MatchMarkdownSnapshot(); } + [Fact] + public async Task Paging_Fetch_First_2_Items_Between() + { + var connectionString = CreateConnectionString(); + await SeedAsync(connectionString); + + var executor = await new ServiceCollection() + .AddScoped(_ => new CatalogContext(connectionString)) + .AddGraphQLServer() + .AddQueryType() + .AddSorting() + .AddDbContextCursorPagingProvider() + .BuildRequestExecutorAsync(); + + var result = await executor.ExecuteAsync(q => q + .SetDocument( + """ + { + brands(first: 2, after:"MQ==", before:"NA==") { + nodes { + name + } + pageInfo { + hasNextPage + hasPreviousPage + endCursor + startCursor + } + } + } + """) + .SetGlobalState("printSQL", true)); + + result.MatchMarkdownSnapshot(); + } + + [Fact] + public async Task Paging_Fetch_Last_2_Items_Between() + { + var connectionString = CreateConnectionString(); + await SeedAsync(connectionString); + + var executor = await new ServiceCollection() + .AddScoped(_ => new CatalogContext(connectionString)) + .AddGraphQLServer() + .AddQueryType() + .AddSorting() + .AddDbContextCursorPagingProvider() + .BuildRequestExecutorAsync(); + + var result = await executor.ExecuteAsync(q => q + .SetDocument( + """ + { + brands(last: 2, after:"OTc=", before:"MTAw") { + nodes { + name + } + pageInfo { + hasNextPage + hasPreviousPage + endCursor + startCursor + } + } + } + """) + .SetGlobalState("printSQL", true)); + + result.MatchMarkdownSnapshot(); + } + public class Query { [UsePaging] diff --git a/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/__snapshots__/IntegrationTests.Paging_Fetch_First_2_Items_Between.md b/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/__snapshots__/IntegrationTests.Paging_Fetch_First_2_Items_Between.md new file mode 100644 index 00000000000..4c7aa752c04 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/__snapshots__/IntegrationTests.Paging_Fetch_First_2_Items_Between.md @@ -0,0 +1,27 @@ +# Paging_Fetch_First_2_Items_Between + +```json +{ + "data": { + "brands": { + "nodes": [ + { + "name": "Brand1" + }, + { + "name": "Brand2" + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": true, + "endCursor": "Mw==", + "startCursor": "Mg==" + } + } + }, + "extensions": { + "sql": "-- @__p_0='1'\n-- @__p_1='4'\n-- @__p_2='3'\nSELECT b.\"Id\", b.\"AlwaysNull\", b.\"DisplayName\", b.\"Name\", b.\"BrandDetails_Country_Name\"\nFROM \"Brands\" AS b\nWHERE b.\"Id\" > @__p_0 AND b.\"Id\" < @__p_1\nORDER BY b.\"Id\"\nLIMIT @__p_2" + } +} +``` diff --git a/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/__snapshots__/IntegrationTests.Paging_Fetch_Last_2_Items_Between.md b/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/__snapshots__/IntegrationTests.Paging_Fetch_Last_2_Items_Between.md new file mode 100644 index 00000000000..ba4bc537766 --- /dev/null +++ b/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/__snapshots__/IntegrationTests.Paging_Fetch_Last_2_Items_Between.md @@ -0,0 +1,27 @@ +# Paging_Fetch_Last_2_Items_Between + +```json +{ + "data": { + "brands": { + "nodes": [ + { + "name": "Brand97" + }, + { + "name": "Brand98" + } + ], + "pageInfo": { + "hasNextPage": true, + "hasPreviousPage": true, + "endCursor": "OTk=", + "startCursor": "OTg=" + } + } + }, + "extensions": { + "sql": "-- @__p_0='97'\n-- @__p_1='100'\n-- @__p_2='3'\nSELECT b.\"Id\", b.\"AlwaysNull\", b.\"DisplayName\", b.\"Name\", b.\"BrandDetails_Country_Name\"\nFROM \"Brands\" AS b\nWHERE b.\"Id\" > @__p_0 AND b.\"Id\" < @__p_1\nORDER BY b.\"Id\" DESC\nLIMIT @__p_2" + } +} +``` From 3d5716ca0f26470ebcec91655cbb169f76af992d Mon Sep 17 00:00:00 2001 From: Daniel Reynolds <55194784+danielreynolds1@users.noreply.github.com> Date: Wed, 11 Dec 2024 13:49:19 +0000 Subject: [PATCH 125/154] Added '@' prefix when exporting directives in Apollo. (#7812) --- .../FederationTypeInterceptor.cs | 2 +- .../Directives/ComposeDirectiveTests.cs | 25 ++++++++++++- ...ExportDirectiveUsingNameCodeFirst.graphql} | 2 +- ....ExportDirectiveUsingTypeCodeFirst.graphql | 37 +++++++++++++++++++ 4 files changed, 63 insertions(+), 3 deletions(-) rename src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/{ComposeDirectiveTests.TestServiceTypeEmptyQueryTypePureCodeFirst.graphql => ComposeDirectiveTests.ExportDirectiveUsingNameCodeFirst.graphql} (86%) create mode 100644 src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ComposeDirectiveTests.ExportDirectiveUsingTypeCodeFirst.graphql diff --git a/src/HotChocolate/ApolloFederation/src/ApolloFederation/FederationTypeInterceptor.cs b/src/HotChocolate/ApolloFederation/src/ApolloFederation/FederationTypeInterceptor.cs index b737233188b..15d0c5c686b 100644 --- a/src/HotChocolate/ApolloFederation/src/ApolloFederation/FederationTypeInterceptor.cs +++ b/src/HotChocolate/ApolloFederation/src/ApolloFederation/FederationTypeInterceptor.cs @@ -294,7 +294,7 @@ private void RegisterExportedDirectives() var typeReference = _typeInspector.GetTypeRef(exportedDirective); if (_typeRegistry.TryGetType(typeReference, out var exportedDirectiveType)) { - composeDirectives.Add(new ComposeDirective(exportedDirectiveType.Type.Name)); + composeDirectives.Add(new ComposeDirective($"@{exportedDirectiveType.Type.Name}")); } } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/ComposeDirectiveTests.cs b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/ComposeDirectiveTests.cs index c67ea6b4a21..ef645d2d67c 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/ComposeDirectiveTests.cs +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/ComposeDirectiveTests.cs @@ -11,7 +11,7 @@ namespace HotChocolate.ApolloFederation; public class ComposeDirectiveTests { [Fact] - public async Task TestServiceTypeEmptyQueryTypePureCodeFirst() + public async Task ExportDirectiveUsingTypeCodeFirst() { // arrange var schema = await new ServiceCollection() @@ -33,6 +33,29 @@ public async Task TestServiceTypeEmptyQueryTypePureCodeFirst() .MatchSnapshot(); } + [Fact] + public async Task ExportDirectiveUsingNameCodeFirst() + { + // arrange + var schema = await new ServiceCollection() + .AddGraphQL() + .AddApolloFederation() + .AddQueryType() + .AddType
() + .ExportDirective("@custom") + .BuildSchemaAsync(); + + var entityType = schema.GetType(FederationTypeNames.ServiceType_Name); + var sdlResolver = entityType.Fields[WellKnownFieldNames.Sdl].Resolver!; + + // act + var value = await sdlResolver(TestHelper.CreateResolverContext(schema)); + + Utf8GraphQLParser + .Parse((string)value!) + .MatchSnapshot(); + } + [Key("field")] public class Address { diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ComposeDirectiveTests.TestServiceTypeEmptyQueryTypePureCodeFirst.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ComposeDirectiveTests.ExportDirectiveUsingNameCodeFirst.graphql similarity index 86% rename from src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ComposeDirectiveTests.TestServiceTypeEmptyQueryTypePureCodeFirst.graphql rename to src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ComposeDirectiveTests.ExportDirectiveUsingNameCodeFirst.graphql index da0786b488a..92387ea4b4a 100644 --- a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ComposeDirectiveTests.TestServiceTypeEmptyQueryTypePureCodeFirst.graphql +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ComposeDirectiveTests.ExportDirectiveUsingNameCodeFirst.graphql @@ -1,4 +1,4 @@ -schema @composeDirective(name: "custom") @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@tag", "FieldSet", "@composeDirective" ]) @link(url: "https:\/\/specs.custom.dev\/custom\/v1.0", import: [ "@custom" ]) { +schema @composeDirective(name: "@custom") @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@tag", "FieldSet", "@composeDirective" ]) @link(url: "https:\/\/specs.custom.dev\/custom\/v1.0", import: [ "@custom" ]) { query: Query } diff --git a/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ComposeDirectiveTests.ExportDirectiveUsingTypeCodeFirst.graphql b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ComposeDirectiveTests.ExportDirectiveUsingTypeCodeFirst.graphql new file mode 100644 index 00000000000..92387ea4b4a --- /dev/null +++ b/src/HotChocolate/ApolloFederation/test/ApolloFederation.Tests/Directives/__snapshots__/ComposeDirectiveTests.ExportDirectiveUsingTypeCodeFirst.graphql @@ -0,0 +1,37 @@ +schema @composeDirective(name: "@custom") @link(url: "https:\/\/specs.apollo.dev\/federation\/v2.6", import: [ "@key", "@tag", "FieldSet", "@composeDirective" ]) @link(url: "https:\/\/specs.custom.dev\/custom\/v1.0", import: [ "@custom" ]) { + query: Query +} + +type Address @key(fields: "field") { + field: String! @custom +} + +type Query { + _service: _Service! + _entities(representations: [_Any!]!): [_Entity]! +} + +"This type provides a field named sdl: String! which exposes the SDL of the service's schema. This SDL (schema definition language) is a printed version of the service's schema including the annotations of federation directives. This SDL does not include the additions of the federation spec." +type _Service { + sdl: String! +} + +"Union of all types that key directive applied. This information is needed by the Apollo federation gateway." +union _Entity = Address + +"Marks underlying custom directive to be included in the Supergraph schema." +directive @composeDirective(name: String!) repeatable on SCHEMA + +directive @custom on FIELD_DEFINITION + +"Used to indicate a combination of fields that can be used to uniquely identify and fetch an object or interface." +directive @key(fields: FieldSet! resolvable: Boolean = true) repeatable on OBJECT | INTERFACE + +"Links definitions within the document to external schemas." +directive @link("Gets imported specification url." url: String! "Gets optional list of imported element names." import: [String!]) repeatable on SCHEMA + +"Scalar representing a set of fields." +scalar FieldSet + +"The _Any scalar is used to pass representations of entities from external services into the root _entities field for execution. Validation of the _Any scalar is done by matching the __typename and @external fields defined in the schema." +scalar _Any From 40c64e8798da7789475167d9d46787f8309d3bf9 Mon Sep 17 00:00:00 2001 From: Daniel Rebouta <34064225+CronKz@users.noreply.github.com> Date: Thu, 12 Dec 2024 13:37:01 +0100 Subject: [PATCH 126/154] Fixed source-generated class names (#7819) --- .../Generators/DataLoaderModuleGenerator.cs | 2 +- .../Types.Analyzers/Helpers/GeneratorUtils.cs | 19 +++++++++++-- .../test/Types.Analyzers.Tests/TestHelper.cs | 4 +-- .../TypeModuleSyntaxGeneratorTests.cs | 16 +++++++++++ ...oblematic_Assembly_Name_MatchesSnapshot.md | 27 +++++++++++++++++++ 5 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/TypeModuleSyntaxGeneratorTests.GenerateSource_With_Problematic_Assembly_Name_MatchesSnapshot.md diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Generators/DataLoaderModuleGenerator.cs b/src/HotChocolate/Core/src/Types.Analyzers/Generators/DataLoaderModuleGenerator.cs index 96903b72814..a98e6a831b4 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Generators/DataLoaderModuleGenerator.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Generators/DataLoaderModuleGenerator.cs @@ -81,7 +81,7 @@ public void Generate( { if (syntaxInfo is DataLoaderModuleInfo module) { - return module; + return new DataLoaderModuleInfo(GeneratorUtils.SanitizeIdentifier(module.ModuleName)); } } diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Helpers/GeneratorUtils.cs b/src/HotChocolate/Core/src/Types.Analyzers/Helpers/GeneratorUtils.cs index a8342c0c3a3..bea2a4f5267 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Helpers/GeneratorUtils.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Helpers/GeneratorUtils.cs @@ -1,4 +1,5 @@ using System.Collections.Immutable; +using System.Text.RegularExpressions; using HotChocolate.Types.Analyzers.Models; using Microsoft.CodeAnalysis; @@ -16,7 +17,7 @@ public static ModuleInfo GetModuleInfo( if (syntaxInfo is ModuleInfo module) { defaultModule = false; - return module; + return new ModuleInfo(SanitizeIdentifier(module.ModuleName), module.Options); } } @@ -61,7 +62,7 @@ public static DataLoaderDefaultsInfo GetDataLoaderDefaults( public static string CreateModuleName(string? assemblyName) => assemblyName is null ? "AssemblyTypes" - : assemblyName.Split('.').Last() + "Types"; + : SanitizeIdentifier(assemblyName.Split('.').Last()) + "Types"; public static string ConvertDefaultValueToString(object? defaultValue, ITypeSymbol type) { @@ -104,4 +105,18 @@ public static string ConvertDefaultValueToString(object? defaultValue, ITypeSymb return defaultValue.ToString(); } + + public static string SanitizeIdentifier(string input) + { + Regex invalidCharsRegex = new("[^a-zA-Z0-9]", RegexOptions.Compiled); + + var sanitized = invalidCharsRegex.Replace(input, "_"); + + if (!char.IsLetter(sanitized[0])) + { + sanitized = "_" + sanitized.Substring(1); + } + + return sanitized; + } } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs index b8df05d5b73..ca406c98ad6 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs @@ -24,7 +24,7 @@ public static Snapshot GetGeneratedSourceSnapshot([StringSyntax("csharp")] strin return GetGeneratedSourceSnapshot([sourceText]); } - public static Snapshot GetGeneratedSourceSnapshot(string[] sourceTexts) + public static Snapshot GetGeneratedSourceSnapshot(string[] sourceTexts, string? assemblyName = "Tests") { IEnumerable references = [ @@ -49,7 +49,7 @@ public static Snapshot GetGeneratedSourceSnapshot(string[] sourceTexts) // Create a Roslyn compilation for the syntax tree. var compilation = CSharpCompilation.Create( - assemblyName: "Tests", + assemblyName: assemblyName, syntaxTrees: sourceTexts.Select(s => CSharpSyntaxTree.ParseText(s)), references); diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/TypeModuleSyntaxGeneratorTests.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/TypeModuleSyntaxGeneratorTests.cs index 58923ba704b..4155cbbb04c 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/TypeModuleSyntaxGeneratorTests.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/TypeModuleSyntaxGeneratorTests.cs @@ -157,4 +157,20 @@ public static async Task> GetObjectByIdAAsync( """ ]).MatchMarkdownAsync(); } + + [Fact] + public async Task GenerateSource_With_Problematic_Assembly_Name_MatchesSnapshot() + { + await TestHelper.GetGeneratedSourceSnapshot( + [ + """ + using HotChocolate.Types; + + namespace TestNamespace; + + internal class ATestBType: ObjectType; + internal record ATestB(int Id); + """ + ], assemblyName:"Custom-Module").MatchMarkdownAsync(); + } } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/TypeModuleSyntaxGeneratorTests.GenerateSource_With_Problematic_Assembly_Name_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/TypeModuleSyntaxGeneratorTests.GenerateSource_With_Problematic_Assembly_Name_MatchesSnapshot.md new file mode 100644 index 00000000000..74bc200fdac --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/TypeModuleSyntaxGeneratorTests.GenerateSource_With_Problematic_Assembly_Name_MatchesSnapshot.md @@ -0,0 +1,27 @@ +# GenerateSource_With_Problematic_Assembly_Name_MatchesSnapshot + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class Custom_ModuleTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddCustom_ModuleTypes(this IRequestExecutorBuilder builder) + { + builder.AddType(); + return builder; + } + } +} + +``` From 2f8d695877a331c4f3b597ed92ed81b17c6589dc Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Mon, 16 Dec 2024 13:53:38 +0100 Subject: [PATCH 127/154] Adds request cost overrides. (#7818) --- .../test/AspNetCore.Tests/CostTests.cs | 50 +++++ ...ests.Cost_Exceeded_With_Cost_Override.snap | 12 ++ ...aMiddlewareTests.Download_GraphQL_SDL.snap | 6 +- ...s.Download_GraphQL_SDL_Explicit_Route.snap | 6 +- ...L_SDL_Explicit_Route_Explicit_Pattern.snap | 6 +- ...MiddlewareTests.Download_GraphQL_Schema.md | 10 +- ...oad_GraphQL_Schema_Slicing_Args_Enabled.md | 10 +- .../src/Abstractions/WellKnownContextData.cs | 5 + .../src/CostAnalysis/CostAnalyzer.cs | 8 +- .../CostAnalysis/CostAnalyzerMiddleware.cs | 23 ++- .../src/CostAnalysis/CostAnalyzerMode.cs | 2 +- .../src/CostAnalysis/CostTypeInterceptor.cs | 16 +- ...nalyzerRequestExecutorBuilderExtensions.cs | 10 + ...AnalyzerObjectFieldDescriptorExtensions.cs | 9 +- .../CostAnalyzerRequestContextExtensions.cs | 87 +++++++- .../src/CostAnalysis/Options/CostOptions.cs | 5 + .../Options/RequestCostOptions.cs | 22 +++ .../CostAnalysis/Types/ListSizeAttribute.cs | 6 + .../CostAnalysis/Types/ListSizeDirective.cs | 17 +- .../Types/ListSizeDirectiveType.cs | 28 ++- .../Utilities/CostAnalyzerUtilities.cs | 19 +- .../CostAnalysis/WellKnownArgumentNames.cs | 3 +- .../test/CostAnalysis.Tests/PagingTests.cs | 113 ++++++++++- .../PagingTests.Do_Not_Apply_Defaults.graphql | 142 ++++++++++++++ ...ult_Page_Size_When_Default_Is_Specified.md | 185 ++++++++++++++++++ ...Ensure_Paging_Defaults_Are_Applied.graphql | 12 +- .../PagingTests.Filtering_Not_Used.md | 4 +- ...iltering_Specific_Expensive_Filter_Used.md | 4 +- ...ingTests.Filtering_Specific_Filter_Used.md | 4 +- .../PagingTests.Filtering_Variable.md | 4 +- ...ult_Page_Size_When_Default_Is_Specified.md | 185 ++++++++++++++++++ ...ospectionClientTests.IntrospectServer.snap | 2 +- ...st.Execute_StarWarsIntrospection_Test.snap | 28 +++ 33 files changed, 965 insertions(+), 78 deletions(-) create mode 100644 src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/CostTests.Cost_Exceeded_With_Cost_Override.snap create mode 100644 src/HotChocolate/CostAnalysis/src/CostAnalysis/Options/RequestCostOptions.cs create mode 100644 src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Do_Not_Apply_Defaults.graphql create mode 100644 src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Do_Not_Use_Default_Page_Size_When_Default_Is_Specified.md create mode 100644 src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Use_Default_Page_Size_When_Default_Is_Specified.md diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/CostTests.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/CostTests.cs index 3033e6f626d..8170f6e63bf 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/CostTests.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/CostTests.cs @@ -1,9 +1,13 @@ #if NET7_0_OR_GREATER +using System.Net; using System.Net.Http.Json; using System.Text; using System.Text.Json; using CookieCrumble; using HotChocolate.AspNetCore.Tests.Utilities; +using HotChocolate.Execution; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; namespace HotChocolate.AspNetCore; @@ -97,5 +101,51 @@ public async Task Request_Validate_Cost_Header() Assert.NotNull(response); result?.RootElement.MatchSnapshot(); } + + [Fact] + public async Task Cost_Exceeded_With_Cost_Override() + { + // arrange + var server = CreateStarWarsServer( + configureServices: services => services + .AddGraphQLServer() + .AddHttpRequestInterceptor() ); + + var uri = new Uri("http://localhost:5000/graphql"); + + var requestBody = + """ + { + "query" : "query Test($id: String!){human(id: $id){name}}" + "variables" : { "id" : "1000" } + } + """; + + var content = new StringContent(requestBody, Encoding.UTF8, "application/json"); + + // act + using var httpClient = server.CreateClient(); + var response = await httpClient.PostAsync(uri, content); + + // assert + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + var result = await response.Content.ReadFromJsonAsync(); + Assert.NotNull(response); + result?.RootElement.MatchSnapshot(); + } + + public class CostInterceptor : DefaultHttpRequestInterceptor + { + public override ValueTask OnCreateAsync( + HttpContext context, + IRequestExecutor requestExecutor, + OperationRequestBuilder requestBuilder, + CancellationToken cancellationToken) + { + var costOptions = requestExecutor.GetCostOptions(); + requestBuilder.SetCostOptions(costOptions with { MaxTypeCost = 1}); + return base.OnCreateAsync(context, requestExecutor, requestBuilder, cancellationToken); + } + } } #endif diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/CostTests.Cost_Exceeded_With_Cost_Override.snap b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/CostTests.Cost_Exceeded_With_Cost_Override.snap new file mode 100644 index 00000000000..d534d5d3d80 --- /dev/null +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/CostTests.Cost_Exceeded_With_Cost_Override.snap @@ -0,0 +1,12 @@ +{ + "errors": [ + { + "message": "The maximum allowed type cost was exceeded.", + "extensions": { + "typeCost": 2, + "maxTypeCost": 1, + "code": "HC0047" + } + } + ] +} diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL.snap b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL.snap index 30c65b8cacb..21f3e15f9ad 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL.snap +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL.snap @@ -17,7 +17,7 @@ type Droid implements Character { id: ID! name: String! appearsIn: [Episode] - friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) + friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 10, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) height(unit: Unit): Float primaryFunction: String traits: JSON @@ -45,7 +45,7 @@ type Human implements Character { id: ID! name: String! appearsIn: [Episode] - friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) + friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 10, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) otherHuman: Human height(unit: Unit): Float homePlanet: String @@ -128,7 +128,7 @@ directive @defer("If this argument label has a value other than null, it will be directive @foo(bar: Int!) on SUBSCRIPTION "The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." -directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!] "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true) on FIELD_DEFINITION +directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!] "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." slicingArgumentDefaultValue: Int "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true) on FIELD_DEFINITION "The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! = 0 "Streamed when true." if: Boolean) on FIELD diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL_Explicit_Route.snap b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL_Explicit_Route.snap index b2a1cda45f1..737ddde8670 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL_Explicit_Route.snap +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL_Explicit_Route.snap @@ -17,7 +17,7 @@ type Droid implements Character { id: ID! name: String! appearsIn: [Episode] - friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) + friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 10, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) height(unit: Unit): Float primaryFunction: String traits: JSON @@ -45,7 +45,7 @@ type Human implements Character { id: ID! name: String! appearsIn: [Episode] - friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) + friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 10, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) otherHuman: Human height(unit: Unit): Float homePlanet: String @@ -126,7 +126,7 @@ directive @cost("The `weight` argument defines what value to add to the overall directive @defer("If this argument label has a value other than null, it will be passed on to the result of this defer directive. This label is intended to give client applications a way to identify to which fragment a deferred result belongs to." label: String "Deferred when true." if: Boolean) on FRAGMENT_SPREAD | INLINE_FRAGMENT "The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." -directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!] "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true) on FIELD_DEFINITION +directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!] "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." slicingArgumentDefaultValue: Int "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true) on FIELD_DEFINITION "The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! = 0 "Streamed when true." if: Boolean) on FIELD diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL_Explicit_Route_Explicit_Pattern.snap b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL_Explicit_Route_Explicit_Pattern.snap index b2a1cda45f1..737ddde8670 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL_Explicit_Route_Explicit_Pattern.snap +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL_Explicit_Route_Explicit_Pattern.snap @@ -17,7 +17,7 @@ type Droid implements Character { id: ID! name: String! appearsIn: [Episode] - friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) + friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 10, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) height(unit: Unit): Float primaryFunction: String traits: JSON @@ -45,7 +45,7 @@ type Human implements Character { id: ID! name: String! appearsIn: [Episode] - friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) + friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 10, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) otherHuman: Human height(unit: Unit): Float homePlanet: String @@ -126,7 +126,7 @@ directive @cost("The `weight` argument defines what value to add to the overall directive @defer("If this argument label has a value other than null, it will be passed on to the result of this defer directive. This label is intended to give client applications a way to identify to which fragment a deferred result belongs to." label: String "Deferred when true." if: Boolean) on FRAGMENT_SPREAD | INLINE_FRAGMENT "The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." -directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!] "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true) on FIELD_DEFINITION +directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!] "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." slicingArgumentDefaultValue: Int "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true) on FIELD_DEFINITION "The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! = 0 "Streamed when true." if: Boolean) on FIELD diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema.md b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema.md index 72badc86f61..2577501c118 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema.md +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema.md @@ -2,12 +2,12 @@ ```text Headers: -ETag: "1-LXklYHgmBtUalJ0Ugb0vQzLmP6FuyFt1keIXDn4SE/Y=" +ETag: "1-zgi5AzGsi9KCkeA00b2KpL3HoZ++qVVoP05qFxiKUig=" Cache-Control: public, must-revalidate, max-age=3600 Content-Type: application/graphql; charset=utf-8 Content-Disposition: attachment; filename="schema.graphql" Last-Modified: Fri, 01 Jan 2021 00:00:00 GMT -Content-Length: 7304 +Content-Length: 7567 --------------------------> Status Code: OK --------------------------> @@ -30,7 +30,7 @@ type Droid implements Character { id: ID! name: String! appearsIn: [Episode] - friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) + friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 10, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) height(unit: Unit): Float primaryFunction: String traits: JSON @@ -58,7 +58,7 @@ type Human implements Character { id: ID! name: String! appearsIn: [Episode] - friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) + friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 10, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) otherHuman: Human height(unit: Unit): Float homePlanet: String @@ -141,7 +141,7 @@ directive @defer("If this argument label has a value other than null, it will be directive @foo(bar: Int!) on SUBSCRIPTION "The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." -directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!] "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true) on FIELD_DEFINITION +directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!] "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." slicingArgumentDefaultValue: Int "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true) on FIELD_DEFINITION "The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! = 0 "Streamed when true." if: Boolean) on FIELD diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema_Slicing_Args_Enabled.md b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema_Slicing_Args_Enabled.md index e1d4b309a5f..a791e7a118e 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema_Slicing_Args_Enabled.md +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema_Slicing_Args_Enabled.md @@ -2,12 +2,12 @@ ```text Headers: -ETag: "1-SGSC/P4ipajfTuy/tz1WcPpecD5c1THPYtIkhxzZoQE=" +ETag: "1-tZzSREUjWTbo3aR/f3UyqLOpQlYXAhTMxMLBUopbx10=" Cache-Control: public, must-revalidate, max-age=3600 Content-Type: application/graphql; charset=utf-8 Content-Disposition: attachment; filename="schema.graphql" Last-Modified: Fri, 01 Jan 2021 00:00:00 GMT -Content-Length: 7236 +Content-Length: 7499 --------------------------> Status Code: OK --------------------------> @@ -30,7 +30,7 @@ type Droid implements Character { id: ID! name: String! appearsIn: [Episode] - friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) + friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 10, sizedFields: [ "edges", "nodes" ]) height(unit: Unit): Float primaryFunction: String traits: JSON @@ -58,7 +58,7 @@ type Human implements Character { id: ID! name: String! appearsIn: [Episode] - friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) + friends("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): FriendsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 10, sizedFields: [ "edges", "nodes" ]) otherHuman: Human height(unit: Unit): Float homePlanet: String @@ -141,7 +141,7 @@ directive @defer("If this argument label has a value other than null, it will be directive @foo(bar: Int!) on SUBSCRIPTION "The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." -directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!] "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true) on FIELD_DEFINITION +directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!] "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." slicingArgumentDefaultValue: Int "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true) on FIELD_DEFINITION "The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! = 0 "Streamed when true." if: Boolean) on FIELD diff --git a/src/HotChocolate/Core/src/Abstractions/WellKnownContextData.cs b/src/HotChocolate/Core/src/Abstractions/WellKnownContextData.cs index efd00842378..4bb3d96a00e 100644 --- a/src/HotChocolate/Core/src/Abstractions/WellKnownContextData.cs +++ b/src/HotChocolate/Core/src/Abstractions/WellKnownContextData.cs @@ -309,6 +309,11 @@ public static class WellKnownContextData /// public const string ValidateCost = "HotChocolate.CostAnalysis.ValidateCost"; + /// + /// The key to access the cost options on the context data.. + /// + public const string RequestCostOptions = "HotChocolate.CostAnalysis.CostRequestOptions"; + /// /// The key to access the paging observers stored on the local resolver state. /// diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostAnalyzer.cs b/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostAnalyzer.cs index abcbc9562b3..591f257d30b 100644 --- a/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostAnalyzer.cs +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostAnalyzer.cs @@ -11,7 +11,7 @@ namespace HotChocolate.CostAnalysis; -internal sealed class CostAnalyzer(CostOptions options) : TypeDocumentValidatorVisitor +internal sealed class CostAnalyzer(RequestCostOptions options) : TypeDocumentValidatorVisitor { private readonly Dictionary _selectionSetCost = new(); private readonly HashSet _processed = new(); @@ -280,9 +280,9 @@ private CostSummary GetSelectionSetCost(SelectionSetNode selectionSetNode) if ((argument.Flags & FieldFlags.FilterArgument) == FieldFlags.FilterArgument && argumentNode.Value.Kind == SyntaxKind.Variable - && options.Filtering.VariableMultiplier.HasValue) + && options.FilterVariableMultiplier.HasValue) { - argumentCost *= options.Filtering.VariableMultiplier.Value; + argumentCost *= options.FilterVariableMultiplier.Value; } fieldCost += argumentCost; @@ -317,7 +317,7 @@ private CostSummary GetSelectionSetCost(SelectionSetNode selectionSetNode) // if the field is a list type we are multiplying the cost // by the estimated list size. - var listSize = field.GetListSize(arguments, listSizeDirective, context.Variables); + var listSize = field.GetListSize(arguments, listSizeDirective); typeCost *= listSize; selectionSetCost *= listSize; } diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostAnalyzerMiddleware.cs b/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostAnalyzerMiddleware.cs index ebef63d639a..a13cc8c04c2 100644 --- a/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostAnalyzerMiddleware.cs +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostAnalyzerMiddleware.cs @@ -14,7 +14,7 @@ namespace HotChocolate.CostAnalysis; internal sealed class CostAnalyzerMiddleware( RequestDelegate next, - [SchemaService] CostOptions options, + [SchemaService] RequestCostOptions options, DocumentValidatorContextPool contextPool, ICostMetricsCache cache, [SchemaService] IExecutionDiagnosticEvents diagnosticEvents) @@ -35,7 +35,8 @@ public async ValueTask InvokeAsync(IRequestContext context) context.OperationId = operationId; } - var mode = context.GetCostAnalyzerMode(options); + var requestOptions = context.TryGetCostOptions() ?? options; + var mode = context.GetCostAnalyzerMode(requestOptions.EnforceCostLimits); if (mode == CostAnalyzerMode.Skip) { @@ -43,7 +44,7 @@ public async ValueTask InvokeAsync(IRequestContext context) return; } - if (!TryAnalyze(context, mode, context.Document, operationId, out var costMetrics)) + if (!TryAnalyze(context, requestOptions, mode, context.Document, operationId, out var costMetrics)) { // a error happened during the analysis and the error is already set. return; @@ -65,6 +66,7 @@ context.Result is null private bool TryAnalyze( IRequestContext context, + RequestCostOptions requestOptions, CostAnalyzerMode mode, DocumentNode document, string operationId, @@ -80,12 +82,13 @@ private bool TryAnalyze( // we check if the operation was already resolved by another middleware, // if not we resolve the operation. var operationDefinition = - context.Operation?.Definition ?? document.GetOperation(context.Request.OperationName); + context.Operation?.Definition + ?? document.GetOperation(context.Request.OperationName); validatorContext = contextPool.Get(); PrepareContext(context, document, validatorContext); - var analyzer = new CostAnalyzer(options); + var analyzer = new CostAnalyzer(requestOptions); costMetrics = analyzer.Analyze(operationDefinition, validatorContext); cache.TryAddCostMetrics(operationId, costMetrics); } @@ -95,20 +98,20 @@ private bool TryAnalyze( if ((mode & CostAnalyzerMode.Enforce) == CostAnalyzerMode.Enforce) { - if (costMetrics.FieldCost > options.MaxFieldCost) + if (costMetrics.FieldCost > requestOptions.MaxFieldCost) { context.Result = ErrorHelper.MaxFieldCostReached( costMetrics, - options.MaxFieldCost, + requestOptions.MaxFieldCost, (mode & CostAnalyzerMode.Report) == CostAnalyzerMode.Report); return false; } - if (costMetrics.TypeCost > options.MaxTypeCost) + if (costMetrics.TypeCost > requestOptions.MaxTypeCost) { context.Result = ErrorHelper.MaxTypeCostReached( costMetrics, - options.MaxTypeCost, + requestOptions.MaxTypeCost, (mode & CostAnalyzerMode.Report) == CostAnalyzerMode.Report); return false; } @@ -155,7 +158,7 @@ public static RequestCoreMiddleware Create() return (core, next) => { // this needs to be a schema service - var options = core.SchemaServices.GetRequiredService(); + var options = core.SchemaServices.GetRequiredService(); var contextPool = core.Services.GetRequiredService(); var cache = core.Services.GetRequiredService(); var diagnosticEvents = core.SchemaServices.GetRequiredService(); diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostAnalyzerMode.cs b/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostAnalyzerMode.cs index 181c7f831ed..4bc887ae754 100644 --- a/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostAnalyzerMode.cs +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostAnalyzerMode.cs @@ -17,7 +17,7 @@ internal enum CostAnalyzerMode Analyze = 1, /// - /// Enforces the defined cost limits but does not report any metrics in the response. + /// Includes the operation cost metrics in the response. /// Report = 2, diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostTypeInterceptor.cs b/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostTypeInterceptor.cs index 9b179f602ba..ae769173431 100644 --- a/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostTypeInterceptor.cs +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostTypeInterceptor.cs @@ -32,15 +32,16 @@ private readonly ImmutableArray _offsetSizedFields internal override uint Position => int.MaxValue; + internal override bool IsEnabled(IDescriptorContext context) + => context.Services.GetRequiredService().ApplyCostDefaults; + internal override void InitializeContext( IDescriptorContext context, TypeInitializer typeInitializer, TypeRegistry typeRegistry, TypeLookup typeLookup, TypeReferenceResolver typeReferenceResolver) - { - _options = context.Services.GetRequiredService(); - } + => _options = context.Services.GetRequiredService(); public override void OnAfterCompleteName(ITypeCompletionContext completionContext, DefinitionBase definition) { @@ -79,12 +80,19 @@ public override void OnAfterCompleteName(ITypeCompletionContext completionContex slicingArgs.Length > 0 && (options.RequirePagingBoundaries ?? false); + int? slicingArgumentDefaultValue = null; + if (_options.ApplySlicingArgumentDefaultValue) + { + slicingArgumentDefaultValue = options.DefaultPageSize ?? DefaultPageSize; + } + fieldDef.AddDirective( new ListSizeDirective( assumedSize, slicingArgs, sizeFields, - requirePagingBoundaries), + requirePagingBoundaries, + slicingArgumentDefaultValue), completionContext.DescriptorContext.TypeInspector); } diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/DependencyInjection/CostAnalyzerRequestExecutorBuilderExtensions.cs b/src/HotChocolate/CostAnalysis/src/CostAnalysis/DependencyInjection/CostAnalyzerRequestExecutorBuilderExtensions.cs index b84499359c4..64560d2c558 100644 --- a/src/HotChocolate/CostAnalysis/src/CostAnalysis/DependencyInjection/CostAnalyzerRequestExecutorBuilderExtensions.cs +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/DependencyInjection/CostAnalyzerRequestExecutorBuilderExtensions.cs @@ -48,6 +48,16 @@ public static IRequestExecutorBuilder AddCostAnalyzer(this IRequestExecutorBuild return options; }); + + services.TryAddSingleton(sp => + { + var requestOptions = sp.GetRequiredService(); + return new RequestCostOptions( + requestOptions.MaxFieldCost, + requestOptions.MaxTypeCost, + requestOptions.EnforceCostLimits, + requestOptions.Filtering.VariableMultiplier); + }); }) .AddDirectiveType() .AddDirectiveType() diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/Extensions/CostAnalyzerObjectFieldDescriptorExtensions.cs b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Extensions/CostAnalyzerObjectFieldDescriptorExtensions.cs index 3bad7dfdf56..1ccb17f1b8c 100644 --- a/src/HotChocolate/CostAnalysis/src/CostAnalysis/Extensions/CostAnalyzerObjectFieldDescriptorExtensions.cs +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Extensions/CostAnalyzerObjectFieldDescriptorExtensions.cs @@ -60,6 +60,9 @@ public static IObjectFieldDescriptor Cost(this IObjectFieldDescriptor descriptor /// Whether to require a single slicing argument in the query. If that is not the case (i.e., if /// none or multiple slicing arguments are present), the static analysis will throw an error. /// + /// + /// The default value to use for slicing arguments if no slicing argument is provided. + /// /// /// Returns the object field descriptor for configuration chaining. /// @@ -71,7 +74,8 @@ public static IObjectFieldDescriptor ListSize( int? assumedSize = null, ImmutableArray? slicingArguments = null, ImmutableArray? sizedFields = null, - bool requireOneSlicingArgument = true) + bool requireOneSlicingArgument = true, + int? slicingArgumentDefaultValue = null) { if (descriptor is null) { @@ -83,6 +87,7 @@ public static IObjectFieldDescriptor ListSize( assumedSize, slicingArguments, sizedFields, - requireOneSlicingArgument)); + requireOneSlicingArgument, + slicingArgumentDefaultValue)); } } diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/Extensions/CostAnalyzerRequestContextExtensions.cs b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Extensions/CostAnalyzerRequestContextExtensions.cs index 91adbf8c712..e3c42d18810 100644 --- a/src/HotChocolate/CostAnalysis/src/CostAnalysis/Extensions/CostAnalyzerRequestContextExtensions.cs +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Extensions/CostAnalyzerRequestContextExtensions.cs @@ -1,4 +1,6 @@ using HotChocolate.CostAnalysis; +using HotChocolate.Resolvers; +using Microsoft.Extensions.DependencyInjection; namespace HotChocolate.Execution; @@ -56,18 +58,13 @@ public static CostMetrics GetCostMetrics( internal static CostAnalyzerMode GetCostAnalyzerMode( this IRequestContext context, - CostOptions options) + bool enforceCostLimits) { if (context is null) { throw new ArgumentNullException(nameof(context)); } - if (options is null) - { - throw new ArgumentNullException(nameof(options)); - } - if (context.ContextData.ContainsKey(WellKnownContextData.ValidateCost)) { return CostAnalyzerMode.Analyze | CostAnalyzerMode.Report; @@ -75,7 +72,7 @@ internal static CostAnalyzerMode GetCostAnalyzerMode( var flags = CostAnalyzerMode.Analyze; - if (options.EnforceCostLimits) + if (enforceCostLimits) { flags |= CostAnalyzerMode.Enforce; } @@ -89,4 +86,80 @@ internal static CostAnalyzerMode GetCostAnalyzerMode( return flags; } + + /// + /// Gets the cost options for the current request. + /// + /// + /// The request context. + /// + /// + /// Returns the cost options. + /// + public static RequestCostOptions GetCostOptions(this IRequestContext context) + { + if (context.ContextData.TryGetValue(WellKnownContextData.RequestCostOptions, out var value) + && value is RequestCostOptions options) + { + return options; + } + + return context.Schema.Services.GetRequiredService(); + } + + /// + /// Gets the global cost options from the executor. + /// + /// + /// The GraphQL executor. + /// + /// + /// Returns the global cost options. + /// + public static RequestCostOptions GetCostOptions(this IRequestExecutor executor) + { + return executor.Schema.Services.GetRequiredService(); + } + + internal static RequestCostOptions? TryGetCostOptions(this IRequestContext context) + { + if (context.ContextData.TryGetValue(WellKnownContextData.RequestCostOptions, out var value) + && value is RequestCostOptions options) + { + return options; + } + + return null; + } + + /// + /// Sets the cost options for the current request. + /// + /// + /// The request context. + /// + /// + /// The cost options. + /// + public static void SetCostOptions(this IRequestContext context, RequestCostOptions options) + { + context.ContextData[WellKnownContextData.RequestCostOptions] = options; + } + + /// + /// Sets the cost options for the current request. + /// + /// + /// The operation request builder. + /// + /// + /// The cost options. + /// + /// + /// Returns the operation request builder. + /// + public static OperationRequestBuilder SetCostOptions( + this OperationRequestBuilder builder, + RequestCostOptions options) + => builder.SetGlobalState(WellKnownContextData.RequestCostOptions, options); } diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/Options/CostOptions.cs b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Options/CostOptions.cs index 6d84907069b..b036e72187b 100644 --- a/src/HotChocolate/CostAnalysis/src/CostAnalysis/Options/CostOptions.cs +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Options/CostOptions.cs @@ -25,6 +25,11 @@ public sealed class CostOptions /// public bool ApplyCostDefaults { get; set; } = true; + /// + /// Defines if the non-spec slicing argument default value shall be applied. + /// + public bool ApplySlicingArgumentDefaultValue { get; set; } = true; + /// /// Gets or sets the default cost for an async resolver pipeline. /// diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/Options/RequestCostOptions.cs b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Options/RequestCostOptions.cs new file mode 100644 index 00000000000..0b111285f7d --- /dev/null +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Options/RequestCostOptions.cs @@ -0,0 +1,22 @@ +namespace HotChocolate.CostAnalysis; + +/// +/// Request options for cost analysis. +/// +/// +/// The maximum allowed field cost. +/// +/// +/// The maximum allowed type cost. +/// +/// +/// Defines if the analyzer shall enforce cost limits. +/// +/// +/// The filter variable multiplier. +/// +public record RequestCostOptions( + double MaxFieldCost, + double MaxTypeCost, + bool EnforceCostLimits, + int? FilterVariableMultiplier); diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/Types/ListSizeAttribute.cs b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Types/ListSizeAttribute.cs index 16bf14efee0..3d1daf2c133 100644 --- a/src/HotChocolate/CostAnalysis/src/CostAnalysis/Types/ListSizeAttribute.cs +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Types/ListSizeAttribute.cs @@ -29,6 +29,12 @@ public int AssumedSize /// public string[]? SlicingArguments { get; init; } + /// + /// The default value for a slicing argument, which is used if the argument is not present in a + /// query. + /// + public int? SlicingArgumentDefaultValue { get; init; } + /// /// The subfield(s) that the list size applies to. /// diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/Types/ListSizeDirective.cs b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Types/ListSizeDirective.cs index ceace987f75..897818e226f 100644 --- a/src/HotChocolate/CostAnalysis/src/CostAnalysis/Types/ListSizeDirective.cs +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Types/ListSizeDirective.cs @@ -20,14 +20,17 @@ public sealed class ListSizeDirective /// /// Specification URL /// - public ListSizeDirective(int? assumedSize = null, + public ListSizeDirective( + int? assumedSize = null, ImmutableArray? slicingArguments = null, ImmutableArray? sizedFields = null, - bool? requireOneSlicingArgument = null) + bool? requireOneSlicingArgument = null, + int? slicingArgumentDefaultValue = null) { AssumedSize = assumedSize; SlicingArguments = slicingArguments ?? ImmutableArray.Empty; SizedFields = sizedFields ?? ImmutableArray.Empty; + SlicingArgumentDefaultValue = slicingArgumentDefaultValue; // https://ibm.github.io/graphql-specs/cost-spec.html#sec-requireOneSlicingArgument // Per default, requireOneSlicingArgument is enabled, @@ -54,6 +57,16 @@ public ListSizeDirective(int? assumedSize = null, /// public ImmutableArray SlicingArguments { get; } + /// + /// Specifies the default value to use for slicing arguments if no slicing argument is provided. + /// + /// + /// This property is a non-spec addition and can provide a default value for slicing arguments if no slicing + /// argument is provided. This is useful for fields that have a default value for a slicing + /// argument that cannot be expressed in the schema. + /// + public int? SlicingArgumentDefaultValue { get; } + /// /// The sizedFields argument can be used to define that the value of the /// assumedSize argument or of a slicing argument does not affect the size of a list diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/Types/ListSizeDirectiveType.cs b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Types/ListSizeDirectiveType.cs index c20b9f8a55d..bdfded05c21 100644 --- a/src/HotChocolate/CostAnalysis/src/CostAnalysis/Types/ListSizeDirectiveType.cs +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Types/ListSizeDirectiveType.cs @@ -48,6 +48,14 @@ protected override void Configure(IDirectiveTypeDescriptor de "determines the size of the list returned by that field. It may specify a list " + "of multiple slicing arguments."); + descriptor + .Argument(t => t.SlicingArgumentDefaultValue) + .Name(SlicingArgumentDefaultValue) + .Type() + .Description( + "The `slicingArgumentDefaultValue` argument can be used to define a default value " + + "for a slicing argument, which is used if the argument is not present in a query."); + descriptor .Argument(t => t.SizedFields) .Name(SizedFields) @@ -81,6 +89,7 @@ private static object ParseLiteral(DirectiveNode directiveNode) var slicingArguments = ImmutableArray.Empty; var sizedFields = ImmutableArray.Empty; var requireOneSlicingArgument = false; + int? slicingArgumentDefaultValue = null; foreach (var argument in directiveNode.Arguments) { @@ -107,12 +116,21 @@ private static object ParseLiteral(DirectiveNode directiveNode) requireOneSlicingArgument = argument.Value.ExpectBoolean(); break; + case SlicingArgumentDefaultValue: + slicingArgumentDefaultValue = argument.Value.ExpectInt(); + break; + default: throw new InvalidOperationException("Invalid argument name."); } } - return new ListSizeDirective(assumedSize, slicingArguments, sizedFields, requireOneSlicingArgument); + return new ListSizeDirective( + assumedSize, + slicingArguments, + sizedFields, + requireOneSlicingArgument, + slicingArgumentDefaultValue); } protected override Func OnCompleteFormat( @@ -137,6 +155,14 @@ private static DirectiveNode FormatValue(object value) if (directive.SlicingArguments.Length > 0) { arguments.Add(new ArgumentNode(SlicingArguments, directive.SlicingArguments.ToListValueNode())); + + if(directive.SlicingArgumentDefaultValue.HasValue) + { + arguments.Add( + new ArgumentNode( + SlicingArgumentDefaultValue, + directive.SlicingArgumentDefaultValue.Value)); + } } if (directive.SizedFields.Length > 0) diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/Utilities/CostAnalyzerUtilities.cs b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Utilities/CostAnalyzerUtilities.cs index edde29c1f76..fb6d3199d16 100644 --- a/src/HotChocolate/CostAnalysis/src/CostAnalysis/Utilities/CostAnalyzerUtilities.cs +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Utilities/CostAnalyzerUtilities.cs @@ -88,8 +88,7 @@ public static double GetTypeWeight(this IType type) public static double GetListSize( this IOutputField field, IReadOnlyList arguments, - ListSizeDirective? listSizeDirective, - IDictionary variables) + ListSizeDirective? listSizeDirective) { const int defaultListSize = 1; @@ -114,11 +113,10 @@ public static double GetListSize( slicingValues[index++] = intValueNode.ToInt32(); continue; - case VariableNode variableNode - when variables[variableNode.Name.Value].DefaultValue is - IntValueNode intValueNode: - slicingValues[index++] = intValueNode.ToInt32(); - continue; + // if one of the slicing arguments is variable we will assume the + // maximum allowed page size. + case VariableNode when listSizeDirective.AssumedSize.HasValue: + return listSizeDirective.AssumedSize.Value; } } @@ -129,6 +127,13 @@ when variables[variableNode.Name.Value].DefaultValue is } } + if (index == 0 && listSizeDirective.SlicingArgumentDefaultValue.HasValue) + { + // if no slicing arguments were found we assume the + // paging default size if one is set. + return listSizeDirective.SlicingArgumentDefaultValue.Value; + } + if (index == 1) { return slicingValues[0]; diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/WellKnownArgumentNames.cs b/src/HotChocolate/CostAnalysis/src/CostAnalysis/WellKnownArgumentNames.cs index f268ff0b07f..c76f59bc7be 100644 --- a/src/HotChocolate/CostAnalysis/src/CostAnalysis/WellKnownArgumentNames.cs +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/WellKnownArgumentNames.cs @@ -3,10 +3,9 @@ namespace HotChocolate.CostAnalysis; internal static class WellKnownArgumentNames { public const string AssumedSize = "assumedSize"; - public const string RegexName = "regexName"; - public const string RegexPath = "regexPath"; public const string RequireOneSlicingArgument = "requireOneSlicingArgument"; public const string SizedFields = "sizedFields"; public const string SlicingArguments = "slicingArguments"; + public const string SlicingArgumentDefaultValue = "slicingArgumentDefaultValue"; public const string Weight = "weight"; } diff --git a/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/PagingTests.cs b/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/PagingTests.cs index 2bbf4ca8415..69913b82b90 100644 --- a/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/PagingTests.cs +++ b/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/PagingTests.cs @@ -27,6 +27,22 @@ public async Task Ensure_Paging_Defaults_Are_Applied() schema.MatchSnapshot(); } + [Fact] + public async Task Do_Not_Apply_Defaults() + { + var schema = + await new ServiceCollection() + .AddGraphQLServer() + .AddQueryType() + .AddFiltering() + .AddSorting() + .ModifyPagingOptions(o => o.RequirePagingBoundaries = true) + .ModifyCostOptions(o => o.ApplyCostDefaults = false) + .BuildSchemaAsync(); + + schema.MatchSnapshot(); + } + [Fact] public async Task Filtering_Not_Used() { @@ -69,7 +85,7 @@ public async Task Filtering_Not_Used() """ { "fieldCost": 6, - "typeCost": 52 + "typeCost": 12 } """); @@ -382,7 +398,7 @@ public async Task Filtering_Specific_Filter_Used() """ { "fieldCost": 9, - "typeCost": 52 + "typeCost": 12 } """); @@ -435,7 +451,7 @@ public async Task Filtering_Specific_Expensive_Filter_Used() """ { "fieldCost": 10, - "typeCost": 52 + "typeCost": 12 } """); @@ -488,7 +504,7 @@ public async Task Filtering_Variable() """ { "fieldCost": 10, - "typeCost": 52 + "typeCost": 12 } """); @@ -499,6 +515,95 @@ await snapshot .MatchMarkdownAsync(); } + [Fact] + public async Task Use_Default_Page_Size_When_Default_Is_Specified() + { + // arrange + var snapshot = new Snapshot(); + + var operation = + Utf8GraphQLParser.Parse( + """ + { + books { + nodes { + title + } + } + } + """); + + var request = + OperationRequestBuilder.New() + .SetDocument(operation) + .ReportCost() + .Build(); + + var executor = + await new ServiceCollection() + .AddGraphQLServer() + .AddQueryType() + .AddFiltering() + .AddSorting() + .ModifyPagingOptions(o => o.DefaultPageSize = 2) + .BuildRequestExecutorAsync(); + + // act + var response = await executor.ExecuteAsync(request); + + // assert + await snapshot + .Add(operation, "Operation") + .Add(response, "Response") + .Add(executor.Schema, "Schema") + .MatchMarkdownAsync(); + } + + [Fact] + public async Task Do_Not_Use_Default_Page_Size_When_Default_Is_Specified() + { + // arrange + var snapshot = new Snapshot(); + + var operation = + Utf8GraphQLParser.Parse( + """ + { + books { + nodes { + title + } + } + } + """); + + var request = + OperationRequestBuilder.New() + .SetDocument(operation) + .ReportCost() + .Build(); + + var executor = + await new ServiceCollection() + .AddGraphQLServer() + .AddQueryType() + .AddFiltering() + .AddSorting() + .ModifyPagingOptions(o => o.DefaultPageSize = 2) + .ModifyCostOptions(o => o.ApplySlicingArgumentDefaultValue = false) + .BuildRequestExecutorAsync(); + + // act + var response = await executor.ExecuteAsync(request); + + // assert + await snapshot + .Add(operation, "Operation") + .Add(response, "Response") + .Add(executor.Schema, "Schema") + .MatchMarkdownAsync(); + } + public class Query { [UsePaging] diff --git a/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Do_Not_Apply_Defaults.graphql b/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Do_Not_Apply_Defaults.graphql new file mode 100644 index 00000000000..529d01bb859 --- /dev/null +++ b/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Do_Not_Apply_Defaults.graphql @@ -0,0 +1,142 @@ +schema { + query: Query +} + +type Author { + name: String! +} + +"A connection to a list of items." +type AuthorsConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [AuthorsEdge!] + "A flattened list of the nodes." + nodes: [Author!] +} + +"An edge in a connection." +type AuthorsEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: Author! +} + +type Book { + title: String! + authors("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): AuthorsConnection +} + +"A connection to a list of items." +type BooksConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [BooksEdge!] + "A flattened list of the nodes." + nodes: [Book!] +} + +"An edge in a connection." +type BooksEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: Book! +} + +"A segment of a collection." +type BooksOffsetCollectionSegment { + "Information to aid in pagination." + pageInfo: CollectionSegmentInfo! + "A flattened list of the items." + items: [Book!] +} + +"A segment of a collection." +type BooksTotalCollectionSegment { + "Information to aid in pagination." + pageInfo: CollectionSegmentInfo! + "A flattened list of the items." + items: [Book!] + totalCount: Int! +} + +"A connection to a list of items." +type BooksTotalConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [BooksTotalEdge!] + "A flattened list of the nodes." + nodes: [Book!] + "Identifies the total count of items in the connection." + totalCount: Int! +} + +"An edge in a connection." +type BooksTotalEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: Book! +} + +"Information about the offset pagination." +type CollectionSegmentInfo { + "Indicates whether more items exist following the set defined by the clients arguments." + hasNextPage: Boolean! + "Indicates whether more items exist prior the set defined by the clients arguments." + hasPreviousPage: Boolean! +} + +"Information about pagination in a connection." +type PageInfo { + "Indicates whether more edges exist following the set defined by the clients arguments." + hasNextPage: Boolean! + "Indicates whether more edges exist prior the set defined by the clients arguments." + hasPreviousPage: Boolean! + "When paginating backwards, the cursor to continue." + startCursor: String + "When paginating forwards, the cursor to continue." + endCursor: String +} + +type Query { + books("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String where: BookFilterInput order: [BookSortInput!]): BooksConnection + booksWithTotalCount("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String where: BookFilterInput order: [BookSortInput!]): BooksTotalConnection + booksOffset(skip: Int take: Int where: BookFilterInput order: [BookSortInput!]): BooksOffsetCollectionSegment + booksOffsetWithTotalCount(skip: Int take: Int where: BookFilterInput order: [BookSortInput!]): BooksTotalCollectionSegment +} + +input BookFilterInput { + and: [BookFilterInput!] + or: [BookFilterInput!] + title: StringOperationFilterInput +} + +input BookSortInput { + title: SortEnumType +} + +input StringOperationFilterInput { + and: [StringOperationFilterInput!] + or: [StringOperationFilterInput!] + eq: String + neq: String + contains: String + ncontains: String + in: [String] + nin: [String] + startsWith: String + nstartsWith: String + endsWith: String + nendsWith: String +} + +enum SortEnumType { + ASC + DESC +} diff --git a/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Do_Not_Use_Default_Page_Size_When_Default_Is_Specified.md b/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Do_Not_Use_Default_Page_Size_When_Default_Is_Specified.md new file mode 100644 index 00000000000..eb8da3790ca --- /dev/null +++ b/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Do_Not_Use_Default_Page_Size_When_Default_Is_Specified.md @@ -0,0 +1,185 @@ +# Do_Not_Use_Default_Page_Size_When_Default_Is_Specified + +## Operation + +```graphql +{ + books { + nodes { + title + } + } +} +``` + +## Response + +```json +{ + "data": { + "books": { + "nodes": [] + } + }, + "extensions": { + "operationCost": { + "fieldCost": 11, + "typeCost": 52 + } + } +} +``` + +## Schema + +```graphql +schema { + query: Query +} + +type Author { + name: String! +} + +"A connection to a list of items." +type AuthorsConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [AuthorsEdge!] + "A flattened list of the nodes." + nodes: [Author!] +} + +"An edge in a connection." +type AuthorsEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: Author! +} + +type Book { + title: String! + authors("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): AuthorsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) +} + +"A connection to a list of items." +type BooksConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [BooksEdge!] + "A flattened list of the nodes." + nodes: [Book!] +} + +"An edge in a connection." +type BooksEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: Book! +} + +"A segment of a collection." +type BooksOffsetCollectionSegment { + "Information to aid in pagination." + pageInfo: CollectionSegmentInfo! + "A flattened list of the items." + items: [Book!] +} + +"A segment of a collection." +type BooksTotalCollectionSegment { + "Information to aid in pagination." + pageInfo: CollectionSegmentInfo! + "A flattened list of the items." + items: [Book!] + totalCount: Int! @cost(weight: "10") +} + +"A connection to a list of items." +type BooksTotalConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [BooksTotalEdge!] + "A flattened list of the nodes." + nodes: [Book!] + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type BooksTotalEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: Book! +} + +"Information about the offset pagination." +type CollectionSegmentInfo { + "Indicates whether more items exist following the set defined by the clients arguments." + hasNextPage: Boolean! + "Indicates whether more items exist prior the set defined by the clients arguments." + hasPreviousPage: Boolean! +} + +"Information about pagination in a connection." +type PageInfo { + "Indicates whether more edges exist following the set defined by the clients arguments." + hasNextPage: Boolean! + "Indicates whether more edges exist prior the set defined by the clients arguments." + hasPreviousPage: Boolean! + "When paginating backwards, the cursor to continue." + startCursor: String + "When paginating forwards, the cursor to continue." + endCursor: String +} + +type Query { + books("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String where: BookFilterInput @cost(weight: "10") order: [BookSortInput!] @cost(weight: "10")): BooksConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + booksWithTotalCount("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String where: BookFilterInput @cost(weight: "10") order: [BookSortInput!] @cost(weight: "10")): BooksTotalConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + booksOffset(skip: Int take: Int where: BookFilterInput @cost(weight: "10") order: [BookSortInput!] @cost(weight: "10")): BooksOffsetCollectionSegment @listSize(assumedSize: 50, slicingArguments: [ "take" ], sizedFields: [ "items" ], requireOneSlicingArgument: false) @cost(weight: "10") + booksOffsetWithTotalCount(skip: Int take: Int where: BookFilterInput @cost(weight: "10") order: [BookSortInput!] @cost(weight: "10")): BooksTotalCollectionSegment @listSize(assumedSize: 50, slicingArguments: [ "take" ], sizedFields: [ "items" ], requireOneSlicingArgument: false) @cost(weight: "10") +} + +input BookFilterInput { + and: [BookFilterInput!] + or: [BookFilterInput!] + title: StringOperationFilterInput +} + +input BookSortInput { + title: SortEnumType @cost(weight: "10") +} + +input StringOperationFilterInput { + and: [StringOperationFilterInput!] + or: [StringOperationFilterInput!] + eq: String @cost(weight: "10") + neq: String @cost(weight: "10") + contains: String @cost(weight: "20") + ncontains: String @cost(weight: "20") + in: [String] @cost(weight: "10") + nin: [String] @cost(weight: "10") + startsWith: String @cost(weight: "20") + nstartsWith: String @cost(weight: "20") + endsWith: String @cost(weight: "20") + nendsWith: String @cost(weight: "20") +} + +enum SortEnumType { + ASC + DESC +} + +"The purpose of the `cost` directive is to define a `weight` for GraphQL types, fields, and arguments. Static analysis can use these weights when calculating the overall cost of a query or response." +directive @cost("The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." weight: String!) on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM | INPUT_FIELD_DEFINITION + +"The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." +directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!] "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." slicingArgumentDefaultValue: Int "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true) on FIELD_DEFINITION +``` + diff --git a/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Ensure_Paging_Defaults_Are_Applied.graphql b/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Ensure_Paging_Defaults_Are_Applied.graphql index 2d8d451093e..a442411be1f 100644 --- a/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Ensure_Paging_Defaults_Are_Applied.graphql +++ b/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Ensure_Paging_Defaults_Are_Applied.graphql @@ -26,7 +26,7 @@ type AuthorsEdge { type Book { title: String! - authors("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): AuthorsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) + authors("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): AuthorsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 10, sizedFields: [ "edges", "nodes" ]) } "A connection to a list of items." @@ -105,10 +105,10 @@ type PageInfo { } type Query { - books("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String where: BookFilterInput @cost(weight: "10") order: [BookSortInput!] @cost(weight: "10")): BooksConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) @cost(weight: "10") - booksWithTotalCount("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String where: BookFilterInput @cost(weight: "10") order: [BookSortInput!] @cost(weight: "10")): BooksTotalConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) @cost(weight: "10") - booksOffset(skip: Int take: Int where: BookFilterInput @cost(weight: "10") order: [BookSortInput!] @cost(weight: "10")): BooksOffsetCollectionSegment @listSize(assumedSize: 50, slicingArguments: [ "take" ], sizedFields: [ "items" ]) @cost(weight: "10") - booksOffsetWithTotalCount(skip: Int take: Int where: BookFilterInput @cost(weight: "10") order: [BookSortInput!] @cost(weight: "10")): BooksTotalCollectionSegment @listSize(assumedSize: 50, slicingArguments: [ "take" ], sizedFields: [ "items" ]) @cost(weight: "10") + books("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String where: BookFilterInput @cost(weight: "10") order: [BookSortInput!] @cost(weight: "10")): BooksConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 10, sizedFields: [ "edges", "nodes" ]) @cost(weight: "10") + booksWithTotalCount("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String where: BookFilterInput @cost(weight: "10") order: [BookSortInput!] @cost(weight: "10")): BooksTotalConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 10, sizedFields: [ "edges", "nodes" ]) @cost(weight: "10") + booksOffset(skip: Int take: Int where: BookFilterInput @cost(weight: "10") order: [BookSortInput!] @cost(weight: "10")): BooksOffsetCollectionSegment @listSize(assumedSize: 50, slicingArguments: [ "take" ], slicingArgumentDefaultValue: 10, sizedFields: [ "items" ]) @cost(weight: "10") + booksOffsetWithTotalCount(skip: Int take: Int where: BookFilterInput @cost(weight: "10") order: [BookSortInput!] @cost(weight: "10")): BooksTotalCollectionSegment @listSize(assumedSize: 50, slicingArguments: [ "take" ], slicingArgumentDefaultValue: 10, sizedFields: [ "items" ]) @cost(weight: "10") } input BookFilterInput { @@ -145,4 +145,4 @@ enum SortEnumType { directive @cost("The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." weight: String!) on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM | INPUT_FIELD_DEFINITION "The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." -directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!] "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true) on FIELD_DEFINITION +directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!] "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." slicingArgumentDefaultValue: Int "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true) on FIELD_DEFINITION diff --git a/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Filtering_Not_Used.md b/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Filtering_Not_Used.md index 4b621d04385..a41d781ccde 100644 --- a/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Filtering_Not_Used.md +++ b/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Filtering_Not_Used.md @@ -17,7 +17,7 @@ ```json { "fieldCost": 6, - "typeCost": 52 + "typeCost": 12 } ``` @@ -33,7 +33,7 @@ "extensions": { "operationCost": { "fieldCost": 11, - "typeCost": 52 + "typeCost": 12 } } } diff --git a/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Filtering_Specific_Expensive_Filter_Used.md b/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Filtering_Specific_Expensive_Filter_Used.md index 6596be628ee..bed8786b283 100644 --- a/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Filtering_Specific_Expensive_Filter_Used.md +++ b/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Filtering_Specific_Expensive_Filter_Used.md @@ -17,7 +17,7 @@ ```json { "fieldCost": 10, - "typeCost": 52 + "typeCost": 12 } ``` @@ -33,7 +33,7 @@ "extensions": { "operationCost": { "fieldCost": 42, - "typeCost": 52 + "typeCost": 12 } } } diff --git a/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Filtering_Specific_Filter_Used.md b/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Filtering_Specific_Filter_Used.md index 13442194c7b..9abe12cd5a4 100644 --- a/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Filtering_Specific_Filter_Used.md +++ b/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Filtering_Specific_Filter_Used.md @@ -17,7 +17,7 @@ ```json { "fieldCost": 9, - "typeCost": 52 + "typeCost": 12 } ``` @@ -33,7 +33,7 @@ "extensions": { "operationCost": { "fieldCost": 32, - "typeCost": 52 + "typeCost": 12 } } } diff --git a/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Filtering_Variable.md b/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Filtering_Variable.md index ae154500bc1..9147cb4c836 100644 --- a/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Filtering_Variable.md +++ b/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Filtering_Variable.md @@ -17,7 +17,7 @@ query($where: BookFilterInput) { ```json { "fieldCost": 10, - "typeCost": 52 + "typeCost": 12 } ``` @@ -33,7 +33,7 @@ query($where: BookFilterInput) { "extensions": { "operationCost": { "fieldCost": 901, - "typeCost": 52 + "typeCost": 12 } } } diff --git a/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Use_Default_Page_Size_When_Default_Is_Specified.md b/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Use_Default_Page_Size_When_Default_Is_Specified.md new file mode 100644 index 00000000000..39c02b57c43 --- /dev/null +++ b/src/HotChocolate/CostAnalysis/test/CostAnalysis.Tests/__snapshots__/PagingTests.Use_Default_Page_Size_When_Default_Is_Specified.md @@ -0,0 +1,185 @@ +# Use_Default_Page_Size_When_Default_Is_Specified + +## Operation + +```graphql +{ + books { + nodes { + title + } + } +} +``` + +## Response + +```json +{ + "data": { + "books": { + "nodes": [] + } + }, + "extensions": { + "operationCost": { + "fieldCost": 11, + "typeCost": 4 + } + } +} +``` + +## Schema + +```graphql +schema { + query: Query +} + +type Author { + name: String! +} + +"A connection to a list of items." +type AuthorsConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [AuthorsEdge!] + "A flattened list of the nodes." + nodes: [Author!] +} + +"An edge in a connection." +type AuthorsEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: Author! +} + +type Book { + title: String! + authors("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String): AuthorsConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 2, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) +} + +"A connection to a list of items." +type BooksConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [BooksEdge!] + "A flattened list of the nodes." + nodes: [Book!] +} + +"An edge in a connection." +type BooksEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: Book! +} + +"A segment of a collection." +type BooksOffsetCollectionSegment { + "Information to aid in pagination." + pageInfo: CollectionSegmentInfo! + "A flattened list of the items." + items: [Book!] +} + +"A segment of a collection." +type BooksTotalCollectionSegment { + "Information to aid in pagination." + pageInfo: CollectionSegmentInfo! + "A flattened list of the items." + items: [Book!] + totalCount: Int! @cost(weight: "10") +} + +"A connection to a list of items." +type BooksTotalConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [BooksTotalEdge!] + "A flattened list of the nodes." + nodes: [Book!] + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type BooksTotalEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: Book! +} + +"Information about the offset pagination." +type CollectionSegmentInfo { + "Indicates whether more items exist following the set defined by the clients arguments." + hasNextPage: Boolean! + "Indicates whether more items exist prior the set defined by the clients arguments." + hasPreviousPage: Boolean! +} + +"Information about pagination in a connection." +type PageInfo { + "Indicates whether more edges exist following the set defined by the clients arguments." + hasNextPage: Boolean! + "Indicates whether more edges exist prior the set defined by the clients arguments." + hasPreviousPage: Boolean! + "When paginating backwards, the cursor to continue." + startCursor: String + "When paginating forwards, the cursor to continue." + endCursor: String +} + +type Query { + books("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String where: BookFilterInput @cost(weight: "10") order: [BookSortInput!] @cost(weight: "10")): BooksConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 2, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + booksWithTotalCount("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String "Returns the last _n_ elements from the list." last: Int "Returns the elements in the list that come before the specified cursor." before: String where: BookFilterInput @cost(weight: "10") order: [BookSortInput!] @cost(weight: "10")): BooksTotalConnection @listSize(assumedSize: 50, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 2, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + booksOffset(skip: Int take: Int where: BookFilterInput @cost(weight: "10") order: [BookSortInput!] @cost(weight: "10")): BooksOffsetCollectionSegment @listSize(assumedSize: 50, slicingArguments: [ "take" ], slicingArgumentDefaultValue: 2, sizedFields: [ "items" ], requireOneSlicingArgument: false) @cost(weight: "10") + booksOffsetWithTotalCount(skip: Int take: Int where: BookFilterInput @cost(weight: "10") order: [BookSortInput!] @cost(weight: "10")): BooksTotalCollectionSegment @listSize(assumedSize: 50, slicingArguments: [ "take" ], slicingArgumentDefaultValue: 2, sizedFields: [ "items" ], requireOneSlicingArgument: false) @cost(weight: "10") +} + +input BookFilterInput { + and: [BookFilterInput!] + or: [BookFilterInput!] + title: StringOperationFilterInput +} + +input BookSortInput { + title: SortEnumType @cost(weight: "10") +} + +input StringOperationFilterInput { + and: [StringOperationFilterInput!] + or: [StringOperationFilterInput!] + eq: String @cost(weight: "10") + neq: String @cost(weight: "10") + contains: String @cost(weight: "20") + ncontains: String @cost(weight: "20") + in: [String] @cost(weight: "10") + nin: [String] @cost(weight: "10") + startsWith: String @cost(weight: "20") + nstartsWith: String @cost(weight: "20") + endsWith: String @cost(weight: "20") + nendsWith: String @cost(weight: "20") +} + +enum SortEnumType { + ASC + DESC +} + +"The purpose of the `cost` directive is to define a `weight` for GraphQL types, fields, and arguments. Static analysis can use these weights when calculating the overall cost of a query or response." +directive @cost("The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." weight: String!) on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM | INPUT_FIELD_DEFINITION + +"The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." +directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!] "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." slicingArgumentDefaultValue: Int "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true) on FIELD_DEFINITION +``` + diff --git a/src/HotChocolate/Utilities/test/Utilities.Introspection.Tests/__snapshots__/IntrospectionClientTests.IntrospectServer.snap b/src/HotChocolate/Utilities/test/Utilities.Introspection.Tests/__snapshots__/IntrospectionClientTests.IntrospectServer.snap index 88333bf5516..fe4ae70bfe9 100644 --- a/src/HotChocolate/Utilities/test/Utilities.Introspection.Tests/__snapshots__/IntrospectionClientTests.IntrospectServer.snap +++ b/src/HotChocolate/Utilities/test/Utilities.Introspection.Tests/__snapshots__/IntrospectionClientTests.IntrospectServer.snap @@ -128,6 +128,6 @@ scalar Long directive @cost("The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." weight: String!) on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM | INPUT_FIELD_DEFINITION "The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." -directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!] "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true) on FIELD_DEFINITION +directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!] "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." slicingArgumentDefaultValue: Int "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true) on FIELD_DEFINITION directive @foo(bar: Int!) on SUBSCRIPTION diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/__snapshots__/StarWarsIntrospectionTest.Execute_StarWarsIntrospection_Test.snap b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/__snapshots__/StarWarsIntrospectionTest.Execute_StarWarsIntrospection_Test.snap index 112324bc208..cb0b9828ac7 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/__snapshots__/StarWarsIntrospectionTest.Execute_StarWarsIntrospection_Test.snap +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/__snapshots__/StarWarsIntrospectionTest.Execute_StarWarsIntrospection_Test.snap @@ -2462,6 +2462,16 @@ }, "DefaultValue": null }, + { + "Name": "slicingArgumentDefaultValue", + "Description": "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query.", + "Type": { + "Kind": "Scalar", + "Name": "Int", + "OfType": null + }, + "DefaultValue": null + }, { "Name": "sizedFields", "Description": "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields.", @@ -6824,6 +6834,24 @@ }, "DefaultValue": null }, + { + "__typename": "__InputValue", + "Name": "slicingArgumentDefaultValue", + "Description": "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query.", + "Type": { + "__typename": "__Type", + "Name": "Int", + "Kind": "Scalar", + "Description": null, + "Fields": null, + "InputFields": null, + "Interfaces": null, + "EnumValues": null, + "PossibleTypes": null, + "OfType": null + }, + "DefaultValue": null + }, { "__typename": "__InputValue", "Name": "sizedFields", From 5ccdc282cdea79bb35efc5a830f0d0a32f923659 Mon Sep 17 00:00:00 2001 From: PascalSenn Date: Mon, 16 Dec 2024 19:05:09 +0100 Subject: [PATCH 128/154] Adds support for async service scope in request executor (#7826) Co-authored-by: Michael Staib --- .../Execution/VariableBatchRequest.cs | 21 +++++++ .../Core/src/Execution/RequestExecutor.cs | 57 +++++++++++++++++-- 2 files changed, 73 insertions(+), 5 deletions(-) diff --git a/src/HotChocolate/Core/src/Abstractions/Execution/VariableBatchRequest.cs b/src/HotChocolate/Core/src/Abstractions/Execution/VariableBatchRequest.cs index c1d8f2e35fa..d854f43cf15 100644 --- a/src/HotChocolate/Core/src/Abstractions/Execution/VariableBatchRequest.cs +++ b/src/HotChocolate/Core/src/Abstractions/Execution/VariableBatchRequest.cs @@ -112,4 +112,25 @@ public VariableBatchRequest( /// GraphQL request flags allow to limit the GraphQL executor capabilities. /// public GraphQLRequestFlags Flags { get; } + + /// + /// Creates a new request with the specified services. + /// + /// + /// The services that shall be used while executing the operation. + /// + /// + /// Returns a new request with the specified services. + /// + public VariableBatchRequest WithServices(IServiceProvider services) => + new( + Document, + DocumentId, + DocumentHash, + OperationName, + VariableValues, + Extensions, + ContextData, + services, + Flags); } diff --git a/src/HotChocolate/Core/src/Execution/RequestExecutor.cs b/src/HotChocolate/Core/src/Execution/RequestExecutor.cs index a3661bff83a..00c9098c3eb 100644 --- a/src/HotChocolate/Core/src/Execution/RequestExecutor.cs +++ b/src/HotChocolate/Core/src/Execution/RequestExecutor.cs @@ -99,7 +99,7 @@ internal async Task ExecuteAsync( if (scopeDataLoader) { - // we ensure that at the begin of each execution there is a fresh batching scope. + // we ensure that at the beginning of each execution there is a fresh batching scope. services.InitializeDataLoaderScope(); } @@ -153,7 +153,14 @@ internal async Task ExecuteAsync( _contextPool.Return(context); } - scope?.Dispose(); + if(scope is IAsyncDisposable asyncScope) + { + await asyncScope.DisposeAsync(); + } + else + { + scope?.Dispose(); + } } } @@ -174,7 +181,7 @@ public Task ExecuteBatchAsync( private async IAsyncEnumerable CreateResponseStream( OperationRequestBatch requestBatch, - [EnumeratorCancellation] CancellationToken cancellationToken = default) + [EnumeratorCancellation] CancellationToken ct = default) { IServiceScope? scope = null; @@ -197,6 +204,31 @@ private async IAsyncEnumerable CreateResponseStream( // we ensure that at the start of each execution there is a fresh batching scope. services.InitializeDataLoaderScope(); + try + { + await foreach (var result in ExecuteBatchStream(requestBatch, services, ct).ConfigureAwait(false)) + { + yield return result; + } + } + finally + { + if(scope is IAsyncDisposable asyncScope) + { + await asyncScope.DisposeAsync(); + } + else + { + scope?.Dispose(); + } + } + } + + private async IAsyncEnumerable ExecuteBatchStream( + OperationRequestBatch requestBatch, + IServiceProvider services, + [EnumeratorCancellation] CancellationToken ct = default) + { var requests = requestBatch.Requests; var requestCount = requests.Count; var tasks = new List(requestCount); @@ -205,7 +237,7 @@ private async IAsyncEnumerable CreateResponseStream( for (var i = 0; i < requestCount; i++) { - tasks.Add(ExecuteBatchItemAsync(requests[i], i, completed, cancellationToken)); + tasks.Add(ExecuteBatchItemAsync(WithServices(requests[i], services), i, completed, ct)); } var buffer = new IOperationResult[8]; @@ -228,7 +260,7 @@ private async IAsyncEnumerable CreateResponseStream( if (task.Status is not TaskStatus.RanToCompletion) { - // we await to throw if its not successful. + // we await to throw if it's not successful. await task; } @@ -252,6 +284,21 @@ private async IAsyncEnumerable CreateResponseStream( while (tasks.Count > 0 || bufferCount > 0); } + private static IOperationRequest WithServices(IOperationRequest request, IServiceProvider services) + { + switch (request) + { + case OperationRequest operationRequest: + return operationRequest.WithServices(services); + + case VariableBatchRequest variableBatchRequest: + return variableBatchRequest.WithServices(services); + + default: + throw new InvalidOperationException("Unexpected request type."); + } + } + private async Task ExecuteBatchItemAsync( IOperationRequest request, int requestIndex, From 6747630e8c45cabc7e5ea8d9aa06cd4e9146d82b Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Mon, 16 Dec 2024 19:01:32 +0100 Subject: [PATCH 129/154] Ensure that the execution batching provider is disposed. (#7831) --- src/HotChocolate/Core/src/Fetching/BatchScheduler.cs | 10 ++++++++++ src/HotChocolate/Core/src/Fetching/IBatchHandler.cs | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/HotChocolate/Core/src/Fetching/BatchScheduler.cs b/src/HotChocolate/Core/src/Fetching/BatchScheduler.cs index 697ee30453b..5fc912688f5 100644 --- a/src/HotChocolate/Core/src/Fetching/BatchScheduler.cs +++ b/src/HotChocolate/Core/src/Fetching/BatchScheduler.cs @@ -17,6 +17,7 @@ public sealed class BatchScheduler : IBatchHandler private readonly List> _tasks = []; private bool _dispatchOnSchedule; private readonly List _listeners = []; + private bool _disposed; /// public event EventHandler TaskEnqueued @@ -219,4 +220,13 @@ private async Task DispatchOnEnqueue(Func dispatch) _semaphore.Release(); } } + + public void Dispose() + { + if (!_disposed) + { + _semaphore.Dispose(); + _disposed = true; + } + } } diff --git a/src/HotChocolate/Core/src/Fetching/IBatchHandler.cs b/src/HotChocolate/Core/src/Fetching/IBatchHandler.cs index 43bd1161d26..a45bc5cbf25 100644 --- a/src/HotChocolate/Core/src/Fetching/IBatchHandler.cs +++ b/src/HotChocolate/Core/src/Fetching/IBatchHandler.cs @@ -5,4 +5,4 @@ namespace HotChocolate.Fetching; /// /// The execution engine batch scheduler and dispatcher. /// -public interface IBatchHandler : IBatchDispatcher, IBatchScheduler; +public interface IBatchHandler : IBatchDispatcher, IBatchScheduler, IDisposable; From 23cf377eebe34b804dd467adeab3d8ac3c8a9d9b Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Mon, 16 Dec 2024 19:41:11 +0100 Subject: [PATCH 130/154] Fixed batching completes before all responses are returned. (#7832) --- .../Core/src/Execution/RequestExecutor.cs | 37 +++++-------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/src/HotChocolate/Core/src/Execution/RequestExecutor.cs b/src/HotChocolate/Core/src/Execution/RequestExecutor.cs index 00c9098c3eb..1172f15bfe0 100644 --- a/src/HotChocolate/Core/src/Execution/RequestExecutor.cs +++ b/src/HotChocolate/Core/src/Execution/RequestExecutor.cs @@ -241,47 +241,30 @@ private async IAsyncEnumerable ExecuteBatchStream( } var buffer = new IOperationResult[8]; - int bufferCount; do { - bufferCount = completed.TryPopRange(buffer); + var resultCount = completed.TryPopRange(buffer); - for (var i = 0; i < bufferCount; i++) + for (var i = 0; i < resultCount; i++) { yield return buffer[i]; } - if (bufferCount == 0) + if (completed.IsEmpty && tasks.Count > 0) { - if(tasks.Any(t => !t.IsCompleted)) - { - var task = await Task.WhenAny(tasks); + var task = await Task.WhenAny(tasks); - if (task.Status is not TaskStatus.RanToCompletion) - { - // we await to throw if it's not successful. - await task; - } - - tasks.Remove(task); - } - else + // we await to throw if it's not successful. + if (task.Status is not TaskStatus.RanToCompletion) { - foreach (var task in tasks) - { - if (task.Status is not TaskStatus.RanToCompletion) - { - // we await to throw if it's not successful. - await task; - } - } - - tasks.Clear(); + await task; } + + tasks.Remove(task); } } - while (tasks.Count > 0 || bufferCount > 0); + while (tasks.Count > 0 || !completed.IsEmpty); } private static IOperationRequest WithServices(IOperationRequest request, IServiceProvider services) From 05e821315bba70f9c200435e4a6262840dd34019 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Mon, 16 Dec 2024 20:36:56 +0100 Subject: [PATCH 131/154] Fixed options monitor race condition. (#7833) --- .../DefaultRequestExecutorOptionsMonitor.cs | 63 +++++++++++-------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/src/HotChocolate/Core/src/Execution/Configuration/DefaultRequestExecutorOptionsMonitor.cs b/src/HotChocolate/Core/src/Execution/Configuration/DefaultRequestExecutorOptionsMonitor.cs index 25d79d2deb2..fdefe66164a 100644 --- a/src/HotChocolate/Core/src/Execution/Configuration/DefaultRequestExecutorOptionsMonitor.cs +++ b/src/HotChocolate/Core/src/Execution/Configuration/DefaultRequestExecutorOptionsMonitor.cs @@ -9,8 +9,7 @@ internal sealed class DefaultRequestExecutorOptionsMonitor private readonly SemaphoreSlim _semaphore = new(1, 1); private readonly IOptionsMonitor _optionsMonitor; private readonly IRequestExecutorOptionsProvider[] _optionsProviders; - private readonly Dictionary> _configs = - new(); + private readonly Dictionary> _configs = new(); private readonly List _disposables = []; private readonly List> _listeners = []; private bool _initialized; @@ -28,7 +27,7 @@ public async ValueTask GetAsync( string schemaName, CancellationToken cancellationToken = default) { - await InitializeAsync(cancellationToken).ConfigureAwait(false); + await TryInitializeAsync(cancellationToken).ConfigureAwait(false); var options = new RequestExecutorSetup(); _optionsMonitor.Get(schemaName).CopyTo(options); @@ -44,47 +43,57 @@ public async ValueTask GetAsync( return options; } - private async ValueTask InitializeAsync(CancellationToken cancellationToken) + private async ValueTask TryInitializeAsync(CancellationToken cancellationToken) { if (!_initialized) { await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - if (!_initialized) + try { - _configs.Clear(); + await TryInitializeUnsafeAsync(cancellationToken).ConfigureAwait(false); + } + finally + { + _semaphore.Release(); + } + } + } - foreach (var provider in _optionsProviders) - { - _disposables.Add(provider.OnChange(OnChange)); + private async ValueTask TryInitializeUnsafeAsync(CancellationToken cancellationToken) + { + if (!_initialized) + { + _configs.Clear(); + + foreach (var provider in _optionsProviders) + { + _disposables.Add(provider.OnChange(OnChange)); - var allConfigurations = - await provider.GetOptionsAsync(cancellationToken) - .ConfigureAwait(false); + var allConfigurations = + await provider.GetOptionsAsync(cancellationToken) + .ConfigureAwait(false); - foreach (var configuration in allConfigurations) + foreach (var configuration in allConfigurations) + { + if (!_configs.TryGetValue( + configuration.SchemaName, + out var configurations)) { - if (!_configs.TryGetValue( - configuration.SchemaName, - out var configurations)) - { - configurations = []; - _configs.Add(configuration.SchemaName, configurations); - } - - configurations.Add(configuration); + configurations = []; + _configs.Add(configuration.SchemaName, configurations); } - } - _initialized = true; + configurations.Add(configuration); + } } - _semaphore.Release(); + _initialized = true; } } - public IDisposable OnChange(Action listener) => - new Session(this, listener); + public IDisposable OnChange(Action listener) + => new Session(this, listener); private void OnChange(IConfigureRequestExecutorSetup changes) { From 24cf5c5203765c0abdf7b2ef195390aa2bffdd05 Mon Sep 17 00:00:00 2001 From: huysentruitw Date: Mon, 16 Dec 2024 21:53:19 +0100 Subject: [PATCH 132/154] Use the new .NET api to check for nullable reference types (#7827) --- .../Projections/SelectionExpressionBuilder.cs | 11 ++-- .../Projections/ProjectableDataLoaderTests.cs | 66 +++++++++++++++++++ ...ableDataLoaderTests.Branches_Are_Merged.md | 2 +- ...ojectableDataLoaderTests.Force_A_Branch.md | 4 +- ...s.Product_With_Name_And_Brand_With_Name.md | 2 +- ...Nullable_Reference_Property_Set_To_Null.md | 24 +++++++ .../Execution.Tests/TestContext/Product.cs | 4 -- ....Ensure_Nullable_Connections_Dont_Throw.md | 2 +- ...nsure_Nullable_Connections_Dont_Throw_2.md | 2 +- ...ullable_Connections_Dont_Throw_2_NET9_0.md | 2 +- ..._Nullable_Connections_Dont_Throw_NET9_0.md | 2 +- 11 files changed, 102 insertions(+), 19 deletions(-) create mode 100644 src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Product_With_Nullable_Reference_Property_Set_To_Null.md diff --git a/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs b/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs index d368c0a1a2d..4c00ea0c149 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/SelectionExpressionBuilder.cs @@ -12,6 +12,8 @@ namespace HotChocolate.Execution.Projections; internal sealed class SelectionExpressionBuilder { + private static readonly NullabilityInfoContext _nullabilityInfoContext = new(); + public Expression> BuildExpression(ISelection selection) { var rootType = typeof(TRoot); @@ -293,14 +295,9 @@ private static bool IsNullableType(PropertyInfo propertyInfo) return Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null; } - var nullableAttribute = propertyInfo.GetCustomAttribute(); - - if (nullableAttribute != null) - { - return nullableAttribute.NullableFlags[0] == 2; - } + var nullabilityInfo = _nullabilityInfoContext.Create(propertyInfo); - return false; + return nullabilityInfo.WriteState == NullabilityState.Nullable; } #else private static bool IsNullableType(PropertyInfo propertyInfo) diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs index 9c676b7897b..91563e37f0e 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs @@ -849,6 +849,39 @@ public async Task Project_Key_To_Collection_Expression_Integration() .MatchMarkdownSnapshot(); } + [Fact] + public async Task Product_With_Nullable_Reference_Property_Set_To_Null() + { + // Arrange + var queries = new List(); + var connectionString = CreateConnectionString(); + await CatalogContext.SeedAsync(connectionString); + + // Act + var result = await new ServiceCollection() + .AddScoped(_ => queries) + .AddTransient(_ => new CatalogContext(connectionString)) + .AddGraphQLServer() + .AddQueryType() + .ExecuteRequestAsync( + """ + { + productById(id: 1) { + name + type { + name + } + } + } + """); + + // Assert + Snapshot.Create() + .AddSql(queries) + .Add(result, "Result") + .MatchMarkdownSnapshot(); + } + public class Query { public async Task GetBrandByIdAsync( @@ -926,6 +959,39 @@ public async Task> GetBrandByIdAsync( .ToListAsync(cancellationToken); } + public class ProductsWithNullPropertyQuery + { + public async Task GetProductByIdAsync( + int id, + List queries, + ISelection selection, + CatalogContext context, + CancellationToken cancellationToken) + { + var query = context.Products + .Where(p => p.Id == id) + .Select(p => new ProductProjection // Cast to an object with nullable Type property + { + Name = p.Name, + }) + .Select(selection.AsSelector()); + + lock (queries) + { + queries.Add(query.ToQueryString()); + } + + return await query.SingleOrDefaultAsync(cancellationToken); + } + + public class ProductProjection + { + public string Name { get; set; } = default!; + + public ProductType? Type { get; set; } + } + } + [ExtendObjectType] public class BrandExtensions { diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Branches_Are_Merged.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Branches_Are_Merged.md index 51f63105e83..4be19358ab3 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Branches_Are_Merged.md +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Branches_Are_Merged.md @@ -4,7 +4,7 @@ ```text -- @__keys_0={ '1', '2' } (DbType = Object) -SELECT p."Name", b."Name", p."Id" +SELECT p."Name", FALSE, b."Name", p."Id" FROM "Products" AS p INNER JOIN "Brands" AS b ON p."BrandId" = b."Id" WHERE p."Id" = ANY (@__keys_0) diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Force_A_Branch.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Force_A_Branch.md index ba719bb4fd2..81bfdc331cf 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Force_A_Branch.md +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Force_A_Branch.md @@ -4,12 +4,12 @@ ```text -- @__keys_0={ '1' } (DbType = Object) -SELECT p."Name", b."Name", p."Id" +SELECT p."Name", FALSE, b."Name", p."Id" FROM "Products" AS p INNER JOIN "Brands" AS b ON p."BrandId" = b."Id" WHERE p."Id" = ANY (@__keys_0) -- @__keys_0={ '1' } (DbType = Object) -SELECT p."Id", b."Id" +SELECT p."Id", FALSE, b."Id" FROM "Products" AS p INNER JOIN "Brands" AS b ON p."BrandId" = b."Id" WHERE p."Id" = ANY (@__keys_0) diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Product_With_Name_And_Brand_With_Name.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Product_With_Name_And_Brand_With_Name.md index 1b3f8637545..e1538e6d6fd 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Product_With_Name_And_Brand_With_Name.md +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Product_With_Name_And_Brand_With_Name.md @@ -4,7 +4,7 @@ ```text -- @__keys_0={ '1' } (DbType = Object) -SELECT p."Name", b."Name", p."Id" +SELECT p."Name", FALSE, b."Name", p."Id" FROM "Products" AS p INNER JOIN "Brands" AS b ON p."BrandId" = b."Id" WHERE p."Id" = ANY (@__keys_0) diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Product_With_Nullable_Reference_Property_Set_To_Null.md b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Product_With_Nullable_Reference_Property_Set_To_Null.md new file mode 100644 index 00000000000..47a5a69d1f6 --- /dev/null +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/__snapshots__/ProjectableDataLoaderTests.Product_With_Nullable_Reference_Property_Set_To_Null.md @@ -0,0 +1,24 @@ +# Product_With_Nullable_Reference_Property_Set_To_Null + +## SQL + +```text +-- @__id_0='1' +SELECT p."Name" +FROM "Products" AS p +WHERE p."Id" = @__id_0 +``` + +## Result + +```json +{ + "data": { + "productById": { + "name": "Product 0-0", + "type": null + } + } +} +``` + diff --git a/src/HotChocolate/Core/test/Execution.Tests/TestContext/Product.cs b/src/HotChocolate/Core/test/Execution.Tests/TestContext/Product.cs index a7000bafe11..f85bf875ca2 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/TestContext/Product.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/TestContext/Product.cs @@ -34,10 +34,6 @@ public class Product // Maximum number of units that can be in-stock at any time (due to physicial/logistical constraints in warehouses) public int MaxStockThreshold { get; set; } - /// Optional embedding for the catalog item's description. - // [JsonIgnore] - // public Vector Embedding { get; set; } - /// /// True if item is on reorder /// diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw.md index 0de22cee471..ec9d41e71b2 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw.md @@ -18,7 +18,7 @@ ORDER BY t."Name", t."Id" ## Expression 0 ```text -[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name), Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description)})}).Take(11) +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = root.Name, Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description)})}).Take(11) ``` ## Result diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2.md index 4b0b1ca96b4..a6aced4da00 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2.md @@ -18,7 +18,7 @@ ORDER BY t."Name", t."Id" ## Expression 0 ```text -[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name), Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description), SomeField1 = IIF((root.Bar.SomeField1 == null), null, root.Bar.SomeField1), SomeField2 = IIF((root.Bar.SomeField2 == null), null, root.Bar.SomeField2)})}).Take(11) +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = root.Name, Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description), SomeField1 = root.Bar.SomeField1, SomeField2 = IIF((root.Bar.SomeField2 == null), null, root.Bar.SomeField2)})}).Take(11) ``` ## Result diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET9_0.md index 9bc3d874080..2f72d135ece 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET9_0.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET9_0.md @@ -18,7 +18,7 @@ ORDER BY f0."Name", f0."Id" ## Expression 0 ```text -[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = root.Name, Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = root.Bar.Description, SomeField1 = root.Bar.SomeField1, SomeField2 = root.Bar.SomeField2})}).Take(11) +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = root.Name, Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description), SomeField1 = root.Bar.SomeField1, SomeField2 = IIF((root.Bar.SomeField2 == null), null, root.Bar.SomeField2)})}).Take(11) ``` ## Result diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET9_0.md index e65b7625c0d..546c292f110 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET9_0.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET9_0.md @@ -18,7 +18,7 @@ ORDER BY f0."Name", f0."Id" ## Expression 0 ```text -[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = root.Name, Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = root.Bar.Description})}).Take(11) +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = root.Name, Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description)})}).Take(11) ``` ## Result From 4d6e37d90747242fca3aa3d06a2f4111d39c86ca Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Mon, 16 Dec 2024 22:26:08 +0100 Subject: [PATCH 133/154] Fixed Test Snapshots --- .../AuthorizationPolicyProviderTess.cs | 5 + ...lewareTests.Download_GraphQL_SDL_NET6.snap | 2 + ...ewareTests.Download_GraphQL_Schema_NET6.md | 6 +- ...raphQL_Schema_Slicing_Args_Enabled_NET6.md | 6 +- ...ests.Paging_Fetch_First_2_Items_Between.md | 2 +- ...Tests.Paging_Fetch_Last_2_Items_Between.md | 2 +- ...ngHelperTests.BatchPaging_Last_5_NET7_0.md | 150 +++++++++++++++++- ...ngHelperTests.BatchPaging_Last_5_NET8_0.md | 150 +++++++++++++++++- ....Ensure_Nullable_Connections_Dont_Throw.md | 2 +- ...nsure_Nullable_Connections_Dont_Throw_2.md | 2 +- ...ullable_Connections_Dont_Throw_2_NET8_0.md | 2 +- ..._Nullable_Connections_Dont_Throw_NET8_0.md | 2 +- ...tionClientTests.IntrospectServer_NET6.snap | 2 + 13 files changed, 321 insertions(+), 12 deletions(-) diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Authorization.Tests/AuthorizationPolicyProviderTess.cs b/src/HotChocolate/AspNetCore/test/AspNetCore.Authorization.Tests/AuthorizationPolicyProviderTess.cs index 230b8ca1e21..399fca09b79 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Authorization.Tests/AuthorizationPolicyProviderTess.cs +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Authorization.Tests/AuthorizationPolicyProviderTess.cs @@ -80,7 +80,12 @@ public async Task Policies_Are_Not_Cached_If_PolicyProvider_Disallows_Caching() Assert.Null(result1.Errors); Assert.Equal(HttpStatusCode.OK, result2.StatusCode); Assert.Null(result2.Errors); + +#if NET6_0 + Assert.Equal(1, policyProvider.InvocationsOfGetPolicyAsync); +#else Assert.Equal(2, policyProvider.InvocationsOfGetPolicyAsync); +#endif } public class Query diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL_NET6.snap b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL_NET6.snap index 82609b31301..6a7007f02ca 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL_NET6.snap +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_SDL_NET6.snap @@ -122,6 +122,8 @@ enum Unit { "The `@defer` directive may be provided for fragment spreads and inline fragments to inform the executor to delay the execution of the current fragment to indicate deprioritization of the current fragment. A query with `@defer` directive will cause the request to potentially return multiple responses, where non-deferred data is delivered in the initial response and data deferred is delivered in a subsequent response. `@include` and `@skip` take precedence over `@defer`." directive @defer("If this argument label has a value other than null, it will be passed on to the result of this defer directive. This label is intended to give client applications a way to identify to which fragment a deferred result belongs to." label: String "Deferred when true." if: Boolean) on FRAGMENT_SPREAD | INLINE_FRAGMENT +directive @foo(bar: Int!) on SUBSCRIPTION + "The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! = 0 "Streamed when true." if: Boolean) on FIELD diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema_NET6.md b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema_NET6.md index c5f61004536..e8181b9f0b8 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema_NET6.md +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema_NET6.md @@ -2,12 +2,12 @@ ```text Headers: -ETag: "1-kBEjhe2t+jfqbeZRxnezu0WDQFYAc0qzjLF1RlHs428=" +ETag: "1-kqrhyLMoq/15r5KsM6OidIHC9SxZ/RTdPKy5i3Bw42Y=" Cache-Control: public, must-revalidate, max-age=3600 Content-Type: application/graphql; charset=utf-8 Content-Disposition: attachment; filename="schema.graphql" Last-Modified: Fri, 01 Jan 2021 00:00:00 GMT -Content-Length: 5070 +Content-Length: 5113 --------------------------> Status Code: OK --------------------------> @@ -135,6 +135,8 @@ enum Unit { "The `@defer` directive may be provided for fragment spreads and inline fragments to inform the executor to delay the execution of the current fragment to indicate deprioritization of the current fragment. A query with `@defer` directive will cause the request to potentially return multiple responses, where non-deferred data is delivered in the initial response and data deferred is delivered in a subsequent response. `@include` and `@skip` take precedence over `@defer`." directive @defer("If this argument label has a value other than null, it will be passed on to the result of this defer directive. This label is intended to give client applications a way to identify to which fragment a deferred result belongs to." label: String "Deferred when true." if: Boolean) on FRAGMENT_SPREAD | INLINE_FRAGMENT +directive @foo(bar: Int!) on SUBSCRIPTION + "The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! = 0 "Streamed when true." if: Boolean) on FIELD diff --git a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema_Slicing_Args_Enabled_NET6.md b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema_Slicing_Args_Enabled_NET6.md index 8959905adf8..03a2562dadf 100644 --- a/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema_Slicing_Args_Enabled_NET6.md +++ b/src/HotChocolate/AspNetCore/test/AspNetCore.Tests/__snapshots__/HttpGetSchemaMiddlewareTests.Download_GraphQL_Schema_Slicing_Args_Enabled_NET6.md @@ -2,12 +2,12 @@ ```text Headers: -ETag: "1-kBEjhe2t+jfqbeZRxnezu0WDQFYAc0qzjLF1RlHs428=" +ETag: "1-kqrhyLMoq/15r5KsM6OidIHC9SxZ/RTdPKy5i3Bw42Y=" Cache-Control: public, must-revalidate, max-age=3600 Content-Type: application/graphql; charset=utf-8 Content-Disposition: attachment; filename="schema.graphql" Last-Modified: Fri, 01 Jan 2021 00:00:00 GMT -Content-Length: 5070 +Content-Length: 5113 --------------------------> Status Code: OK --------------------------> @@ -135,6 +135,8 @@ enum Unit { "The `@defer` directive may be provided for fragment spreads and inline fragments to inform the executor to delay the execution of the current fragment to indicate deprioritization of the current fragment. A query with `@defer` directive will cause the request to potentially return multiple responses, where non-deferred data is delivered in the initial response and data deferred is delivered in a subsequent response. `@include` and `@skip` take precedence over `@defer`." directive @defer("If this argument label has a value other than null, it will be passed on to the result of this defer directive. This label is intended to give client applications a way to identify to which fragment a deferred result belongs to." label: String "Deferred when true." if: Boolean) on FRAGMENT_SPREAD | INLINE_FRAGMENT +directive @foo(bar: Int!) on SUBSCRIPTION + "The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! = 0 "Streamed when true." if: Boolean) on FIELD diff --git a/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/__snapshots__/IntegrationTests.Paging_Fetch_First_2_Items_Between.md b/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/__snapshots__/IntegrationTests.Paging_Fetch_First_2_Items_Between.md index 4c7aa752c04..e590803b2a7 100644 --- a/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/__snapshots__/IntegrationTests.Paging_Fetch_First_2_Items_Between.md +++ b/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/__snapshots__/IntegrationTests.Paging_Fetch_First_2_Items_Between.md @@ -21,7 +21,7 @@ } }, "extensions": { - "sql": "-- @__p_0='1'\n-- @__p_1='4'\n-- @__p_2='3'\nSELECT b.\"Id\", b.\"AlwaysNull\", b.\"DisplayName\", b.\"Name\", b.\"BrandDetails_Country_Name\"\nFROM \"Brands\" AS b\nWHERE b.\"Id\" > @__p_0 AND b.\"Id\" < @__p_1\nORDER BY b.\"Id\"\nLIMIT @__p_2" + "sql": "-- @__p_0='1'\n-- @__p_1='4'\n-- @__p_2='3'\nSELECT b.\"Id\", b.\"AlwaysNull\", b.\"DisplayName\", b.\"Name\", b.\"BrandDetails_Country_Name\"\nFROM \"Brands\" AS b\nWHERE (b.\"Id\" > @__p_0) AND (b.\"Id\" < @__p_1)\nORDER BY b.\"Id\"\nLIMIT @__p_2" } } ``` diff --git a/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/__snapshots__/IntegrationTests.Paging_Fetch_Last_2_Items_Between.md b/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/__snapshots__/IntegrationTests.Paging_Fetch_Last_2_Items_Between.md index ba4bc537766..40461263c81 100644 --- a/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/__snapshots__/IntegrationTests.Paging_Fetch_Last_2_Items_Between.md +++ b/src/HotChocolate/Data/test/Data.EntityFramework.Pagination.Tests/__snapshots__/IntegrationTests.Paging_Fetch_Last_2_Items_Between.md @@ -21,7 +21,7 @@ } }, "extensions": { - "sql": "-- @__p_0='97'\n-- @__p_1='100'\n-- @__p_2='3'\nSELECT b.\"Id\", b.\"AlwaysNull\", b.\"DisplayName\", b.\"Name\", b.\"BrandDetails_Country_Name\"\nFROM \"Brands\" AS b\nWHERE b.\"Id\" > @__p_0 AND b.\"Id\" < @__p_1\nORDER BY b.\"Id\" DESC\nLIMIT @__p_2" + "sql": "-- @__p_0='97'\n-- @__p_1='100'\n-- @__p_2='3'\nSELECT b.\"Id\", b.\"AlwaysNull\", b.\"DisplayName\", b.\"Name\", b.\"BrandDetails_Country_Name\"\nFROM \"Brands\" AS b\nWHERE (b.\"Id\" > @__p_0) AND (b.\"Id\" < @__p_1)\nORDER BY b.\"Id\" DESC\nLIMIT @__p_2" } } ``` diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5_NET7_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5_NET7_0.md index d433943523c..1ff0e6d16a4 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5_NET7_0.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5_NET7_0.md @@ -1,5 +1,153 @@ # BatchPaging_Last_5 +## 1 + +```json +{ + "First": "MTAw", + "Last": "OTk=", + "Items": [ + { + "Id": 100, + "Name": "Product 0-99", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 1, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + }, + { + "Id": 99, + "Name": "Product 0-98", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 1, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + } + ] +} +``` + +## 2 + +```json +{ + "First": "MjAw", + "Last": "MTk5", + "Items": [ + { + "Id": 200, + "Name": "Product 1-99", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 2, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + }, + { + "Id": 199, + "Name": "Product 1-98", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 2, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + } + ] +} +``` + +## 3 + ```json -{} +{ + "First": "MzAw", + "Last": "Mjk5", + "Items": [ + { + "Id": 300, + "Name": "Product 2-99", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 3, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + }, + { + "Id": 299, + "Name": "Product 2-98", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 3, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + } + ] +} +``` + +## SQL 0 + +```sql +SELECT t."BrandId", t0."Id", t0."AvailableStock", t0."BrandId", t0."Description", t0."ImageFileName", t0."MaxStockThreshold", t0."Name", t0."OnReorder", t0."Price", t0."RestockThreshold", t0."TypeId" +FROM ( + SELECT p."BrandId" + FROM "Products" AS p + WHERE p."BrandId" IN (1, 2, 3) + GROUP BY p."BrandId" +) AS t +LEFT JOIN ( + SELECT t1."Id", t1."AvailableStock", t1."BrandId", t1."Description", t1."ImageFileName", t1."MaxStockThreshold", t1."Name", t1."OnReorder", t1."Price", t1."RestockThreshold", t1."TypeId" + FROM ( + SELECT p0."Id", p0."AvailableStock", p0."BrandId", p0."Description", p0."ImageFileName", p0."MaxStockThreshold", p0."Name", p0."OnReorder", p0."Price", p0."RestockThreshold", p0."TypeId", ROW_NUMBER() OVER(PARTITION BY p0."BrandId" ORDER BY p0."Id" DESC) AS row + FROM "Products" AS p0 + WHERE p0."BrandId" = 1 OR p0."BrandId" = 2 OR p0."BrandId" = 3 + ) AS t1 + WHERE t1.row <= 3 +) AS t0 ON t."BrandId" = t0."BrandId" +ORDER BY t."BrandId", t0."BrandId", t0."Id" DESC ``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Where(t => (((t.BrandId == 1) OrElse (t.BrandId == 2)) OrElse (t.BrandId == 3))).GroupBy(k => k.BrandId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderByDescending(p => p.Id).Take(3).ToList()}) +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5_NET8_0.md index d433943523c..1ff0e6d16a4 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5_NET8_0.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.BatchPaging_Last_5_NET8_0.md @@ -1,5 +1,153 @@ # BatchPaging_Last_5 +## 1 + +```json +{ + "First": "MTAw", + "Last": "OTk=", + "Items": [ + { + "Id": 100, + "Name": "Product 0-99", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 1, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + }, + { + "Id": 99, + "Name": "Product 0-98", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 1, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + } + ] +} +``` + +## 2 + +```json +{ + "First": "MjAw", + "Last": "MTk5", + "Items": [ + { + "Id": 200, + "Name": "Product 1-99", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 2, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + }, + { + "Id": 199, + "Name": "Product 1-98", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 2, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + } + ] +} +``` + +## 3 + ```json -{} +{ + "First": "MzAw", + "Last": "Mjk5", + "Items": [ + { + "Id": 300, + "Name": "Product 2-99", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 3, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + }, + { + "Id": 299, + "Name": "Product 2-98", + "Description": null, + "Price": 0.0, + "ImageFileName": null, + "TypeId": 1, + "Type": null, + "BrandId": 3, + "Brand": null, + "AvailableStock": 0, + "RestockThreshold": 0, + "MaxStockThreshold": 0, + "OnReorder": false + } + ] +} +``` + +## SQL 0 + +```sql +SELECT t."BrandId", t0."Id", t0."AvailableStock", t0."BrandId", t0."Description", t0."ImageFileName", t0."MaxStockThreshold", t0."Name", t0."OnReorder", t0."Price", t0."RestockThreshold", t0."TypeId" +FROM ( + SELECT p."BrandId" + FROM "Products" AS p + WHERE p."BrandId" IN (1, 2, 3) + GROUP BY p."BrandId" +) AS t +LEFT JOIN ( + SELECT t1."Id", t1."AvailableStock", t1."BrandId", t1."Description", t1."ImageFileName", t1."MaxStockThreshold", t1."Name", t1."OnReorder", t1."Price", t1."RestockThreshold", t1."TypeId" + FROM ( + SELECT p0."Id", p0."AvailableStock", p0."BrandId", p0."Description", p0."ImageFileName", p0."MaxStockThreshold", p0."Name", p0."OnReorder", p0."Price", p0."RestockThreshold", p0."TypeId", ROW_NUMBER() OVER(PARTITION BY p0."BrandId" ORDER BY p0."Id" DESC) AS row + FROM "Products" AS p0 + WHERE p0."BrandId" = 1 OR p0."BrandId" = 2 OR p0."BrandId" = 3 + ) AS t1 + WHERE t1.row <= 3 +) AS t0 ON t."BrandId" = t0."BrandId" +ORDER BY t."BrandId", t0."BrandId", t0."Id" DESC ``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Where(t => (((t.BrandId == 1) OrElse (t.BrandId == 2)) OrElse (t.BrandId == 3))).GroupBy(k => k.BrandId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderByDescending(p => p.Id).Take(3).ToList()}) +``` + diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw.md index ec9d41e71b2..0de22cee471 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw.md @@ -18,7 +18,7 @@ ORDER BY t."Name", t."Id" ## Expression 0 ```text -[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = root.Name, Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description)})}).Take(11) +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name), Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description)})}).Take(11) ``` ## Result diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2.md index a6aced4da00..4b0b1ca96b4 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2.md @@ -18,7 +18,7 @@ ORDER BY t."Name", t."Id" ## Expression 0 ```text -[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = root.Name, Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description), SomeField1 = root.Bar.SomeField1, SomeField2 = IIF((root.Bar.SomeField2 == null), null, root.Bar.SomeField2)})}).Take(11) +[Microsoft.EntityFrameworkCore.Query.QueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = IIF((root.Name == null), null, root.Name), Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description), SomeField1 = IIF((root.Bar.SomeField1 == null), null, root.Bar.SomeField1), SomeField2 = IIF((root.Bar.SomeField2 == null), null, root.Bar.SomeField2)})}).Take(11) ``` ## Result diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET8_0.md index c2a1a8f8595..e1bbd9a9c4c 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET8_0.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_2_NET8_0.md @@ -18,7 +18,7 @@ ORDER BY t."Name", t."Id" ## Expression 0 ```text -[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = root.Name, Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = root.Bar.Description, SomeField1 = root.Bar.SomeField1, SomeField2 = root.Bar.SomeField2})}).Take(11) +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = root.Name, Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description), SomeField1 = root.Bar.SomeField1, SomeField2 = IIF((root.Bar.SomeField2 == null), null, root.Bar.SomeField2)})}).Take(11) ``` ## Result diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET8_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET8_0.md index 26d995e5bec..f2c40b1ebc2 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET8_0.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Ensure_Nullable_Connections_Dont_Throw_NET8_0.md @@ -18,7 +18,7 @@ ORDER BY t."Name", t."Id" ## Expression 0 ```text -[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = root.Name, Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = root.Bar.Description})}).Take(11) +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Foo() {Id = root.Id, Name = root.Name, Bar = IIF((root.Bar == null), null, new Bar() {Id = root.Bar.Id, Description = IIF((root.Bar.Description == null), null, root.Bar.Description)})}).Take(11) ``` ## Result diff --git a/src/HotChocolate/Utilities/test/Utilities.Introspection.Tests/__snapshots__/IntrospectionClientTests.IntrospectServer_NET6.snap b/src/HotChocolate/Utilities/test/Utilities.Introspection.Tests/__snapshots__/IntrospectionClientTests.IntrospectServer_NET6.snap index 008f2a5e7ae..491e76ad57e 100644 --- a/src/HotChocolate/Utilities/test/Utilities.Introspection.Tests/__snapshots__/IntrospectionClientTests.IntrospectServer_NET6.snap +++ b/src/HotChocolate/Utilities/test/Utilities.Introspection.Tests/__snapshots__/IntrospectionClientTests.IntrospectServer_NET6.snap @@ -123,3 +123,5 @@ type FriendsEdge { "The `Long` scalar type represents non-fractional signed whole 64-bit numeric values. Long can represent values between -(2^63) and 2^63 - 1." scalar Long + +directive @foo(bar: Int!) on SUBSCRIPTION From 63ab9f396a4bcfdc18035f333fbf9b381b00b1b9 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Mon, 16 Dec 2024 22:01:11 +0100 Subject: [PATCH 134/154] Fixed initialization race-condition in generated code. (#7834) * Fixed initialization race-condition in generated code. * Updated Snapshots --- .../FileBuilders/ResolverFileBuilder.cs | 85 +++++++++++-------- .../Core/test/Types.Analyzers.Tests/TestMe.cs | 68 +++++++++++++++ ...eSource_BatchDataLoader_MatchesSnapshot.md | 54 ++++++------ ...WithGlobalStateArgument_MatchesSnapshot.md | 50 ++++++----- ...alStateSetStateArgument_MatchesSnapshot.md | 50 ++++++----- ...rWithLocalStateArgument_MatchesSnapshot.md | 50 ++++++----- ...alStateSetStateArgument_MatchesSnapshot.md | 50 ++++++----- ...WithScopedStateArgument_MatchesSnapshot.md | 50 ++++++----- ...edStateSetStateArgument_MatchesSnapshot.md | 50 ++++++----- ...urce_TypeModuleOrdering_MatchesSnapshot.md | 2 + 10 files changed, 318 insertions(+), 191 deletions(-) create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/TestMe.cs diff --git a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ResolverFileBuilder.cs b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ResolverFileBuilder.cs index 28708e04db4..9382db06371 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ResolverFileBuilder.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ResolverFileBuilder.cs @@ -37,6 +37,7 @@ public string WriteBeginClass(string typeName) _writer.WriteIndentedLine("internal static class {0}", typeName); _writer.WriteIndentedLine("{"); _writer.IncreaseIndent(); + _writer.WriteIndentedLine("private static readonly object _sync = new object();"); _writer.WriteIndentedLine("private static bool _bindingsInitialized;"); return typeName; } @@ -94,15 +95,18 @@ public void AddParameterInitializer(IEnumerable resolvers, ILocalTypeL if (first) { - _writer.WriteIndentedLine("if (_bindingsInitialized)"); + _writer.WriteIndentedLine("if (!_bindingsInitialized)"); _writer.WriteIndentedLine("{"); - using (_writer.IncreaseIndent()) - { - _writer.WriteIndentedLine("return;"); - } + _writer.IncreaseIndent(); + + _writer.WriteIndentedLine("lock (_sync)"); + _writer.WriteIndentedLine("{"); + _writer.IncreaseIndent(); + + _writer.WriteIndentedLine("if (!_bindingsInitialized)"); + _writer.WriteIndentedLine("{"); + _writer.IncreaseIndent(); - _writer.WriteIndentedLine("}"); - _writer.WriteIndentedLine("_bindingsInitialized = true;"); _writer.WriteLine(); _writer.WriteIndentedLine( "const global::{0} bindingFlags =", @@ -120,6 +124,8 @@ public void AddParameterInitializer(IEnumerable resolvers, ILocalTypeL _writer.WriteIndentedLine("var type = typeof({0});", method.ContainingType.ToFullyQualified()); _writer.WriteIndentedLine("global::System.Reflection.MethodInfo resolver = default!;"); _writer.WriteIndentedLine("global::System.Reflection.ParameterInfo[] parameters = default!;"); + + _writer.WriteIndentedLine("_bindingsInitialized = true;"); first = false; } @@ -182,8 +188,8 @@ public void AddParameterInitializer(IEnumerable resolvers, ILocalTypeL using (_writer.WriteForEach("binding", $"_args_{resolver.TypeName}_{resolver.Member.Name}")) { using (_writer.WriteIfClause( - "binding.Kind == global::{0}.Argument", - WellKnownTypes.ArgumentKind)) + "binding.Kind == global::{0}.Argument", + WellKnownTypes.ArgumentKind)) { _writer.WriteIndentedLine("argumentCount++;"); } @@ -204,8 +210,8 @@ public void AddParameterInitializer(IEnumerable resolvers, ILocalTypeL using (_writer.IncreaseIndent()) { _writer.WriteIndentedLine( - ".SetMessage(\"The node resolver `{0}.{1}` mustn't have more than one " + - "argument. Node resolvers can only have a single argument called `id`.\")", + ".SetMessage(\"The node resolver `{0}.{1}` mustn't have more than one " + + "argument. Node resolvers can only have a single argument called `id`.\")", resolver.Member.ContainingType.ToDisplayString(), resolver.Member.Name); _writer.WriteIndentedLine(".Build());"); @@ -214,6 +220,16 @@ public void AddParameterInitializer(IEnumerable resolvers, ILocalTypeL } } } + + if (!first) + { + _writer.DecreaseIndent(); + _writer.WriteIndentedLine("}"); + _writer.DecreaseIndent(); + _writer.WriteIndentedLine("}"); + _writer.DecreaseIndent(); + _writer.WriteIndentedLine("}"); + } } _writer.WriteIndentedLine("}"); @@ -224,8 +240,7 @@ private static string ToFullyQualifiedString( IMethodSymbol resolverMethod, ILocalTypeLookup typeLookup) { - if (type.TypeKind is TypeKind.Error && - typeLookup.TryGetTypeName(type, resolverMethod, out var typeDisplayName)) + if (type.TypeKind is TypeKind.Error && typeLookup.TryGetTypeName(type, resolverMethod, out var typeDisplayName)) { return typeDisplayName; } @@ -269,9 +284,9 @@ private void AddStaticStandardResolver( ILocalTypeLookup typeLookup) { using (_writer.WriteMethod( - "public static", - returnType: WellKnownTypes.FieldResolverDelegates, - methodName: $"{resolver.TypeName}_{resolver.Member.Name}")) + "public static", + returnType: WellKnownTypes.FieldResolverDelegates, + methodName: $"{resolver.TypeName}_{resolver.Member.Name}")) { using (_writer.WriteIfClause(condition: "!_bindingsInitialized")) { @@ -341,9 +356,9 @@ private void AddStaticStandardResolver( private void AddStaticPureResolver(Resolver resolver, IMethodSymbol resolverMethod, ILocalTypeLookup typeLookup) { using (_writer.WriteMethod( - "public static", - returnType: WellKnownTypes.FieldResolverDelegates, - methodName: $"{resolver.TypeName}_{resolver.Member.Name}")) + "public static", + returnType: WellKnownTypes.FieldResolverDelegates, + methodName: $"{resolver.TypeName}_{resolver.Member.Name}")) { using (_writer.WriteIfClause(condition: "!_bindingsInitialized")) { @@ -436,9 +451,9 @@ private void AddStaticPureResolver(Resolver resolver, IMethodSymbol resolverMeth private void AddStaticPropertyResolver(Resolver resolver) { using (_writer.WriteMethod( - "public static", - returnType: WellKnownTypes.FieldResolverDelegates, - methodName: $"{resolver.TypeName}_{resolver.Member.Name}")) + "public static", + returnType: WellKnownTypes.FieldResolverDelegates, + methodName: $"{resolver.TypeName}_{resolver.Member.Name}")) { using (_writer.WriteIfClause(condition: "!_bindingsInitialized")) { @@ -489,13 +504,13 @@ private void AddResolverArguments(Resolver resolver, IMethodSymbol resolverMetho { var parameter = resolver.Parameters[i]; - if(resolver.IsNodeResolver - && parameter.Kind is ResolverParameterKind.Argument or ResolverParameterKind.Unknown - && (parameter.Name == "id" || parameter.Key == "id")) + if (resolver.IsNodeResolver + && parameter.Kind is ResolverParameterKind.Argument or ResolverParameterKind.Unknown + && (parameter.Name == "id" || parameter.Key == "id")) { _writer.WriteIndentedLine( - "var args{0} = context.GetLocalState<{1}>(" + - "global::HotChocolate.WellKnownContextData.InternalId);", + "var args{0} = context.GetLocalState<{1}>(" + + "global::HotChocolate.WellKnownContextData.InternalId);", i, parameter.Type.ToFullyQualified()); continue; @@ -524,8 +539,8 @@ private void AddResolverArguments(Resolver resolver, IMethodSymbol resolverMetho break; case ResolverParameterKind.EventMessage: _writer.WriteIndentedLine( - "var args{0} = context.GetScopedState<{1}>(" + - "global::HotChocolate.WellKnownContextData.EventMessage);", + "var args{0} = context.GetScopedState<{1}>(" + + "global::HotChocolate.WellKnownContextData.EventMessage);", i, parameter.Type.ToFullyQualified()); break; @@ -593,8 +608,8 @@ private void AddResolverArguments(Resolver resolver, IMethodSymbol resolverMetho } case ResolverParameterKind.SetGlobalState: _writer.WriteIndentedLine( - "var args{0} = new HotChocolate.SetState<{1}>(" + - "value => context.SetGlobalState(\"{2}\", value));", + "var args{0} = new HotChocolate.SetState<{1}>(" + + "value => context.SetGlobalState(\"{2}\", value));", i, ((INamedTypeSymbol)parameter.Type).TypeArguments[0].ToFullyQualified(), parameter.Key); @@ -633,8 +648,8 @@ private void AddResolverArguments(Resolver resolver, IMethodSymbol resolverMetho } case ResolverParameterKind.SetScopedState: _writer.WriteIndentedLine( - "var args{0} = new HotChocolate.SetState<{1}>(" + - "value => context.SetScopedState(\"{2}\", value));", + "var args{0} = new HotChocolate.SetState<{1}>(" + + "value => context.SetScopedState(\"{2}\", value));", i, ((INamedTypeSymbol)parameter.Type).TypeArguments[0].ToFullyQualified(), parameter.Key); @@ -673,8 +688,8 @@ private void AddResolverArguments(Resolver resolver, IMethodSymbol resolverMetho } case ResolverParameterKind.SetLocalState: _writer.WriteIndentedLine( - "var args{0} = new HotChocolate.SetState<{1}>(" + - "value => context.SetLocalState(\"{2}\", value));", + "var args{0} = new HotChocolate.SetState<{1}>(" + + "value => context.SetLocalState(\"{2}\", value));", i, ((INamedTypeSymbol)parameter.Type).TypeArguments[0].ToFullyQualified(), parameter.Key); diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestMe.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestMe.cs new file mode 100644 index 00000000000..763fdb81dea --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestMe.cs @@ -0,0 +1,68 @@ +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using HotChocolate.Internal; + +namespace HotChocolate.Types +{ + internal static class EntityInterfaceResolvers2 + { + private static readonly object _sync = new object(); + private static bool _bindingsInitialized; + private readonly static global::HotChocolate.Internal.IParameterBinding[] _args_EntityInterface_IdString = new global::HotChocolate.Internal.IParameterBinding[1]; + + public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver) + { + if (!_bindingsInitialized) + { + lock (_sync) + { + if (!_bindingsInitialized) + { + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var type = typeof(global::HotChocolate.Types.EntityInterface); + global::System.Reflection.MethodInfo resolver = default!; + global::System.Reflection.ParameterInfo[] parameters = default!; + + resolver = type.GetMethod( + "IdString", + bindingFlags, + new global::System.Type[] { typeof(global::HotChocolate.Types.IEntity) })!; + parameters = resolver.GetParameters(); + _args_EntityInterface_IdString[0] = bindingResolver.GetBinding(parameters[0]); + + _bindingsInitialized = true; + } + } + } + } + + public static HotChocolate.Resolvers.FieldResolverDelegates EntityInterface_IdString() + { + if(!_bindingsInitialized) + { + throw new global::System.InvalidOperationException("The bindings must be initialized before the resolvers can be created."); + } + return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: EntityInterface_IdString_Resolver); + } + + private static global::System.Object? EntityInterface_IdString_Resolver(global::HotChocolate.Resolvers.IResolverContext context) + { + var args0 = context.Parent(); + var result = global::HotChocolate.Types.EntityInterface.IdString(args0); + return result; + } + } +} + diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeTests.GenerateSource_BatchDataLoader_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeTests.GenerateSource_BatchDataLoader_MatchesSnapshot.md index f05187d8bcd..ea2aebec076 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeTests.GenerateSource_BatchDataLoader_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeTests.GenerateSource_BatchDataLoader_MatchesSnapshot.md @@ -19,37 +19,43 @@ namespace TestNamespace { internal static class BookNodeResolvers { + private static readonly object _sync = new object(); private static bool _bindingsInitialized; private readonly static global::HotChocolate.Internal.IParameterBinding[] _args_BookNode_GetAuthorAsync = new global::HotChocolate.Internal.IParameterBinding[2]; public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver) { - if (_bindingsInitialized) + if (!_bindingsInitialized) { - return; - } - _bindingsInitialized = true; - - const global::System.Reflection.BindingFlags bindingFlags = - global::System.Reflection.BindingFlags.Public - | global::System.Reflection.BindingFlags.NonPublic - | global::System.Reflection.BindingFlags.Static; - - var type = typeof(global::TestNamespace.BookNode); - global::System.Reflection.MethodInfo resolver = default!; - global::System.Reflection.ParameterInfo[] parameters = default!; - - resolver = type.GetMethod( - "GetAuthorAsync", - bindingFlags, - new global::System.Type[] + lock (_sync) { - typeof(global::TestNamespace.Book), - typeof(global::System.Threading.CancellationToken) - })!; - parameters = resolver.GetParameters(); - _args_BookNode_GetAuthorAsync[0] = bindingResolver.GetBinding(parameters[0]); - _args_BookNode_GetAuthorAsync[1] = bindingResolver.GetBinding(parameters[1]); + if (!_bindingsInitialized) + { + + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var type = typeof(global::TestNamespace.BookNode); + global::System.Reflection.MethodInfo resolver = default!; + global::System.Reflection.ParameterInfo[] parameters = default!; + _bindingsInitialized = true; + + resolver = type.GetMethod( + "GetAuthorAsync", + bindingFlags, + new global::System.Type[] + { + typeof(global::TestNamespace.Book), + typeof(global::System.Threading.CancellationToken) + })!; + parameters = resolver.GetParameters(); + _args_BookNode_GetAuthorAsync[0] = bindingResolver.GetBinding(parameters[0]); + _args_BookNode_GetAuthorAsync[1] = bindingResolver.GetBinding(parameters[1]); + } + } + } } public static HotChocolate.Resolvers.FieldResolverDelegates BookNode_GetAuthorAsync() diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithGlobalStateArgument_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithGlobalStateArgument_MatchesSnapshot.md index b9030b00531..13a37042bf9 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithGlobalStateArgument_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithGlobalStateArgument_MatchesSnapshot.md @@ -19,35 +19,41 @@ namespace TestNamespace { internal static class TestTypeResolvers { + private static readonly object _sync = new object(); private static bool _bindingsInitialized; private readonly static global::HotChocolate.Internal.IParameterBinding[] _args_TestType_GetTest = new global::HotChocolate.Internal.IParameterBinding[1]; public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver) { - if (_bindingsInitialized) + if (!_bindingsInitialized) { - return; - } - _bindingsInitialized = true; - - const global::System.Reflection.BindingFlags bindingFlags = - global::System.Reflection.BindingFlags.Public - | global::System.Reflection.BindingFlags.NonPublic - | global::System.Reflection.BindingFlags.Static; - - var type = typeof(global::TestNamespace.TestType); - global::System.Reflection.MethodInfo resolver = default!; - global::System.Reflection.ParameterInfo[] parameters = default!; - - resolver = type.GetMethod( - "GetTest", - bindingFlags, - new global::System.Type[] + lock (_sync) { - typeof(int) - })!; - parameters = resolver.GetParameters(); - _args_TestType_GetTest[0] = bindingResolver.GetBinding(parameters[0]); + if (!_bindingsInitialized) + { + + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var type = typeof(global::TestNamespace.TestType); + global::System.Reflection.MethodInfo resolver = default!; + global::System.Reflection.ParameterInfo[] parameters = default!; + _bindingsInitialized = true; + + resolver = type.GetMethod( + "GetTest", + bindingFlags, + new global::System.Type[] + { + typeof(int) + })!; + parameters = resolver.GetParameters(); + _args_TestType_GetTest[0] = bindingResolver.GetBinding(parameters[0]); + } + } + } } public static HotChocolate.Resolvers.FieldResolverDelegates TestType_GetTest() diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithGlobalStateSetStateArgument_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithGlobalStateSetStateArgument_MatchesSnapshot.md index b5d71594ca7..b196f075236 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithGlobalStateSetStateArgument_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithGlobalStateSetStateArgument_MatchesSnapshot.md @@ -19,35 +19,41 @@ namespace TestNamespace { internal static class TestTypeResolvers { + private static readonly object _sync = new object(); private static bool _bindingsInitialized; private readonly static global::HotChocolate.Internal.IParameterBinding[] _args_TestType_GetTest = new global::HotChocolate.Internal.IParameterBinding[1]; public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver) { - if (_bindingsInitialized) + if (!_bindingsInitialized) { - return; - } - _bindingsInitialized = true; - - const global::System.Reflection.BindingFlags bindingFlags = - global::System.Reflection.BindingFlags.Public - | global::System.Reflection.BindingFlags.NonPublic - | global::System.Reflection.BindingFlags.Static; - - var type = typeof(global::TestNamespace.TestType); - global::System.Reflection.MethodInfo resolver = default!; - global::System.Reflection.ParameterInfo[] parameters = default!; - - resolver = type.GetMethod( - "GetTest", - bindingFlags, - new global::System.Type[] + lock (_sync) { - typeof(global::HotChocolate.SetState) - })!; - parameters = resolver.GetParameters(); - _args_TestType_GetTest[0] = bindingResolver.GetBinding(parameters[0]); + if (!_bindingsInitialized) + { + + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var type = typeof(global::TestNamespace.TestType); + global::System.Reflection.MethodInfo resolver = default!; + global::System.Reflection.ParameterInfo[] parameters = default!; + _bindingsInitialized = true; + + resolver = type.GetMethod( + "GetTest", + bindingFlags, + new global::System.Type[] + { + typeof(global::HotChocolate.SetState) + })!; + parameters = resolver.GetParameters(); + _args_TestType_GetTest[0] = bindingResolver.GetBinding(parameters[0]); + } + } + } } public static HotChocolate.Resolvers.FieldResolverDelegates TestType_GetTest() diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithLocalStateArgument_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithLocalStateArgument_MatchesSnapshot.md index 0e1dcdb1396..efd3b27a04a 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithLocalStateArgument_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithLocalStateArgument_MatchesSnapshot.md @@ -19,35 +19,41 @@ namespace TestNamespace { internal static class TestTypeResolvers { + private static readonly object _sync = new object(); private static bool _bindingsInitialized; private readonly static global::HotChocolate.Internal.IParameterBinding[] _args_TestType_GetTest = new global::HotChocolate.Internal.IParameterBinding[1]; public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver) { - if (_bindingsInitialized) + if (!_bindingsInitialized) { - return; - } - _bindingsInitialized = true; - - const global::System.Reflection.BindingFlags bindingFlags = - global::System.Reflection.BindingFlags.Public - | global::System.Reflection.BindingFlags.NonPublic - | global::System.Reflection.BindingFlags.Static; - - var type = typeof(global::TestNamespace.TestType); - global::System.Reflection.MethodInfo resolver = default!; - global::System.Reflection.ParameterInfo[] parameters = default!; - - resolver = type.GetMethod( - "GetTest", - bindingFlags, - new global::System.Type[] + lock (_sync) { - typeof(int) - })!; - parameters = resolver.GetParameters(); - _args_TestType_GetTest[0] = bindingResolver.GetBinding(parameters[0]); + if (!_bindingsInitialized) + { + + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var type = typeof(global::TestNamespace.TestType); + global::System.Reflection.MethodInfo resolver = default!; + global::System.Reflection.ParameterInfo[] parameters = default!; + _bindingsInitialized = true; + + resolver = type.GetMethod( + "GetTest", + bindingFlags, + new global::System.Type[] + { + typeof(int) + })!; + parameters = resolver.GetParameters(); + _args_TestType_GetTest[0] = bindingResolver.GetBinding(parameters[0]); + } + } + } } public static HotChocolate.Resolvers.FieldResolverDelegates TestType_GetTest() diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithLocalStateSetStateArgument_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithLocalStateSetStateArgument_MatchesSnapshot.md index 57951d45d81..74e82253f53 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithLocalStateSetStateArgument_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithLocalStateSetStateArgument_MatchesSnapshot.md @@ -19,35 +19,41 @@ namespace TestNamespace { internal static class TestTypeResolvers { + private static readonly object _sync = new object(); private static bool _bindingsInitialized; private readonly static global::HotChocolate.Internal.IParameterBinding[] _args_TestType_GetTest = new global::HotChocolate.Internal.IParameterBinding[1]; public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver) { - if (_bindingsInitialized) + if (!_bindingsInitialized) { - return; - } - _bindingsInitialized = true; - - const global::System.Reflection.BindingFlags bindingFlags = - global::System.Reflection.BindingFlags.Public - | global::System.Reflection.BindingFlags.NonPublic - | global::System.Reflection.BindingFlags.Static; - - var type = typeof(global::TestNamespace.TestType); - global::System.Reflection.MethodInfo resolver = default!; - global::System.Reflection.ParameterInfo[] parameters = default!; - - resolver = type.GetMethod( - "GetTest", - bindingFlags, - new global::System.Type[] + lock (_sync) { - typeof(global::HotChocolate.SetState) - })!; - parameters = resolver.GetParameters(); - _args_TestType_GetTest[0] = bindingResolver.GetBinding(parameters[0]); + if (!_bindingsInitialized) + { + + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var type = typeof(global::TestNamespace.TestType); + global::System.Reflection.MethodInfo resolver = default!; + global::System.Reflection.ParameterInfo[] parameters = default!; + _bindingsInitialized = true; + + resolver = type.GetMethod( + "GetTest", + bindingFlags, + new global::System.Type[] + { + typeof(global::HotChocolate.SetState) + })!; + parameters = resolver.GetParameters(); + _args_TestType_GetTest[0] = bindingResolver.GetBinding(parameters[0]); + } + } + } } public static HotChocolate.Resolvers.FieldResolverDelegates TestType_GetTest() diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithScopedStateArgument_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithScopedStateArgument_MatchesSnapshot.md index aa2f8e21468..119f8ec2954 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithScopedStateArgument_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithScopedStateArgument_MatchesSnapshot.md @@ -19,35 +19,41 @@ namespace TestNamespace { internal static class TestTypeResolvers { + private static readonly object _sync = new object(); private static bool _bindingsInitialized; private readonly static global::HotChocolate.Internal.IParameterBinding[] _args_TestType_GetTest = new global::HotChocolate.Internal.IParameterBinding[1]; public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver) { - if (_bindingsInitialized) + if (!_bindingsInitialized) { - return; - } - _bindingsInitialized = true; - - const global::System.Reflection.BindingFlags bindingFlags = - global::System.Reflection.BindingFlags.Public - | global::System.Reflection.BindingFlags.NonPublic - | global::System.Reflection.BindingFlags.Static; - - var type = typeof(global::TestNamespace.TestType); - global::System.Reflection.MethodInfo resolver = default!; - global::System.Reflection.ParameterInfo[] parameters = default!; - - resolver = type.GetMethod( - "GetTest", - bindingFlags, - new global::System.Type[] + lock (_sync) { - typeof(int) - })!; - parameters = resolver.GetParameters(); - _args_TestType_GetTest[0] = bindingResolver.GetBinding(parameters[0]); + if (!_bindingsInitialized) + { + + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var type = typeof(global::TestNamespace.TestType); + global::System.Reflection.MethodInfo resolver = default!; + global::System.Reflection.ParameterInfo[] parameters = default!; + _bindingsInitialized = true; + + resolver = type.GetMethod( + "GetTest", + bindingFlags, + new global::System.Type[] + { + typeof(int) + })!; + parameters = resolver.GetParameters(); + _args_TestType_GetTest[0] = bindingResolver.GetBinding(parameters[0]); + } + } + } } public static HotChocolate.Resolvers.FieldResolverDelegates TestType_GetTest() diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithScopedStateSetStateArgument_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithScopedStateSetStateArgument_MatchesSnapshot.md index 137b7b827b3..a224d91f3e8 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithScopedStateSetStateArgument_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithScopedStateSetStateArgument_MatchesSnapshot.md @@ -19,35 +19,41 @@ namespace TestNamespace { internal static class TestTypeResolvers { + private static readonly object _sync = new object(); private static bool _bindingsInitialized; private readonly static global::HotChocolate.Internal.IParameterBinding[] _args_TestType_GetTest = new global::HotChocolate.Internal.IParameterBinding[1]; public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver) { - if (_bindingsInitialized) + if (!_bindingsInitialized) { - return; - } - _bindingsInitialized = true; - - const global::System.Reflection.BindingFlags bindingFlags = - global::System.Reflection.BindingFlags.Public - | global::System.Reflection.BindingFlags.NonPublic - | global::System.Reflection.BindingFlags.Static; - - var type = typeof(global::TestNamespace.TestType); - global::System.Reflection.MethodInfo resolver = default!; - global::System.Reflection.ParameterInfo[] parameters = default!; - - resolver = type.GetMethod( - "GetTest", - bindingFlags, - new global::System.Type[] + lock (_sync) { - typeof(global::HotChocolate.SetState) - })!; - parameters = resolver.GetParameters(); - _args_TestType_GetTest[0] = bindingResolver.GetBinding(parameters[0]); + if (!_bindingsInitialized) + { + + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var type = typeof(global::TestNamespace.TestType); + global::System.Reflection.MethodInfo resolver = default!; + global::System.Reflection.ParameterInfo[] parameters = default!; + _bindingsInitialized = true; + + resolver = type.GetMethod( + "GetTest", + bindingFlags, + new global::System.Type[] + { + typeof(global::HotChocolate.SetState) + })!; + parameters = resolver.GetParameters(); + _args_TestType_GetTest[0] = bindingResolver.GetBinding(parameters[0]); + } + } + } } public static HotChocolate.Resolvers.FieldResolverDelegates TestType_GetTest() diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/TypeModuleSyntaxGeneratorTests.GenerateSource_TypeModuleOrdering_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/TypeModuleSyntaxGeneratorTests.GenerateSource_TypeModuleOrdering_MatchesSnapshot.md index 30114ad4044..9ab52b46170 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/TypeModuleSyntaxGeneratorTests.GenerateSource_TypeModuleOrdering_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/TypeModuleSyntaxGeneratorTests.GenerateSource_TypeModuleOrdering_MatchesSnapshot.md @@ -140,6 +140,7 @@ namespace TestNamespace { internal static class ATestBAttrTypeResolvers { + private static readonly object _sync = new object(); private static bool _bindingsInitialized; public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver) { @@ -148,6 +149,7 @@ namespace TestNamespace internal static class ATestAAttrTypeResolvers { + private static readonly object _sync = new object(); private static bool _bindingsInitialized; public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver) { From 307c0581f17a5d26505f43a58c6e6c1ee43f6be4 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Tue, 17 Dec 2024 10:26:04 +0100 Subject: [PATCH 135/154] Made AddAuthorizeDirectiveType public for customizations. (#7836) --- .../src/Abstractions/WellKnownContextData.cs | 5 +++ .../AuthorizeSchemaBuilderExtensions.cs | 40 ++++++++++++++----- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/HotChocolate/Core/src/Abstractions/WellKnownContextData.cs b/src/HotChocolate/Core/src/Abstractions/WellKnownContextData.cs index 4bb3d96a00e..e3c71d429b1 100644 --- a/src/HotChocolate/Core/src/Abstractions/WellKnownContextData.cs +++ b/src/HotChocolate/Core/src/Abstractions/WellKnownContextData.cs @@ -338,4 +338,9 @@ public static class WellKnownContextData /// The key to determine whether the request is a warmup request. /// public const string IsWarmupRequest = "HotChocolate.AspNetCore.Warmup.IsWarmupRequest"; + + /// + /// The key to determine whether the @authorize directive was already registered. + /// + public const string AreAuthorizeDirectivesRegistered = "HotChocolate.Authorization.AuthDirectivesRegistered"; } diff --git a/src/HotChocolate/Core/src/Authorization/Extensions/AuthorizeSchemaBuilderExtensions.cs b/src/HotChocolate/Core/src/Authorization/Extensions/AuthorizeSchemaBuilderExtensions.cs index e0f0ca023e1..e8abcaac2e6 100644 --- a/src/HotChocolate/Core/src/Authorization/Extensions/AuthorizeSchemaBuilderExtensions.cs +++ b/src/HotChocolate/Core/src/Authorization/Extensions/AuthorizeSchemaBuilderExtensions.cs @@ -2,8 +2,23 @@ namespace HotChocolate; -internal static class AuthorizeSchemaBuilderExtensions +/// +/// Provides extension methods for the schema builder. +/// +public static class AuthorizeSchemaBuilderExtensions { + /// + /// Adds the authorize directive types to the schema. + /// + /// + /// The schema builder. + /// + /// + /// Returns the schema builder for configuration chaining. + /// + /// + /// The is null. + /// public static ISchemaBuilder AddAuthorizeDirectiveType(this ISchemaBuilder builder) { if (builder is null) @@ -11,14 +26,21 @@ public static ISchemaBuilder AddAuthorizeDirectiveType(this ISchemaBuilder build throw new ArgumentNullException(nameof(builder)); } - var authorize = new AuthorizeDirectiveType(); - var allowAnonymous = new AllowAnonymousDirectiveType(); + if (!builder.ContextData.ContainsKey(WellKnownContextData.AreAuthorizeDirectivesRegistered)) + { + var authorize = new AuthorizeDirectiveType(); + var allowAnonymous = new AllowAnonymousDirectiveType(); + + builder + .AddDirectiveType(authorize) + .AddDirectiveType(allowAnonymous) + .TryAddSchemaDirective(authorize) + .TryAddSchemaDirective(allowAnonymous) + .TryAddTypeInterceptor(); + + builder.SetContextData(WellKnownContextData.AreAuthorizeDirectivesRegistered, true); + } - return builder - .AddDirectiveType(authorize) - .AddDirectiveType(allowAnonymous) - .TryAddSchemaDirective(authorize) - .TryAddSchemaDirective(allowAnonymous) - .TryAddTypeInterceptor(); + return builder; } } From d4534145b0195dccdb1f25e76f6f8c80d789e5f0 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Tue, 24 Dec 2024 13:37:58 +0100 Subject: [PATCH 136/154] Added Fusion Cost Analyzer Improvements. (#7867) --- .../Configuration/AggregateTypeInterceptor.cs | 16 ++ .../Types/Configuration/TypeInterceptor.cs | 3 + .../Types/Factories/SchemaSyntaxVisitor.cs | 5 + .../CostAnalysis/CostAnalyzerMiddleware.cs | 2 +- .../src/CostAnalysis/CostTypeInterceptor.cs | 7 + ...nalyzerRequestExecutorBuilderExtensions.cs | 2 + .../CostAnalyzerRequestContextExtensions.cs | 9 +- .../HotChocolate.CostAnalysis.csproj | 1 + .../src/CostAnalysis/Options/CostOptions.cs | 33 +++- .../Options/RequestCostOptions.cs | 159 ++++++++++++++++-- .../Composition/Extensions/MergeExtensions.cs | 21 +++ .../FusionRequestExecutorBuilderExtensions.cs | 12 +- .../Composition.Tests/DemoIntegrationTests.cs | 18 ++ ...sts.Accounts_And_Reviews_With_Cost.graphql | 148 ++++++++++++++++ .../test/Core.Tests/DemoIntegrationTests.cs | 52 ++++++ ..._And_Reviews_And_Products_Introspection.md | 4 +- ...eviews_Query_GetUserReviews_Report_Cost.md | 129 ++++++++++++++ .../test/Shared/Accounts/AccountQuery.cs | 1 + .../Fusion/test/Shared/DemoProject.cs | 42 +++-- .../Shared/DemoProjectSchemaExtensions.cs | 44 +++++ .../test/Shared/Reviews/ReviewsQuery.cs | 1 + .../src/Skimmed/DirectiveDefinition.cs | 12 +- 22 files changed, 682 insertions(+), 39 deletions(-) create mode 100644 src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/DemoIntegrationTests.Accounts_And_Reviews_With_Cost.graphql create mode 100644 src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/DemoIntegrationTests.Authors_And_Reviews_Query_GetUserReviews_Report_Cost.md diff --git a/src/HotChocolate/Core/src/Types/Configuration/AggregateTypeInterceptor.cs b/src/HotChocolate/Core/src/Types/Configuration/AggregateTypeInterceptor.cs index 91943e1ec38..ebaa9b546c4 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/AggregateTypeInterceptor.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/AggregateTypeInterceptor.cs @@ -110,6 +110,22 @@ internal override void InitializeContext( } } + internal override bool SkipDirectiveDefinition(DirectiveDefinitionNode node) + { + ref var first = ref GetReference(); + var length = _typeInterceptors.Length; + + for (var i = 0; i < length; i++) + { + if (Unsafe.Add(ref first, i).SkipDirectiveDefinition(node)) + { + return true; + } + } + + return false; + } + public override void OnBeforeDiscoverTypes() { ref var first = ref GetReference(); diff --git a/src/HotChocolate/Core/src/Types/Configuration/TypeInterceptor.cs b/src/HotChocolate/Core/src/Types/Configuration/TypeInterceptor.cs index 5b2d812414a..658292eaf48 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/TypeInterceptor.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/TypeInterceptor.cs @@ -74,6 +74,9 @@ public virtual void OnBeforeDiscoverTypes() { } /// public virtual void OnAfterDiscoverTypes() { } + internal virtual bool SkipDirectiveDefinition(DirectiveDefinitionNode node) + => false; + /// /// This event is triggered after the type instance was created but before /// any type definition was initialized. diff --git a/src/HotChocolate/Core/src/Types/Types/Factories/SchemaSyntaxVisitor.cs b/src/HotChocolate/Core/src/Types/Types/Factories/SchemaSyntaxVisitor.cs index 0d2b01c50cd..4f1aec7cab4 100644 --- a/src/HotChocolate/Core/src/Types/Types/Factories/SchemaSyntaxVisitor.cs +++ b/src/HotChocolate/Core/src/Types/Types/Factories/SchemaSyntaxVisitor.cs @@ -171,6 +171,11 @@ protected override ISyntaxVisitorAction VisitChildren( goto EXIT; } + if(context.DescriptorContext.TypeInterceptor.SkipDirectiveDefinition(node)) + { + goto EXIT; + } + context.Types.Add( TypeReference.Create( _directiveTypeFactory.Create( diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostAnalyzerMiddleware.cs b/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostAnalyzerMiddleware.cs index a13cc8c04c2..66290d93cd0 100644 --- a/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostAnalyzerMiddleware.cs +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostAnalyzerMiddleware.cs @@ -36,7 +36,7 @@ public async ValueTask InvokeAsync(IRequestContext context) } var requestOptions = context.TryGetCostOptions() ?? options; - var mode = context.GetCostAnalyzerMode(requestOptions.EnforceCostLimits); + var mode = context.GetCostAnalyzerMode(requestOptions); if (mode == CostAnalyzerMode.Skip) { diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostTypeInterceptor.cs b/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostTypeInterceptor.cs index ae769173431..514c9c95aa6 100644 --- a/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostTypeInterceptor.cs +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/CostTypeInterceptor.cs @@ -171,6 +171,13 @@ public override void OnBeforeCompleteType(ITypeCompletionContext completionConte } } +internal sealed class CostDirectiveTypeInterceptor : TypeInterceptor +{ + internal override bool SkipDirectiveDefinition(DirectiveDefinitionNode node) + => node.Name.Value.Equals("cost", StringComparison.Ordinal) + || node.Name.Value.Equals("listSize", StringComparison.Ordinal); +} + file static class Extensions { public static bool HasCostDirective(this IHasDirectiveDefinition directiveProvider) diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/DependencyInjection/CostAnalyzerRequestExecutorBuilderExtensions.cs b/src/HotChocolate/CostAnalysis/src/CostAnalysis/DependencyInjection/CostAnalyzerRequestExecutorBuilderExtensions.cs index 64560d2c558..7777311ac9e 100644 --- a/src/HotChocolate/CostAnalysis/src/CostAnalysis/DependencyInjection/CostAnalyzerRequestExecutorBuilderExtensions.cs +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/DependencyInjection/CostAnalyzerRequestExecutorBuilderExtensions.cs @@ -56,12 +56,14 @@ public static IRequestExecutorBuilder AddCostAnalyzer(this IRequestExecutorBuild requestOptions.MaxFieldCost, requestOptions.MaxTypeCost, requestOptions.EnforceCostLimits, + requestOptions.SkipAnalyzer, requestOptions.Filtering.VariableMultiplier); }); }) .AddDirectiveType() .AddDirectiveType() .TryAddTypeInterceptor() + .TryAddTypeInterceptor() // we are replacing the default pipeline if the cost analyzer is added. .Configure(c => c.DefaultPipelineFactory = AddDefaultPipeline); diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/Extensions/CostAnalyzerRequestContextExtensions.cs b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Extensions/CostAnalyzerRequestContextExtensions.cs index e3c42d18810..e768a648434 100644 --- a/src/HotChocolate/CostAnalysis/src/CostAnalysis/Extensions/CostAnalyzerRequestContextExtensions.cs +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Extensions/CostAnalyzerRequestContextExtensions.cs @@ -58,13 +58,18 @@ public static CostMetrics GetCostMetrics( internal static CostAnalyzerMode GetCostAnalyzerMode( this IRequestContext context, - bool enforceCostLimits) + RequestCostOptions options) { if (context is null) { throw new ArgumentNullException(nameof(context)); } + if (options.SkipAnalyzer) + { + return CostAnalyzerMode.Skip; + } + if (context.ContextData.ContainsKey(WellKnownContextData.ValidateCost)) { return CostAnalyzerMode.Analyze | CostAnalyzerMode.Report; @@ -72,7 +77,7 @@ internal static CostAnalyzerMode GetCostAnalyzerMode( var flags = CostAnalyzerMode.Analyze; - if (enforceCostLimits) + if (options.EnforceCostLimits) { flags |= CostAnalyzerMode.Enforce; } diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/HotChocolate.CostAnalysis.csproj b/src/HotChocolate/CostAnalysis/src/CostAnalysis/HotChocolate.CostAnalysis.csproj index e3fdbd68bfd..c3bd3025c3f 100644 --- a/src/HotChocolate/CostAnalysis/src/CostAnalysis/HotChocolate.CostAnalysis.csproj +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/HotChocolate.CostAnalysis.csproj @@ -2,6 +2,7 @@ + diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/Options/CostOptions.cs b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Options/CostOptions.cs index b036e72187b..3fd1ec5f593 100644 --- a/src/HotChocolate/CostAnalysis/src/CostAnalysis/Options/CostOptions.cs +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Options/CostOptions.cs @@ -5,6 +5,9 @@ namespace HotChocolate.CostAnalysis; /// public sealed class CostOptions { + private bool _skipAnalyzer = false; + private bool _enforceCostLimits = true; + /// /// Gets or sets the maximum allowed field cost. /// @@ -18,7 +21,35 @@ public sealed class CostOptions /// /// Defines if the analyzer shall enforce cost limits. /// - public bool EnforceCostLimits { get; set; } = true; + public bool EnforceCostLimits + { + get => _enforceCostLimits; + set + { + if(value) + { + SkipAnalyzer = false; + } + + _enforceCostLimits = value; + } + } + + /// + /// Skips the cost analyzer. + /// + public bool SkipAnalyzer + { + get => _skipAnalyzer; + set + { + if(value) + { + EnforceCostLimits = false; + } + _skipAnalyzer = value; + } + } /// /// Defines if cost defaults shall be applied to the schema. diff --git a/src/HotChocolate/CostAnalysis/src/CostAnalysis/Options/RequestCostOptions.cs b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Options/RequestCostOptions.cs index 0b111285f7d..6d64d41630f 100644 --- a/src/HotChocolate/CostAnalysis/src/CostAnalysis/Options/RequestCostOptions.cs +++ b/src/HotChocolate/CostAnalysis/src/CostAnalysis/Options/RequestCostOptions.cs @@ -3,20 +3,145 @@ namespace HotChocolate.CostAnalysis; /// /// Request options for cost analysis. /// -/// -/// The maximum allowed field cost. -/// -/// -/// The maximum allowed type cost. -/// -/// -/// Defines if the analyzer shall enforce cost limits. -/// -/// -/// The filter variable multiplier. -/// -public record RequestCostOptions( - double MaxFieldCost, - double MaxTypeCost, - bool EnforceCostLimits, - int? FilterVariableMultiplier); +public record RequestCostOptions +{ + /// + /// Request options for cost analysis. + /// + /// + /// The maximum allowed field cost. + /// + /// + /// The maximum allowed type cost. + /// + /// + /// Defines if the analyzer shall enforce cost limits. + /// + /// + /// The filter variable multiplier. + /// + public RequestCostOptions( + double maxFieldCost, + double maxTypeCost, + bool enforceCostLimits, + int? filterVariableMultiplier) + { + MaxFieldCost = maxFieldCost; + MaxTypeCost = maxTypeCost; + EnforceCostLimits = enforceCostLimits; + FilterVariableMultiplier = filterVariableMultiplier; + } + + /// + /// Gets the maximum allowed field cost. + /// + /// + /// The maximum allowed field cost. + /// + /// + /// The maximum allowed type cost. + /// + /// + /// Defines if the analyzer shall enforce cost limits. + /// + /// + /// Defines if the cost analyzer shall be skipped. + /// + /// + /// The filter variable multiplier. + /// + public RequestCostOptions( + double maxFieldCost, + double maxTypeCost, + bool enforceCostLimits, + bool skipAnalyzer, + int? filterVariableMultiplier) + { + MaxFieldCost = maxFieldCost; + MaxTypeCost = maxTypeCost; + EnforceCostLimits = enforceCostLimits; + SkipAnalyzer = skipAnalyzer; + FilterVariableMultiplier = filterVariableMultiplier; + } + + /// + /// Gets the maximum allowed field cost. + /// + public double MaxFieldCost { get; init; } + + /// + /// Gets the maximum allowed type cost. + /// + public double MaxTypeCost { get; init; } + + /// + /// Defines if the analyzer shall enforce cost limits. + /// + public bool EnforceCostLimits + { + get; + init + { + if (value) + { + SkipAnalyzer = false; + } + + field = value; + } + } + + /// + /// Defines if the cost analyzer shall be skipped. + /// + public bool SkipAnalyzer + { + get; + init + { + if (value) + { + EnforceCostLimits = false; + } + + field = value; + } + } + + /// + /// Gets the filter variable multiplier. + /// + public int? FilterVariableMultiplier { get; init; } + + /// + /// Deconstructs the request options. + /// + /// + /// The maximum allowed field cost. + /// + /// + /// The maximum allowed type cost. + /// + /// + /// Defines if the analyzer shall enforce cost limits. + /// + /// + /// Defines if the cost analyzer shall be skipped. + /// + /// + /// The filter variable multiplier. + /// + public void Deconstruct( + out double maxFieldCost, + out double maxTypeCost, + out bool enforceCostLimits, + out bool skipAnalyzer, + out int? filterVariableMultiplier) + { + maxFieldCost = MaxFieldCost; + maxTypeCost = MaxTypeCost; + enforceCostLimits = EnforceCostLimits; + skipAnalyzer = SkipAnalyzer; + filterVariableMultiplier = FilterVariableMultiplier; + } +} diff --git a/src/HotChocolate/Fusion/src/Composition/Extensions/MergeExtensions.cs b/src/HotChocolate/Fusion/src/Composition/Extensions/MergeExtensions.cs index 49503d22ec0..4e504bd2907 100644 --- a/src/HotChocolate/Fusion/src/Composition/Extensions/MergeExtensions.cs +++ b/src/HotChocolate/Fusion/src/Composition/Extensions/MergeExtensions.cs @@ -1,5 +1,7 @@ +using HotChocolate.Language; using HotChocolate.Skimmed; using HotChocolate.Types; +using HotChocolate.Utilities; namespace HotChocolate.Fusion.Composition; @@ -189,6 +191,25 @@ internal static void MergeDirectivesWith( } else { + if (directive.Name.EqualsOrdinal("cost")) + { + var currentCost = target.Directives.FirstOrDefault("cost")!; + if (currentCost.Arguments.TryGetValue("weight", out var value) + && value is StringValueNode stringValueNode + && double.TryParse(stringValueNode.Value, out var currentWeight) + && directive.Arguments.TryGetValue("weight", out value) + && value is StringValueNode newStringValueNode + && double.TryParse(newStringValueNode.Value, out var newWeight) + && newWeight > currentWeight) + { + target.Directives.Remove(currentCost); + target.Directives.Add(directive); + } + + continue; + } + + if (directiveDefinition is not null && directiveDefinition.IsRepeatable) { target.Directives.Add(directive); diff --git a/src/HotChocolate/Fusion/src/Core/DependencyInjection/FusionRequestExecutorBuilderExtensions.cs b/src/HotChocolate/Fusion/src/Core/DependencyInjection/FusionRequestExecutorBuilderExtensions.cs index 12ba1e5490a..7deb82e82c6 100644 --- a/src/HotChocolate/Fusion/src/Core/DependencyInjection/FusionRequestExecutorBuilderExtensions.cs +++ b/src/HotChocolate/Fusion/src/Core/DependencyInjection/FusionRequestExecutorBuilderExtensions.cs @@ -1,4 +1,5 @@ using HotChocolate; +using HotChocolate.CostAnalysis; using HotChocolate.Execution; using HotChocolate.Execution.Configuration; using HotChocolate.Execution.Errors; @@ -30,6 +31,9 @@ public static class FusionRequestExecutorBuilderExtensions /// /// The name of the fusion graph. /// + /// + /// If set to true the default security policy is disabled. + /// /// /// Returns the that can be used to configure the Gateway. /// @@ -38,7 +42,8 @@ public static class FusionRequestExecutorBuilderExtensions /// public static FusionGatewayBuilder AddFusionGatewayServer( this IServiceCollection services, - string? graphName = null) + string? graphName = null, + bool disableDefaultSecurity = false) { ArgumentNullException.ThrowIfNull(services); @@ -53,11 +58,12 @@ public static FusionGatewayBuilder AddFusionGatewayServer( sp.GetRequiredService())); var builder = services - .AddGraphQLServer(graphName, disableDefaultSecurity: true) + .AddGraphQLServer(graphName, disableDefaultSecurity: disableDefaultSecurity) .UseField(next => next) .AddOperationCompilerOptimizer() .AddOperationCompilerOptimizer() .AddConvention(_ => new DefaultNamingConventions()) + .ModifyCostOptions(o => o.ApplyCostDefaults = false) .Configure( c => { @@ -562,6 +568,7 @@ private static IRequestExecutorBuilder UseFusionDefaultPipeline( .UseDocumentCache() .UseDocumentParser() .UseDocumentValidation() + .UseCostAnalyzer() .UseOperationCache() .UseOperationResolver() .UseSkipWarmupExecution() @@ -638,6 +645,7 @@ internal static void AddDefaultPipeline(this IList pipeli pipeline.Add(DocumentCacheMiddleware.Create()); pipeline.Add(DocumentParserMiddleware.Create()); pipeline.Add(DocumentValidationMiddleware.Create()); + pipeline.Add(CostAnalyzerMiddleware.Create()); pipeline.Add(OperationCacheMiddleware.Create()); pipeline.Add(OperationResolverMiddleware.Create()); pipeline.Add(OperationVariableCoercionMiddleware.Create()); diff --git a/src/HotChocolate/Fusion/test/Composition.Tests/DemoIntegrationTests.cs b/src/HotChocolate/Fusion/test/Composition.Tests/DemoIntegrationTests.cs index dd15057c9d6..9192af30607 100644 --- a/src/HotChocolate/Fusion/test/Composition.Tests/DemoIntegrationTests.cs +++ b/src/HotChocolate/Fusion/test/Composition.Tests/DemoIntegrationTests.cs @@ -26,6 +26,24 @@ public async Task Accounts_And_Reviews() fusionConfig.MatchSnapshot(extension: ".graphql"); } + [Fact] + public async Task Accounts_And_Reviews_With_Cost() + { + // arrange + using var demoProject = await DemoProject.CreateAsync(enableCost: true); + + var composer = new FusionGraphComposer(logFactory: _logFactory); + + var fusionConfig = await composer.ComposeAsync( + [ + demoProject.Accounts.ToConfiguration(AccountsExtensionWithCostSdl), + demoProject.Reviews.ToConfiguration(ReviewsExtensionWithCostSdl) + ]); + + fusionConfig.MatchSnapshot(extension: ".graphql"); + } + + [Fact] public async Task Accounts_And_Reviews_Infer_Patterns() { diff --git a/src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/DemoIntegrationTests.Accounts_And_Reviews_With_Cost.graphql b/src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/DemoIntegrationTests.Accounts_And_Reviews_With_Cost.graphql new file mode 100644 index 00000000000..06f25f2f81a --- /dev/null +++ b/src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/DemoIntegrationTests.Accounts_And_Reviews_With_Cost.graphql @@ -0,0 +1,148 @@ +schema + @fusion(version: 1) + @transport(subgraph: "Accounts", location: "http:\/\/localhost:5000\/graphql", kind: "HTTP") + @transport(subgraph: "Accounts", location: "ws:\/\/localhost:5000\/graphql", kind: "WebSocket") + @transport(subgraph: "Reviews", location: "http:\/\/localhost:5000\/graphql", kind: "HTTP") + @transport(subgraph: "Reviews", location: "ws:\/\/localhost:5000\/graphql", kind: "WebSocket") { + query: Query + mutation: Mutation + subscription: Subscription +} + +type Query { + errorField: String + @resolver(subgraph: "Accounts", select: "{ errorField }") + productById(id: ID!): Product + @variable(subgraph: "Reviews", name: "id", argument: "id") + @resolver(subgraph: "Reviews", select: "{ productById(id: $id) }", arguments: [ { name: "id", type: "ID!" } ]) + reviewById(id: ID!): Review + @variable(subgraph: "Reviews", name: "id", argument: "id") + @resolver(subgraph: "Reviews", select: "{ reviewById(id: $id) }", arguments: [ { name: "id", type: "ID!" } ]) + reviewOrAuthor: ReviewOrAuthor! + @resolver(subgraph: "Reviews", select: "{ reviewOrAuthor }") + reviews: [Review!]! + @resolver(subgraph: "Reviews", select: "{ reviews }") + userById(id: ID!): User + @variable(subgraph: "Accounts", name: "id", argument: "id") + @resolver(subgraph: "Accounts", select: "{ userById(id: $id) }", arguments: [ { name: "id", type: "ID!" } ]) + @cost(weight: "2.0") + @variable(subgraph: "Reviews", name: "id", argument: "id") + @resolver(subgraph: "Reviews", select: "{ authorById(id: $id) }", arguments: [ { name: "id", type: "ID!" } ]) + users: [User!]! + @resolver(subgraph: "Accounts", select: "{ users }") + usersById(ids: [ID!]!): [User!]! + @variable(subgraph: "Accounts", name: "ids", argument: "ids") + @resolver(subgraph: "Accounts", select: "{ usersById(ids: $ids) }", arguments: [ { name: "ids", type: "[ID!]!" } ]) + viewer: Viewer! + @resolver(subgraph: "Accounts", select: "{ viewer }") +} + +type Mutation { + addReview(input: AddReviewInput!): AddReviewPayload! + @variable(subgraph: "Reviews", name: "input", argument: "input") + @resolver(subgraph: "Reviews", select: "{ addReview(input: $input) }", arguments: [ { name: "input", type: "AddReviewInput!" } ]) + addUser(input: AddUserInput!): AddUserPayload! + @variable(subgraph: "Accounts", name: "input", argument: "input") + @resolver(subgraph: "Accounts", select: "{ addUser(input: $input) }", arguments: [ { name: "input", type: "AddUserInput!" } ]) +} + +type Subscription { + onError: Review! + @resolver(subgraph: "Reviews", select: "{ onError }", kind: "SUBSCRIBE") + onNewReview: Review! + @resolver(subgraph: "Reviews", select: "{ onNewReview }", kind: "SUBSCRIBE") +} + +type AddReviewPayload { + review: Review + @source(subgraph: "Reviews") +} + +type AddUserPayload { + user: User + @source(subgraph: "Accounts") +} + +type Product + @variable(subgraph: "Reviews", name: "Product_id", select: "id") + @resolver(subgraph: "Reviews", select: "{ productById(id: $Product_id) }", arguments: [ { name: "Product_id", type: "ID!" } ]) { + id: ID! + @source(subgraph: "Reviews") + reviews: [Review!]! + @source(subgraph: "Reviews") +} + +type Review implements Node + @variable(subgraph: "Reviews", name: "Review_id", select: "id") + @resolver(subgraph: "Reviews", select: "{ reviewById(id: $Review_id) }", arguments: [ { name: "Review_id", type: "ID!" } ]) + @resolver(subgraph: "Reviews", select: "{ nodes(ids: $Review_id) { ... on Review { ... Review } } }", arguments: [ { name: "Review_id", type: "[ID!]!" } ], kind: "BATCH") { + author: User! + @source(subgraph: "Reviews") + body: String! + @source(subgraph: "Reviews") + id: ID! + @source(subgraph: "Reviews") + product: Product! + @source(subgraph: "Reviews") +} + +type SomeData { + accountValue: String! + @source(subgraph: "Accounts") +} + +type User implements Node + @source(subgraph: "Reviews", name: "Author") + @variable(subgraph: "Accounts", name: "User_id", select: "id") + @variable(subgraph: "Reviews", 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") + @resolver(subgraph: "Reviews", select: "{ authorById(id: $User_id) }", arguments: [ { name: "User_id", type: "ID!" } ]) + @resolver(subgraph: "Reviews", select: "{ nodes(ids: $User_id) { ... on User { ... User } } }", arguments: [ { name: "User_id", type: "[ID!]!" } ], kind: "BATCH") { + birthdate: Date! + @source(subgraph: "Accounts") + errorField: String + @source(subgraph: "Accounts") + id: ID! + @source(subgraph: "Accounts") + @source(subgraph: "Reviews") + name: String! + @source(subgraph: "Accounts") + @source(subgraph: "Reviews") + reviews: [Review!]! + @source(subgraph: "Reviews") + username: String! + @source(subgraph: "Accounts") +} + +type Viewer { + data: SomeData! + @source(subgraph: "Accounts") + user: User + @source(subgraph: "Accounts") +} + +"The node interface is implemented by entities that have a global unique identifier." +interface Node { + id: ID! +} + +union ReviewOrAuthor = User | Review + +input AddReviewInput { + authorId: Int! + body: String! + upc: Int! +} + +input AddUserInput { + birthdate: Date! + name: String! + username: String! +} + +"The `Date` scalar represents an ISO-8601 compliant date type." +scalar Date + +"The purpose of the `cost` directive is to define a `weight` for GraphQL types, fields, and arguments. Static analysis can use these weights when calculating the overall cost of a query or response." +directive @cost("The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." weight: String!) on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM | INPUT_FIELD_DEFINITION diff --git a/src/HotChocolate/Fusion/test/Core.Tests/DemoIntegrationTests.cs b/src/HotChocolate/Fusion/test/Core.Tests/DemoIntegrationTests.cs index efeb833a388..006af311fa8 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/DemoIntegrationTests.cs +++ b/src/HotChocolate/Fusion/test/Core.Tests/DemoIntegrationTests.cs @@ -104,6 +104,58 @@ query GetUser { Assert.Null(result.ExpectOperationResult().Errors); } + [Fact] + public async Task Authors_And_Reviews_Query_GetUserReviews_Report_Cost() + { + // arrange + using var demoProject = await DemoProject.CreateAsync(enableCost: true); + + // act + var fusionGraph = await new FusionGraphComposer(logFactory: _logFactory) + .ComposeAsync( + [ + demoProject.Reviews2.ToConfiguration(Reviews2ExtensionWithCostSdl), + demoProject.Accounts.ToConfiguration(AccountsExtensionWithCostSdl) + ]); + + var executor = await new ServiceCollection() + .AddSingleton(demoProject.HttpClientFactory) + .AddSingleton(demoProject.WebSocketConnectionFactory) + .AddFusionGatewayServer() + .ConfigureFromDocument(SchemaFormatter.FormatAsDocument(fusionGraph)) + .BuildRequestExecutorAsync(); + + var request = Parse( + """ + query GetUser { + users { + name + reviews { + body + author { + name + } + } + } + } + """); + + // act + await using var result = await executor.ExecuteAsync( + OperationRequestBuilder + .New() + .SetDocument(request) + .ReportCost() + .Build()); + + // assert + var snapshot = new Snapshot(); + CollectSnapshotData(snapshot, request, result); + await snapshot.MatchMarkdownAsync(); + + Assert.Null(result.ExpectOperationResult().Errors); + } + [Fact] public async Task Authors_And_Reviews_Query_GetUserReviews_Skip_Author() { diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/DemoIntegrationTests.Authors_And_Reviews_And_Products_Introspection.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/DemoIntegrationTests.Authors_And_Reviews_And_Products_Introspection.md index 564a0fe1c95..987ebb3967c 100644 --- a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/DemoIntegrationTests.Authors_And_Reviews_And_Products_Introspection.md +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/DemoIntegrationTests.Authors_And_Reviews_And_Products_Introspection.md @@ -936,12 +936,12 @@ "fields": null }, { - "name": "ID", + "name": "Int", "kind": "SCALAR", "fields": null }, { - "name": "Int", + "name": "ID", "kind": "SCALAR", "fields": null }, diff --git a/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/DemoIntegrationTests.Authors_And_Reviews_Query_GetUserReviews_Report_Cost.md b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/DemoIntegrationTests.Authors_And_Reviews_Query_GetUserReviews_Report_Cost.md new file mode 100644 index 00000000000..3e3c08d5265 --- /dev/null +++ b/src/HotChocolate/Fusion/test/Core.Tests/__snapshots__/DemoIntegrationTests.Authors_And_Reviews_Query_GetUserReviews_Report_Cost.md @@ -0,0 +1,129 @@ +# Authors_And_Reviews_Query_GetUserReviews_Report_Cost + +## Result + +```json +{ + "data": { + "users": [ + { + "name": "Ada Lovelace", + "reviews": [ + { + "body": "Love it!", + "author": { + "name": "@ada" + } + }, + { + "body": "Could be better.", + "author": { + "name": "@ada" + } + } + ] + }, + { + "name": "Alan Turing", + "reviews": [ + { + "body": "Too expensive.", + "author": { + "name": "@alan" + } + }, + { + "body": "Prefer something else.", + "author": { + "name": "@alan" + } + } + ] + } + ] + }, + "extensions": { + "operationCost": { + "fieldCost": 12, + "typeCost": 22 + } + } +} +``` + +## Request + +```graphql +query GetUser { + users { + name + reviews { + body + author { + name + } + } + } +} +``` + +## QueryPlan Hash + +```text +8F6E2CCB58DA60498BA9F134BF2F1B8D5C24FBA0 +``` + +## QueryPlan + +```json +{ + "document": "query GetUser { users { name reviews { body author { name } } } }", + "operation": "GetUser", + "rootNode": { + "type": "Sequence", + "nodes": [ + { + "type": "Resolve", + "subgraph": "Accounts", + "document": "query GetUser_1 { users { name __fusion_exports__1: id } }", + "selectionSetId": 0, + "provides": [ + { + "variable": "__fusion_exports__1" + } + ] + }, + { + "type": "Compose", + "selectionSetIds": [ + 0 + ] + }, + { + "type": "ResolveByKeyBatch", + "subgraph": "Reviews2", + "document": "query GetUser_2($__fusion_exports__1: [ID!]!) { nodes(ids: $__fusion_exports__1) { ... on User { reviews { body author { name } } __fusion_exports__1: id } } }", + "selectionSetId": 1, + "path": [ + "nodes" + ], + "requires": [ + { + "variable": "__fusion_exports__1" + } + ] + }, + { + "type": "Compose", + "selectionSetIds": [ + 1 + ] + } + ] + }, + "state": { + "__fusion_exports__1": "User_id" + } +} +``` + diff --git a/src/HotChocolate/Fusion/test/Shared/Accounts/AccountQuery.cs b/src/HotChocolate/Fusion/test/Shared/Accounts/AccountQuery.cs index eac2c01bbab..640a496e00d 100644 --- a/src/HotChocolate/Fusion/test/Shared/Accounts/AccountQuery.cs +++ b/src/HotChocolate/Fusion/test/Shared/Accounts/AccountQuery.cs @@ -1,3 +1,4 @@ +using HotChocolate.CostAnalysis.Types; using HotChocolate.Resolvers; using HotChocolate.Types.Relay; diff --git a/src/HotChocolate/Fusion/test/Shared/DemoProject.cs b/src/HotChocolate/Fusion/test/Shared/DemoProject.cs index a5abf239d40..89b14e0dbb5 100644 --- a/src/HotChocolate/Fusion/test/Shared/DemoProject.cs +++ b/src/HotChocolate/Fusion/test/Shared/DemoProject.cs @@ -82,7 +82,14 @@ private DemoProject( public DemoSubgraph Resale { get; } - public static async Task CreateAsync(CancellationToken ct = default) + public static async Task CreateAsync( + CancellationToken ct = default) + => await CreateAsync(false, ct).ConfigureAwait(false); + + + public static async Task CreateAsync( + bool enableCost, + CancellationToken ct = default) { var disposables = new List(); TestServerFactory testServerFactory = new(); @@ -92,7 +99,8 @@ public static async Task CreateAsync(CancellationToken ct = default s => s .AddRouting() .AddSingleton() - .AddGraphQLServer(disableDefaultSecurity: true) + .AddGraphQLServer(disableDefaultSecurity: !enableCost) + .DisableIntrospection(false) .AddQueryType() .AddMutationType() .AddSubscriptionType() @@ -115,7 +123,8 @@ public static async Task CreateAsync(CancellationToken ct = default s => s .AddRouting() .AddSingleton() - .AddGraphQLServer(disableDefaultSecurity: true) + .AddGraphQLServer(disableDefaultSecurity: !enableCost) + .DisableIntrospection(false) .AddQueryType() .AddMutationType() .AddSubscriptionType() @@ -138,7 +147,8 @@ public static async Task CreateAsync(CancellationToken ct = default s => s .AddRouting() .AddSingleton() - .AddGraphQLServer(disableDefaultSecurity: true) + .AddGraphQLServer(disableDefaultSecurity: !enableCost) + .DisableIntrospection(false) .AddQueryType() .AddMutationType() .AddMutationConventions() @@ -159,7 +169,8 @@ public static async Task CreateAsync(CancellationToken ct = default s => s .AddRouting() .AddSingleton() - .AddGraphQLServer(disableDefaultSecurity: true) + .AddGraphQLServer(disableDefaultSecurity: !enableCost) + .DisableIntrospection(false) .AddQueryType() .AddMutationType() .AddGlobalObjectIdentification() @@ -180,7 +191,8 @@ public static async Task CreateAsync(CancellationToken ct = default var shipping = testServerFactory.Create( s => s .AddRouting() - .AddGraphQLServer(disableDefaultSecurity: true) + .AddGraphQLServer(disableDefaultSecurity: !enableCost) + .DisableIntrospection(false) .AddQueryType() .ConfigureSchema(b => b.SetContextData(GlobalIdSupportEnabled, 1)) .AddConvention(_ => new DefaultNamingConventions()), @@ -198,7 +210,8 @@ public static async Task CreateAsync(CancellationToken ct = default var shipping2 = testServerFactory.Create( s => s .AddRouting() - .AddGraphQLServer(disableDefaultSecurity: true) + .AddGraphQLServer(disableDefaultSecurity: !enableCost) + .DisableIntrospection(false) .AddQueryType() .ConfigureSchema(b => b.SetContextData(GlobalIdSupportEnabled, 1)) .AddConvention(_ => new DefaultNamingConventions()), @@ -216,7 +229,8 @@ public static async Task CreateAsync(CancellationToken ct = default var appointment = testServerFactory.Create( s => s .AddRouting() - .AddGraphQLServer(disableDefaultSecurity: true) + .AddGraphQLServer(disableDefaultSecurity: !enableCost) + .DisableIntrospection(false) .AddQueryType() .AddObjectType() .AddObjectType() @@ -236,7 +250,8 @@ public static async Task CreateAsync(CancellationToken ct = default var patient1 = testServerFactory.Create( s => s .AddRouting() - .AddGraphQLServer(disableDefaultSecurity: true) + .AddGraphQLServer(disableDefaultSecurity: !enableCost) + .DisableIntrospection(false) .AddQueryType() .AddGlobalObjectIdentification() .AddConvention(_ => new DefaultNamingConventions()), @@ -254,7 +269,8 @@ public static async Task CreateAsync(CancellationToken ct = default var books = testServerFactory.Create( s => s .AddRouting() - .AddGraphQLServer(disableDefaultSecurity: true) + .AddGraphQLServer(disableDefaultSecurity: !enableCost) + .DisableIntrospection(false) .AddQueryType() .AddConvention(_ => new DefaultNamingConventions()), c => c @@ -271,7 +287,8 @@ public static async Task CreateAsync(CancellationToken ct = default var authors = testServerFactory.Create( s => s .AddRouting() - .AddGraphQLServer(disableDefaultSecurity: true) + .AddGraphQLServer(disableDefaultSecurity: !enableCost) + .DisableIntrospection(false) .AddQueryType() .AddConvention(_ => new DefaultNamingConventions()), c => c @@ -288,7 +305,8 @@ public static async Task CreateAsync(CancellationToken ct = default var resale = testServerFactory.Create( s => s .AddRouting() - .AddGraphQLServer(disableDefaultSecurity: true) + .AddGraphQLServer(disableDefaultSecurity: !enableCost) + .DisableIntrospection(false) .AddQueryType() .AddGlobalObjectIdentification() .AddMutationConventions() diff --git a/src/HotChocolate/Fusion/test/Shared/DemoProjectSchemaExtensions.cs b/src/HotChocolate/Fusion/test/Shared/DemoProjectSchemaExtensions.cs index 295509aa216..845d755f053 100644 --- a/src/HotChocolate/Fusion/test/Shared/DemoProjectSchemaExtensions.cs +++ b/src/HotChocolate/Fusion/test/Shared/DemoProjectSchemaExtensions.cs @@ -10,6 +10,14 @@ extend type Query { } """; + public const string AccountsExtensionWithCostSdl = + """ + extend type Query { + userById(id: ID! @is(field: "id")): User! @cost(weight: "1.0") + usersById(ids: [ID!]! @is(field: "id")): [User!]! + } + """; + public const string AccountsExtensionWithTagSdl = """ extend type Query { @@ -46,6 +54,19 @@ extend type Query { } """; + public const string ReviewsExtensionWithCostSdl = + """ + extend type Query { + authorById(id: ID! @is(field: "id")): Author @cost(weight: "2.0") + productById(id: ID! @is(field: "id")): Product + } + + schema + @rename(coordinate: "Query.authorById", newName: "userById") + @rename(coordinate: "Author", newName: "User") { + } + """; + public const string ReviewsExtensionWithTagSdl = """ extend type Query { @@ -72,6 +93,29 @@ extend type Query { } """; + public const string Reviews2ExtensionWithCostSdl = + """ + extend type Query { + authorById(id: ID! @is(field: "id")): User @cost(weight: "2.0") + productById(id: ID! @is(field: "id")): Product @cost(weight: "1.0") + } + + extend type User { + reviews: [Review!]! @listSize(assumedSize: 10) + } + + schema + @rename(coordinate: "Query.authorById", newName: "userById") { + } + + directive @listSize( + assumedSize: Int + slicingArguments: [String!] + slicingArgumentDefaultValue: Int + sizedFields: [String!] + requireOneSlicingArgument: Boolean! = true) on FIELD_DEFINITION + """; + public const string ProductsExtensionSdl = """ extend type Query { diff --git a/src/HotChocolate/Fusion/test/Shared/Reviews/ReviewsQuery.cs b/src/HotChocolate/Fusion/test/Shared/Reviews/ReviewsQuery.cs index f7ebfc499ae..dcf083cca56 100644 --- a/src/HotChocolate/Fusion/test/Shared/Reviews/ReviewsQuery.cs +++ b/src/HotChocolate/Fusion/test/Shared/Reviews/ReviewsQuery.cs @@ -1,3 +1,4 @@ +using HotChocolate.CostAnalysis.Types; using HotChocolate.Types.Relay; namespace HotChocolate.Fusion.Shared.Reviews; diff --git a/src/HotChocolate/Skimmed/src/Skimmed/DirectiveDefinition.cs b/src/HotChocolate/Skimmed/src/Skimmed/DirectiveDefinition.cs index 604c28c0edb..c2416810d49 100644 --- a/src/HotChocolate/Skimmed/src/Skimmed/DirectiveDefinition.cs +++ b/src/HotChocolate/Skimmed/src/Skimmed/DirectiveDefinition.cs @@ -8,13 +8,13 @@ namespace HotChocolate.Skimmed; /// /// Represents a GraphQL directive definition. /// -public class DirectiveDefinition(string name) +public class DirectiveDefinition : INamedTypeSystemMemberDefinition , IDescriptionProvider , IFeatureProvider , ISealable { - private string _name = name.EnsureGraphQLName(); + private string _name; private IInputFieldDefinitionCollection? _arguments; private IFeatureCollection? _features; private string? _description; @@ -23,6 +23,14 @@ public class DirectiveDefinition(string name) private DirectiveLocation _locations; private bool _isReadOnly; + /// + /// Represents a GraphQL directive definition. + /// + public DirectiveDefinition(string name) + { + _name = name.EnsureGraphQLName(); + } + /// /// Gets or sets the name of the directive. /// From 72eda6fb2409df93f0067e390d0b4c1fb2f94ef0 Mon Sep 17 00:00:00 2001 From: Glen Date: Mon, 23 Dec 2024 19:11:18 +0200 Subject: [PATCH 137/154] Updated package references in the gateway templates (#7861) --- .../HotChocolate.Template.Gateway.Aspire.csproj | 4 ++-- .../HotChocolate.Template.Gateway.Managed.csproj | 4 ++-- templates/gateway/HotChocolate.Template.Gateway.csproj | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/templates/gateway-aspire/HotChocolate.Template.Gateway.Aspire.csproj b/templates/gateway-aspire/HotChocolate.Template.Gateway.Aspire.csproj index 715e307a171..37dd05bb9ca 100644 --- a/templates/gateway-aspire/HotChocolate.Template.Gateway.Aspire.csproj +++ b/templates/gateway-aspire/HotChocolate.Template.Gateway.Aspire.csproj @@ -8,12 +8,12 @@ - - + + diff --git a/templates/gateway-managed/HotChocolate.Template.Gateway.Managed.csproj b/templates/gateway-managed/HotChocolate.Template.Gateway.Managed.csproj index 715e307a171..e36dd849b12 100644 --- a/templates/gateway-managed/HotChocolate.Template.Gateway.Managed.csproj +++ b/templates/gateway-managed/HotChocolate.Template.Gateway.Managed.csproj @@ -8,12 +8,12 @@ - - + + diff --git a/templates/gateway/HotChocolate.Template.Gateway.csproj b/templates/gateway/HotChocolate.Template.Gateway.csproj index f29ed8b2693..61027582dd4 100644 --- a/templates/gateway/HotChocolate.Template.Gateway.csproj +++ b/templates/gateway/HotChocolate.Template.Gateway.csproj @@ -7,7 +7,7 @@ - + From 239719cbf0b12833fd540641725add69242365d3 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Mon, 23 Dec 2024 15:46:06 +0100 Subject: [PATCH 138/154] Access formatters more efficiently. (#7863) --- .../Types.Mutations/MutationConventionTypeInterceptor.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/HotChocolate/Core/src/Types.Mutations/MutationConventionTypeInterceptor.cs b/src/HotChocolate/Core/src/Types.Mutations/MutationConventionTypeInterceptor.cs index 8dd76a0d80f..192e13a07bb 100644 --- a/src/HotChocolate/Core/src/Types.Mutations/MutationConventionTypeInterceptor.cs +++ b/src/HotChocolate/Core/src/Types.Mutations/MutationConventionTypeInterceptor.cs @@ -227,12 +227,12 @@ private void TryApplyInputConvention( var argumentType = _completionContext.GetType(argument.Type!); - var formatter = - argument.Formatters.Count switch + var formatters = argument.GetFormatters(); + var formatter = formatters.Count switch { 0 => null, - 1 => argument.Formatters[0], - _ => new AggregateInputValueFormatter(argument.Formatters), + 1 => formatters[0], + _ => new AggregateInputValueFormatter(formatters), }; var defaultValue = argument.DefaultValue; From a0207c01f71376133d70730c4e8af3c6a7a3278c Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Thu, 9 Jan 2025 22:01:49 +0100 Subject: [PATCH 139/154] Added generic NamedType helper. (#7923) --- .../Types/Types/Extensions/TypeExtensions.cs | 48 +++++++++++++++++++ .../Types.Tests/Types/TypeExtensionsTests.cs | 29 +++++++++++ 2 files changed, 77 insertions(+) diff --git a/src/HotChocolate/Core/src/Types/Types/Extensions/TypeExtensions.cs b/src/HotChocolate/Core/src/Types/Types/Extensions/TypeExtensions.cs index c53e6853ca4..acd27545093 100644 --- a/src/HotChocolate/Core/src/Types/Types/Extensions/TypeExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Types/Extensions/TypeExtensions.cs @@ -459,6 +459,54 @@ public static ListType ListType(this IType type) throw new ArgumentException(TypeResources.TypeExtensions_InvalidStructure); } + /// + /// Gets the named type (the most inner type) from a type structure. + /// + /// + /// The type from which the named type shall be extracted. + /// + /// + /// The expected type of the named type. + /// + /// + /// Returns the named type. + /// + /// + /// is null. + /// + /// + /// The type structure is invalid or + /// the named type is not of the expected type. + /// + public static T NamedType(this IType type) where T : INamedType + { + var namedType = type.NamedType(); + + if(namedType is T t) + { + return t; + } + + throw new ArgumentException( + "The named type is not of the expected type.", + nameof(type)); + } + + /// + /// Gets the named type (the most inner type) from a type structure. + /// + /// + /// The type from which the named type shall be extracted. + /// + /// + /// Returns the named type. + /// + /// + /// is null. + /// + /// + /// The type structure is invalid. + /// public static INamedType NamedType(this IType type) { if (type is null) diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/TypeExtensionsTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/TypeExtensionsTests.cs index 34f655b936f..25efe1504de 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/TypeExtensionsTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Types/TypeExtensionsTests.cs @@ -103,6 +103,35 @@ public static void NamedType() Assert.NotNull(stringType); } + [Fact] + public static void NamedType_Of_T() + { + // arrange + var type = new NonNullType( + new ListType( + new NonNullType( + new StringType()))); + + // act + var stringType = type.NamedType(); + + // assert + Assert.NotNull(stringType); + } + + [Fact] + public static void NamedType_Of_T_Is_Not_Of_T() + { + // arrange + var type = new NonNullType( + new ListType( + new NonNullType( + new StringType()))); + + // act & assert + Assert.Throws(() => type.NamedType()); + } + [Fact] public static void NamedType_Type_Is_Null() { From a395e677c1ab76955921f34d4eca9c5e36029f0b Mon Sep 17 00:00:00 2001 From: Glen Date: Mon, 9 Dec 2024 10:15:55 +0200 Subject: [PATCH 140/154] Allowed built-in scalars and directives to be parsed by the SchemaParser (#7809) --- .../src/Skimmed/Serialization/SchemaParser.cs | 33 +++++--------- .../test/Skimmed.Tests/SchemaParserTests.cs | 43 +++++++++++++++++++ 2 files changed, 53 insertions(+), 23 deletions(-) diff --git a/src/HotChocolate/Skimmed/src/Skimmed/Serialization/SchemaParser.cs b/src/HotChocolate/Skimmed/src/Skimmed/Serialization/SchemaParser.cs index 28640e6c5eb..d3aa0da048b 100644 --- a/src/HotChocolate/Skimmed/src/Skimmed/Serialization/SchemaParser.cs +++ b/src/HotChocolate/Skimmed/src/Skimmed/Serialization/SchemaParser.cs @@ -38,19 +38,17 @@ private static void DiscoverDirectives(SchemaDefinition schema, DocumentNode doc { if (definition is DirectiveDefinitionNode def) { - if (BuiltIns.IsBuiltInDirective(def.Name.Value)) - { - // If a built-in directive is redefined in the schema, we just ignore it. - continue; - } - if (schema.DirectiveDefinitions.ContainsName(def.Name.Value)) { // TODO : parsing error throw new Exception("duplicate"); } - schema.DirectiveDefinitions.Add(new DirectiveDefinition(def.Name.Value)); + schema.DirectiveDefinitions.Add( + new DirectiveDefinition(def.Name.Value) + { + IsSpecDirective = BuiltIns.IsBuiltInDirective(def.Name.Value) + }); } } } @@ -61,11 +59,6 @@ private static void DiscoverTypes(SchemaDefinition schema, DocumentNode document { if (definition is ITypeDefinitionNode typeDef) { - if (BuiltIns.IsBuiltInScalar(typeDef.Name.Value)) - { - continue; - } - if (schema.Types.ContainsName(typeDef.Name.Value)) { // TODO : parsing error @@ -91,7 +84,11 @@ private static void DiscoverTypes(SchemaDefinition schema, DocumentNode document break; case ScalarTypeDefinitionNode: - schema.Types.Add(new ScalarTypeDefinition(typeDef.Name.Value)); + schema.Types.Add( + new ScalarTypeDefinition(typeDef.Name.Value) + { + IsSpecScalar = BuiltIns.IsBuiltInScalar(typeDef.Name.Value) + }); break; case UnionTypeDefinitionNode: @@ -196,11 +193,6 @@ private static void BuildTypes(SchemaDefinition schema, DocumentNode document) break; case ScalarTypeDefinitionNode typeDef: - if (BuiltIns.IsBuiltInScalar(typeDef.Name.Value)) - { - continue; - } - BuildScalarType( schema, (ScalarTypeDefinition)schema.Types[typeDef.Name.Value], @@ -545,11 +537,6 @@ private static void BuildDirectiveTypes(SchemaDefinition schema, DocumentNode do { if (definition is DirectiveDefinitionNode directiveDef) { - if (BuiltIns.IsBuiltInDirective(directiveDef.Name.Value)) - { - continue; - } - BuildDirectiveType( schema, schema.DirectiveDefinitions[directiveDef.Name.Value], diff --git a/src/HotChocolate/Skimmed/test/Skimmed.Tests/SchemaParserTests.cs b/src/HotChocolate/Skimmed/test/Skimmed.Tests/SchemaParserTests.cs index f827677511c..1149a6bbb62 100644 --- a/src/HotChocolate/Skimmed/test/Skimmed.Tests/SchemaParserTests.cs +++ b/src/HotChocolate/Skimmed/test/Skimmed.Tests/SchemaParserTests.cs @@ -1,5 +1,6 @@ using System.Text; using HotChocolate.Skimmed.Serialization; +using HotChocolate.Types; namespace HotChocolate.Skimmed; @@ -118,4 +119,46 @@ extend type Foo { }); }); } + + [Fact] + public void Parse_With_Custom_BuiltIn_Scalar_Type() + { + // arrange + var sdl = + """ + "Custom description" + scalar String @custom + """; + + // act + var schema = SchemaParser.Parse(Encoding.UTF8.GetBytes(sdl)); + var scalar = schema.Types["String"]; + + // assert + Assert.Equal("Custom description", scalar.Description); + Assert.True(scalar.Directives.ContainsName("custom")); + } + + [Fact] + public void Parse_With_Custom_BuiltIn_Directive() + { + // arrange + var sdl = + """ + "Custom description" + directive @skip("Custom argument description" ifCustom: String! @custom) on ENUM_VALUE + """; + + // act + var schema = SchemaParser.Parse(Encoding.UTF8.GetBytes(sdl)); + var directive = schema.DirectiveDefinitions["skip"]; + var argument = directive.Arguments["ifCustom"]; + + // assert + Assert.Equal("Custom description", directive.Description); + Assert.Equal("Custom argument description", argument.Description); + Assert.Equal("String", argument.Type.NamedType().Name); + Assert.True(argument.Directives.ContainsName("custom")); + Assert.Equal(DirectiveLocation.EnumValue, directive.Locations); + } } From 2c86080547b916a96e371fd6c48a82274654934a Mon Sep 17 00:00:00 2001 From: Daniel Reynolds <55194784+danielreynolds1@users.noreply.github.com> Date: Fri, 3 Jan 2025 09:07:26 +0000 Subject: [PATCH 141/154] Resolved issue in SchemaParser that prevented default values parsing in Input Objects (#7895) --- .../src/Skimmed/Serialization/SchemaParser.cs | 1 + .../test/Skimmed.Tests/SchemaParserTests.cs | 96 ++++++++++++++++++- 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/src/HotChocolate/Skimmed/src/Skimmed/Serialization/SchemaParser.cs b/src/HotChocolate/Skimmed/src/Skimmed/Serialization/SchemaParser.cs index d3aa0da048b..95454115e86 100644 --- a/src/HotChocolate/Skimmed/src/Skimmed/Serialization/SchemaParser.cs +++ b/src/HotChocolate/Skimmed/src/Skimmed/Serialization/SchemaParser.cs @@ -434,6 +434,7 @@ private static void ExtendInputObjectType( var field = new InputFieldDefinition(fieldNode.Name.Value); field.Description = fieldNode.Description?.Value; field.Type = schema.Types.ResolveType(fieldNode.Type); + field.DefaultValue = fieldNode.DefaultValue; BuildDirectiveCollection(schema, field.Directives, fieldNode.Directives); diff --git a/src/HotChocolate/Skimmed/test/Skimmed.Tests/SchemaParserTests.cs b/src/HotChocolate/Skimmed/test/Skimmed.Tests/SchemaParserTests.cs index 1149a6bbb62..a314dae81b4 100644 --- a/src/HotChocolate/Skimmed/test/Skimmed.Tests/SchemaParserTests.cs +++ b/src/HotChocolate/Skimmed/test/Skimmed.Tests/SchemaParserTests.cs @@ -1,6 +1,7 @@ using System.Text; +using HotChocolate.Language; using HotChocolate.Skimmed.Serialization; -using HotChocolate.Types; +using DirectiveLocation = HotChocolate.Types.DirectiveLocation; namespace HotChocolate.Skimmed; @@ -161,4 +162,97 @@ directive @skip("Custom argument description" ifCustom: String! @custom) on ENUM Assert.True(argument.Directives.ContainsName("custom")); Assert.Equal(DirectiveLocation.EnumValue, directive.Locations); } + + [Fact] + public void Parse_Input_Object_With_Default_Value() + { + // arrange + var sdl = + """ + input BookFilter { + genre: Genre = FANTASY + } + enum Genre { + FANTASY + SCIENCE_FICTION + } + """; + + // act + var schema = SchemaParser.Parse(Encoding.UTF8.GetBytes(sdl)); + // assert + Assert.Collection( + schema.Types.OrderBy(t => t.Name), + type => + { + var inputType = Assert.IsType(type); + Assert.Equal("BookFilter", inputType.Name); + var genreField = Assert.Single(inputType.Fields); + Assert.Equal("genre", genreField.Name); + Assert.IsType(genreField.Type); + Assert.NotNull(genreField.DefaultValue); + Assert.Equal("FANTASY", genreField.DefaultValue.Value); + }, + type => + { + var genreType = Assert.IsType(type); + Assert.Equal("Genre", genreType.Name); + }); + } + + [Fact] + public void Parse_Input_Object_With_Multiple_Default_Values() + { + // arrange + var sdl = + """ + input BookFilter { + genre: Genre = FANTASY + author: String = "Lorem ipsum" + } + enum Genre { + FANTASY + SCIENCE_FICTION + } + + scalar String + """; + + // act + var schema = SchemaParser.Parse(Encoding.UTF8.GetBytes(sdl)); + // assert + Assert.Collection( + schema.Types.OrderBy(t => t.Name), + type => + { + var inputType = Assert.IsType(type); + Assert.Equal("BookFilter", inputType.Name); + Assert.Collection(inputType.Fields.OrderBy(f => f.Name), + authorField => + { + Assert.Equal("author", authorField.Name); + var fieldType = Assert.IsType(authorField.Type); + Assert.Equal("String", fieldType.Name); + Assert.NotNull(authorField.DefaultValue); + Assert.Equal("Lorem ipsum", authorField.DefaultValue.Value); + }, + genreField => + { + Assert.Equal("genre", genreField.Name); + Assert.IsType(genreField.Type); + Assert.NotNull(genreField.DefaultValue); + Assert.Equal("FANTASY", genreField.DefaultValue.Value); + }); + }, + type => + { + var genreType = Assert.IsType(type); + Assert.Equal("Genre", genreType.Name); + }, + type => + { + var stringType = Assert.IsType(type); + Assert.Equal("String", stringType.Name); + }); + } } From 531ca73978497cda9e662b32b1aa482315153f22 Mon Sep 17 00:00:00 2001 From: Tobias Tengler <45513122+tobias-tengler@users.noreply.github.com> Date: Fri, 17 Jan 2025 15:44:56 +0100 Subject: [PATCH 142/154] Correctly handle `null` for lists of nullable value-type IDs (#7933) --- .../Relay/Extensions/RelayIdFieldHelpers.cs | 2 +- .../Types/Relay/IdAttributeTests.cs | 109 +++++++++--------- .../IdAttributeTests.Id_On_Arguments.snap | 62 +++++++--- ...ributeTests.Id_On_Objects_Given_Nulls.snap | 4 +- ...teTests.Id_Type_Is_Correctly_Inferred.snap | 45 ++++---- ...ibuteTests.InterceptedId_On_Arguments.snap | 9 +- 6 files changed, 130 insertions(+), 101 deletions(-) diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/Extensions/RelayIdFieldHelpers.cs b/src/HotChocolate/Core/src/Types/Types/Relay/Extensions/RelayIdFieldHelpers.cs index 081a108ba16..f4c16a68d67 100644 --- a/src/HotChocolate/Core/src/Types/Types/Relay/Extensions/RelayIdFieldHelpers.cs +++ b/src/HotChocolate/Core/src/Types/Types/Relay/Extensions/RelayIdFieldHelpers.cs @@ -261,7 +261,7 @@ private static IInputValueFormatter CreateSerializer( return new GlobalIdInputValueFormatter( completionContext.DescriptorContext.NodeIdSerializerAccessor, resultTypeInfo.NamedType, - resultType.ElementType?.Type ?? resultTypeInfo.NamedType, + resultType.ElementType?.Source ?? resultTypeInfo.NamedType, typeName ?? completionContext.Type.Name, validateType); } diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/IdAttributeTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/IdAttributeTests.cs index e02d332c753..950e2b0eaa8 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/IdAttributeTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/IdAttributeTests.cs @@ -52,25 +52,25 @@ query foo( nullableIntIdGivenNull: nullableIntId(id: $nullIntId) optionalIntId(id: $intId) optionalIntIdGivenNothing: optionalIntId - intIdList(id: [$intId]) - nullableIntIdList(id: [$intId, $nullIntId]) - optionalIntIdList(id: [$intId]) + intIdList(ids: [$intId]) + nullableIntIdList(ids: [$intId, $nullIntId]) + optionalIntIdList(ids: [$intId]) stringId(id: $stringId) nullableStringId(id: $stringId) nullableStringIdGivenNull: nullableStringId(id: $nullStringId) optionalStringId(id: $stringId) optionalStringIdGivenNothing: optionalStringId - stringIdList(id: [$stringId]) - nullableStringIdList(id: [$stringId, $nullStringId]) - optionalStringIdList(id: [$stringId]) + stringIdList(ids: [$stringId]) + nullableStringIdList(ids: [$stringId, $nullStringId]) + optionalStringIdList(ids: [$stringId]) guidId(id: $guidId) nullableGuidId(id: $guidId) nullableGuidIdGivenNull: nullableGuidId(id: $nullGuidId) optionalGuidId(id: $guidId) optionalGuidIdGivenNothing: optionalGuidId - guidIdList(id: [$guidId $guidId]) - nullableGuidIdList(id: [$guidId $nullGuidId $guidId]) - optionalGuidIdList(id: [$guidId $guidId]) + guidIdList(ids: [$guidId $guidId]) + nullableGuidIdList(ids: [$guidId $nullGuidId $guidId]) + optionalGuidIdList(ids: [$guidId $guidId]) customId(id: $customId) nullableCustomId(id: $customId) nullableCustomIdGivenNull: nullableCustomId(id: $nullCustomId) @@ -107,7 +107,7 @@ public async Task InterceptedId_On_Arguments() OperationRequestBuilder.New() .SetDocument(@"query foo { interceptedId(id: 1) - interceptedIds(id: [1, 2]) + interceptedIds(ids: [1, 2]) }") .Build()); @@ -465,56 +465,53 @@ public async Task EnsureIdIsOnlyAppliedOnce() [SuppressMessage("Performance", "CA1822:Mark members as static")] public class Query { - public string IntId([ID] int id) => id.ToString(); - public string IntIdList([ID] int[] id) => - string.Join(", ", id.Select(t => t.ToString())); + public int IntId([ID] int id) => id; - public string NullableIntId([ID] int? id) => id?.ToString() ?? "null"; - public string NullableIntIdList([ID] int?[] id) => - string.Join(", ", id.Select(t => t?.ToString() ?? "null")); + public int[] IntIdList([ID] int[] ids) => ids; - public string OptionalIntId([DefaultValue("UXVlcnk6MA==")][ID] Optional id) => - id.HasValue ? id.Value.ToString() : "NO VALUE"; - public string OptionalIntIdList([DefaultValue(new int[] {})][ID] Optional id) => - id.HasValue ? string.Join(", ", id.Value.Select(t => t.ToString())) : "NO VALUE"; + public int? NullableIntId([ID] int? id) => id; + + public int?[] NullableIntIdList([ID] int?[] ids) => ids; + + public int? OptionalIntId([DefaultValue("UXVlcnk6MA==")][ID] Optional id) + => id.HasValue ? id.Value : null; + + public int[]? OptionalIntIdList([DefaultValue(new int[] {})][ID] Optional ids) + => ids.HasValue ? ids.Value : null; public string StringId([ID] string id) => id; - public string StringIdList([ID] string[] id) => - string.Join(", ", id.Select(t => t.ToString())); - - public string NullableStringId([ID] string? id) => id ?? "null"; - public string NullableStringIdList([ID] string?[] id) => - string.Join(", ", id.Select(t => t?.ToString() ?? "null")); - - public string OptionalStringId( - [DefaultValue("UXVlcnk6")][ID] Optional id) => - id.HasValue ? id.Value : "NO VALUE"; - public string OptionalStringIdList( - [DefaultValue(new string[] {})][ID] Optional id) => - id.HasValue ? string.Join(", ", id.Value) : "NO VALUE"; - - public string GuidId([ID] Guid id) => id.ToString(); - public string GuidIdList([ID] IReadOnlyList id) => - string.Join(", ", id.Select(t => t.ToString())); - - public string NullableGuidId([ID] Guid? id) => id?.ToString() ?? "null"; - public string NullableGuidIdList([ID] IReadOnlyList id) => - string.Join(", ", id.Select(t => t?.ToString() ?? "null")); - - public string OptionalGuidId( - [DefaultValue("UXVlcnk6AAAAAAAAAAAAAAAAAAAAAA==")][ID] Optional id) => - id.HasValue ? id.Value.ToString() : "NO VALUE"; - public string OptionalGuidIdList( - [DefaultValue(new object[] {})][ID] Optional id) => - id.HasValue ? string.Join(", ", id.Value.Select(t => t.ToString())) : "NO VALUE"; - - public string InterceptedId([InterceptedID("Query")] [ID] int id) => id.ToString(); - - public string InterceptedIds([InterceptedID("Query")] [ID] int[] id) => - string.Join(", ", id.Select(t => t.ToString())); - - public string CustomId([ID] StronglyTypedId id) => - id.ToString(); + + public string[] StringIdList([ID] string[] ids) => ids; + + public string? NullableStringId([ID] string? id) => id; + + public string?[] NullableStringIdList([ID] string?[] ids) => ids; + + public string? OptionalStringId([DefaultValue("UXVlcnk6")][ID] Optional id) + => id.HasValue ? id.Value : null; + + public string[]? OptionalStringIdList([DefaultValue(new string[] { })] [ID] Optional ids) + => ids.HasValue ? ids.Value : null; + + public Guid GuidId([ID] Guid id) => id; + + public IReadOnlyList GuidIdList([ID] IReadOnlyList ids) => ids; + + public Guid? NullableGuidId([ID] Guid? id) => id; + + public IReadOnlyList NullableGuidIdList([ID] IReadOnlyList ids) => ids; + + public Guid? OptionalGuidId([DefaultValue("UXVlcnk6AAAAAAAAAAAAAAAAAAAAAA==")][ID] Optional id) + => id.HasValue ? id.Value : null; + + public Guid[]? OptionalGuidIdList([DefaultValue(new object[] { })] [ID] Optional ids) + => ids.HasValue ? ids.Value : null; + + public int InterceptedId([InterceptedID("Query")] [ID] int id) => id; + + public int[] InterceptedIds([InterceptedID("Query")] [ID] int[] ids) => ids; + + public string CustomId([ID] StronglyTypedId id) => id.ToString(); public string NullableCustomId([ID] StronglyTypedId? id) => id?.ToString() ?? "null"; diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.Id_On_Arguments.snap b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.Id_On_Arguments.snap index 1df3eba32aa..27804d64c97 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.Id_On_Arguments.snap +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.Id_On_Arguments.snap @@ -1,29 +1,53 @@ -{ +{ "data": { - "intId": "1", - "nullableIntId": "1", - "nullableIntIdGivenNull": "null", - "optionalIntId": "1", - "optionalIntIdGivenNothing": "NO VALUE", - "intIdList": "1", - "nullableIntIdList": "1, 0", - "optionalIntIdList": "1", + "intId": 1, + "nullableIntId": 1, + "nullableIntIdGivenNull": null, + "optionalIntId": 1, + "optionalIntIdGivenNothing": null, + "intIdList": [ + 1 + ], + "nullableIntIdList": [ + 1, + null + ], + "optionalIntIdList": [ + 1 + ], "stringId": "abc", "nullableStringId": "abc", - "nullableStringIdGivenNull": "null", + "nullableStringIdGivenNull": null, "optionalStringId": "abc", - "optionalStringIdGivenNothing": "NO VALUE", - "stringIdList": "abc", - "nullableStringIdList": "abc, null", - "optionalStringIdList": "abc", + "optionalStringIdGivenNothing": null, + "stringIdList": [ + "abc" + ], + "nullableStringIdList": [ + "abc", + null + ], + "optionalStringIdList": [ + "abc" + ], "guidId": "26a2dc8f-4dab-408c-88c6-523a0a89a2b5", "nullableGuidId": "26a2dc8f-4dab-408c-88c6-523a0a89a2b5", - "nullableGuidIdGivenNull": "null", + "nullableGuidIdGivenNull": null, "optionalGuidId": "26a2dc8f-4dab-408c-88c6-523a0a89a2b5", - "optionalGuidIdGivenNothing": "NO VALUE", - "guidIdList": "26a2dc8f-4dab-408c-88c6-523a0a89a2b5, 26a2dc8f-4dab-408c-88c6-523a0a89a2b5", - "nullableGuidIdList": "26a2dc8f-4dab-408c-88c6-523a0a89a2b5, 00000000-0000-0000-0000-000000000000, 26a2dc8f-4dab-408c-88c6-523a0a89a2b5", - "optionalGuidIdList": "26a2dc8f-4dab-408c-88c6-523a0a89a2b5, 26a2dc8f-4dab-408c-88c6-523a0a89a2b5", + "optionalGuidIdGivenNothing": null, + "guidIdList": [ + "26a2dc8f-4dab-408c-88c6-523a0a89a2b5", + "26a2dc8f-4dab-408c-88c6-523a0a89a2b5" + ], + "nullableGuidIdList": [ + "26a2dc8f-4dab-408c-88c6-523a0a89a2b5", + null, + "26a2dc8f-4dab-408c-88c6-523a0a89a2b5" + ], + "optionalGuidIdList": [ + "26a2dc8f-4dab-408c-88c6-523a0a89a2b5", + "26a2dc8f-4dab-408c-88c6-523a0a89a2b5" + ], "customId": "1-2", "nullableCustomId": "1-2", "nullableCustomIdGivenNull": "null", diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.Id_On_Objects_Given_Nulls.snap b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.Id_On_Objects_Given_Nulls.snap index 1508b3abee8..d78bb2d624c 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.Id_On_Objects_Given_Nulls.snap +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.Id_On_Objects_Given_Nulls.snap @@ -1,5 +1,5 @@ -{ - "result": "{\n \"data\": {\n \"foo\": {\n \"someId\": \"QmFyOjE=\",\n \"someNullableId\": null,\n \"someIds\": [\n \"QmF6OjE=\"\n ],\n \"someNullableIds\": [\n \"QmF6OjA=\",\n \"QmF6OjE=\"\n ]\n }\n }\n}", +{ + "result": "{\n \"data\": {\n \"foo\": {\n \"someId\": \"QmFyOjE=\",\n \"someNullableId\": null,\n \"someIds\": [\n \"QmF6OjE=\"\n ],\n \"someNullableIds\": [\n null,\n \"QmF6OjE=\"\n ]\n }\n }\n}", "someId": "U29tZTox", "someIntId": "U29tZTox" } diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.Id_Type_Is_Correctly_Inferred.snap b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.Id_Type_Is_Correctly_Inferred.snap index b8899292293..1b888ee3c62 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.Id_Type_Is_Correctly_Inferred.snap +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.Id_Type_Is_Correctly_Inferred.snap @@ -1,4 +1,4 @@ -schema { +schema { query: Query } @@ -23,26 +23,26 @@ type FooPayload implements IFooPayload { } type Query { - intId(id: ID!): String! - intIdList(id: [ID!]!): String! - nullableIntId(id: ID): String! - nullableIntIdList(id: [ID]!): String! - optionalIntId(id: ID = "UXVlcnk6MA=="): String! - optionalIntIdList(id: [ID!] = [ ]): String! + intId(id: ID!): Int! + intIdList(ids: [ID!]!): [Int!]! + nullableIntId(id: ID): Int + nullableIntIdList(ids: [ID]!): [Int]! + optionalIntId(id: ID = "UXVlcnk6MA=="): Int + optionalIntIdList(ids: [ID!] = [ ]): [Int!] stringId(id: ID!): String! - stringIdList(id: [ID!]!): String! - nullableStringId(id: ID): String! - nullableStringIdList(id: [ID]!): String! - optionalStringId(id: ID = "UXVlcnk6"): String! - optionalStringIdList(id: [ID] = [ ]): String! - guidId(id: ID!): String! - guidIdList(id: [ID!]!): String! - nullableGuidId(id: ID): String! - nullableGuidIdList(id: [ID]!): String! - optionalGuidId(id: ID = "UXVlcnk6AAAAAAAAAAAAAAAAAAAAAA=="): String! - optionalGuidIdList(id: [ID] = [ ]): String! - interceptedId(id: ID!): String! - interceptedIds(id: [ID!]!): String! + stringIdList(ids: [ID!]!): [String!]! + nullableStringId(id: ID): String + nullableStringIdList(ids: [ID]!): [String]! + optionalStringId(id: ID = "UXVlcnk6"): String + optionalStringIdList(ids: [ID] = [ ]): [String!] + guidId(id: ID!): UUID! + guidIdList(ids: [ID!]!): [UUID!]! + nullableGuidId(id: ID): UUID + nullableGuidIdList(ids: [ID]!): [UUID]! + optionalGuidId(id: ID = "UXVlcnk6AAAAAAAAAAAAAAAAAAAAAA=="): UUID + optionalGuidIdList(ids: [ID] = [ ]): [UUID!] + interceptedId(id: ID!): Int! + interceptedIds(ids: [ID!]!): [Int!]! customId(id: ID!): String! nullableCustomId(id: ID): String! customIds(ids: [ID!]!): String! @@ -60,3 +60,8 @@ input FooInput { interceptedId: ID interceptedIds: [ID!] } + +"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." +directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR + +scalar UUID @specifiedBy(url: "https:\/\/tools.ietf.org\/html\/rfc4122") diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.InterceptedId_On_Arguments.snap b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.InterceptedId_On_Arguments.snap index 7ff6b6bcda4..3772a99b285 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.InterceptedId_On_Arguments.snap +++ b/src/HotChocolate/Core/test/Types.Tests/Types/Relay/__snapshots__/IdAttributeTests.InterceptedId_On_Arguments.snap @@ -1,6 +1,9 @@ -{ +{ "data": { - "interceptedId": "1", - "interceptedIds": "1, 2" + "interceptedId": 1, + "interceptedIds": [ + 1, + 2 + ] } } From a329840714b506f81ec538b1f0749666b795c907 Mon Sep 17 00:00:00 2001 From: Tim Ward Date: Wed, 22 Jan 2025 02:18:47 -0800 Subject: [PATCH 143/154] Support @include/@skip for non-nullable fields (#7918) --- .../DirectiveCollectionExtensions.cs | 4 +- .../TypeDescriptors/PropertyKind.cs | 5 + .../Mappers/TypeDescriptorMapper.cs | 26 +- ...gmentIncludeAndSkipDirectiveTest.Client.cs | 2030 +++++++++++++++++ ...WithFragmentIncludeAndSkipDirectiveTest.cs | 46 + .../Integration/TestGeneration.cs | 31 + 6 files changed, 2137 insertions(+), 5 deletions(-) create mode 100644 src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveTest.Client.cs create mode 100644 src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveTest.cs diff --git a/src/HotChocolate/Core/src/Types/Types/Extensions/DirectiveCollectionExtensions.cs b/src/HotChocolate/Core/src/Types/Types/Extensions/DirectiveCollectionExtensions.cs index 9f01171896c..d9a98989e13 100644 --- a/src/HotChocolate/Core/src/Types/Types/Extensions/DirectiveCollectionExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Types/Extensions/DirectiveCollectionExtensions.cs @@ -105,11 +105,11 @@ private static IValueNode GetIfArgumentValue(DirectiveNode directive) throw ThrowHelper.MissingIfArgument(directive); } - private static DirectiveNode? GetSkipDirectiveNode( + internal static DirectiveNode? GetSkipDirectiveNode( this IReadOnlyList directives) => GetDirectiveNode(directives, WellKnownDirectives.Skip); - private static DirectiveNode? GetIncludeDirectiveNode( + internal static DirectiveNode? GetIncludeDirectiveNode( this IReadOnlyList directives) => GetDirectiveNode(directives, WellKnownDirectives.Include); diff --git a/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Descriptors/TypeDescriptors/PropertyKind.cs b/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Descriptors/TypeDescriptors/PropertyKind.cs index ac2b9be1c8f..76a9dc3a599 100644 --- a/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Descriptors/TypeDescriptors/PropertyKind.cs +++ b/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Descriptors/TypeDescriptors/PropertyKind.cs @@ -19,4 +19,9 @@ public enum PropertyKind /// A non-null field that is deferred. /// DeferredField, + + /// + /// A non-null field that is included or skipped conditionally. + /// + SkipOrIncludeField } diff --git a/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Mappers/TypeDescriptorMapper.cs b/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Mappers/TypeDescriptorMapper.cs index 7216096f6ac..f475491bb2d 100644 --- a/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Mappers/TypeDescriptorMapper.cs +++ b/src/StrawberryShake/CodeGeneration/src/CodeGeneration/Mappers/TypeDescriptorMapper.cs @@ -375,6 +375,12 @@ private static void CollectClassesThatImplementInterface( } } + private static bool IncludeOrSkipDirective(OutputFieldModel field) + { + return field.SyntaxNode.Directives.GetIncludeDirectiveNode() is not null || + field.SyntaxNode.Directives.GetSkipDirectiveNode() is not null; + } + private static void AddProperties( ClientModel model, Dictionary typeDescriptors, @@ -388,6 +394,7 @@ private static void AddProperties( { INamedTypeDescriptor? fieldType; var namedType = field.Type.NamedType(); + var includeOrSkipDirective = IncludeOrSkipDirective(field); if (namedType.IsScalarType() || namedType.IsEnumType()) { @@ -402,14 +409,19 @@ private static void AddProperties( typeDescriptors); } + var propertyKind = includeOrSkipDirective + ? PropertyKind.SkipOrIncludeField + : PropertyKind.Field; properties.Add( new PropertyDescriptor( field.Name, field.ResponseName, BuildFieldType( field.Type, - fieldType), - field.Description)); + fieldType, + propertyKind), + field.Description, + propertyKind)); } typeDescriptorModel.Descriptor.CompleteProperties(properties); @@ -478,10 +490,18 @@ private static INamedTypeDescriptor GetFieldTypeDescriptor( private static ITypeDescriptor BuildFieldType( this IType original, - INamedTypeDescriptor namedTypeDescriptor) + INamedTypeDescriptor namedTypeDescriptor, + PropertyKind kind = PropertyKind.Field) { if (original is NonNullType nnt) { + if (kind == PropertyKind.SkipOrIncludeField) + { + return BuildFieldType( + nnt.Type, + namedTypeDescriptor); + } + return new NonNullTypeDescriptor( BuildFieldType( nnt.Type, diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveTest.Client.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveTest.Client.cs new file mode 100644 index 00000000000..f6b9fae6263 --- /dev/null +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveTest.Client.cs @@ -0,0 +1,2030 @@ +// ReSharper disable BuiltInTypeReferenceStyle +// ReSharper disable RedundantNameQualifier +// ReSharper disable ArrangeObjectCreationWhenTypeEvident +// ReSharper disable UnusedType.Global +// ReSharper disable PartialTypeWithSinglePart +// ReSharper disable UnusedMethodReturnValue.Local +// ReSharper disable ConvertToAutoProperty +// ReSharper disable UnusedMember.Global +// ReSharper disable SuggestVarOrType_SimpleTypes +// ReSharper disable InconsistentNaming + +// StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveClient + +// +#nullable enable annotations +#nullable disable warnings + +namespace Microsoft.Extensions.DependencyInjection +{ + // StrawberryShake.CodeGeneration.CSharp.Generators.DependencyInjectionGenerator + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public static partial class StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveClientServiceCollectionExtensions + { + public static global::StrawberryShake.IClientBuilder AddStarWarsGetHeroWithFragmentIncludeAndSkipDirectiveClient(this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::StrawberryShake.ExecutionStrategy strategy = global::StrawberryShake.ExecutionStrategy.NetworkOnly) + { + var serviceCollection = new global::Microsoft.Extensions.DependencyInjection.ServiceCollection(); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services, sp => + { + ConfigureClientDefault(sp, serviceCollection, strategy); + return new ClientServiceProvider(global::Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(serviceCollection)); + }); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services, sp => new global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveClientStoreAccessor(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(sp)), global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(sp)), global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(sp)), global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService>(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(sp)), global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService>(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(sp)))); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services, sp => global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(sp))); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services, sp => global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(sp))); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services, sp => global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(sp))); + return new global::StrawberryShake.ClientBuilder("StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveClient", services, serviceCollection); + } + + private static global::Microsoft.Extensions.DependencyInjection.IServiceCollection ConfigureClientDefault(global::System.IServiceProvider parentServices, global::Microsoft.Extensions.DependencyInjection.ServiceCollection services, global::StrawberryShake.ExecutionStrategy strategy = global::StrawberryShake.ExecutionStrategy.NetworkOnly) + { + global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAddSingleton(services); + global::Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAddSingleton(services, sp => new global::StrawberryShake.OperationStore(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(sp))); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services, sp => + { + var clientFactory = global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(parentServices); + return new global::StrawberryShake.Transport.Http.HttpConnection(() => clientFactory.CreateClient("StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveClient")); + }); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton, global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.GetHeroWithFragmentIncludeAndSkipDirective_Hero_DroidFromDroidEntityMapper>(services); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton, global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.GetHeroWithFragmentIncludeAndSkipDirective_Hero_HumanFromHumanEntityMapper>(services); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services, sp => new global::StrawberryShake.Serialization.SerializerResolver(global::System.Linq.Enumerable.Concat(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService>(parentServices), global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService>(sp)))); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton, global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.GetHeroWithFragmentIncludeAndSkipDirectiveResultFactory>(services); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services, sp => global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService>(sp)); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services, sp => global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(sp)); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton, global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.GetHeroWithFragmentIncludeAndSkipDirectiveBuilder>(services); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton>(services, sp => new global::StrawberryShake.OperationExecutor(global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(sp), () => global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService>(sp), () => global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService>(sp), global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(sp), strategy)); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton, global::StrawberryShake.Json.JsonResultPatcher>(services); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services, sp => global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(sp)); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services); + global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton(services, sp => global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(sp)); + return services; + } + + private sealed class ClientServiceProvider : System.IServiceProvider, System.IDisposable + { + private readonly System.IServiceProvider _provider; + public ClientServiceProvider(System.IServiceProvider provider) + { + _provider = provider; + } + + public object? GetService(System.Type serviceType) + { + return _provider.GetService(serviceType); + } + + public void Dispose() + { + if (_provider is System.IDisposable d) + { + d.Dispose(); + } + } + } + } +} + +namespace StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective +{ + // StrawberryShake.CodeGeneration.CSharp.Generators.ResultTypeGenerator + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class GetHeroWithFragmentIncludeAndSkipDirectiveResult : global::System.IEquatable, IGetHeroWithFragmentIncludeAndSkipDirectiveResult + { + public GetHeroWithFragmentIncludeAndSkipDirectiveResult(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirective_Hero? hero) + { + Hero = hero; + } + + public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirective_Hero? Hero { get; } + + public virtual global::System.Boolean Equals(GetHeroWithFragmentIncludeAndSkipDirectiveResult? other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + if (other.GetType() != GetType()) + { + return false; + } + + return (((Hero is null && other.Hero is null) || Hero != null && Hero.Equals(other.Hero))); + } + + public override global::System.Boolean Equals(global::System.Object? obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + + return Equals((GetHeroWithFragmentIncludeAndSkipDirectiveResult)obj); + } + + public override global::System.Int32 GetHashCode() + { + unchecked + { + int hash = 5; + if (Hero != null) + { + hash ^= 397 * Hero.GetHashCode(); + } + + return hash; + } + } + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.ResultTypeGenerator + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class GetHeroWithFragmentIncludeAndSkipDirective_Hero_Droid : global::System.IEquatable, IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Droid + { + public GetHeroWithFragmentIncludeAndSkipDirective_Hero_Droid(global::System.String id, global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends? friends) + { + Id = id; + Friends = friends; + } + + public global::System.String Id { get; } + public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends? Friends { get; } + + public virtual global::System.Boolean Equals(GetHeroWithFragmentIncludeAndSkipDirective_Hero_Droid? other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + if (other.GetType() != GetType()) + { + return false; + } + + return (Id.Equals(other.Id)) && ((Friends is null && other.Friends is null) || Friends != null && Friends.Equals(other.Friends)); + } + + public override global::System.Boolean Equals(global::System.Object? obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + + return Equals((GetHeroWithFragmentIncludeAndSkipDirective_Hero_Droid)obj); + } + + public override global::System.Int32 GetHashCode() + { + unchecked + { + int hash = 5; + hash ^= 397 * Id.GetHashCode(); + if (Friends != null) + { + hash ^= 397 * Friends.GetHashCode(); + } + + return hash; + } + } + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.ResultTypeGenerator + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class GetHeroWithFragmentIncludeAndSkipDirective_Hero_Human : global::System.IEquatable, IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Human + { + public GetHeroWithFragmentIncludeAndSkipDirective_Hero_Human(global::System.String id, global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends? friends) + { + Id = id; + Friends = friends; + } + + public global::System.String Id { get; } + public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends? Friends { get; } + + public virtual global::System.Boolean Equals(GetHeroWithFragmentIncludeAndSkipDirective_Hero_Human? other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + if (other.GetType() != GetType()) + { + return false; + } + + return (Id.Equals(other.Id)) && ((Friends is null && other.Friends is null) || Friends != null && Friends.Equals(other.Friends)); + } + + public override global::System.Boolean Equals(global::System.Object? obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + + return Equals((GetHeroWithFragmentIncludeAndSkipDirective_Hero_Human)obj); + } + + public override global::System.Int32 GetHashCode() + { + unchecked + { + int hash = 5; + hash ^= 397 * Id.GetHashCode(); + if (Friends != null) + { + hash ^= 397 * Friends.GetHashCode(); + } + + return hash; + } + } + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.ResultTypeGenerator + /// + /// A connection to a list of items. + /// + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class GetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_FriendsConnection : global::System.IEquatable, IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_FriendsConnection + { + public GetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_FriendsConnection(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_IncludedPageInfo? includedPageInfo, global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_SkippedPageInfo? skippedPageInfo) + { + IncludedPageInfo = includedPageInfo; + SkippedPageInfo = skippedPageInfo; + } + + /// + /// Information to aid in pagination. + /// + public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_IncludedPageInfo? IncludedPageInfo { get; } + /// + /// Information to aid in pagination. + /// + public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_SkippedPageInfo? SkippedPageInfo { get; } + + public virtual global::System.Boolean Equals(GetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_FriendsConnection? other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + if (other.GetType() != GetType()) + { + return false; + } + + return (((IncludedPageInfo is null && other.IncludedPageInfo is null) || IncludedPageInfo != null && IncludedPageInfo.Equals(other.IncludedPageInfo))) && ((SkippedPageInfo is null && other.SkippedPageInfo is null) || SkippedPageInfo != null && SkippedPageInfo.Equals(other.SkippedPageInfo)); + } + + public override global::System.Boolean Equals(global::System.Object? obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + + return Equals((GetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_FriendsConnection)obj); + } + + public override global::System.Int32 GetHashCode() + { + unchecked + { + int hash = 5; + if (IncludedPageInfo != null) + { + hash ^= 397 * IncludedPageInfo.GetHashCode(); + } + + if (SkippedPageInfo != null) + { + hash ^= 397 * SkippedPageInfo.GetHashCode(); + } + + return hash; + } + } + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.ResultTypeGenerator + /// + /// Information about pagination in a connection. + /// + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class GetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_IncludedPageInfo_PageInfo : global::System.IEquatable, IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_IncludedPageInfo_PageInfo + { + public GetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_IncludedPageInfo_PageInfo(global::System.Boolean hasNextPage) + { + HasNextPage = hasNextPage; + } + + /// + /// Indicates whether more edges exist following the set defined by the clients arguments. + /// + public global::System.Boolean HasNextPage { get; } + + public virtual global::System.Boolean Equals(GetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_IncludedPageInfo_PageInfo? other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + if (other.GetType() != GetType()) + { + return false; + } + + return (global::System.Object.Equals(HasNextPage, other.HasNextPage)); + } + + public override global::System.Boolean Equals(global::System.Object? obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + + return Equals((GetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_IncludedPageInfo_PageInfo)obj); + } + + public override global::System.Int32 GetHashCode() + { + unchecked + { + int hash = 5; + hash ^= 397 * HasNextPage.GetHashCode(); + return hash; + } + } + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.ResultTypeGenerator + /// + /// Information about pagination in a connection. + /// + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class GetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_SkippedPageInfo_PageInfo : global::System.IEquatable, IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_SkippedPageInfo_PageInfo + { + public GetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_SkippedPageInfo_PageInfo(global::System.Boolean hasNextPage) + { + HasNextPage = hasNextPage; + } + + /// + /// Indicates whether more edges exist following the set defined by the clients arguments. + /// + public global::System.Boolean HasNextPage { get; } + + public virtual global::System.Boolean Equals(GetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_SkippedPageInfo_PageInfo? other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + if (other.GetType() != GetType()) + { + return false; + } + + return (global::System.Object.Equals(HasNextPage, other.HasNextPage)); + } + + public override global::System.Boolean Equals(global::System.Object? obj) + { + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + + return Equals((GetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_SkippedPageInfo_PageInfo)obj); + } + + public override global::System.Int32 GetHashCode() + { + unchecked + { + int hash = 5; + hash ^= 397 * HasNextPage.GetHashCode(); + return hash; + } + } + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.ResultInterfaceGenerator + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial interface IGetHeroWithFragmentIncludeAndSkipDirectiveResult + { + public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirective_Hero? Hero { get; } + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.ResultInterfaceGenerator + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial interface IHeroFragment + { + public global::System.String Id { get; } + public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends? Friends { get; } + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.ResultInterfaceGenerator + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial interface IGetHeroWithFragmentIncludeAndSkipDirective_Hero : IHeroFragment + { + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.ResultInterfaceGenerator + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial interface IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Droid : IGetHeroWithFragmentIncludeAndSkipDirective_Hero + { + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.ResultInterfaceGenerator + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial interface IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Human : IGetHeroWithFragmentIncludeAndSkipDirective_Hero + { + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.ResultInterfaceGenerator + /// + /// A connection to a list of items. + /// + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial interface IFriendsFragment + { + /// + /// Information to aid in pagination. + /// + public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_IncludedPageInfo? IncludedPageInfo { get; } + /// + /// Information to aid in pagination. + /// + public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_SkippedPageInfo? SkippedPageInfo { get; } + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.ResultInterfaceGenerator + /// + /// A connection to a list of items. + /// + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial interface IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends : IFriendsFragment + { + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.ResultInterfaceGenerator + /// + /// A connection to a list of items. + /// + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial interface IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_FriendsConnection : IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends + { + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.ResultInterfaceGenerator + /// + /// Information about pagination in a connection. + /// + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial interface IPageInfoFragment + { + /// + /// Indicates whether more edges exist following the set defined by the clients arguments. + /// + public global::System.Boolean HasNextPage { get; } + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.ResultInterfaceGenerator + /// + /// Information about pagination in a connection. + /// + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial interface IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_IncludedPageInfo : IPageInfoFragment + { + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.ResultInterfaceGenerator + /// + /// Information about pagination in a connection. + /// + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial interface IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_IncludedPageInfo_PageInfo : IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_IncludedPageInfo + { + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.ResultInterfaceGenerator + /// + /// Information about pagination in a connection. + /// + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial interface IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_SkippedPageInfo : IPageInfoFragment + { + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.ResultInterfaceGenerator + /// + /// Information about pagination in a connection. + /// + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial interface IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_SkippedPageInfo_PageInfo : IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_SkippedPageInfo + { + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.OperationDocumentGenerator + /// + /// Represents the operation service of the GetHeroWithFragmentIncludeAndSkipDirective GraphQL operation + /// + /// query GetHeroWithFragmentIncludeAndSkipDirective($includePageInfo: Boolean = false, $skipPageInfo: Boolean = true) { + /// hero(episode: NEW_HOPE) { + /// __typename + /// ... HeroFragment + /// ... on Droid { + /// id + /// } + /// ... on Human { + /// id + /// } + /// } + /// } + /// + /// fragment HeroFragment on Character { + /// id + /// friends { + /// __typename + /// ... FriendsFragment + /// } + /// } + /// + /// fragment FriendsFragment on FriendsConnection { + /// includedPageInfo: pageInfo @include(if: $includePageInfo) { + /// __typename + /// ... PageInfoFragment + /// } + /// skippedPageInfo: pageInfo @skip(if: $skipPageInfo) { + /// __typename + /// ... PageInfoFragment + /// } + /// } + /// + /// fragment PageInfoFragment on PageInfo { + /// hasNextPage + /// } + /// + /// + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class GetHeroWithFragmentIncludeAndSkipDirectiveQueryDocument : global::StrawberryShake.IDocument + { + private GetHeroWithFragmentIncludeAndSkipDirectiveQueryDocument() + { + } + + public static GetHeroWithFragmentIncludeAndSkipDirectiveQueryDocument Instance { get; } = new GetHeroWithFragmentIncludeAndSkipDirectiveQueryDocument(); + public global::StrawberryShake.OperationKind Kind => global::StrawberryShake.OperationKind.Query; + public global::System.ReadOnlySpan Body => new global::System.Byte[] + { + 0x71, + 0x75, + 0x65, + 0x72, + 0x79, + 0x20, + 0x47, + 0x65, + 0x74, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x57, + 0x69, + 0x74, + 0x68, + 0x46, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x49, + 0x6e, + 0x63, + 0x6c, + 0x75, + 0x64, + 0x65, + 0x41, + 0x6e, + 0x64, + 0x53, + 0x6b, + 0x69, + 0x70, + 0x44, + 0x69, + 0x72, + 0x65, + 0x63, + 0x74, + 0x69, + 0x76, + 0x65, + 0x28, + 0x24, + 0x69, + 0x6e, + 0x63, + 0x6c, + 0x75, + 0x64, + 0x65, + 0x50, + 0x61, + 0x67, + 0x65, + 0x49, + 0x6e, + 0x66, + 0x6f, + 0x3a, + 0x20, + 0x42, + 0x6f, + 0x6f, + 0x6c, + 0x65, + 0x61, + 0x6e, + 0x20, + 0x3d, + 0x20, + 0x66, + 0x61, + 0x6c, + 0x73, + 0x65, + 0x2c, + 0x20, + 0x24, + 0x73, + 0x6b, + 0x69, + 0x70, + 0x50, + 0x61, + 0x67, + 0x65, + 0x49, + 0x6e, + 0x66, + 0x6f, + 0x3a, + 0x20, + 0x42, + 0x6f, + 0x6f, + 0x6c, + 0x65, + 0x61, + 0x6e, + 0x20, + 0x3d, + 0x20, + 0x74, + 0x72, + 0x75, + 0x65, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x68, + 0x65, + 0x72, + 0x6f, + 0x28, + 0x65, + 0x70, + 0x69, + 0x73, + 0x6f, + 0x64, + 0x65, + 0x3a, + 0x20, + 0x4e, + 0x45, + 0x57, + 0x5f, + 0x48, + 0x4f, + 0x50, + 0x45, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x46, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x44, + 0x72, + 0x6f, + 0x69, + 0x64, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x48, + 0x75, + 0x6d, + 0x61, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x48, + 0x65, + 0x72, + 0x6f, + 0x46, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x43, + 0x68, + 0x61, + 0x72, + 0x61, + 0x63, + 0x74, + 0x65, + 0x72, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x64, + 0x20, + 0x66, + 0x72, + 0x69, + 0x65, + 0x6e, + 0x64, + 0x73, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x46, + 0x72, + 0x69, + 0x65, + 0x6e, + 0x64, + 0x73, + 0x46, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x46, + 0x72, + 0x69, + 0x65, + 0x6e, + 0x64, + 0x73, + 0x46, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x46, + 0x72, + 0x69, + 0x65, + 0x6e, + 0x64, + 0x73, + 0x43, + 0x6f, + 0x6e, + 0x6e, + 0x65, + 0x63, + 0x74, + 0x69, + 0x6f, + 0x6e, + 0x20, + 0x7b, + 0x20, + 0x69, + 0x6e, + 0x63, + 0x6c, + 0x75, + 0x64, + 0x65, + 0x64, + 0x50, + 0x61, + 0x67, + 0x65, + 0x49, + 0x6e, + 0x66, + 0x6f, + 0x3a, + 0x20, + 0x70, + 0x61, + 0x67, + 0x65, + 0x49, + 0x6e, + 0x66, + 0x6f, + 0x20, + 0x40, + 0x69, + 0x6e, + 0x63, + 0x6c, + 0x75, + 0x64, + 0x65, + 0x28, + 0x69, + 0x66, + 0x3a, + 0x20, + 0x24, + 0x69, + 0x6e, + 0x63, + 0x6c, + 0x75, + 0x64, + 0x65, + 0x50, + 0x61, + 0x67, + 0x65, + 0x49, + 0x6e, + 0x66, + 0x6f, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x50, + 0x61, + 0x67, + 0x65, + 0x49, + 0x6e, + 0x66, + 0x6f, + 0x46, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x7d, + 0x20, + 0x73, + 0x6b, + 0x69, + 0x70, + 0x70, + 0x65, + 0x64, + 0x50, + 0x61, + 0x67, + 0x65, + 0x49, + 0x6e, + 0x66, + 0x6f, + 0x3a, + 0x20, + 0x70, + 0x61, + 0x67, + 0x65, + 0x49, + 0x6e, + 0x66, + 0x6f, + 0x20, + 0x40, + 0x73, + 0x6b, + 0x69, + 0x70, + 0x28, + 0x69, + 0x66, + 0x3a, + 0x20, + 0x24, + 0x73, + 0x6b, + 0x69, + 0x70, + 0x50, + 0x61, + 0x67, + 0x65, + 0x49, + 0x6e, + 0x66, + 0x6f, + 0x29, + 0x20, + 0x7b, + 0x20, + 0x5f, + 0x5f, + 0x74, + 0x79, + 0x70, + 0x65, + 0x6e, + 0x61, + 0x6d, + 0x65, + 0x20, + 0x2e, + 0x2e, + 0x2e, + 0x20, + 0x50, + 0x61, + 0x67, + 0x65, + 0x49, + 0x6e, + 0x66, + 0x6f, + 0x46, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x7d, + 0x20, + 0x7d, + 0x20, + 0x66, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x50, + 0x61, + 0x67, + 0x65, + 0x49, + 0x6e, + 0x66, + 0x6f, + 0x46, + 0x72, + 0x61, + 0x67, + 0x6d, + 0x65, + 0x6e, + 0x74, + 0x20, + 0x6f, + 0x6e, + 0x20, + 0x50, + 0x61, + 0x67, + 0x65, + 0x49, + 0x6e, + 0x66, + 0x6f, + 0x20, + 0x7b, + 0x20, + 0x68, + 0x61, + 0x73, + 0x4e, + 0x65, + 0x78, + 0x74, + 0x50, + 0x61, + 0x67, + 0x65, + 0x20, + 0x7d + }; + public global::StrawberryShake.DocumentHash Hash { get; } = new global::StrawberryShake.DocumentHash("sha1Hash", "87f3c93b0135cfd1f7e0467b57ae1a6ef7d4e828"); + + public override global::System.String ToString() + { +#if NETCOREAPP3_1_OR_GREATER + return global::System.Text.Encoding.UTF8.GetString(Body); +#else + return global::System.Text.Encoding.UTF8.GetString(Body.ToArray()); +#endif + } + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.OperationServiceGenerator + /// + /// Represents the operation service of the GetHeroWithFragmentIncludeAndSkipDirective GraphQL operation + /// + /// query GetHeroWithFragmentIncludeAndSkipDirective($includePageInfo: Boolean = false, $skipPageInfo: Boolean = true) { + /// hero(episode: NEW_HOPE) { + /// __typename + /// ... HeroFragment + /// ... on Droid { + /// id + /// } + /// ... on Human { + /// id + /// } + /// } + /// } + /// + /// fragment HeroFragment on Character { + /// id + /// friends { + /// __typename + /// ... FriendsFragment + /// } + /// } + /// + /// fragment FriendsFragment on FriendsConnection { + /// includedPageInfo: pageInfo @include(if: $includePageInfo) { + /// __typename + /// ... PageInfoFragment + /// } + /// skippedPageInfo: pageInfo @skip(if: $skipPageInfo) { + /// __typename + /// ... PageInfoFragment + /// } + /// } + /// + /// fragment PageInfoFragment on PageInfo { + /// hasNextPage + /// } + /// + /// + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class GetHeroWithFragmentIncludeAndSkipDirectiveQuery : global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirectiveQuery + { + private readonly global::StrawberryShake.IOperationExecutor _operationExecutor; + private readonly global::StrawberryShake.Serialization.IInputValueFormatter _booleanFormatter; + public GetHeroWithFragmentIncludeAndSkipDirectiveQuery(global::StrawberryShake.IOperationExecutor operationExecutor, global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) + { + _operationExecutor = operationExecutor ?? throw new global::System.ArgumentNullException(nameof(operationExecutor)); + _booleanFormatter = serializerResolver.GetInputValueFormatter("Boolean"); + } + + global::System.Type global::StrawberryShake.IOperationRequestFactory.ResultType => typeof(IGetHeroWithFragmentIncludeAndSkipDirectiveResult); + + public async global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Boolean? includePageInfo, global::System.Boolean? skipPageInfo, global::System.Threading.CancellationToken cancellationToken = default) + { + var request = CreateRequest(includePageInfo, skipPageInfo); + return await _operationExecutor.ExecuteAsync(request, cancellationToken).ConfigureAwait(false); + } + + public global::System.IObservable> Watch(global::System.Boolean? includePageInfo, global::System.Boolean? skipPageInfo, global::StrawberryShake.ExecutionStrategy? strategy = null) + { + var request = CreateRequest(includePageInfo, skipPageInfo); + return _operationExecutor.Watch(request, strategy); + } + + private global::StrawberryShake.OperationRequest CreateRequest(global::System.Boolean? includePageInfo, global::System.Boolean? skipPageInfo) + { + var variables = new global::System.Collections.Generic.Dictionary(); + variables.Add("includePageInfo", FormatIncludePageInfo(includePageInfo)); + variables.Add("skipPageInfo", FormatSkipPageInfo(skipPageInfo)); + return CreateRequest(variables); + } + + private global::StrawberryShake.OperationRequest CreateRequest(global::System.Collections.Generic.IReadOnlyDictionary? variables) + { + return new global::StrawberryShake.OperationRequest(id: GetHeroWithFragmentIncludeAndSkipDirectiveQueryDocument.Instance.Hash.Value, name: "GetHeroWithFragmentIncludeAndSkipDirective", document: GetHeroWithFragmentIncludeAndSkipDirectiveQueryDocument.Instance, strategy: global::StrawberryShake.RequestStrategy.Default, variables: variables); + } + + private global::System.Object? FormatIncludePageInfo(global::System.Boolean? value) + { + if (value is null) + { + return value; + } + else + { + return _booleanFormatter.Format(value); + } + } + + private global::System.Object? FormatSkipPageInfo(global::System.Boolean? value) + { + if (value is null) + { + return value; + } + else + { + return _booleanFormatter.Format(value); + } + } + + global::StrawberryShake.OperationRequest global::StrawberryShake.IOperationRequestFactory.Create(global::System.Collections.Generic.IReadOnlyDictionary? variables) + { + return CreateRequest(variables!); + } + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.OperationServiceInterfaceGenerator + /// + /// Represents the operation service of the GetHeroWithFragmentIncludeAndSkipDirective GraphQL operation + /// + /// query GetHeroWithFragmentIncludeAndSkipDirective($includePageInfo: Boolean = false, $skipPageInfo: Boolean = true) { + /// hero(episode: NEW_HOPE) { + /// __typename + /// ... HeroFragment + /// ... on Droid { + /// id + /// } + /// ... on Human { + /// id + /// } + /// } + /// } + /// + /// fragment HeroFragment on Character { + /// id + /// friends { + /// __typename + /// ... FriendsFragment + /// } + /// } + /// + /// fragment FriendsFragment on FriendsConnection { + /// includedPageInfo: pageInfo @include(if: $includePageInfo) { + /// __typename + /// ... PageInfoFragment + /// } + /// skippedPageInfo: pageInfo @skip(if: $skipPageInfo) { + /// __typename + /// ... PageInfoFragment + /// } + /// } + /// + /// fragment PageInfoFragment on PageInfo { + /// hasNextPage + /// } + /// + /// + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial interface IGetHeroWithFragmentIncludeAndSkipDirectiveQuery : global::StrawberryShake.IOperationRequestFactory + { + global::System.Threading.Tasks.Task> ExecuteAsync(global::System.Boolean? includePageInfo, global::System.Boolean? skipPageInfo, global::System.Threading.CancellationToken cancellationToken = default); + global::System.IObservable> Watch(global::System.Boolean? includePageInfo, global::System.Boolean? skipPageInfo, global::StrawberryShake.ExecutionStrategy? strategy = null); + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.ClientGenerator + /// + /// Represents the StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveClient GraphQL client + /// + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveClient : global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IStarWarsGetHeroWithFragmentIncludeAndSkipDirectiveClient + { + private readonly global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirectiveQuery _getHeroWithFragmentIncludeAndSkipDirective; + public StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveClient(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirectiveQuery getHeroWithFragmentIncludeAndSkipDirective) + { + _getHeroWithFragmentIncludeAndSkipDirective = getHeroWithFragmentIncludeAndSkipDirective ?? throw new global::System.ArgumentNullException(nameof(getHeroWithFragmentIncludeAndSkipDirective)); + } + + public static global::System.String ClientName => "StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveClient"; + public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirectiveQuery GetHeroWithFragmentIncludeAndSkipDirective => _getHeroWithFragmentIncludeAndSkipDirective; + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.ClientInterfaceGenerator + /// + /// Represents the StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveClient GraphQL client + /// + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial interface IStarWarsGetHeroWithFragmentIncludeAndSkipDirectiveClient + { + global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirectiveQuery GetHeroWithFragmentIncludeAndSkipDirective { get; } + } +} + +namespace StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State +{ + // StrawberryShake.CodeGeneration.CSharp.Generators.EntityTypeGenerator + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "0.0.0.0")] + public partial class DroidEntity + { + public DroidEntity(global::System.String id = default !, global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.FriendsConnectionData? friends = default !) + { + Id = id; + Friends = friends; + } + + public global::System.String Id { get; } + public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.FriendsConnectionData? Friends { get; } + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.EntityTypeGenerator + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "0.0.0.0")] + public partial class HumanEntity + { + public HumanEntity(global::System.String id = default !, global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.FriendsConnectionData? friends = default !) + { + Id = id; + Friends = friends; + } + + public global::System.String Id { get; } + public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.FriendsConnectionData? Friends { get; } + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.ResultDataFactoryGenerator + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class GetHeroWithFragmentIncludeAndSkipDirectiveResultFactory : global::StrawberryShake.IOperationResultDataFactory + { + private readonly global::StrawberryShake.IEntityStore _entityStore; + private readonly global::StrawberryShake.IEntityMapper _getHeroWithFragmentIncludeAndSkipDirective_Hero_DroidFromDroidEntityMapper; + private readonly global::StrawberryShake.IEntityMapper _getHeroWithFragmentIncludeAndSkipDirective_Hero_HumanFromHumanEntityMapper; + public GetHeroWithFragmentIncludeAndSkipDirectiveResultFactory(global::StrawberryShake.IEntityStore entityStore, global::StrawberryShake.IEntityMapper getHeroWithFragmentIncludeAndSkipDirective_Hero_DroidFromDroidEntityMapper, global::StrawberryShake.IEntityMapper getHeroWithFragmentIncludeAndSkipDirective_Hero_HumanFromHumanEntityMapper) + { + _entityStore = entityStore ?? throw new global::System.ArgumentNullException(nameof(entityStore)); + _getHeroWithFragmentIncludeAndSkipDirective_Hero_DroidFromDroidEntityMapper = getHeroWithFragmentIncludeAndSkipDirective_Hero_DroidFromDroidEntityMapper ?? throw new global::System.ArgumentNullException(nameof(getHeroWithFragmentIncludeAndSkipDirective_Hero_DroidFromDroidEntityMapper)); + _getHeroWithFragmentIncludeAndSkipDirective_Hero_HumanFromHumanEntityMapper = getHeroWithFragmentIncludeAndSkipDirective_Hero_HumanFromHumanEntityMapper ?? throw new global::System.ArgumentNullException(nameof(getHeroWithFragmentIncludeAndSkipDirective_Hero_HumanFromHumanEntityMapper)); + } + + global::System.Type global::StrawberryShake.IOperationResultDataFactory.ResultType => typeof(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirectiveResult); + + public GetHeroWithFragmentIncludeAndSkipDirectiveResult Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) + { + if (snapshot is null) + { + snapshot = _entityStore.CurrentSnapshot; + } + + if (dataInfo is GetHeroWithFragmentIncludeAndSkipDirectiveResultInfo info) + { + return new GetHeroWithFragmentIncludeAndSkipDirectiveResult(MapIGetHeroWithFragmentIncludeAndSkipDirective_Hero(info.Hero, snapshot)); + } + + throw new global::System.ArgumentException("GetHeroWithFragmentIncludeAndSkipDirectiveResultInfo expected."); + } + + private global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirective_Hero? MapIGetHeroWithFragmentIncludeAndSkipDirective_Hero(global::StrawberryShake.EntityId? entityId, global::StrawberryShake.IEntityStoreSnapshot snapshot) + { + if (entityId is null) + { + return null; + } + + if (entityId.Value.Name.Equals("Droid", global::System.StringComparison.Ordinal)) + { + return _getHeroWithFragmentIncludeAndSkipDirective_Hero_DroidFromDroidEntityMapper.Map(snapshot.GetEntity(entityId.Value) ?? throw new global::StrawberryShake.GraphQLClientException()); + } + + if (entityId.Value.Name.Equals("Human", global::System.StringComparison.Ordinal)) + { + return _getHeroWithFragmentIncludeAndSkipDirective_Hero_HumanFromHumanEntityMapper.Map(snapshot.GetEntity(entityId.Value) ?? throw new global::StrawberryShake.GraphQLClientException()); + } + + throw new global::System.NotSupportedException(); + } + + global::System.Object global::StrawberryShake.IOperationResultDataFactory.Create(global::StrawberryShake.IOperationResultDataInfo dataInfo, global::StrawberryShake.IEntityStoreSnapshot? snapshot) + { + return Create(dataInfo, snapshot); + } + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.ResultInfoGenerator + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class GetHeroWithFragmentIncludeAndSkipDirectiveResultInfo : global::StrawberryShake.IOperationResultDataInfo + { + private readonly global::System.Collections.Generic.IReadOnlyCollection _entityIds; + private readonly global::System.UInt64 _version; + public GetHeroWithFragmentIncludeAndSkipDirectiveResultInfo(global::StrawberryShake.EntityId? hero, global::System.Collections.Generic.IReadOnlyCollection entityIds, global::System.UInt64 version) + { + Hero = hero; + _entityIds = entityIds ?? throw new global::System.ArgumentNullException(nameof(entityIds)); + _version = version; + } + + public global::StrawberryShake.EntityId? Hero { get; } + public global::System.Collections.Generic.IReadOnlyCollection EntityIds => _entityIds; + public global::System.UInt64 Version => _version; + + public global::StrawberryShake.IOperationResultDataInfo WithVersion(global::System.UInt64 version) + { + return new GetHeroWithFragmentIncludeAndSkipDirectiveResultInfo(Hero, _entityIds, version); + } + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.JsonResultBuilderGenerator + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class GetHeroWithFragmentIncludeAndSkipDirectiveBuilder : global::StrawberryShake.OperationResultBuilder + { + private readonly global::StrawberryShake.IEntityStore _entityStore; + private readonly global::StrawberryShake.IEntityIdSerializer _idSerializer; + private readonly global::StrawberryShake.Serialization.ILeafValueParser _booleanParser; + private readonly global::StrawberryShake.Serialization.ILeafValueParser _iDParser; + public GetHeroWithFragmentIncludeAndSkipDirectiveBuilder(global::StrawberryShake.IEntityStore entityStore, global::StrawberryShake.IEntityIdSerializer idSerializer, global::StrawberryShake.IOperationResultDataFactory resultDataFactory, global::StrawberryShake.Serialization.ISerializerResolver serializerResolver) + { + _entityStore = entityStore ?? throw new global::System.ArgumentNullException(nameof(entityStore)); + _idSerializer = idSerializer ?? throw new global::System.ArgumentNullException(nameof(idSerializer)); + ResultDataFactory = resultDataFactory ?? throw new global::System.ArgumentNullException(nameof(resultDataFactory)); + _booleanParser = serializerResolver.GetLeafValueParser("Boolean") ?? throw new global::System.ArgumentException("No serializer for type `Boolean` found."); + _iDParser = serializerResolver.GetLeafValueParser("ID") ?? throw new global::System.ArgumentException("No serializer for type `ID` found."); + } + + protected override global::StrawberryShake.IOperationResultDataFactory ResultDataFactory { get; } + + protected override global::StrawberryShake.IOperationResultDataInfo BuildData(global::System.Text.Json.JsonElement obj) + { + var entityIds = new global::System.Collections.Generic.HashSet(); + global::StrawberryShake.IEntityStoreSnapshot snapshot = default !; + global::StrawberryShake.EntityId? heroId = default !; + _entityStore.Update(session => + { + heroId = Update_IGetHeroWithFragmentIncludeAndSkipDirective_HeroEntity(session, global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "hero"), entityIds); + snapshot = session.CurrentSnapshot; + }); + return new GetHeroWithFragmentIncludeAndSkipDirectiveResultInfo(heroId, entityIds, snapshot.Version); + } + + private global::StrawberryShake.EntityId? Update_IGetHeroWithFragmentIncludeAndSkipDirective_HeroEntity(global::StrawberryShake.IEntityStoreUpdateSession session, global::System.Text.Json.JsonElement? obj, global::System.Collections.Generic.ISet entityIds) + { + if (!obj.HasValue) + { + return null; + } + + if (obj.Value.ValueKind == global::System.Text.Json.JsonValueKind.Null) + { + return null; + } + + global::StrawberryShake.EntityId entityId = _idSerializer.Parse(obj.Value); + entityIds.Add(entityId); + if (entityId.Name.Equals("Droid", global::System.StringComparison.Ordinal)) + { + if (session.CurrentSnapshot.TryGetEntity(entityId, out global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.DroidEntity? entity)) + { + session.SetEntity(entityId, new global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.DroidEntity(Deserialize_NonNullableString(global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "id")), Deserialize_IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends(global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "friends")))); + } + else + { + session.SetEntity(entityId, new global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.DroidEntity(Deserialize_NonNullableString(global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "id")), Deserialize_IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends(global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "friends")))); + } + + return entityId; + } + + if (entityId.Name.Equals("Human", global::System.StringComparison.Ordinal)) + { + if (session.CurrentSnapshot.TryGetEntity(entityId, out global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.HumanEntity? entity)) + { + session.SetEntity(entityId, new global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.HumanEntity(Deserialize_NonNullableString(global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "id")), Deserialize_IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends(global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "friends")))); + } + else + { + session.SetEntity(entityId, new global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.HumanEntity(Deserialize_NonNullableString(global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "id")), Deserialize_IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends(global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "friends")))); + } + + return entityId; + } + + throw new global::System.NotSupportedException(); + } + + private global::System.String Deserialize_NonNullableString(global::System.Text.Json.JsonElement? obj) + { + if (!obj.HasValue) + { + throw new global::System.ArgumentNullException(); + } + + if (obj.Value.ValueKind == global::System.Text.Json.JsonValueKind.Null) + { + throw new global::System.ArgumentNullException(); + } + + return _iDParser.Parse(obj.Value.GetString()!); + } + + private global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.FriendsConnectionData? Deserialize_IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends(global::System.Text.Json.JsonElement? obj) + { + if (!obj.HasValue) + { + return null; + } + + if (obj.Value.ValueKind == global::System.Text.Json.JsonValueKind.Null) + { + return null; + } + + var typename = obj.Value.GetProperty("__typename").GetString(); + if (typename?.Equals("FriendsConnection", global::System.StringComparison.Ordinal) ?? false) + { + return new global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.FriendsConnectionData(typename, includedPageInfo: Deserialize_IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_IncludedPageInfo(global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "includedPageInfo")), skippedPageInfo: Deserialize_IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_SkippedPageInfo(global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "skippedPageInfo"))); + } + + throw new global::System.NotSupportedException(); + } + + private global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.PageInfoData? Deserialize_IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_IncludedPageInfo(global::System.Text.Json.JsonElement? obj) + { + if (!obj.HasValue) + { + return null; + } + + if (obj.Value.ValueKind == global::System.Text.Json.JsonValueKind.Null) + { + return null; + } + + var typename = obj.Value.GetProperty("__typename").GetString(); + if (typename?.Equals("PageInfo", global::System.StringComparison.Ordinal) ?? false) + { + return new global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.PageInfoData(typename, hasNextPage: Deserialize_NonNullableBoolean(global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "hasNextPage"))); + } + + throw new global::System.NotSupportedException(); + } + + private global::System.Boolean Deserialize_NonNullableBoolean(global::System.Text.Json.JsonElement? obj) + { + if (!obj.HasValue) + { + throw new global::System.ArgumentNullException(); + } + + if (obj.Value.ValueKind == global::System.Text.Json.JsonValueKind.Null) + { + throw new global::System.ArgumentNullException(); + } + + return _booleanParser.Parse(obj.Value.GetBoolean()!); + } + + private global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.PageInfoData? Deserialize_IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_SkippedPageInfo(global::System.Text.Json.JsonElement? obj) + { + if (!obj.HasValue) + { + return null; + } + + if (obj.Value.ValueKind == global::System.Text.Json.JsonValueKind.Null) + { + return null; + } + + var typename = obj.Value.GetProperty("__typename").GetString(); + if (typename?.Equals("PageInfo", global::System.StringComparison.Ordinal) ?? false) + { + return new global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.PageInfoData(typename, hasNextPage: Deserialize_NonNullableBoolean(global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "hasNextPage"))); + } + + throw new global::System.NotSupportedException(); + } + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.DataTypeGenerator + ///A connection to a list of items. + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "0.0.0.0")] + public partial class FriendsConnectionData + { + public FriendsConnectionData(global::System.String __typename, global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.PageInfoData? includedPageInfo = default !, global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.PageInfoData? skippedPageInfo = default !) + { + this.__typename = __typename ?? throw new global::System.ArgumentNullException(nameof(__typename)); + IncludedPageInfo = includedPageInfo; + SkippedPageInfo = skippedPageInfo; + } + + public global::System.String __typename { get; } + ///Information to aid in pagination. + public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.PageInfoData? IncludedPageInfo { get; } + ///Information to aid in pagination. + public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.PageInfoData? SkippedPageInfo { get; } + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.DataTypeGenerator + ///Information about pagination in a connection. + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "0.0.0.0")] + public partial class PageInfoData + { + public PageInfoData(global::System.String __typename, global::System.Boolean? hasNextPage = default !) + { + this.__typename = __typename ?? throw new global::System.ArgumentNullException(nameof(__typename)); + HasNextPage = hasNextPage; + } + + public global::System.String __typename { get; } + ///Indicates whether more edges exist following the set defined by the clients arguments. + public global::System.Boolean? HasNextPage { get; } + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.ResultFromEntityTypeMapperGenerator + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class GetHeroWithFragmentIncludeAndSkipDirective_Hero_DroidFromDroidEntityMapper : global::StrawberryShake.IEntityMapper + { + private readonly global::StrawberryShake.IEntityStore _entityStore; + public GetHeroWithFragmentIncludeAndSkipDirective_Hero_DroidFromDroidEntityMapper(global::StrawberryShake.IEntityStore entityStore) + { + _entityStore = entityStore ?? throw new global::System.ArgumentNullException(nameof(entityStore)); + } + + public GetHeroWithFragmentIncludeAndSkipDirective_Hero_Droid Map(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.DroidEntity entity, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) + { + if (snapshot is null) + { + snapshot = _entityStore.CurrentSnapshot; + } + + return new GetHeroWithFragmentIncludeAndSkipDirective_Hero_Droid(entity.Id, MapIGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends(entity.Friends, snapshot)); + } + + private global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends? MapIGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.FriendsConnectionData? data, global::StrawberryShake.IEntityStoreSnapshot snapshot) + { + if (data is null) + { + return null; + } + + IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends returnValue = default !; + if (data?.__typename.Equals("FriendsConnection", global::System.StringComparison.Ordinal) ?? false) + { + returnValue = new GetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_FriendsConnection(MapIGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_IncludedPageInfo(data.IncludedPageInfo, snapshot), MapIGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_SkippedPageInfo(data.SkippedPageInfo, snapshot)); + } + else + { + throw new global::System.NotSupportedException(); + } + + return returnValue; + } + + private global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_IncludedPageInfo? MapIGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_IncludedPageInfo(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.PageInfoData? data, global::StrawberryShake.IEntityStoreSnapshot snapshot) + { + if (data is null) + { + return null; + } + + IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_IncludedPageInfo returnValue = default !; + if (data?.__typename.Equals("PageInfo", global::System.StringComparison.Ordinal) ?? false) + { + returnValue = new GetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_IncludedPageInfo_PageInfo(data.HasNextPage ?? throw new global::System.ArgumentNullException()); + } + else + { + throw new global::System.NotSupportedException(); + } + + return returnValue; + } + + private global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_SkippedPageInfo? MapIGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_SkippedPageInfo(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.PageInfoData? data, global::StrawberryShake.IEntityStoreSnapshot snapshot) + { + if (data is null) + { + return null; + } + + IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_SkippedPageInfo returnValue = default !; + if (data?.__typename.Equals("PageInfo", global::System.StringComparison.Ordinal) ?? false) + { + returnValue = new GetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_SkippedPageInfo_PageInfo(data.HasNextPage ?? throw new global::System.ArgumentNullException()); + } + else + { + throw new global::System.NotSupportedException(); + } + + return returnValue; + } + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.ResultFromEntityTypeMapperGenerator + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class GetHeroWithFragmentIncludeAndSkipDirective_Hero_HumanFromHumanEntityMapper : global::StrawberryShake.IEntityMapper + { + private readonly global::StrawberryShake.IEntityStore _entityStore; + public GetHeroWithFragmentIncludeAndSkipDirective_Hero_HumanFromHumanEntityMapper(global::StrawberryShake.IEntityStore entityStore) + { + _entityStore = entityStore ?? throw new global::System.ArgumentNullException(nameof(entityStore)); + } + + public GetHeroWithFragmentIncludeAndSkipDirective_Hero_Human Map(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.HumanEntity entity, global::StrawberryShake.IEntityStoreSnapshot? snapshot = null) + { + if (snapshot is null) + { + snapshot = _entityStore.CurrentSnapshot; + } + + return new GetHeroWithFragmentIncludeAndSkipDirective_Hero_Human(entity.Id, MapIGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends(entity.Friends, snapshot)); + } + + private global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends? MapIGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.FriendsConnectionData? data, global::StrawberryShake.IEntityStoreSnapshot snapshot) + { + if (data is null) + { + return null; + } + + IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends returnValue = default !; + if (data?.__typename.Equals("FriendsConnection", global::System.StringComparison.Ordinal) ?? false) + { + returnValue = new GetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_FriendsConnection(MapIGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_IncludedPageInfo(data.IncludedPageInfo, snapshot), MapIGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_SkippedPageInfo(data.SkippedPageInfo, snapshot)); + } + else + { + throw new global::System.NotSupportedException(); + } + + return returnValue; + } + + private global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_IncludedPageInfo? MapIGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_IncludedPageInfo(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.PageInfoData? data, global::StrawberryShake.IEntityStoreSnapshot snapshot) + { + if (data is null) + { + return null; + } + + IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_IncludedPageInfo returnValue = default !; + if (data?.__typename.Equals("PageInfo", global::System.StringComparison.Ordinal) ?? false) + { + returnValue = new GetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_IncludedPageInfo_PageInfo(data.HasNextPage ?? throw new global::System.ArgumentNullException()); + } + else + { + throw new global::System.NotSupportedException(); + } + + return returnValue; + } + + private global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_SkippedPageInfo? MapIGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_SkippedPageInfo(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective.State.PageInfoData? data, global::StrawberryShake.IEntityStoreSnapshot snapshot) + { + if (data is null) + { + return null; + } + + IGetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_SkippedPageInfo returnValue = default !; + if (data?.__typename.Equals("PageInfo", global::System.StringComparison.Ordinal) ?? false) + { + returnValue = new GetHeroWithFragmentIncludeAndSkipDirective_Hero_Friends_SkippedPageInfo_PageInfo(data.HasNextPage ?? throw new global::System.ArgumentNullException()); + } + else + { + throw new global::System.NotSupportedException(); + } + + return returnValue; + } + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.EntityIdFactoryGenerator + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveClientEntityIdFactory : global::StrawberryShake.IEntityIdSerializer + { + private static readonly global::System.Text.Json.JsonWriterOptions _options = new global::System.Text.Json.JsonWriterOptions() + { + Indented = false + }; + public global::StrawberryShake.EntityId Parse(global::System.Text.Json.JsonElement obj) + { + global::System.String __typename = obj.GetProperty("__typename").GetString()!; + return __typename switch + { + "Droid" => ParseDroidEntityId(obj, __typename), + "Human" => ParseHumanEntityId(obj, __typename), + _ => throw new global::System.NotSupportedException()}; + } + + public global::System.String Format(global::StrawberryShake.EntityId entityId) + { + return entityId.Name switch + { + "Droid" => FormatDroidEntityId(entityId), + "Human" => FormatHumanEntityId(entityId), + _ => throw new global::System.NotSupportedException()}; + } + + private global::StrawberryShake.EntityId ParseDroidEntityId(global::System.Text.Json.JsonElement obj, global::System.String type) + { + return new global::StrawberryShake.EntityId(type, obj.GetProperty("id").GetString()!); + } + + private global::System.String FormatDroidEntityId(global::StrawberryShake.EntityId entityId) + { + using var writer = new global::StrawberryShake.Internal.ArrayWriter(); + using var jsonWriter = new global::System.Text.Json.Utf8JsonWriter(writer, _options); + jsonWriter.WriteStartObject(); + jsonWriter.WriteString("__typename", entityId.Name); + jsonWriter.WriteString("id", (global::System.String)entityId.Value); + jsonWriter.WriteEndObject(); + jsonWriter.Flush(); + return global::System.Text.Encoding.UTF8.GetString(writer.GetInternalBuffer(), 0, writer.Length); + } + + private global::StrawberryShake.EntityId ParseHumanEntityId(global::System.Text.Json.JsonElement obj, global::System.String type) + { + return new global::StrawberryShake.EntityId(type, obj.GetProperty("id").GetString()!); + } + + private global::System.String FormatHumanEntityId(global::StrawberryShake.EntityId entityId) + { + using var writer = new global::StrawberryShake.Internal.ArrayWriter(); + using var jsonWriter = new global::System.Text.Json.Utf8JsonWriter(writer, _options); + jsonWriter.WriteStartObject(); + jsonWriter.WriteString("__typename", entityId.Name); + jsonWriter.WriteString("id", (global::System.String)entityId.Value); + jsonWriter.WriteEndObject(); + jsonWriter.Flush(); + return global::System.Text.Encoding.UTF8.GetString(writer.GetInternalBuffer(), 0, writer.Length); + } + } + + // StrawberryShake.CodeGeneration.CSharp.Generators.StoreAccessorGenerator + [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] + public partial class StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveClientStoreAccessor : global::StrawberryShake.StoreAccessor + { + public StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveClientStoreAccessor(global::StrawberryShake.IOperationStore operationStore, global::StrawberryShake.IEntityStore entityStore, global::StrawberryShake.IEntityIdSerializer entityIdSerializer, global::System.Collections.Generic.IEnumerable requestFactories, global::System.Collections.Generic.IEnumerable resultDataFactories) : base(operationStore, entityStore, entityIdSerializer, requestFactories, resultDataFactories) + { + } + } +} + + diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveTest.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveTest.cs new file mode 100644 index 00000000000..34c94242b79 --- /dev/null +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveTest.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using HotChocolate.AspNetCore.Tests.Utilities; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using StrawberryShake.Transport.WebSockets; +using Xunit; + +namespace StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetHeroWithFragmentIncludeAndSkipDirective; + +public class StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveTest : ServerTestBase +{ + public StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveTest(TestServerFactory serverFactory) : base(serverFactory) + { + } + + [Fact] + public async Task Execute_StarWarsGetHeroWithFragmentIncludeAndSkipDirective_Test() + { + // arrange + CancellationToken ct = new CancellationTokenSource(20_000).Token; + using IWebHost host = TestServerHelper.CreateServer( + _ => { }, + out var port); + var serviceCollection = new ServiceCollection(); + serviceCollection.AddHttpClient( + StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveClient.ClientName, + c => c.BaseAddress = new Uri("http://localhost:" + port + "/graphql")); + serviceCollection.AddWebSocketClient( + StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveClient.ClientName, + c => c.Uri = new Uri("ws://localhost:" + port + "/graphql")); + serviceCollection.AddStarWarsGetHeroWithFragmentIncludeAndSkipDirectiveClient(); + IServiceProvider services = serviceCollection.BuildServiceProvider(); + StarWarsGetHeroWithFragmentIncludeAndSkipDirectiveClient client = services.GetRequiredService(); + + // act + var result = await client.GetHeroWithFragmentIncludeAndSkipDirective.ExecuteAsync(false, true, ct); + + // assert + result.EnsureNoErrors(); + Assert.Null(result.Data!.Hero!.Friends!.IncludedPageInfo); + Assert.Null(result.Data!.Hero!.Friends!.SkippedPageInfo); + } +} diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/TestGeneration.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/TestGeneration.cs index 1a547a4b613..6a79609fb6a 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/TestGeneration.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/TestGeneration.cs @@ -16,6 +16,37 @@ public void StarWarsGetHero() => } }"); + + [Fact] + public void StarWarsGetHeroWithFragmentIncludeAndSkipDirective() => + AssertStarWarsResult( + CreateIntegrationTest(), + @"query GetHeroWithFragmentIncludeAndSkipDirective($includePageInfo: Boolean = false, $skipPageInfo: Boolean = true) { + hero(episode: NEW_HOPE) { + ...HeroFragment + } + } + + fragment HeroFragment on Character { + id + friends { + ...FriendsFragment + } + } + + fragment FriendsFragment on FriendsConnection { + includedPageInfo: pageInfo @include(if: $includePageInfo) { + ...PageInfoFragment + } + skippedPageInfo: pageInfo @skip(if: $skipPageInfo) { + ...PageInfoFragment + } + } + + fragment PageInfoFragment on PageInfo { + hasNextPage + }"); + [Fact] public void StarWarsGetFriendsNoStore() => AssertStarWarsResult( From 4eb11c352aa4aea6658029854bd0b536d56ee93c Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 22 Jan 2025 13:24:35 +0100 Subject: [PATCH 144/154] Reworked the invalid GraphQL name error message. (#7939) --- ...Should_Fail_When_OperationsIsNotNamed.snap | 4 ++- .../Primitives/HotChocolate.Primitives.sln | 9 +++++ .../Primitives/src/Primitives/NameUtils.cs | 2 +- .../PrimitivesResources.Designer.cs | 36 +++++++++++-------- .../Properties/PrimitivesResources.resx | 11 ++++-- .../Primitives/test/Directory.Build.props | 4 +-- .../HotChocolate.Primitives.Tests.csproj | 12 +++++++ .../test/Primitives.Tests/NameUtilsTests.cs | 36 +++++++++++++++++++ 8 files changed, 92 insertions(+), 22 deletions(-) create mode 100644 src/HotChocolate/Primitives/test/Primitives.Tests/HotChocolate.Primitives.Tests.csproj create mode 100644 src/HotChocolate/Primitives/test/Primitives.Tests/NameUtilsTests.cs diff --git a/src/HotChocolate/Data/test/Data.Sorting.Tests/Convention/__snapshots__/SortConventionTests.SortConvention_Should_Fail_When_OperationsIsNotNamed.snap b/src/HotChocolate/Data/test/Data.Sorting.Tests/Convention/__snapshots__/SortConventionTests.SortConvention_Should_Fail_When_OperationsIsNotNamed.snap index 4c4671578bd..06ea3df96a4 100644 --- a/src/HotChocolate/Data/test/Data.Sorting.Tests/Convention/__snapshots__/SortConventionTests.SortConvention_Should_Fail_When_OperationsIsNotNamed.snap +++ b/src/HotChocolate/Data/test/Data.Sorting.Tests/Convention/__snapshots__/SortConventionTests.SortConvention_Should_Fail_When_OperationsIsNotNamed.snap @@ -1,3 +1,5 @@ For more details look at the `Errors` property. -1. The specified name is not a valid GraphQL name. (Parameter 'definition.Name') (HotChocolate.Data.Sorting.SortConventionTests.FooSortType) +1. `` is not a valid GraphQL name. +https://spec.graphql.org/October2021/#sec-Names +(Parameter 'definition.Name') (HotChocolate.Data.Sorting.SortConventionTests.FooSortType) diff --git a/src/HotChocolate/Primitives/HotChocolate.Primitives.sln b/src/HotChocolate/Primitives/HotChocolate.Primitives.sln index e2d1b34f923..8350cdae72b 100644 --- a/src/HotChocolate/Primitives/HotChocolate.Primitives.sln +++ b/src/HotChocolate/Primitives/HotChocolate.Primitives.sln @@ -7,6 +7,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{42689032-400 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Primitives", "src\Primitives\HotChocolate.Primitives.csproj", "{F5CEC44C-8AB9-4639-9B38-4F523E3ABF2A}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{B598E71D-74BD-4055-A2F7-10B7DB892ECB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Primitives.Tests", "test\Primitives.Tests\HotChocolate.Primitives.Tests.csproj", "{F7643027-2CEF-4F06-8DFA-46FFA8C17C4F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -20,8 +24,13 @@ Global {F5CEC44C-8AB9-4639-9B38-4F523E3ABF2A}.Debug|Any CPU.Build.0 = Debug|Any CPU {F5CEC44C-8AB9-4639-9B38-4F523E3ABF2A}.Release|Any CPU.ActiveCfg = Release|Any CPU {F5CEC44C-8AB9-4639-9B38-4F523E3ABF2A}.Release|Any CPU.Build.0 = Release|Any CPU + {F7643027-2CEF-4F06-8DFA-46FFA8C17C4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F7643027-2CEF-4F06-8DFA-46FFA8C17C4F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F7643027-2CEF-4F06-8DFA-46FFA8C17C4F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F7643027-2CEF-4F06-8DFA-46FFA8C17C4F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {F5CEC44C-8AB9-4639-9B38-4F523E3ABF2A} = {42689032-4007-47A4-B045-12D2681BF2CF} + {F7643027-2CEF-4F06-8DFA-46FFA8C17C4F} = {B598E71D-74BD-4055-A2F7-10B7DB892ECB} EndGlobalSection EndGlobal diff --git a/src/HotChocolate/Primitives/src/Primitives/NameUtils.cs b/src/HotChocolate/Primitives/src/Primitives/NameUtils.cs index 8a4b1cf497b..c379d0a0871 100644 --- a/src/HotChocolate/Primitives/src/Primitives/NameUtils.cs +++ b/src/HotChocolate/Primitives/src/Primitives/NameUtils.cs @@ -34,7 +34,7 @@ public static string EnsureGraphQLName( return name!; } - throw new ArgumentException(NameUtils_InvalidGraphQLName, argumentName); + throw new ArgumentException(string.Format(NameUtils_EnsureGraphQLName_InvalidName, name), argumentName); } /// diff --git a/src/HotChocolate/Primitives/src/Primitives/Properties/PrimitivesResources.Designer.cs b/src/HotChocolate/Primitives/src/Primitives/Properties/PrimitivesResources.Designer.cs index 2a23b4c0adb..2f5b11fb84d 100644 --- a/src/HotChocolate/Primitives/src/Primitives/Properties/PrimitivesResources.Designer.cs +++ b/src/HotChocolate/Primitives/src/Primitives/Properties/PrimitivesResources.Designer.cs @@ -9,21 +9,21 @@ namespace HotChocolate.Properties { using System; - - + + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [System.Diagnostics.DebuggerNonUserCodeAttribute()] [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class PrimitivesResources { - + private static System.Resources.ResourceManager resourceMan; - + private static System.Globalization.CultureInfo resourceCulture; - + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal PrimitivesResources() { } - + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] internal static System.Resources.ResourceManager ResourceManager { get { @@ -34,7 +34,7 @@ internal static System.Resources.ResourceManager ResourceManager { return resourceMan; } } - + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] internal static System.Globalization.CultureInfo Culture { get { @@ -44,23 +44,29 @@ internal static System.Globalization.CultureInfo Culture { resourceCulture = value; } } - - internal static string NameUtils_InvalidGraphQLName { - get { - return ResourceManager.GetString("NameUtils_InvalidGraphQLName", resourceCulture); - } - } - + internal static string ThrowHelper_SchemaCoordinate_ArgumentNameCannotBeSetWithoutMemberName { get { return ResourceManager.GetString("ThrowHelper_SchemaCoordinate_ArgumentNameCannotBeSetWithoutMemberName", resourceCulture); } } - + internal static string ThrowHelper_SchemaCoordinate_MemberNameCannotBeSetOnADirectiveCoordinate { get { return ResourceManager.GetString("ThrowHelper_SchemaCoordinate_MemberNameCannotBeSetOnADirectiveCoordinate", resourceCulture); } } + + internal static string DirectiveTypeFactory_LocationNotSupported { + get { + return ResourceManager.GetString("DirectiveTypeFactory_LocationNotSupported", resourceCulture); + } + } + + internal static string NameUtils_EnsureGraphQLName_InvalidName { + get { + return ResourceManager.GetString("NameUtils_EnsureGraphQLName_InvalidName", resourceCulture); + } + } } } diff --git a/src/HotChocolate/Primitives/src/Primitives/Properties/PrimitivesResources.resx b/src/HotChocolate/Primitives/src/Primitives/Properties/PrimitivesResources.resx index 0463132731e..6056adcc97f 100644 --- a/src/HotChocolate/Primitives/src/Primitives/Properties/PrimitivesResources.resx +++ b/src/HotChocolate/Primitives/src/Primitives/Properties/PrimitivesResources.resx @@ -18,13 +18,18 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - The specified name is not a valid GraphQL name. - A argument name without a member name is only allowed on directive coordinates. A directive cannot contain a member name. + + The specified location `{0}` is not supported. + + + `{0}` is not a valid GraphQL name. +https://spec.graphql.org/October2021/#sec-Names + + diff --git a/src/HotChocolate/Primitives/test/Directory.Build.props b/src/HotChocolate/Primitives/test/Directory.Build.props index 17fcb6c07b9..8be6d5b6c1e 100644 --- a/src/HotChocolate/Primitives/test/Directory.Build.props +++ b/src/HotChocolate/Primitives/test/Directory.Build.props @@ -2,7 +2,6 @@ - $(TestTargetFrameworks) false false @@ -12,7 +11,8 @@ - + + diff --git a/src/HotChocolate/Primitives/test/Primitives.Tests/HotChocolate.Primitives.Tests.csproj b/src/HotChocolate/Primitives/test/Primitives.Tests/HotChocolate.Primitives.Tests.csproj new file mode 100644 index 00000000000..253e466d1b5 --- /dev/null +++ b/src/HotChocolate/Primitives/test/Primitives.Tests/HotChocolate.Primitives.Tests.csproj @@ -0,0 +1,12 @@ + + + + HotChocolate.Primitives.Tests + HotChocolate.Primitives + + + + + + + diff --git a/src/HotChocolate/Primitives/test/Primitives.Tests/NameUtilsTests.cs b/src/HotChocolate/Primitives/test/Primitives.Tests/NameUtilsTests.cs new file mode 100644 index 00000000000..7a27503f41d --- /dev/null +++ b/src/HotChocolate/Primitives/test/Primitives.Tests/NameUtilsTests.cs @@ -0,0 +1,36 @@ +using HotChocolate.Utilities; + +namespace HotChocolate.Primitives; + +public class NameUtilsTests +{ + [Theory] + [InlineData("1Bar")] + [InlineData("$Bar")] + [InlineData("1_Bar")] + [InlineData("B/ar")] + [InlineData("B+ar")] + public void InvalidName(string name) + { + var message = Assert.Throws(() => name.EnsureGraphQLName()).Message; + Assert.Equal( + $"`{name}` is not a valid GraphQL name.{Environment.NewLine}" + + $"https://spec.graphql.org/October2021/#sec-Names{Environment.NewLine}" + + $" (Parameter 'name')", + message); + } + + [Theory] + [InlineData("_1Bar")] + [InlineData("Bar")] + [InlineData("_Bar")] + [InlineData("Bar123")] + [InlineData("Bar_123")] + [InlineData("ABCDEFGHIJKLMNOPQRSTUVWXYZ")] + [InlineData("abcdefghijklmnopqrstuvwxyz")] + [InlineData("_1234567890")] + public void ValidName(string name) + { + name.EnsureGraphQLName(); + } +} From 2d9d70ec0ccf54d640a3dd9a0c760254a030dd3f Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 22 Jan 2025 15:31:50 +0100 Subject: [PATCH 145/154] Do not inline total count by default into paging queries. (#7942) --- .../src/Abstractions/IQueryableExecutable.cs | 1 - ...orPagingRequestExecutorBuilderExtension.cs | 9 ++- .../QueryableCursorPagingHandler.cs | 54 ++++++++++++----- .../QueryableCursorPagingProvider.cs | 21 +++++-- .../SqlLiteCursorTestBase.cs | 23 +++----- .../UseDbContextTests.cs | 59 ++++++++++++++----- .../FilterVisitorTestBase.cs | 21 ++++--- .../FilteringAndPaging.cs | 23 +++++++- .../QueryableFilterVisitorInterfacesTests.cs | 4 +- .../Data.Marten.Filters.Tests/SchemaCache.cs | 3 +- ...eringAndPaging.Paging_With_TotalCount.snap | 12 ++++ 11 files changed, 162 insertions(+), 68 deletions(-) create mode 100644 src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/__snapshots__/FilteringAndPaging.Paging_With_TotalCount.snap diff --git a/src/HotChocolate/Core/src/Abstractions/IQueryableExecutable.cs b/src/HotChocolate/Core/src/Abstractions/IQueryableExecutable.cs index 2dc7024e908..850a06bfb18 100644 --- a/src/HotChocolate/Core/src/Abstractions/IQueryableExecutable.cs +++ b/src/HotChocolate/Core/src/Abstractions/IQueryableExecutable.cs @@ -23,7 +23,6 @@ public interface IQueryableExecutable : IExecutable /// The new instance of an enumerable executable IQueryableExecutable WithSource(IQueryable source); - /// /// Returns a new executable with the provided source /// diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/Extensions/CursorPagingRequestExecutorBuilderExtension.cs b/src/HotChocolate/Core/src/Types.CursorPagination/Extensions/CursorPagingRequestExecutorBuilderExtension.cs index c6d8d1c6b5a..4f26dae8120 100644 --- a/src/HotChocolate/Core/src/Types.CursorPagination/Extensions/CursorPagingRequestExecutorBuilderExtension.cs +++ b/src/HotChocolate/Core/src/Types.CursorPagination/Extensions/CursorPagingRequestExecutorBuilderExtension.cs @@ -22,6 +22,11 @@ public static class CursorPagingRequestExecutorBuilderExtension /// /// Defines if the registered provider shall be registered as the default provider. /// + /// + /// Specifies that the paging provider shall inline the total count query into the + /// sliced query in order to have a single database call. + /// Some database providers might not support this feature. + /// /// /// The request executor builder. /// @@ -31,9 +36,11 @@ public static class CursorPagingRequestExecutorBuilderExtension public static IRequestExecutorBuilder AddQueryableCursorPagingProvider( this IRequestExecutorBuilder builder, string? providerName = null, - bool defaultProvider = false) + bool defaultProvider = false, + bool? inlineTotalCount = null) => AddCursorPagingProvider( builder, + _ => new QueryableCursorPagingProvider(inlineTotalCount), providerName, defaultProvider); diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/QueryableCursorPagingHandler.cs b/src/HotChocolate/Core/src/Types.CursorPagination/QueryableCursorPagingHandler.cs index f0404a05908..af20a90886a 100644 --- a/src/HotChocolate/Core/src/Types.CursorPagination/QueryableCursorPagingHandler.cs +++ b/src/HotChocolate/Core/src/Types.CursorPagination/QueryableCursorPagingHandler.cs @@ -3,9 +3,19 @@ namespace HotChocolate.Types.Pagination; -internal sealed class QueryableCursorPagingHandler(PagingOptions options) - : CursorPagingHandler, TEntity>(options) +internal sealed class QueryableCursorPagingHandler : CursorPagingHandler, TEntity> { + private readonly bool? _inlineTotalCount; + + public QueryableCursorPagingHandler(PagingOptions options) : this(options, false) + { + } + + public QueryableCursorPagingHandler(PagingOptions options, bool? inlineTotalCount) : base(options) + { + _inlineTotalCount = inlineTotalCount; + } + private static readonly QueryableCursorPaginationAlgorithm _paginationAlgorithm = QueryableCursorPaginationAlgorithm.Instance; @@ -18,7 +28,7 @@ public ValueTask> SliceAsync( source.Source, arguments, _paginationAlgorithm, - new QueryExecutor(source), + new QueryExecutor(source, _inlineTotalCount), context.RequestAborted); protected override ValueTask SliceAsync( @@ -42,11 +52,11 @@ private async ValueTask SliceAsyncInternal( source.Source, arguments, _paginationAlgorithm, - new QueryExecutor(source), + new QueryExecutor(source, _inlineTotalCount), context.RequestAborted) .ConfigureAwait(false); - private sealed class QueryExecutor(IQueryableExecutable executable) + private sealed class QueryExecutor(IQueryableExecutable executable, bool? allowInlining) : ICursorPaginationQueryExecutor, TEntity> { public ValueTask CountAsync( @@ -66,17 +76,33 @@ public async ValueTask> QueryAsync( if (includeTotalCount) { - var combinedQuery = slicedQuery.Select(t => new { TotalCount = originalQuery.Count(), Item = t }); - totalCount = 0; + if (allowInlining ?? false) + { + var combinedQuery = slicedQuery.Select(t => new { TotalCount = originalQuery.Count(), Item = t }); + totalCount = 0; - var index = offset; - await foreach (var item in executable - .WithSource(combinedQuery) - .ToAsyncEnumerable(cancellationToken) - .ConfigureAwait(false)) + var index = offset; + await foreach (var item in executable + .WithSource(combinedQuery) + .ToAsyncEnumerable(cancellationToken) + .ConfigureAwait(false)) + { + edges.Add(IndexEdge.Create(item.Item, index++)); + totalCount = item.TotalCount; + } + } + else { - edges.Add(IndexEdge.Create(item.Item, index++)); - totalCount = item.TotalCount; + var index = offset; + await foreach (var item in executable + .WithSource(slicedQuery) + .ToAsyncEnumerable(cancellationToken) + .ConfigureAwait(false)) + { + edges.Add(IndexEdge.Create(item, index++)); + } + + totalCount = await executable.CountAsync(cancellationToken).ConfigureAwait(false); } } else diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/QueryableCursorPagingProvider.cs b/src/HotChocolate/Core/src/Types.CursorPagination/QueryableCursorPagingProvider.cs index 13d3df76bfa..ff9062968c2 100644 --- a/src/HotChocolate/Core/src/Types.CursorPagination/QueryableCursorPagingProvider.cs +++ b/src/HotChocolate/Core/src/Types.CursorPagination/QueryableCursorPagingProvider.cs @@ -1,3 +1,4 @@ +using System.Collections.Concurrent; using System.Reflection; using HotChocolate.Internal; @@ -8,11 +9,21 @@ namespace HotChocolate.Types.Pagination; /// public class QueryableCursorPagingProvider : CursorPagingProvider { + private static readonly ConcurrentDictionary _factoryCache = new(); + private readonly bool? _inlineTotalCount; + private static readonly MethodInfo _createHandler = typeof(QueryableCursorPagingProvider).GetMethod( nameof(CreateHandlerInternal), BindingFlags.Static | BindingFlags.NonPublic)!; + public QueryableCursorPagingProvider() { } + + public QueryableCursorPagingProvider(bool? inlineTotalCount) + { + _inlineTotalCount = inlineTotalCount; + } + /// public override bool CanHandle(IExtendedType source) { @@ -34,12 +45,12 @@ protected override CursorPagingHandler CreateHandler( throw new ArgumentNullException(nameof(source)); } - return (CursorPagingHandler)_createHandler - .MakeGenericMethod(source.ElementType?.Source ?? source.Source) - .Invoke(null, [options,])!; + var key = source.ElementType?.Source ?? source.Source; + var factory = _factoryCache.GetOrAdd(key, static type => _createHandler.MakeGenericMethod(type)); + return (CursorPagingHandler)factory.Invoke(null, [options, _inlineTotalCount])!; } private static QueryableCursorPagingHandler CreateHandlerInternal( - PagingOptions options) => - new(options); + PagingOptions options, bool? inlineTotalCount) + => new(options, inlineTotalCount); } diff --git a/src/HotChocolate/Data/test/Data.EntityFramework.Tests/SqlLiteCursorTestBase.cs b/src/HotChocolate/Data/test/Data.EntityFramework.Tests/SqlLiteCursorTestBase.cs index 9acb942dc1b..1155ea5512f 100644 --- a/src/HotChocolate/Data/test/Data.EntityFramework.Tests/SqlLiteCursorTestBase.cs +++ b/src/HotChocolate/Data/test/Data.EntityFramework.Tests/SqlLiteCursorTestBase.cs @@ -32,7 +32,10 @@ private DatabaseContext BuildContext( protected IRequestExecutor CreateSchema(TEntity[] entities) where TEntity : class { - var builder = SchemaBuilder.New() + return new ServiceCollection() + .AddDbContextPool>( + b => b.UseSqlite($"Data Source={Guid.NewGuid():N}.db")) + .AddGraphQL() .AddQueryType( c => { @@ -64,10 +67,7 @@ protected IRequestExecutor CreateSchema(TEntity[] entities) } }) .UsePaging>( - options: new() - { - IncludeTotalCount = true, - }); + options: new() { IncludeTotalCount = true, }); c.Field("root1") .Resolve( @@ -94,17 +94,8 @@ protected IRequestExecutor CreateSchema(TEntity[] entities) } } }); - }); - - var schema = builder.Create(); - - return new ServiceCollection() - .Configure( - Schema.DefaultName, - o => o.Schema = schema) - .AddDbContextPool>( - b => b.UseSqlite($"Data Source={Guid.NewGuid():N}.db")) - .AddGraphQL() + }) + .AddQueryableCursorPagingProvider(inlineTotalCount: true) .UseDefaultPipeline() .Services .BuildServiceProvider() diff --git a/src/HotChocolate/Data/test/Data.EntityFramework.Tests/UseDbContextTests.cs b/src/HotChocolate/Data/test/Data.EntityFramework.Tests/UseDbContextTests.cs index afb204ecf8f..b24a2fae0a4 100644 --- a/src/HotChocolate/Data/test/Data.EntityFramework.Tests/UseDbContextTests.cs +++ b/src/HotChocolate/Data/test/Data.EntityFramework.Tests/UseDbContextTests.cs @@ -20,6 +20,7 @@ public async Task Execute_Queryable() .AddSorting() .AddProjections() .AddQueryType() + .AddQueryableCursorPagingProvider(inlineTotalCount: true) .Services .BuildServiceProvider(); @@ -52,6 +53,7 @@ public async Task Execute_Queryable_Task() .AddSorting() .AddProjections() .AddQueryType() + .AddQueryableCursorPagingProvider(inlineTotalCount: true) .Services .BuildServiceProvider(); @@ -86,6 +88,7 @@ public async Task Execute_Queryable_ValueTask() .AddSorting() .AddProjections() .AddQueryType() + .AddQueryableCursorPagingProvider(inlineTotalCount: true) .Services .BuildServiceProvider(); @@ -120,6 +123,7 @@ public async Task Execute_Queryable_OffsetPaging_TotalCount() .AddSorting() .AddProjections() .AddQueryType() + .AddQueryableCursorPagingProvider(inlineTotalCount: true) .Services .BuildServiceProvider(); @@ -137,18 +141,20 @@ await services.GetRequiredService() // act var result = await executor.ExecuteAsync( - @"query Test { - authorOffsetPaging { - items { - name - } - pageInfo { - hasNextPage - hasPreviousPage - } - totalCount + """ + query Test { + authorOffsetPaging { + items { + name } - }"); + pageInfo { + hasNextPage + hasPreviousPage + } + totalCount + } + } + """); // assert result.MatchSnapshot(); @@ -167,6 +173,7 @@ public async Task Execute_Queryable_OffsetPaging_TotalCount_Task() .AddSorting() .AddProjections() .AddQueryType() + .AddQueryableCursorPagingProvider(inlineTotalCount: true) .Services .BuildServiceProvider(); @@ -214,6 +221,7 @@ public async Task Execute_Queryable_OffsetPaging_TotalCount_QueryableExtensions( .AddSorting() .AddProjections() .AddQueryType() + .AddQueryableCursorPagingProvider(inlineTotalCount: true) .Services .BuildServiceProvider(); @@ -261,6 +269,7 @@ public async Task Execute_Queryable_OffsetPaging_TotalCount_ValueTask() .AddSorting() .AddProjections() .AddQueryType() + .AddQueryableCursorPagingProvider(inlineTotalCount: true) .Services .BuildServiceProvider(); @@ -310,6 +319,7 @@ public async Task Execute_Queryable_OffsetPaging() .AddSorting() .AddProjections() .AddQueryType() + .AddQueryableCursorPagingProvider(inlineTotalCount: true) .Services .BuildServiceProvider(); @@ -356,6 +366,7 @@ public async Task Execute_Queryable_OffsetPaging_Task() .AddSorting() .AddProjections() .AddQueryType() + .AddQueryableCursorPagingProvider(inlineTotalCount: true) .Services .BuildServiceProvider(); @@ -402,6 +413,7 @@ public async Task Execute_Queryable_OffsetPaging_ValueTask() .AddSorting() .AddProjections() .AddQueryType() + .AddQueryableCursorPagingProvider(inlineTotalCount: true) .Services .BuildServiceProvider(); @@ -450,6 +462,7 @@ public async Task Execute_Single() .AddSorting() .AddProjections() .AddQueryType() + .AddQueryableCursorPagingProvider(inlineTotalCount: true) .Services .BuildServiceProvider(); @@ -485,6 +498,7 @@ public async Task Infer_Schema_From_IQueryable_Fields() .AddSorting() .AddProjections() .AddQueryType() + .AddQueryableCursorPagingProvider(inlineTotalCount: true) .BuildSchemaAsync(); // assert @@ -505,6 +519,7 @@ public async Task Infer_Schema_From_IQueryable_Task_Fields() .AddSorting() .AddProjections() .AddQueryType() + .AddQueryableCursorPagingProvider(inlineTotalCount: true) .BuildSchemaAsync(); // assert @@ -525,6 +540,7 @@ public async Task Infer_Schema_From_IQueryable_ValueTask_Fields() .AddSorting() .AddProjections() .AddQueryType() + .AddQueryableCursorPagingProvider(inlineTotalCount: true) .BuildSchemaAsync(); // assert @@ -541,15 +557,18 @@ public async Task DbContext_ResolverExtension() b => b.UseInMemoryDatabase(CreateConnectionString())) .AddGraphQL() .AddQueryType() + .AddQueryableCursorPagingProvider(inlineTotalCount: true) .BuildRequestExecutorAsync(); // act var result = await executor.ExecuteAsync( - @"query Test { - books { - id - } - }"); + """ + query Test { + books { + id + } + } + """); // assert result.MatchSnapshot(); @@ -568,6 +587,8 @@ public async Task Execute_Queryable_CursorPaging_TotalCount() .AddSorting() .AddProjections() .AddQueryType() + .AddQueryableCursorPagingProvider(inlineTotalCount: true) + .AddQueryableCursorPagingProvider(inlineTotalCount: true) .Services .BuildServiceProvider(); @@ -615,6 +636,7 @@ public async Task Execute_Queryable_CursorPaging_TotalCount_Task() .AddSorting() .AddProjections() .AddQueryType() + .AddQueryableCursorPagingProvider(inlineTotalCount: true) .Services .BuildServiceProvider(); @@ -662,6 +684,7 @@ public async Task Execute_Queryable_CursorPaging_TotalCount_QueryableExtensions( .AddSorting() .AddProjections() .AddQueryType() + .AddQueryableCursorPagingProvider(inlineTotalCount: true) .Services .BuildServiceProvider(); @@ -711,6 +734,7 @@ public async Task Execute_Queryable_CursorPaging_TotalCount_ValueTask() .AddSorting() .AddProjections() .AddQueryType() + .AddQueryableCursorPagingProvider(inlineTotalCount: true) .Services .BuildServiceProvider(); @@ -758,6 +782,7 @@ public async Task Execute_Queryable_CursorPaging() .AddSorting() .AddProjections() .AddQueryType() + .AddQueryableCursorPagingProvider(inlineTotalCount: true) .Services .BuildServiceProvider(); @@ -804,6 +829,7 @@ public async Task Execute_Queryable_CursorPaging_Task() .AddSorting() .AddProjections() .AddQueryType() + .AddQueryableCursorPagingProvider(inlineTotalCount: true) .Services .BuildServiceProvider(); @@ -850,6 +876,7 @@ public async Task Execute_Queryable_CursorPaging_ValueTask() .AddSorting() .AddProjections() .AddQueryType() + .AddQueryableCursorPagingProvider(inlineTotalCount: true) .Services .BuildServiceProvider(); diff --git a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/FilterVisitorTestBase.cs b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/FilterVisitorTestBase.cs index ede2ee80c54..9cff3ff20ab 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/FilterVisitorTestBase.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/FilterVisitorTestBase.cs @@ -23,7 +23,7 @@ protected async Task CreateSchemaAsync( TEntity[] entities, FilterConvention? convention = null, bool withPaging = false, - Action? configure = null) + Action? configure = null) where TEntity : class where T : FilterInputType { @@ -33,7 +33,10 @@ protected async Task CreateSchemaAsync( var resolver = await BuildResolverAsync(store, entities); - var builder = SchemaBuilder.New() + var services = new ServiceCollection(); + var builder = services.AddGraphQL(); + + builder .AddMartenFiltering() .AddQueryType( c => @@ -54,13 +57,7 @@ protected async Task CreateSchemaAsync( configure?.Invoke(builder); - var schema = builder.Create(); - - return await new ServiceCollection() - .Configure( - Schema.DefaultName, - o => o.Schema = schema) - .AddGraphQL() + builder .UseRequest( next => async context => { @@ -74,9 +71,11 @@ protected async Task CreateSchemaAsync( .Build(); } }) + .ModifyPagingOptions(o => o.IncludeTotalCount = true) .ModifyRequestOptions(x => x.IncludeExceptionDetails = true) - .UseDefaultPipeline() - .Services + .UseDefaultPipeline(); + + return await services .BuildServiceProvider() .GetRequiredService() .GetRequestExecutorAsync(); diff --git a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/FilteringAndPaging.cs b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/FilteringAndPaging.cs index 6362f7798aa..a07187816e9 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/FilteringAndPaging.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/FilteringAndPaging.cs @@ -18,12 +18,12 @@ public async Task Create_BooleanEqual_Expression() // act var res1 = await tester.ExecuteAsync( OperationRequestBuilder.New() - .SetDocument("{ root(where: { bar: { eq: true}}){ nodes { bar } }}") + .SetDocument("{ root(where: { bar: { eq: true } }){ nodes { bar } } }") .Build()); var res2 = await tester.ExecuteAsync( OperationRequestBuilder.New() - .SetDocument("{ root(where: { bar: { eq: false}}){ nodes { bar }}}") + .SetDocument("{ root(where: { bar: { eq: false } }){ nodes { bar } } }") .Build()); // assert @@ -34,6 +34,25 @@ await Snapshot .MatchAsync(); } + [Fact] + public async Task Paging_With_TotalCount() + { + // arrange + var tester = await cache.CreateSchemaAsync(_fooEntities, true); + + // act + var res1 = await tester.ExecuteAsync( + OperationRequestBuilder.New() + .SetDocument("{ root(where: { bar: { eq: true } }) { nodes { bar } totalCount } }") + .Build()); + + // assert + await Snapshot + .Create() + .Add(res1, "Result with TotalCount") + .MatchAsync(); + } + public class Foo { public int Id { get; set; } diff --git a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorInterfacesTests.cs b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorInterfacesTests.cs index c73b02eabcb..15c92493029 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorInterfacesTests.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/QueryableFilterVisitorInterfacesTests.cs @@ -2,7 +2,9 @@ using CookieCrumble; using HotChocolate.Data.Filters; using HotChocolate.Execution; +using HotChocolate.Execution.Configuration; using HotChocolate.Types; +using Microsoft.Extensions.DependencyInjection; namespace HotChocolate.Data; @@ -64,7 +66,7 @@ await Snapshot .MatchAsync(); } - private static void Configure(ISchemaBuilder builder) + private static void Configure(IRequestExecutorBuilder builder) => builder .AddObjectType(x => x.Implements>()) .AddObjectType(x => x.Implements>()) diff --git a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/SchemaCache.cs b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/SchemaCache.cs index db3b42b5275..f981aee67c6 100644 --- a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/SchemaCache.cs +++ b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/SchemaCache.cs @@ -1,6 +1,7 @@ using System.Collections.Concurrent; using HotChocolate.Data.Filters; using HotChocolate.Execution; +using HotChocolate.Execution.Configuration; namespace HotChocolate.Data; @@ -11,7 +12,7 @@ public class SchemaCache : FilterVisitorTestBase public async Task CreateSchemaAsync( T[] entities, bool withPaging = false, - Action? configure = null) + Action? configure = null) where T : class where TType : FilterInputType { diff --git a/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/__snapshots__/FilteringAndPaging.Paging_With_TotalCount.snap b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/__snapshots__/FilteringAndPaging.Paging_With_TotalCount.snap new file mode 100644 index 00000000000..b3ba2d8a2ef --- /dev/null +++ b/src/HotChocolate/Marten/test/Data.Marten.Filters.Tests/__snapshots__/FilteringAndPaging.Paging_With_TotalCount.snap @@ -0,0 +1,12 @@ +{ + "data": { + "root": { + "nodes": [ + { + "bar": true + } + ], + "totalCount": 1 + } + } +} From 40af4afb74dfef1de07c2aa0c20e35323f63e447 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 22 Jan 2025 15:38:27 +0100 Subject: [PATCH 146/154] Fixed issues with IsSelected and composite lists. (#7941) --- .../Extensions/ResolverContextExtensions.cs | 24 +++--- .../Types.Tests/Resolvers/IsSelectedTests.cs | 73 +++++++++++++++++++ ...sSelectedTests.IsSelected_Pattern_Lists.md | 15 ++++ ...ctedTests.IsSelected_Pattern_Root_Lists.md | 12 +++ 4 files changed, 112 insertions(+), 12 deletions(-) create mode 100644 src/HotChocolate/Core/test/Types.Tests/Resolvers/__snapshots__/IsSelectedTests.IsSelected_Pattern_Lists.md create mode 100644 src/HotChocolate/Core/test/Types.Tests/Resolvers/__snapshots__/IsSelectedTests.IsSelected_Pattern_Root_Lists.md diff --git a/src/HotChocolate/Core/src/Types/Extensions/ResolverContextExtensions.cs b/src/HotChocolate/Core/src/Types/Extensions/ResolverContextExtensions.cs index ee31965e6e2..ad5966e4bd9 100644 --- a/src/HotChocolate/Core/src/Types/Extensions/ResolverContextExtensions.cs +++ b/src/HotChocolate/Core/src/Types/Extensions/ResolverContextExtensions.cs @@ -662,13 +662,13 @@ public static bool IsSelected(this IResolverContext context, string fieldName) nameof(fieldName)); } - if (!context.Selection.Type.IsCompositeType()) + var namedType = context.Selection.Type.NamedType(); + + if (!namedType.IsCompositeType()) { return false; } - var namedType = context.Selection.Type.NamedType(); - if (namedType.IsAbstractType()) { foreach (var possibleType in context.Schema.GetPossibleTypes(namedType)) @@ -741,13 +741,13 @@ public static bool IsSelected(this IResolverContext context, string fieldName1, nameof(fieldName2)); } - if (!context.Selection.Type.IsCompositeType()) + var namedType = context.Selection.Type.NamedType(); + + if (!namedType.IsCompositeType()) { return false; } - var namedType = context.Selection.Type.NamedType(); - if (namedType.IsAbstractType()) { foreach (var possibleType in context.Schema.GetPossibleTypes(namedType)) @@ -839,13 +839,13 @@ public static bool IsSelected( nameof(fieldName3)); } - if (!context.Selection.Type.IsCompositeType()) + var namedType = context.Selection.Type.NamedType(); + + if (!namedType.IsCompositeType()) { return false; } - var namedType = context.Selection.Type.NamedType(); - if (namedType.IsAbstractType()) { foreach (var possibleType in context.Schema.GetPossibleTypes(namedType)) @@ -913,13 +913,13 @@ public static bool IsSelected( throw new ArgumentNullException(nameof(fieldNames)); } - if (!context.Selection.Type.IsCompositeType()) + var namedType = context.Selection.Type.NamedType(); + + if (!namedType.IsCompositeType()) { return false; } - var namedType = context.Selection.Type.NamedType(); - if (namedType.IsAbstractType()) { foreach (var possibleType in context.Schema.GetPossibleTypes(namedType)) diff --git a/src/HotChocolate/Core/test/Types.Tests/Resolvers/IsSelectedTests.cs b/src/HotChocolate/Core/test/Types.Tests/Resolvers/IsSelectedTests.cs index 06ba58313ee..f126b646e72 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Resolvers/IsSelectedTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Resolvers/IsSelectedTests.cs @@ -811,6 +811,49 @@ public async Task Select_Category_Level_2() result.MatchMarkdownSnapshot(); } + [Fact] + public async Task IsSelected_Pattern_Lists() + { + var result = + await new ServiceCollection() + .AddGraphQL() + .AddQueryType() + .ExecuteRequestAsync( + """ + query { + user_Attribute_List { + email + tags { + name + } + } + } + """); + + + result.MatchMarkdownSnapshot(); + } + + [Fact] + public async Task IsSelected_Pattern_Root_Lists() + { + var result = + await new ServiceCollection() + .AddGraphQL() + .AddQueryType() + .ExecuteRequestAsync( + """ + query { + books { + author { name } + } + } + """); + + + result.MatchMarkdownSnapshot(); + } + public class Query { public static User DummyUser { get; } = @@ -984,6 +1027,32 @@ public User GetUser_Context_4(IResolverContext context) City = "f", }; } + + public User GetUser_Attribute_List( + [IsSelected("tags { name }")] + bool isSelected, + IResolverContext context) + { + ((IMiddlewareContext)context).OperationResult.SetExtension("isSelected", isSelected); + return new User + { + Name = "a", + Email = "b", + Password = "c", + PhoneNumber = "d", + Address = "e", + City = "f", + }; + } + + public List GetBooks( + [IsSelected("author")] + bool isSelected, + IResolverContext context) + { + ((IMiddlewareContext)context).OperationResult.SetExtension("isSelected", isSelected); + return new List(); + } } public class BrokenQuery @@ -1071,4 +1140,8 @@ public class Audit public string EditedBy { get; set; } public string EditedAt { get; set; } } + + public record Book(string Title, Author Author); + + public record Author(string Name); } diff --git a/src/HotChocolate/Core/test/Types.Tests/Resolvers/__snapshots__/IsSelectedTests.IsSelected_Pattern_Lists.md b/src/HotChocolate/Core/test/Types.Tests/Resolvers/__snapshots__/IsSelectedTests.IsSelected_Pattern_Lists.md new file mode 100644 index 00000000000..f0ee9107c15 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests/Resolvers/__snapshots__/IsSelectedTests.IsSelected_Pattern_Lists.md @@ -0,0 +1,15 @@ +# IsSelected_Pattern_Lists + +```json +{ + "data": { + "user_Attribute_List": { + "email": "b", + "tags": null + } + }, + "extensions": { + "isSelected": true + } +} +``` diff --git a/src/HotChocolate/Core/test/Types.Tests/Resolvers/__snapshots__/IsSelectedTests.IsSelected_Pattern_Root_Lists.md b/src/HotChocolate/Core/test/Types.Tests/Resolvers/__snapshots__/IsSelectedTests.IsSelected_Pattern_Root_Lists.md new file mode 100644 index 00000000000..3c6d5213396 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests/Resolvers/__snapshots__/IsSelectedTests.IsSelected_Pattern_Root_Lists.md @@ -0,0 +1,12 @@ +# IsSelected_Pattern_Root_Lists + +```json +{ + "data": { + "books": [] + }, + "extensions": { + "isSelected": true + } +} +``` From 7ca02c00598691bce9ce65b473dc04dadfadb054 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 22 Jan 2025 22:23:32 +0100 Subject: [PATCH 147/154] Added support for total count to ToBatchPageAsync. (#7944) --- .../Extensions/PagingQueryableExtensions.cs | 152 +++++++++++++- .../InterfaceIntegrationTests.cs | 113 +++++++++++ ...sts.Query_Owner_Animals_With_TotalCount.md | 187 ++++++++++++++++++ 3 files changed, 451 insertions(+), 1 deletion(-) create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_TotalCount.md diff --git a/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs index 9fadcc6bfe6..0ffcb382154 100644 --- a/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs +++ b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs @@ -1,5 +1,7 @@ +using System.Collections.Concurrent; using System.Collections.Immutable; using System.Linq.Expressions; +using System.Reflection; using HotChocolate.Pagination.Expressions; using static HotChocolate.Pagination.Expressions.ExpressionHelpers; @@ -11,6 +13,7 @@ namespace HotChocolate.Pagination; public static class PagingQueryableExtensions { private static readonly AsyncLocal _interceptor = new(); + private static readonly ConcurrentDictionary<(Type, Type), Expression> _countExpressionCache = new(); /// /// Executes a query with paging and returns the selected page. @@ -208,6 +211,49 @@ public static ValueTask>> ToBatchPageAsync ToBatchPageAsync(source, keySelector, t => t, arguments, cancellationToken); + /// + /// Executes a batch query with paging and returns the selected pages for each parent. + /// + /// + /// The queryable to be paged. + /// + /// + /// A function to select the key of the parent. + /// + /// + /// The paging arguments. + /// + /// + /// If set to true the total count will be included in the result. + /// + /// + /// The cancellation token. + /// + /// + /// The type of the parent key. + /// + /// + /// The type of the items in the queryable. + /// + /// + /// + /// If the queryable does not have any keys specified. + /// + public static ValueTask>> ToBatchPageAsync( + this IQueryable source, + Expression> keySelector, + PagingArguments arguments, + bool includeTotalCount, + CancellationToken cancellationToken = default) + where TKey : notnull + => ToBatchPageAsync( + source, + keySelector, + t => t, + arguments, + includeTotalCount: includeTotalCount, + cancellationToken); + /// /// Executes a batch query with paging and returns the selected pages for each parent. /// @@ -239,11 +285,55 @@ public static ValueTask>> ToBatchPageAsync /// If the queryable does not have any keys specified. /// + public static ValueTask>> ToBatchPageAsync( + this IQueryable source, + Expression> keySelector, + Func valueSelector, + PagingArguments arguments, + CancellationToken cancellationToken = default) + where TKey : notnull + => ToBatchPageAsync(source, keySelector, valueSelector, arguments, includeTotalCount: false, cancellationToken); + + /// + /// Executes a batch query with paging and returns the selected pages for each parent. + /// + /// + /// The queryable to be paged. + /// + /// + /// A function to select the key of the parent. + /// + /// + /// A function to select the value of the items in the queryable. + /// + /// + /// The paging arguments. + /// + /// + /// If set to true the total count will be included in the result. + /// + /// + /// The cancellation token. + /// + /// + /// The type of the parent key. + /// + /// + /// The type of the items in the queryable. + /// + /// + /// The type of the items in the queryable. + /// + /// + /// + /// If the queryable does not have any keys specified. + /// public static async ValueTask>> ToBatchPageAsync( this IQueryable source, Expression> keySelector, Func valueSelector, PagingArguments arguments, + bool includeTotalCount, CancellationToken cancellationToken = default) where TKey : notnull { @@ -263,6 +353,12 @@ public static async ValueTask>> ToBatchPageAsync? counts = null; + if (includeTotalCount) + { + counts = await GetBatchCountsAsync(source, keySelector, cancellationToken); + } + source = QueryHelpers.EnsureOrderPropsAreSelected(source); // we need to move the ordering into the select expression we are constructing @@ -308,13 +404,67 @@ public static async ValueTask>> ToBatchPageAsync> GetBatchCountsAsync( + IQueryable source, + Expression> keySelector, + CancellationToken cancellationToken) + where TKey : notnull + { + var query = source + .GroupBy(keySelector) + .Select(GetOrCreateCountSelector()); + + TryGetQueryInterceptor()?.OnBeforeExecute(query); + + return await query.ToDictionaryAsync(t => t.Key, t => t.Count, cancellationToken); + } + + private static Expression, CountResult>> GetOrCreateCountSelector() + { + return (Expression, CountResult>>) + _countExpressionCache.GetOrAdd( + (typeof(TKey), typeof(TElement)), + static _ => + { + var groupingType = typeof(IGrouping<,>).MakeGenericType(typeof(TKey), typeof(TElement)); + var param = Expression.Parameter(groupingType, "g"); + var keyProperty = Expression.Property(param, nameof(IGrouping.Key)); + var countMethod = typeof(Enumerable) + .GetMethods(BindingFlags.Static | BindingFlags.Public) + .First(m => m.Name == nameof(Enumerable.Count) && m.GetParameters().Length == 1) + .MakeGenericMethod(typeof(TElement)); + var countCall = Expression.Call(countMethod, param); + + var resultCtor = typeof(CountResult).GetConstructor(Type.EmptyTypes)!; + var newExpr = Expression.New(resultCtor); + + var bindings = new List + { + Expression.Bind(typeof(CountResult).GetProperty(nameof(CountResult.Key))!, + keyProperty), + Expression.Bind(typeof(CountResult).GetProperty(nameof(CountResult.Count))!, + countCall) + }; + + var body = Expression.MemberInit(newExpr, bindings); + return Expression.Lambda, CountResult>>(body, param); + }); + } + + private class CountResult + { + public required TKey Key { get; set; } + public required int Count { get; set; } + } + private static Page CreatePage( ImmutableArray items, PagingArguments arguments, diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/InterfaceIntegrationTests.cs b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/InterfaceIntegrationTests.cs index e8d52383b02..aa9efb8aebd 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/InterfaceIntegrationTests.cs +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/InterfaceIntegrationTests.cs @@ -80,6 +80,61 @@ await Snapshot.Create() .MatchMarkdownAsync(); } + [Fact] + public async Task Query_Owner_Animals_With_TotalCount() + { + var connectionString = CreateConnectionString(); + await SeedAsync(connectionString); + + var queries = new List(); + using var capture = new CapturePagingQueryInterceptor(queries); + + var result = await new ServiceCollection() + .AddScoped(_ => new AnimalContext(connectionString)) + .AddGraphQL() + .AddQueryType() + .AddTypeExtension(typeof(OwnerExtensionsWithTotalCount)) + .AddDataLoader() + .AddObjectType() + .AddObjectType() + .AddPagingArguments() + .ModifyRequestOptions(o => o.IncludeExceptionDetails = true) + .ModifyPagingOptions(o => o.IncludeTotalCount = true) + .ExecuteRequestAsync( + OperationRequestBuilder.New() + .SetDocument( + """ + { + owners(first: 10) { + nodes { + id + name + pets(first: 10) { + nodes { + __typename + id + name + } + totalCount + } + } + } + } + """) + .Build()); + + var operationResult = result.ExpectOperationResult(); + +#if NET9_0_OR_GREATER + await Snapshot.Create("NET_9_0") +#else + await Snapshot.Create() +#endif + .AddQueries(queries) + .Add(operationResult.WithExtensions(ImmutableDictionary.Empty)) + .MatchMarkdownAsync(); + } + [Fact] public async Task Query_Owner_Animals_With_Fragments() { @@ -329,6 +384,24 @@ public static async Task> GetPetsAsync( .ToConnectionAsync(); } + [ExtendObjectType] + public static class OwnerExtensionsWithTotalCount + { + [BindMember(nameof(Owner.Pets))] + [UsePaging] + public static async Task> GetPetsAsync( + [Parent("Id")] Owner owner, + PagingArguments pagingArgs, + AnimalsByOwnerWithCountDataLoader animalsByOwner, + ISelection selection, + CancellationToken cancellationToken) + => await animalsByOwner + .WithPagingArguments(pagingArgs) + .Select(selection) + .LoadAsync(owner.Id, cancellationToken) + .ToConnectionAsync(); + } + public sealed class AnimalsByOwnerDataLoader : StatefulBatchDataLoader> { @@ -367,6 +440,46 @@ protected override async Task>> LoadBatchA cancellationToken); } } + + public sealed class AnimalsByOwnerWithCountDataLoader + : StatefulBatchDataLoader> + { + private readonly IServiceProvider _services; + + public AnimalsByOwnerWithCountDataLoader( + IServiceProvider services, + IBatchScheduler batchScheduler, + DataLoaderOptions options) + : base(batchScheduler, options) + { + _services = services; + } + + protected override async Task>> LoadBatchAsync( + IReadOnlyList keys, + DataLoaderFetchContext> context, + CancellationToken cancellationToken) + { + var pagingArgs = context.GetPagingArguments(); + // var selector = context.GetSelector(); + + await using var scope = _services.CreateAsyncScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + + return await dbContext.Owners + .Where(t => keys.Contains(t.Id)) + .SelectMany(t => t.Pets) + .OrderBy(t => t.Name) + .ThenBy(t => t.Id) + // selections do not work when inheritance is used for nested batching. + // .Select(selector, t => t.OwnerId) + .ToBatchPageAsync( + t => t.OwnerId, + pagingArgs, + includeTotalCount: true, + cancellationToken); + } + } } file static class Extensions diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_TotalCount.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_TotalCount.md new file mode 100644 index 00000000000..ceda3c3111b --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/InterfaceIntegrationTests.Query_Owner_Animals_With_TotalCount.md @@ -0,0 +1,187 @@ +# Query_Owner_Animals_With_TotalCount + +## SQL 0 + +```sql +-- @__p_0='11' +SELECT o."Id", o."Name" +FROM "Owners" AS o +ORDER BY o."Name", o."Id" +LIMIT @__p_0 +``` + +## Expression 0 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].OrderBy(t => t.Name).ThenBy(t => t.Id).Select(root => new Owner() {Id = root.Id, Name = root.Name}).Take(11) +``` + +## SQL 1 + +```sql +-- @__keys_0={ '6', '5', '4', '3', '2', ... } (DbType = Object) +SELECT p."OwnerId" AS "Key", count(*)::int AS "Count" +FROM "Owners" AS o +INNER JOIN "Pets" AS p ON o."Id" = p."OwnerId" +WHERE o."Id" = ANY (@__keys_0) +GROUP BY p."OwnerId" +``` + +## Expression 1 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Where(t => value(HotChocolate.Data.InterfaceIntegrationTests+AnimalsByOwnerWithCountDataLoader+<>c__DisplayClass2_0).keys.Contains(t.Id)).SelectMany(t => t.Pets).OrderBy(t => t.Name).ThenBy(t => t.Id).GroupBy(t => t.OwnerId).Select(g => new CountResult`1() {Key = g.Key, Count = g.Count()}) +``` + +## SQL 2 + +```sql +-- @__keys_0={ '6', '5', '4', '3', '2', ... } (DbType = Object) +SELECT t."OwnerId", t0."Id", t0."AnimalType", t0."Name", t0."OwnerId", t0."IsPurring", t0."IsBarking", t0."Id0" +FROM ( + SELECT p."OwnerId" + FROM "Owners" AS o + INNER JOIN "Pets" AS p ON o."Id" = p."OwnerId" + WHERE o."Id" = ANY (@__keys_0) + GROUP BY p."OwnerId" +) AS t +LEFT JOIN ( + SELECT t1."Id", t1."AnimalType", t1."Name", t1."OwnerId", t1."IsPurring", t1."IsBarking", t1."Id0" + FROM ( + SELECT p0."Id", p0."AnimalType", p0."Name", p0."OwnerId", p0."IsPurring", p0."IsBarking", o0."Id" AS "Id0", ROW_NUMBER() OVER(PARTITION BY p0."OwnerId" ORDER BY p0."Name", p0."Id") AS row + FROM "Owners" AS o0 + INNER JOIN "Pets" AS p0 ON o0."Id" = p0."OwnerId" + WHERE o0."Id" = ANY (@__keys_0) + ) AS t1 + WHERE t1.row <= 11 +) AS t0 ON t."OwnerId" = t0."OwnerId" +ORDER BY t."OwnerId", t0."OwnerId", t0."Name", t0."Id" +``` + +## Expression 2 + +```text +[Microsoft.EntityFrameworkCore.Query.EntityQueryRootExpression].Where(t => value(HotChocolate.Data.InterfaceIntegrationTests+AnimalsByOwnerWithCountDataLoader+<>c__DisplayClass2_0).keys.Contains(t.Id)).SelectMany(t => t.Pets).GroupBy(t => t.OwnerId).Select(g => new Group`2() {Key = g.Key, Items = g.OrderBy(t => t.Name).ThenBy(t => t.Id).Take(11).ToList()}) +``` + +## Result 7 + +```json +{ + "data": { + "owners": { + "nodes": [ + { + "id": 1, + "name": "Owner 1", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 1, + "name": "Cat 1" + }, + { + "__typename": "Dog", + "id": 5, + "name": "Dog 1" + }, + { + "__typename": "Dog", + "id": 6, + "name": "Dog 2" + } + ], + "totalCount": 3 + } + }, + { + "id": 2, + "name": "Owner 2", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 2, + "name": "Cat 2" + }, + { + "__typename": "Dog", + "id": 7, + "name": "Dog 3" + }, + { + "__typename": "Dog", + "id": 8, + "name": "Dog 4" + } + ], + "totalCount": 3 + } + }, + { + "id": 3, + "name": "Owner 3", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 3, + "name": "Cat 3 (Not Pure)" + }, + { + "__typename": "Dog", + "id": 9, + "name": "Dog 5" + }, + { + "__typename": "Dog", + "id": 10, + "name": "Dog 6" + } + ], + "totalCount": 3 + } + }, + { + "id": 4, + "name": "Owner 4 - No Pets", + "pets": { + "nodes": [], + "totalCount": 0 + } + }, + { + "id": 5, + "name": "Owner 5 - Only Cat", + "pets": { + "nodes": [ + { + "__typename": "Cat", + "id": 4, + "name": "Only Cat" + } + ], + "totalCount": 1 + } + }, + { + "id": 6, + "name": "Owner 6 - Only Dog", + "pets": { + "nodes": [ + { + "__typename": "Dog", + "id": 11, + "name": "Only Dog" + } + ], + "totalCount": 1 + } + } + ] + } + } +} +``` + From 90d0c6aa5f534fd77ad5919b89b49d911a2285e5 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Fri, 24 Jan 2025 22:48:14 +0100 Subject: [PATCH 148/154] Reworked the type inititialization flow. (#7953) --- .../FederationTypeInterceptor.cs | 4 +- .../ExternalSetterExpressionHelper.cs | 2 +- .../AuthorizationTypeInterceptor.cs | 2 +- .../Configuration/AggregateTypeInterceptor.cs | 96 +++++++++++++++ .../Types/Configuration/TypeInitializer.cs | 56 ++++++++- .../Types/Configuration/TypeInterceptor.cs | 90 +++++++++++++- .../Types/Internal/TypeDependencyHelper.cs | 18 +-- .../Types/InvalidSchemaCoordinateException.cs | 2 + .../Core/src/Types/Schema.Initialization.cs | 10 +- .../Core/src/Types/SchemaException.cs | 2 + .../Core/src/Types/Types/Argument.cs | 40 +++++- .../src/Types/Types/DirectiveCollection.cs | 7 ++ .../Types/DirectiveType.Initialization.cs | 44 +++++-- .../Directives/DirectiveTypeInterceptor.cs | 10 +- .../Types/Types/EnumType.Initialization.cs | 15 ++- .../Core/src/Types/Types/EnumValue.cs | 33 ++--- .../Core/src/Types/Types/FieldBase.cs | 71 +++++++++-- .../Types/Helpers/IEnumValueCompletion.cs | 12 ++ .../Types/Types/Helpers/IFieldCompletion.cs | 16 +++ .../Core/src/Types/Types/InputField.cs | 42 ++++++- .../Types/InputObjectType.Initialization.cs | 50 ++++++++ .../Core/src/Types/Types/InputObjectType.cs | 5 + .../Core/src/Types/Types/InputParser.cs | 4 +- .../MiddlewareValidationTypeInterceptor.cs | 10 +- .../Types/InterfaceType.Initialization.cs | 37 ++++++ .../Core/src/Types/Types/NamedTypeBase.cs | 7 ++ .../Core/src/Types/Types/ObjectField.cs | 14 ++- .../Types/Types/ObjectType.Initialization.cs | 36 ++++++ .../Core/src/Types/Types/OutputFieldBase.cs | 61 ++++++++- .../Types/Relay/IdSerializationException.cs | 2 + .../Relay/NodeResolverTypeInterceptor.cs | 2 +- .../NodeIdInvalidFormatException.cs | 2 + .../NodeIdMissingSerializerException.cs | 2 + .../Core/src/Types/Types/TypeStatus.cs | 2 + .../src/Types/Types/TypeSystemObjectBase.cs | 64 +++++++++- .../src/Types/Types/TypeSystemObjectBase~1.cs | 116 ++++++++++++++++-- .../test/Types.Tests/Types/EnumTypeTests.cs | 19 +-- .../Projections/ProjectionTypeInterceptor.cs | 2 +- 38 files changed, 896 insertions(+), 111 deletions(-) create mode 100644 src/HotChocolate/Core/src/Types/Types/Helpers/IEnumValueCompletion.cs diff --git a/src/HotChocolate/ApolloFederation/src/ApolloFederation/FederationTypeInterceptor.cs b/src/HotChocolate/ApolloFederation/src/ApolloFederation/FederationTypeInterceptor.cs index 15d0c5c686b..76cb9259fe3 100644 --- a/src/HotChocolate/ApolloFederation/src/ApolloFederation/FederationTypeInterceptor.cs +++ b/src/HotChocolate/ApolloFederation/src/ApolloFederation/FederationTypeInterceptor.cs @@ -338,7 +338,7 @@ public override void OnBeforeCompleteType( definition); } - public override void OnAfterCompleteType( + public override void OnAfterMakeExecutable( ITypeCompletionContext completionContext, DefinitionBase definition) { @@ -350,8 +350,6 @@ public override void OnAfterCompleteType( } } - internal override void OnAfterCreateSchemaInternal(IDescriptorContext context, ISchema schema) { } - private void CompleteExternalFieldSetters(ObjectType type, ObjectTypeDefinition typeDef) => ExternalSetterExpressionHelper.TryAddExternalSetter(type, typeDef); diff --git a/src/HotChocolate/ApolloFederation/src/ApolloFederation/Resolvers/ExternalSetterExpressionHelper.cs b/src/HotChocolate/ApolloFederation/src/ApolloFederation/Resolvers/ExternalSetterExpressionHelper.cs index b5f90a4325c..6f86592ad3f 100644 --- a/src/HotChocolate/ApolloFederation/src/ApolloFederation/Resolvers/ExternalSetterExpressionHelper.cs +++ b/src/HotChocolate/ApolloFederation/src/ApolloFederation/Resolvers/ExternalSetterExpressionHelper.cs @@ -31,7 +31,7 @@ public static void TryAddExternalSetter(ObjectType type, ObjectTypeDefinition ty foreach (var field in type.Fields) { if (field.Directives.ContainsDirective() && - field.Member is PropertyInfo { SetMethod: { }, } property) + field.Member is PropertyInfo { SetMethod: not null, } property) { var expression = CreateTrySetValue(type.RuntimeType, property, field.Name); (block ??= []).Add(expression); diff --git a/src/HotChocolate/Core/src/Authorization/AuthorizationTypeInterceptor.cs b/src/HotChocolate/Core/src/Authorization/AuthorizationTypeInterceptor.cs index dd048ef1bc0..20f5309e59e 100644 --- a/src/HotChocolate/Core/src/Authorization/AuthorizationTypeInterceptor.cs +++ b/src/HotChocolate/Core/src/Authorization/AuthorizationTypeInterceptor.cs @@ -126,7 +126,7 @@ public override void OnBeforeCompleteType( } } - public override void OnAfterCompleteTypes() + public override void OnAfterMakeExecutable() { foreach (var type in _objectTypes) { diff --git a/src/HotChocolate/Core/src/Types/Configuration/AggregateTypeInterceptor.cs b/src/HotChocolate/Core/src/Types/Configuration/AggregateTypeInterceptor.cs index ebaa9b546c4..316a244edc4 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/AggregateTypeInterceptor.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/AggregateTypeInterceptor.cs @@ -403,6 +403,102 @@ public override void OnAfterCompleteType( } } + public override void OnBeforeCompleteMetadata() + { + ref var first = ref GetReference(); + var length = _typeInterceptors.Length; + + for (var i = 0; i < length; i++) + { + Unsafe.Add(ref first, i).OnBeforeCompleteMetadata(); + } + } + + public override void OnAfterCompleteMetadata() + { + ref var first = ref GetReference(); + var length = _typeInterceptors.Length; + + for (var i = 0; i < length; i++) + { + Unsafe.Add(ref first, i).OnAfterCompleteMetadata(); + } + } + + public override void OnBeforeCompleteMetadata( + ITypeCompletionContext context, + DefinitionBase definition) + { + ref var first = ref GetReference(); + var length = _typeInterceptors.Length; + + for (var i = 0; i < length; i++) + { + Unsafe.Add(ref first, i).OnBeforeCompleteMetadata(context, definition); + } + } + + public override void OnAfterCompleteMetadata( + ITypeCompletionContext context, + DefinitionBase definition) + { + ref var first = ref GetReference(); + var length = _typeInterceptors.Length; + + for (var i = 0; i < length; i++) + { + Unsafe.Add(ref first, i).OnAfterCompleteMetadata(context, definition); + } + } + + public override void OnBeforeMakeExecutable() + { + ref var first = ref GetReference(); + var length = _typeInterceptors.Length; + + for (var i = 0; i < length; i++) + { + Unsafe.Add(ref first, i).OnBeforeMakeExecutable(); + } + } + + public override void OnAfterMakeExecutable() + { + ref var first = ref GetReference(); + var length = _typeInterceptors.Length; + + for (var i = 0; i < length; i++) + { + Unsafe.Add(ref first, i).OnAfterMakeExecutable(); + } + } + + public override void OnBeforeMakeExecutable( + ITypeCompletionContext context, + DefinitionBase definition) + { + ref var first = ref GetReference(); + var length = _typeInterceptors.Length; + + for (var i = 0; i < length; i++) + { + Unsafe.Add(ref first, i).OnBeforeMakeExecutable(context, definition); + } + } + + public override void OnAfterMakeExecutable( + ITypeCompletionContext context, + DefinitionBase definition) + { + ref var first = ref GetReference(); + var length = _typeInterceptors.Length; + + for (var i = 0; i < length; i++) + { + Unsafe.Add(ref first, i).OnAfterMakeExecutable(context, definition); + } + } + public override void OnValidateType( ITypeSystemObjectContext validationContext, DefinitionBase definition) diff --git a/src/HotChocolate/Core/src/Types/Configuration/TypeInitializer.cs b/src/HotChocolate/Core/src/Types/Configuration/TypeInitializer.cs index 0ac46352a4a..54f72d507f4 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/TypeInitializer.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/TypeInitializer.cs @@ -101,10 +101,19 @@ public void Initialize() // before we start completing types we will compile the resolvers. CompileResolvers(); - // last we complete the types. Completing types means that we will assign all - // the fields resolving all missing parts and then making the types immutable. + // now we complete the types. This means at this point we are completing + // the type structure. Fields and arguments will be accessible after + // types are completed. CompleteTypes(); + // next we are completing type system directives and feature metadata. + CompleteMetadata(); + + // before we can finalize the types and make the immutable we are going to make the + // types executable. This means that at this point we are taking the compiled resolvers + // and are compiling from them field middleware that are assigned to the output fields. + MakeExecutable(); + // at this point everything is completely initialized, and we just trigger a type // finalize to allow the type to clean up any initialization data structures. FinalizeTypes(); @@ -574,10 +583,13 @@ private void CompleteTypes() { _interceptor.OnBeforeCompleteTypes(); - ProcessTypes(Completed, type => CompleteType(type)); + if(ProcessTypes(Completed, type => CompleteType(type))) + { + _interceptor.OnTypesCompleted(); + } + EnsureNoErrors(); - _interceptor.OnTypesCompleted(); _interceptor.OnAfterCompleteTypes(); } @@ -597,6 +609,40 @@ internal bool CompleteType(RegisteredType registeredType) return true; } + private void CompleteMetadata() + { + _interceptor.OnBeforeCompleteMetadata(); + + foreach (var registeredType in _typeRegistry.Types) + { + if (!registeredType.IsExtension) + { + registeredType.Type.CompleteMetadata(registeredType); + } + } + + EnsureNoErrors(); + + _interceptor.OnAfterCompleteMetadata(); + } + + private void MakeExecutable() + { + _interceptor.OnBeforeMakeExecutable(); + + foreach (var registeredType in _typeRegistry.Types) + { + if (!registeredType.IsExtension) + { + registeredType.Type.MakeExecutable(registeredType); + } + } + + EnsureNoErrors(); + + _interceptor.OnAfterMakeExecutable(); + } + private void FinalizeTypes() { foreach (var registeredType in _typeRegistry.Types) @@ -606,6 +652,8 @@ private void FinalizeTypes() registeredType.Type.FinalizeType(registeredType); } } + + EnsureNoErrors(); } private bool ProcessTypes( diff --git a/src/HotChocolate/Core/src/Types/Configuration/TypeInterceptor.cs b/src/HotChocolate/Core/src/Types/Configuration/TypeInterceptor.cs index 658292eaf48..bc815f5b1ea 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/TypeInterceptor.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/TypeInterceptor.cs @@ -226,6 +226,9 @@ public virtual void OnAfterResolveRootType( { } + /// + /// This event is called after the type name is assigned. + /// public virtual void OnTypesCompletedName() { } /// @@ -259,6 +262,11 @@ public virtual void OnBeforeCompleteMutationField( /// public virtual void OnBeforeCompleteTypes() { } + /// + /// This method is called after the types are completed. + /// + public virtual void OnTypesCompleted() { } + /// /// This method is called after the types are completed. /// @@ -294,6 +302,86 @@ public virtual void OnAfterCompleteType( { } + /// + /// This method is called before the metadata of all types are completed. + /// + public virtual void OnBeforeCompleteMetadata() { } + + /// + /// This method is called after the metadata of all types are completed. + /// + public virtual void OnAfterCompleteMetadata() { } + + /// + /// This event is called before the metadata of the type system member is fully completed. + /// + /// + /// The type completion context. + /// + /// + /// The type definition of the type system member. + /// + public virtual void OnBeforeCompleteMetadata( + ITypeCompletionContext context, + DefinitionBase definition) + { + } + + /// + /// This event is called after the metadata of the type system member was fully completed. + /// + /// + /// The type completion context. + /// + /// + /// The type definition of the type system member. + /// + public virtual void OnAfterCompleteMetadata( + ITypeCompletionContext context, + DefinitionBase definition) + { + } + + /// + /// This method is called before the types are made executable. + /// + public virtual void OnBeforeMakeExecutable() { } + + /// + /// This method is called after the types are made executable. + /// + public virtual void OnAfterMakeExecutable() { } + + /// + /// This event is called before the type system member is made executable. + /// + /// + /// The type completion context. + /// + /// + /// The type definition of the type system member. + /// + public virtual void OnBeforeMakeExecutable( + ITypeCompletionContext context, + DefinitionBase definition) + { + } + + /// + /// This event is called after the type system member is made executable. + /// + /// + /// The type completion context. + /// + /// + /// The type definition of the type system member. + /// + public virtual void OnAfterMakeExecutable( + ITypeCompletionContext context, + DefinitionBase definition) + { + } + /// /// This event is called after the type system member is fully completed and is /// intended to add validation logic. If the type is not valid throw a @@ -311,8 +399,6 @@ public virtual void OnValidateType( { } - public virtual void OnTypesCompleted() { } - // note: this hook is a legacy hook and will be removed once the new schema building API is completed. /// /// This hook is invoked after schema is fully created and gives access diff --git a/src/HotChocolate/Core/src/Types/Internal/TypeDependencyHelper.cs b/src/HotChocolate/Core/src/Types/Internal/TypeDependencyHelper.cs index 50ca6cecc7a..b971dbf076c 100644 --- a/src/HotChocolate/Core/src/Types/Internal/TypeDependencyHelper.cs +++ b/src/HotChocolate/Core/src/Types/Internal/TypeDependencyHelper.cs @@ -109,7 +109,7 @@ public static void CollectDependencies( if (field.Type is not null) { - dependencies.Add(new(field.Type, GetDefaultValueDependencyKind(field))); + dependencies.Add(new(field.Type)); } CollectDirectiveDependencies(field, dependencies); @@ -182,9 +182,7 @@ public static void CollectDependencies( if (argument.Type is not null) { - dependencies.Add(new( - argument.Type, - GetDefaultValueDependencyKind(argument))); + dependencies.Add(new(argument.Type)); } } } @@ -334,16 +332,4 @@ public static void RegisterDependencies( CollectDependencies(definition, context.Dependencies); } - - private static TypeDependencyFulfilled GetDefaultValueDependencyKind( - ArgumentDefinition argumentDefinition) - { - var hasDefaultValue = - argumentDefinition.DefaultValue is not null and not NullValueNode || - argumentDefinition.RuntimeDefaultValue is not null; - - return hasDefaultValue - ? Completed - : Default; - } } diff --git a/src/HotChocolate/Core/src/Types/InvalidSchemaCoordinateException.cs b/src/HotChocolate/Core/src/Types/InvalidSchemaCoordinateException.cs index 9c003017ec6..d76ea17fb68 100644 --- a/src/HotChocolate/Core/src/Types/InvalidSchemaCoordinateException.cs +++ b/src/HotChocolate/Core/src/Types/InvalidSchemaCoordinateException.cs @@ -7,6 +7,7 @@ namespace HotChocolate; /// /// could not be resolved. /// +#pragma warning disable RCS1194 public class InvalidSchemaCoordinateException : Exception { /// @@ -26,3 +27,4 @@ public InvalidSchemaCoordinateException(string message, SchemaCoordinate coordin /// public SchemaCoordinate Coordinate { get; } } +#pragma warning restore RCS1194 diff --git a/src/HotChocolate/Core/src/Types/Schema.Initialization.cs b/src/HotChocolate/Core/src/Types/Schema.Initialization.cs index 39805e83bd4..859bfa251f3 100644 --- a/src/HotChocolate/Core/src/Types/Schema.Initialization.cs +++ b/src/HotChocolate/Core/src/Types/Schema.Initialization.cs @@ -78,11 +78,19 @@ protected override void OnCompleteType( { base.OnCompleteType(context, definition); - Directives = DirectiveCollection.CreateAndComplete(context, this, definition.GetDirectives()); Services = context.Services; Features = definition.Features.ToReadOnly(); } + protected override void OnCompleteMetadata( + ITypeCompletionContext context, + SchemaTypeDefinition definition) + { + base.OnCompleteMetadata(context, definition); + + Directives = DirectiveCollection.CreateAndComplete(context, this, definition.GetDirectives()); + } + internal void CompleteSchema(SchemaTypesDefinition schemaTypesDefinition) { if (schemaTypesDefinition is null) diff --git a/src/HotChocolate/Core/src/Types/SchemaException.cs b/src/HotChocolate/Core/src/Types/SchemaException.cs index e71b9e856c1..b153aa4c826 100644 --- a/src/HotChocolate/Core/src/Types/SchemaException.cs +++ b/src/HotChocolate/Core/src/Types/SchemaException.cs @@ -5,6 +5,7 @@ namespace HotChocolate; +#pragma warning disable RCS1194 public sealed class SchemaException : Exception { public SchemaException(params ISchemaError[] errors) @@ -52,3 +53,4 @@ private static string CreateErrorMessage(IReadOnlyList errors) return message.ToString(); } } +#pragma warning restore RCS1194 diff --git a/src/HotChocolate/Core/src/Types/Types/Argument.cs b/src/HotChocolate/Core/src/Types/Types/Argument.cs index e3bc2851e5f..340a8cfe0c5 100644 --- a/src/HotChocolate/Core/src/Types/Types/Argument.cs +++ b/src/HotChocolate/Core/src/Types/Types/Argument.cs @@ -103,11 +103,49 @@ protected virtual void OnCompleteField( Type = context.GetType(definition.Type!).EnsureInputType(); _runtimeType = definition.GetRuntimeType()!; _runtimeType = CompleteRuntimeType(Type, _runtimeType, out var isOptional); - DefaultValue = CompleteDefaultValue(context, definition, Type, Coordinate); IsOptional = isOptional; DeclaringMember = declaringMember; } + protected sealed override void OnCompleteMetadata( + ITypeCompletionContext context, + ITypeSystemMember declaringMember, + FieldDefinitionBase definition) + => OnCompleteMetadata(context, declaringMember, (ArgumentDefinition)definition); + + protected virtual void OnCompleteMetadata( + ITypeCompletionContext context, + ITypeSystemMember declaringMember, + ArgumentDefinition definition) + { + DefaultValue = CompleteDefaultValue(context, definition, Type, Coordinate); + base.OnCompleteMetadata(context, declaringMember, definition); + } + + protected sealed override void OnMakeExecutable( + ITypeCompletionContext context, + ITypeSystemMember declaringMember, + FieldDefinitionBase definition) + => OnMakeExecutable(context, declaringMember, (ArgumentDefinition)definition); + + protected virtual void OnMakeExecutable( + ITypeCompletionContext context, + ITypeSystemMember declaringMember, + ArgumentDefinition definition) => + base.OnMakeExecutable(context, declaringMember, definition); + + protected sealed override void OnFinalizeField( + ITypeCompletionContext context, + ITypeSystemMember declaringMember, + FieldDefinitionBase definition) + => OnFinalizeField(context, declaringMember, (ArgumentDefinition)definition); + + protected virtual void OnFinalizeField( + ITypeCompletionContext context, + ITypeSystemMember declaringMember, + ArgumentDefinition definition) => + base.OnFinalizeField(context, declaringMember, definition); + /// /// Returns a string that represents the current argument. /// diff --git a/src/HotChocolate/Core/src/Types/Types/DirectiveCollection.cs b/src/HotChocolate/Core/src/Types/Types/DirectiveCollection.cs index 38945ab6119..0b95f00a405 100644 --- a/src/HotChocolate/Core/src/Types/Types/DirectiveCollection.cs +++ b/src/HotChocolate/Core/src/Types/Types/DirectiveCollection.cs @@ -138,6 +138,11 @@ internal static DirectiveCollection CreateAndComplete( throw new ArgumentNullException(nameof(definitions)); } + if (definitions.Count == 0) + { + return Empty; + } + var directives = new Directive[definitions.Count]; var directiveNames = TypeMemHelper.RentNameSet(); var hasErrors = false; @@ -253,4 +258,6 @@ public IEnumerator GetEnumerator() IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + internal static DirectiveCollection Empty { get; } = new DirectiveCollection(Array.Empty()); } diff --git a/src/HotChocolate/Core/src/Types/Types/DirectiveType.Initialization.cs b/src/HotChocolate/Core/src/Types/Types/DirectiveType.Initialization.cs index 8c592bf01c2..2cab32974b5 100644 --- a/src/HotChocolate/Core/src/Types/Types/DirectiveType.Initialization.cs +++ b/src/HotChocolate/Core/src/Types/Types/DirectiveType.Initialization.cs @@ -4,6 +4,7 @@ using HotChocolate.Resolvers; using HotChocolate.Types.Descriptors; using HotChocolate.Types.Descriptors.Definitions; +using HotChocolate.Types.Helpers; using HotChocolate.Utilities; using static HotChocolate.Internal.FieldInitHelper; using static HotChocolate.Utilities.Serialization.InputObjectCompiler; @@ -12,13 +13,6 @@ namespace HotChocolate.Types; -/// -/// A GraphQL schema describes directives which are used to annotate various parts of a -/// GraphQL document as an indicator that they should be evaluated differently by a -/// validator, executor, or client tool such as a code generator. -/// -/// https://spec.graphql.org/draft/#sec-Type-System.Directives -/// public partial class DirectiveType { protected override DirectiveTypeDefinition CreateDefinition(ITypeDiscoveryContext context) @@ -91,6 +85,42 @@ protected override void OnCompleteType( IsTypeSystemDirective = (Locations & DirectiveLocation.TypeSystem) != 0; } + protected override void OnCompleteMetadata( + ITypeCompletionContext context, + DirectiveTypeDefinition definition) + { + base.OnCompleteMetadata(context, definition); + + foreach (IFieldCompletion field in Arguments) + { + field.CompleteMetadata(context, this); + } + } + + protected override void OnMakeExecutable( + ITypeCompletionContext context, + DirectiveTypeDefinition definition) + { + base.OnMakeExecutable(context, definition); + + foreach (IFieldCompletion field in Arguments) + { + field.MakeExecutable(context, this); + } + } + + protected override void OnFinalizeType( + ITypeCompletionContext context, + DirectiveTypeDefinition definition) + { + base.OnFinalizeType(context, definition); + + foreach (IFieldCompletion field in Arguments) + { + field.Finalize(context, this); + } + } + protected virtual FieldCollection OnCompleteFields( ITypeCompletionContext context, DirectiveTypeDefinition definition) diff --git a/src/HotChocolate/Core/src/Types/Types/Directives/DirectiveTypeInterceptor.cs b/src/HotChocolate/Core/src/Types/Types/Directives/DirectiveTypeInterceptor.cs index fec40f37540..2746691c72f 100644 --- a/src/HotChocolate/Core/src/Types/Types/Directives/DirectiveTypeInterceptor.cs +++ b/src/HotChocolate/Core/src/Types/Types/Directives/DirectiveTypeInterceptor.cs @@ -11,13 +11,15 @@ internal sealed class DirectiveTypeInterceptor : TypeInterceptor { private readonly HashSet _usedDirectives = []; - public override void OnAfterCompleteType( - ITypeCompletionContext completionContext, + public override void OnAfterCompleteMetadata( + ITypeCompletionContext context, DefinitionBase definition) { - if (!((RegisteredType)completionContext).HasErrors) + base.OnAfterCompleteMetadata(context, definition); + + if (!((RegisteredType)context).HasErrors) { - InspectType(completionContext.Type); + InspectType(context.Type); } } diff --git a/src/HotChocolate/Core/src/Types/Types/EnumType.Initialization.cs b/src/HotChocolate/Core/src/Types/Types/EnumType.Initialization.cs index ca1f2c3b483..5d058d5d45e 100644 --- a/src/HotChocolate/Core/src/Types/Types/EnumType.Initialization.cs +++ b/src/HotChocolate/Core/src/Types/Types/EnumType.Initialization.cs @@ -8,6 +8,7 @@ using HotChocolate.Properties; using HotChocolate.Types.Descriptors; using HotChocolate.Types.Descriptors.Definitions; +using HotChocolate.Types.Helpers; #nullable enable @@ -151,12 +152,24 @@ protected override void OnCompleteType( #endif } + protected override void OnCompleteMetadata( + ITypeCompletionContext context, + EnumTypeDefinition definition) + { + base.OnCompleteMetadata(context, definition); + + foreach (var value in _values.OfType()) + { + value.CompleteMetadata(context, this); + } + } + protected virtual bool TryCreateEnumValue( ITypeCompletionContext context, EnumValueDefinition definition, [NotNullWhen(true)] out IEnumValue? enumValue) { - enumValue = new EnumValue(context, definition); + enumValue = new EnumValue(definition); return true; } } diff --git a/src/HotChocolate/Core/src/Types/Types/EnumValue.cs b/src/HotChocolate/Core/src/Types/Types/EnumValue.cs index ebc2ba34b0b..df5c5042ae8 100644 --- a/src/HotChocolate/Core/src/Types/Types/EnumValue.cs +++ b/src/HotChocolate/Core/src/Types/Types/EnumValue.cs @@ -1,22 +1,18 @@ using HotChocolate.Configuration; using HotChocolate.Properties; using HotChocolate.Types.Descriptors.Definitions; +using HotChocolate.Types.Helpers; #nullable enable namespace HotChocolate.Types; -public sealed class EnumValue : IEnumValue +public sealed class EnumValue : IEnumValue, IEnumValueCompletion { - public EnumValue( - ITypeCompletionContext completionContext, - EnumValueDefinition enumValueDefinition) - { - if (completionContext == null) - { - throw new ArgumentNullException(nameof(completionContext)); - } + private EnumValueDefinition? _enumValueDefinition; + public EnumValue(EnumValueDefinition enumValueDefinition) + { if (enumValueDefinition is null) { throw new ArgumentNullException(nameof(enumValueDefinition)); @@ -29,6 +25,8 @@ public EnumValue( nameof(enumValueDefinition)); } + _enumValueDefinition = enumValueDefinition; + Name = string.IsNullOrEmpty(enumValueDefinition.Name) ? enumValueDefinition.RuntimeValue.ToString()! : enumValueDefinition.Name; @@ -37,11 +35,6 @@ public EnumValue( IsDeprecated = !string.IsNullOrEmpty(enumValueDefinition.DeprecationReason); Value = enumValueDefinition.RuntimeValue; ContextData = enumValueDefinition.GetContextData(); - - Directives = DirectiveCollection.CreateAndComplete( - completionContext, - this, - enumValueDefinition.GetDirectives()); } public string Name { get; } @@ -54,7 +47,17 @@ public EnumValue( public object Value { get; } - public IDirectiveCollection Directives { get; } + public IDirectiveCollection Directives { get; private set; } = default!; public IReadOnlyDictionary ContextData { get; } + void IEnumValueCompletion.CompleteMetadata( + ITypeCompletionContext context, + ITypeSystemMember declaringMember) + { + Directives = DirectiveCollection.CreateAndComplete( + context, + this, + _enumValueDefinition!.GetDirectives()); + _enumValueDefinition = null; + } } diff --git a/src/HotChocolate/Core/src/Types/Types/FieldBase.cs b/src/HotChocolate/Core/src/Types/Types/FieldBase.cs index 4fba4ea71ed..099c9aa0dc3 100644 --- a/src/HotChocolate/Core/src/Types/Types/FieldBase.cs +++ b/src/HotChocolate/Core/src/Types/Types/FieldBase.cs @@ -67,12 +67,8 @@ internal void CompleteField( ITypeSystemMember declaringMember) { AssertMutable(); - OnCompleteField(context, declaringMember, _definition!); - ContextData = _definition!.GetContextData(); - _definition = null; - _flags |= FieldFlags.Sealed; } protected virtual void OnCompleteField( @@ -84,9 +80,6 @@ protected virtual void OnCompleteField( Coordinate = declaringMember is IField field ? new SchemaCoordinate(context.Type.Name, field.Name, definition.Name) : new SchemaCoordinate(context.Type.Name, definition.Name); - - Directives = DirectiveCollection.CreateAndComplete( - context, this, definition.GetDirectives()); Flags = definition.Flags; } @@ -95,6 +88,70 @@ void IFieldCompletion.CompleteField( ITypeSystemMember declaringMember) => CompleteField(context, declaringMember); + private void CompleteMetadata( + ITypeCompletionContext context, + ITypeSystemMember declaringMember) + { + AssertMutable(); + OnCompleteMetadata(context, declaringMember, _definition!); + } + + protected virtual void OnCompleteMetadata( + ITypeCompletionContext context, + ITypeSystemMember declaringMember, + FieldDefinitionBase definition) + { + Directives = DirectiveCollection.CreateAndComplete( + context, this, definition.GetDirectives()); + } + + void IFieldCompletion.CompleteMetadata( + ITypeCompletionContext context, + ITypeSystemMember declaringMember) + => CompleteMetadata(context, declaringMember); + + private void MakeExecutable( + ITypeCompletionContext context, + ITypeSystemMember declaringMember) + { + AssertMutable(); + OnMakeExecutable(context, declaringMember, _definition!); + } + + protected virtual void OnMakeExecutable( + ITypeCompletionContext context, + ITypeSystemMember declaringMember, + FieldDefinitionBase definition) + { + } + + void IFieldCompletion.MakeExecutable( + ITypeCompletionContext context, + ITypeSystemMember declaringMember) + => MakeExecutable(context, declaringMember); + + private void FinalizeField( + ITypeCompletionContext context, + ITypeSystemMember declaringMember) + { + AssertMutable(); + OnFinalizeField(context, declaringMember, _definition!); + _definition = null; + _flags |= FieldFlags.Sealed; + } + + protected virtual void OnFinalizeField( + ITypeCompletionContext context, + ITypeSystemMember declaringMember, + FieldDefinitionBase definition) + { + } + + void IFieldCompletion.Finalize( + ITypeCompletionContext context, + ITypeSystemMember declaringMember) + => FinalizeField(context, declaringMember); + private void AssertMutable() { if ((_flags & FieldFlags.Sealed) == FieldFlags.Sealed) diff --git a/src/HotChocolate/Core/src/Types/Types/Helpers/IEnumValueCompletion.cs b/src/HotChocolate/Core/src/Types/Types/Helpers/IEnumValueCompletion.cs new file mode 100644 index 00000000000..6efdbfa440d --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Types/Helpers/IEnumValueCompletion.cs @@ -0,0 +1,12 @@ +using HotChocolate.Configuration; + +#nullable enable + +namespace HotChocolate.Types.Helpers; + +internal interface IEnumValueCompletion +{ + void CompleteMetadata( + ITypeCompletionContext context, + ITypeSystemMember declaringMember); +} diff --git a/src/HotChocolate/Core/src/Types/Types/Helpers/IFieldCompletion.cs b/src/HotChocolate/Core/src/Types/Types/Helpers/IFieldCompletion.cs index 786e27ac288..9cf70fdb5be 100644 --- a/src/HotChocolate/Core/src/Types/Types/Helpers/IFieldCompletion.cs +++ b/src/HotChocolate/Core/src/Types/Types/Helpers/IFieldCompletion.cs @@ -2,9 +2,25 @@ namespace HotChocolate.Types.Helpers; +/// +/// This interface is explicitly implemented by fields +/// and is used to trigger the initialization hooks. +/// internal interface IFieldCompletion { void CompleteField( ITypeCompletionContext context, ITypeSystemMember declaringMember); + + void CompleteMetadata( + ITypeCompletionContext context, + ITypeSystemMember declaringMember); + + void MakeExecutable( + ITypeCompletionContext context, + ITypeSystemMember declaringMember); + + void Finalize( + ITypeCompletionContext context, + ITypeSystemMember declaringMember); } diff --git a/src/HotChocolate/Core/src/Types/Types/InputField.cs b/src/HotChocolate/Core/src/Types/Types/InputField.cs index 6399b938d6d..ce75518e8e0 100644 --- a/src/HotChocolate/Core/src/Types/Types/InputField.cs +++ b/src/HotChocolate/Core/src/Types/Types/InputField.cs @@ -80,10 +80,50 @@ protected virtual void OnCompleteField( Type = context.GetType(definition.Type!).EnsureInputType(); _runtimeType = definition.RuntimeType ?? definition.Property?.PropertyType!; _runtimeType = CompleteRuntimeType(Type, _runtimeType, out var isOptional); - DefaultValue = CompleteDefaultValue(context, definition, Type, Coordinate); IsOptional = isOptional; } + protected sealed override void OnCompleteMetadata( + ITypeCompletionContext context, + ITypeSystemMember declaringMember, + FieldDefinitionBase definition) + => OnCompleteMetadata(context, declaringMember, (InputFieldDefinition)definition); + + protected virtual void OnCompleteMetadata( + ITypeCompletionContext context, + ITypeSystemMember declaringMember, + InputFieldDefinition definition) + { + base.OnCompleteMetadata(context, declaringMember, definition); + DefaultValue = CompleteDefaultValue(context, definition, Type, Coordinate); + } + + protected sealed override void OnMakeExecutable( + ITypeCompletionContext context, + ITypeSystemMember declaringMember, + FieldDefinitionBase definition) + => OnMakeExecutable(context, declaringMember, (InputFieldDefinition)definition); + + protected virtual void OnMakeExecutable( + ITypeCompletionContext context, + ITypeSystemMember declaringMember, + InputFieldDefinition definition) + => base.OnMakeExecutable(context, declaringMember, definition); + + protected sealed override void OnFinalizeField( + ITypeCompletionContext context, + ITypeSystemMember declaringMember, + FieldDefinitionBase definition) + => base.OnFinalizeField(context, declaringMember, (InputFieldDefinition)definition); + + protected virtual void OnFinalizeField( + ITypeCompletionContext context, + ITypeSystemMember declaringMember, + InputFieldDefinition definition) + => base.OnFinalizeField(context, declaringMember, definition); + + + /// /// Returns a string that represents the current field. /// diff --git a/src/HotChocolate/Core/src/Types/Types/InputObjectType.Initialization.cs b/src/HotChocolate/Core/src/Types/Types/InputObjectType.Initialization.cs index b16a04b208b..cdd3f7bc32a 100644 --- a/src/HotChocolate/Core/src/Types/Types/InputObjectType.Initialization.cs +++ b/src/HotChocolate/Core/src/Types/Types/InputObjectType.Initialization.cs @@ -1,7 +1,11 @@ +using System.Runtime.CompilerServices; using HotChocolate.Configuration; using HotChocolate.Internal; +using HotChocolate.Language; using HotChocolate.Types.Descriptors; using HotChocolate.Types.Descriptors.Definitions; +using HotChocolate.Types.Helpers; +using HotChocolate.Utilities; using static HotChocolate.Internal.FieldInitHelper; using static HotChocolate.Utilities.Serialization.InputObjectCompiler; @@ -55,11 +59,48 @@ protected override void OnCompleteType( base.OnCompleteType(context, definition); Fields = OnCompleteFields(context, definition); + IsOneOf = definition.GetDirectives().Any(static t => t.IsOneOf()); _createInstance = OnCompleteCreateInstance(context, definition); _getFieldValues = OnCompleteGetFieldValues(context, definition); } + protected override void OnCompleteMetadata( + ITypeCompletionContext context, + InputObjectTypeDefinition definition) + { + base.OnCompleteMetadata(context, definition); + + foreach (IFieldCompletion field in Fields) + { + field.CompleteMetadata(context, this); + } + } + + protected override void OnMakeExecutable( + ITypeCompletionContext context, + InputObjectTypeDefinition definition) + { + base.OnMakeExecutable(context, definition); + + foreach (IFieldCompletion field in Fields) + { + field.MakeExecutable(context, this); + } + } + + protected override void OnFinalizeType( + ITypeCompletionContext context, + InputObjectTypeDefinition definition) + { + base.OnFinalizeType(context, definition); + + foreach (IFieldCompletion field in Fields) + { + field.Finalize(context, this); + } + } + protected virtual FieldCollection OnCompleteFields( ITypeCompletionContext context, InputObjectTypeDefinition definition) @@ -140,3 +181,12 @@ private void CreateDictionaryGetValues(object obj, object?[] fieldValues) } } } + +file static class Extensions +{ + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsOneOf(this DirectiveDefinition directiveDef) + => directiveDef.Value is DirectiveNode node + && node.Name.Value.EqualsOrdinal(WellKnownDirectives.OneOf); +} diff --git a/src/HotChocolate/Core/src/Types/Types/InputObjectType.cs b/src/HotChocolate/Core/src/Types/Types/InputObjectType.cs index 30944f0b971..3d5d1fc9ea8 100644 --- a/src/HotChocolate/Core/src/Types/Types/InputObjectType.cs +++ b/src/HotChocolate/Core/src/Types/Types/InputObjectType.cs @@ -60,6 +60,11 @@ public static InputObjectType CreateUnsafe(InputObjectTypeDefinition definition) /// public override TypeKind Kind => TypeKind.InputObject; + /// + /// Defines if this input object type is a oneOf input object. + /// + public bool IsOneOf { get; private set; } + /// /// Gets the fields of this type. /// diff --git a/src/HotChocolate/Core/src/Types/Types/InputParser.cs b/src/HotChocolate/Core/src/Types/Types/InputParser.cs index 6aa15d1f308..fa873795164 100644 --- a/src/HotChocolate/Core/src/Types/Types/InputParser.cs +++ b/src/HotChocolate/Core/src/Types/Types/InputParser.cs @@ -218,7 +218,7 @@ private object ParseObject( try { var fields = ((ObjectValueNode)resultValue).Fields; - var oneOf = type.Directives.ContainsDirective(WellKnownDirectives.OneOf); + var oneOf = type.IsOneOf; if (oneOf && fields.Count is 0) { @@ -532,7 +532,7 @@ private object DeserializeObject(object resultValue, InputObjectType type, Path { if (resultValue is IReadOnlyDictionary map) { - var oneOf = type.Directives.ContainsDirective(WellKnownDirectives.OneOf); + var oneOf = type.IsOneOf; if (oneOf && map.Count is 0) { diff --git a/src/HotChocolate/Core/src/Types/Types/Interceptors/MiddlewareValidationTypeInterceptor.cs b/src/HotChocolate/Core/src/Types/Types/Interceptors/MiddlewareValidationTypeInterceptor.cs index 436ec45c9bb..72ed9463f0a 100644 --- a/src/HotChocolate/Core/src/Types/Types/Interceptors/MiddlewareValidationTypeInterceptor.cs +++ b/src/HotChocolate/Core/src/Types/Types/Interceptors/MiddlewareValidationTypeInterceptor.cs @@ -17,11 +17,11 @@ internal sealed class MiddlewareValidationTypeInterceptor : TypeInterceptor private readonly HashSet _names = []; - public override void OnValidateType( - ITypeSystemObjectContext validationContext, + public override void OnAfterCompleteType( + ITypeCompletionContext completionContext, DefinitionBase definition) { - if (validationContext.DescriptorContext.Options.ValidatePipelineOrder && + if (completionContext.DescriptorContext.Options.ValidatePipelineOrder && definition is ObjectTypeDefinition objectTypeDef) { foreach (var field in objectTypeDef.Fields) @@ -29,8 +29,8 @@ public override void OnValidateType( if (field.MiddlewareDefinitions.Count > 1) { ValidatePipeline( - validationContext.Type, - new SchemaCoordinate(validationContext.Type.Name, field.Name), + completionContext.Type, + new SchemaCoordinate(completionContext.Type.Name, field.Name), field.MiddlewareDefinitions); } } diff --git a/src/HotChocolate/Core/src/Types/Types/InterfaceType.Initialization.cs b/src/HotChocolate/Core/src/Types/Types/InterfaceType.Initialization.cs index 11480f3d9f4..7a6fa960f11 100644 --- a/src/HotChocolate/Core/src/Types/Types/InterfaceType.Initialization.cs +++ b/src/HotChocolate/Core/src/Types/Types/InterfaceType.Initialization.cs @@ -2,6 +2,7 @@ using HotChocolate.Internal; using HotChocolate.Types.Descriptors; using HotChocolate.Types.Descriptors.Definitions; +using HotChocolate.Types.Helpers; using static HotChocolate.Internal.FieldInitHelper; using static HotChocolate.Types.Helpers.CompleteInterfacesHelper; @@ -60,6 +61,42 @@ protected override void OnCompleteType( _implements = CompleteInterfaces(context, definition.GetInterfaces(), this); } + protected override void OnCompleteMetadata( + ITypeCompletionContext context, + InterfaceTypeDefinition definition) + { + base.OnCompleteMetadata(context, definition); + + foreach (IFieldCompletion field in Fields) + { + field.CompleteMetadata(context, this); + } + } + + protected override void OnMakeExecutable( + ITypeCompletionContext context, + InterfaceTypeDefinition definition) + { + base.OnMakeExecutable(context, definition); + + foreach (IFieldCompletion field in Fields) + { + field.MakeExecutable(context, this); + } + } + + protected override void OnFinalizeType( + ITypeCompletionContext context, + InterfaceTypeDefinition definition) + { + base.OnFinalizeType(context, definition); + + foreach (IFieldCompletion field in Fields) + { + field.Finalize(context, this); + } + } + protected virtual FieldCollection OnCompleteFields( ITypeCompletionContext context, InterfaceTypeDefinition definition) diff --git a/src/HotChocolate/Core/src/Types/Types/NamedTypeBase.cs b/src/HotChocolate/Core/src/Types/Types/NamedTypeBase.cs index 4bcc331691d..25dcd2ec889 100644 --- a/src/HotChocolate/Core/src/Types/Types/NamedTypeBase.cs +++ b/src/HotChocolate/Core/src/Types/Types/NamedTypeBase.cs @@ -94,6 +94,13 @@ protected override void OnCompleteType( base.OnCompleteType(context, definition); UpdateRuntimeType(definition); + } + + protected override void OnCompleteMetadata( + ITypeCompletionContext context, + TDefinition definition) + { + base.OnCompleteMetadata(context, definition); _directives ??= DirectiveCollection.CreateAndComplete( context, this, definition.GetDirectives()); diff --git a/src/HotChocolate/Core/src/Types/Types/ObjectField.cs b/src/HotChocolate/Core/src/Types/Types/ObjectField.cs index b15fca14598..b8081004ffc 100644 --- a/src/HotChocolate/Core/src/Types/Types/ObjectField.cs +++ b/src/HotChocolate/Core/src/Types/Types/ObjectField.cs @@ -1,3 +1,4 @@ +using System.Collections.Concurrent; using System.Linq.Expressions; using System.Reflection; using HotChocolate.Configuration; @@ -109,12 +110,12 @@ private set /// public Expression? ResolverExpression { get; } - protected override void OnCompleteField( + protected override void OnMakeExecutable( ITypeCompletionContext context, ITypeSystemMember declaringMember, OutputFieldDefinitionBase definition) { - base.OnCompleteField(context, declaringMember, definition); + base.OnMakeExecutable(context, declaringMember, definition); CompleteResolver(context, (ObjectFieldDefinition)definition); } @@ -218,8 +219,6 @@ private void CompleteResolver( GetResultType(definition, RuntimeType)); } - return; - bool IsPureContext() { return skipMiddleware || (context.GlobalComponents.Count == 0 && fieldMiddlewareDefinitions.Count == 0); @@ -240,6 +239,8 @@ static Type GetResultType(ObjectFieldDefinition definition, Type runtimeType) file static class ResolverHelpers { + private static readonly ConcurrentDictionary _methodCache = new(); + private static readonly MethodInfo _createListPostProcessor = typeof(ResolverHelpers).GetMethod( nameof(CreateListPostProcessor), @@ -252,13 +253,16 @@ file static class ResolverHelpers if (extendedType.IsArrayOrList) { var elementType = extendedType.ElementType!.Type; - var generic = _createListPostProcessor.MakeGenericMethod(elementType); + var generic = GetFactoryMethod(elementType); return (IResolverResultPostProcessor?)generic.Invoke(null, []); } return null; } + private static MethodInfo GetFactoryMethod(Type elementType) + => _methodCache.GetOrAdd(elementType, static type => _createListPostProcessor.MakeGenericMethod(type)); + private static IResolverResultPostProcessor CreateListPostProcessor() => new ListPostProcessor(); } diff --git a/src/HotChocolate/Core/src/Types/Types/ObjectType.Initialization.cs b/src/HotChocolate/Core/src/Types/Types/ObjectType.Initialization.cs index 08e93b4fe67..a8140733497 100644 --- a/src/HotChocolate/Core/src/Types/Types/ObjectType.Initialization.cs +++ b/src/HotChocolate/Core/src/Types/Types/ObjectType.Initialization.cs @@ -65,6 +65,42 @@ protected override void OnCompleteType( } } + protected override void OnCompleteMetadata( + ITypeCompletionContext context, + ObjectTypeDefinition definition) + { + base.OnCompleteMetadata(context, definition); + + foreach (IFieldCompletion field in Fields) + { + field.CompleteMetadata(context, this); + } + } + + protected override void OnMakeExecutable( + ITypeCompletionContext context, + ObjectTypeDefinition definition) + { + base.OnMakeExecutable(context, definition); + + foreach (IFieldCompletion field in Fields) + { + field.MakeExecutable(context, this); + } + } + + protected override void OnFinalizeType( + ITypeCompletionContext context, + ObjectTypeDefinition definition) + { + base.OnFinalizeType(context, definition); + + foreach (IFieldCompletion field in Fields) + { + field.Finalize(context, this); + } + } + protected virtual FieldCollection OnCompleteFields( ITypeCompletionContext context, ObjectTypeDefinition definition) diff --git a/src/HotChocolate/Core/src/Types/Types/OutputFieldBase.cs b/src/HotChocolate/Core/src/Types/Types/OutputFieldBase.cs index 794fbd77b93..735db533249 100644 --- a/src/HotChocolate/Core/src/Types/Types/OutputFieldBase.cs +++ b/src/HotChocolate/Core/src/Types/Types/OutputFieldBase.cs @@ -1,5 +1,6 @@ using HotChocolate.Configuration; using HotChocolate.Types.Descriptors.Definitions; +using HotChocolate.Types.Helpers; using static HotChocolate.Internal.FieldInitHelper; #nullable enable @@ -31,7 +32,7 @@ internal OutputFieldBase(OutputFieldDefinitionBase definition, int index) IFieldCollection IOutputFieldInfo.Arguments => Arguments; /// - /// Defines if this field as a introspection field. + /// Defines if this field as an introspection field. /// public bool IsIntrospectionField => (Flags & FieldFlags.Introspection) == FieldFlags.Introspection; @@ -64,6 +65,7 @@ protected virtual void OnCompleteField( Arguments = OnCompleteFields(context, definition); } + // TODO: V15: should be renamed to OnCompleteArguments protected virtual FieldCollection OnCompleteFields( ITypeCompletionContext context, OutputFieldDefinitionBase definition) @@ -73,6 +75,63 @@ static Argument CreateArgument(ArgumentDefinition argDef, int index) => new(argDef, index); } + protected sealed override void OnCompleteMetadata( + ITypeCompletionContext context, + ITypeSystemMember declaringMember, + FieldDefinitionBase definition) + => OnCompleteMetadata(context, declaringMember, (OutputFieldDefinitionBase)definition); + + protected virtual void OnCompleteMetadata( + ITypeCompletionContext context, + ITypeSystemMember declaringMember, + OutputFieldDefinitionBase definition) + { + base.OnCompleteMetadata(context, declaringMember, definition); + + foreach (IFieldCompletion argument in Arguments) + { + argument.CompleteMetadata(context, this); + } + } + + protected sealed override void OnMakeExecutable( + ITypeCompletionContext context, + ITypeSystemMember declaringMember, + FieldDefinitionBase definition) + => OnMakeExecutable(context, declaringMember, (OutputFieldDefinitionBase)definition); + + protected virtual void OnMakeExecutable( + ITypeCompletionContext context, + ITypeSystemMember declaringMember, + OutputFieldDefinitionBase definition) + { + base.OnMakeExecutable(context, declaringMember, definition); + + foreach (IFieldCompletion argument in Arguments) + { + argument.MakeExecutable(context, this); + } + } + + protected sealed override void OnFinalizeField( + ITypeCompletionContext context, + ITypeSystemMember declaringMember, + FieldDefinitionBase definition) + => OnFinalizeField(context, declaringMember, (OutputFieldDefinitionBase)definition); + + protected virtual void OnFinalizeField( + ITypeCompletionContext context, + ITypeSystemMember declaringMember, + OutputFieldDefinitionBase definition) + { + base.OnFinalizeField(context, declaringMember, definition); + + foreach (IFieldCompletion argument in Arguments) + { + argument.Finalize(context, this); + } + } + /// /// Returns a string that represents the current field. /// diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/IdSerializationException.cs b/src/HotChocolate/Core/src/Types/Types/Relay/IdSerializationException.cs index 59e51c480bf..4d6dc548c10 100644 --- a/src/HotChocolate/Core/src/Types/Types/Relay/IdSerializationException.cs +++ b/src/HotChocolate/Core/src/Types/Types/Relay/IdSerializationException.cs @@ -2,6 +2,7 @@ namespace HotChocolate.Types.Relay; +#pragma warning disable RCS1194 public class IdSerializationException : GraphQLException { @@ -17,3 +18,4 @@ public IdSerializationException( { } } +#pragma warning restore RCS1194 diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/NodeResolverTypeInterceptor.cs b/src/HotChocolate/Core/src/Types/Types/Relay/NodeResolverTypeInterceptor.cs index b6866d7af4a..fad147fb065 100644 --- a/src/HotChocolate/Core/src/Types/Types/Relay/NodeResolverTypeInterceptor.cs +++ b/src/HotChocolate/Core/src/Types/Types/Relay/NodeResolverTypeInterceptor.cs @@ -179,7 +179,7 @@ fieldDef.Expression is null && } } - public override void OnAfterCompleteTypes() + public override void OnAfterMakeExecutable() { if (QueryType is not null && _nodes.Count > 0) { diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/Serialization/NodeIdInvalidFormatException.cs b/src/HotChocolate/Core/src/Types/Types/Relay/Serialization/NodeIdInvalidFormatException.cs index 25828eb97e0..cb8a748e120 100644 --- a/src/HotChocolate/Core/src/Types/Types/Relay/Serialization/NodeIdInvalidFormatException.cs +++ b/src/HotChocolate/Core/src/Types/Types/Relay/Serialization/NodeIdInvalidFormatException.cs @@ -1,8 +1,10 @@ #nullable enable namespace HotChocolate.Types.Relay; +#pragma warning disable RCS1194 public sealed class NodeIdInvalidFormatException(object originalValue) : GraphQLException(ErrorBuilder.New() .SetMessage("The node ID string has an invalid format.") .SetExtension(nameof(originalValue), originalValue.ToString()) .Build()); +#pragma warning restore RCS1194 diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/Serialization/NodeIdMissingSerializerException.cs b/src/HotChocolate/Core/src/Types/Types/Relay/Serialization/NodeIdMissingSerializerException.cs index 870e7aa35ce..6d60359ff63 100644 --- a/src/HotChocolate/Core/src/Types/Types/Relay/Serialization/NodeIdMissingSerializerException.cs +++ b/src/HotChocolate/Core/src/Types/Types/Relay/Serialization/NodeIdMissingSerializerException.cs @@ -1,8 +1,10 @@ #nullable enable namespace HotChocolate.Types.Relay; +#pragma warning disable RCS1194 public sealed class NodeIdMissingSerializerException(string typeName) : GraphQLException(ErrorBuilder.New() .SetMessage("No serializer registered for type `{0}`.", typeName) .SetExtension(nameof(typeName), typeName) .Build()); +#pragma warning restore RCS1194 diff --git a/src/HotChocolate/Core/src/Types/Types/TypeStatus.cs b/src/HotChocolate/Core/src/Types/Types/TypeStatus.cs index 3205ed81f69..da6de702468 100644 --- a/src/HotChocolate/Core/src/Types/Types/TypeStatus.cs +++ b/src/HotChocolate/Core/src/Types/Types/TypeStatus.cs @@ -6,5 +6,7 @@ internal enum TypeStatus Initialized, Named, Completed, + MetadataCompleted, + Executable, Finalized, } diff --git a/src/HotChocolate/Core/src/Types/Types/TypeSystemObjectBase.cs b/src/HotChocolate/Core/src/Types/Types/TypeSystemObjectBase.cs index c6594a29eb8..e75d1392981 100644 --- a/src/HotChocolate/Core/src/Types/Types/TypeSystemObjectBase.cs +++ b/src/HotChocolate/Core/src/Types/Types/TypeSystemObjectBase.cs @@ -76,6 +76,15 @@ protected internal bool IsNamed protected internal bool IsCompleted => _status is TypeStatus.Completed; + protected internal bool IsMetadataCompleted + => _status is TypeStatus.MetadataCompleted; + + protected internal bool IsExecutable + => _status is TypeStatus.Executable; + + protected internal bool IsSealed + => _status is TypeStatus.Finalized; + /// /// The type configuration is created and dependencies are registered. /// @@ -93,7 +102,7 @@ internal virtual void CompleteName(ITypeCompletionContext context) } /// - /// All type properties are set and the type settings are completed. + /// All type properties are set and the type structure is completed. /// internal virtual void CompleteType(ITypeCompletionContext context) { @@ -101,17 +110,36 @@ internal virtual void CompleteType(ITypeCompletionContext context) } /// + /// All type system directive are completed. + /// + internal virtual void CompleteMetadata(ITypeCompletionContext context) + { + MarkMetadataCompleted(); + } + + /// + /// All resolvers are compiled and the schema becomes executable. + /// + internal virtual void MakeExecutable(ITypeCompletionContext context) + { + MarkExecutable(); + } + + /// + /// /// All types are completed at this point and the type can clean up any /// temporary data structures. - /// + /// + /// /// This step is mainly to cleanup. + /// /// internal virtual void FinalizeType(ITypeCompletionContext context) { MarkFinalized(); } - protected void MarkInitialized() + private protected void MarkInitialized() { Debug.Assert(_status == TypeStatus.Uninitialized); @@ -123,7 +151,7 @@ protected void MarkInitialized() _status = TypeStatus.Initialized; } - protected void MarkNamed() + private protected void MarkNamed() { Debug.Assert(_status == TypeStatus.Initialized); @@ -135,7 +163,7 @@ protected void MarkNamed() _status = TypeStatus.Named; } - protected void MarkCompleted() + private protected void MarkCompleted() { Debug.Assert(_status == TypeStatus.Named); @@ -147,7 +175,7 @@ protected void MarkCompleted() _status = TypeStatus.Completed; } - protected void MarkFinalized() + private protected void MarkMetadataCompleted() { Debug.Assert(_status == TypeStatus.Completed); @@ -156,6 +184,30 @@ protected void MarkFinalized() throw new InvalidOperationException(); } + _status = TypeStatus.MetadataCompleted; + } + + private protected void MarkExecutable() + { + Debug.Assert(_status == TypeStatus.MetadataCompleted); + + if (_status != TypeStatus.MetadataCompleted) + { + throw new InvalidOperationException(); + } + + _status = TypeStatus.Executable; + } + + private protected void MarkFinalized() + { + Debug.Assert(_status == TypeStatus.Executable); + + if (_status != TypeStatus.Executable) + { + throw new InvalidOperationException(); + } + _status = TypeStatus.Finalized; } } diff --git a/src/HotChocolate/Core/src/Types/Types/TypeSystemObjectBase~1.cs b/src/HotChocolate/Core/src/Types/Types/TypeSystemObjectBase~1.cs index 7f69b191ade..3d710daf656 100644 --- a/src/HotChocolate/Core/src/Types/Types/TypeSystemObjectBase~1.cs +++ b/src/HotChocolate/Core/src/Types/Types/TypeSystemObjectBase~1.cs @@ -118,14 +118,58 @@ internal sealed override void CompleteType(ITypeCompletionContext context) OnAfterCompleteType(context, definition); ExecuteConfigurations(context, definition, ApplyConfigurationOn.AfterCompletion); - OnValidateType(context, definition); - MarkCompleted(); } + protected virtual void OnCompleteType( + ITypeCompletionContext context, + TDefinition definition) { } + + internal sealed override void CompleteMetadata(ITypeCompletionContext context) + { + AssertTypeCompleted(); + + var definition = _definition!; + + OnBeforeCompleteMetadata(context, definition); + OnCompleteMetadata(context, definition); + OnAfterCompleteMetadata(context, definition); + + MarkMetadataCompleted(); + } + + protected virtual void OnCompleteMetadata( + ITypeCompletionContext context, + TDefinition definition) { } + + internal sealed override void MakeExecutable(ITypeCompletionContext context) + { + AssertMetadataCompleted(); + + var definition = _definition!; + + OnBeforeMakeExecutable(context, definition); + OnMakeExecutable(context, definition); + OnAfterMakeExecutable(context, definition); + + MarkExecutable(); + } + + protected virtual void OnMakeExecutable( + ITypeCompletionContext context, + TDefinition definition) { } + + protected virtual void OnFinalizeType( + ITypeCompletionContext context, + TDefinition definition) { } + internal sealed override void FinalizeType(ITypeCompletionContext context) { - // we will release the definition here so that it can be collected by the GC. + // first we will call the OnFinalizeType hook. + OnFinalizeType(context, _definition!); + var definition = _definition!; + + // next we will release the definition here so that it can be collected by the GC. _definition = null; // if the ExtensionData object has no data we will release it so it can be @@ -143,13 +187,11 @@ internal sealed override void FinalizeType(ITypeCompletionContext context) _contextData = dictionary; } + OnValidateType(context, definition); + MarkFinalized(); } - protected virtual void OnCompleteType( - ITypeCompletionContext context, - TDefinition definition) { } - private void RegisterConfigurationDependencies( ITypeDiscoveryContext context, TDefinition definition) @@ -221,6 +263,26 @@ protected virtual void OnAfterCompleteType( DefinitionBase definition) => context.TypeInterceptor.OnAfterCompleteType(context, definition); + protected virtual void OnBeforeCompleteMetadata( + ITypeCompletionContext context, + DefinitionBase definition) + => context.TypeInterceptor.OnBeforeCompleteMetadata(context, definition); + + protected virtual void OnAfterCompleteMetadata( + ITypeCompletionContext context, + DefinitionBase definition) + => context.TypeInterceptor.OnAfterCompleteMetadata(context, definition); + + protected virtual void OnBeforeMakeExecutable( + ITypeCompletionContext context, + DefinitionBase definition) + => context.TypeInterceptor.OnBeforeMakeExecutable(context, definition); + + protected virtual void OnAfterMakeExecutable( + ITypeCompletionContext context, + DefinitionBase definition) + => context.TypeInterceptor.OnAfterMakeExecutable(context, definition); + protected virtual void OnValidateType( ITypeSystemObjectContext context, DefinitionBase definition) @@ -282,13 +344,49 @@ private void AssertNamed() } } + private void AssertTypeCompleted() + { + Debug.Assert( + IsCompleted, + "The type must be initialized."); + + if (!IsCompleted) + { + throw new InvalidOperationException(); + } + + if (_definition is null) + { + throw new InvalidOperationException( + TypeResources.TypeSystemObjectBase_DefinitionIsNull); + } + } + + private void AssertMetadataCompleted() + { + Debug.Assert( + IsMetadataCompleted, + "The type must be initialized."); + + if (!IsMetadataCompleted) + { + throw new InvalidOperationException(); + } + + if (_definition is null) + { + throw new InvalidOperationException( + TypeResources.TypeSystemObjectBase_DefinitionIsNull); + } + } + protected internal void AssertMutable() { Debug.Assert( - !IsCompleted, + !IsExecutable, "The type os no longer mutable."); - if (IsCompleted) + if (IsExecutable) { throw new InvalidOperationException("The type is no longer mutable."); } diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/EnumTypeTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/EnumTypeTests.cs index 7d4329754eb..10f6cd67469 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Types/EnumTypeTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Types/EnumTypeTests.cs @@ -457,21 +457,8 @@ public void EnumValue_SetContextData() public void EnumValue_DefinitionIsNull_ArgumentNullException() { // arrange - var completionContext = new Mock(); - // act - void Action() => new EnumValue(completionContext.Object, null!); - - // assert - Assert.Throws(Action); - } - - [Fact] - public void EnumValue_ContextIsNull_ArgumentNullException() - { - // arrange - // act - void Action() => new EnumValue(null!, new EnumValueDefinition()); + void Action() => new EnumValue(null!); // assert Assert.Throws(Action); @@ -481,10 +468,8 @@ public void EnumValue_ContextIsNull_ArgumentNullException() public void EnumValue_DefinitionValueIsNull_ArgumentNullException() { // arrange - var completionContext = new Mock(); - // act - void Action() => new EnumValue(completionContext.Object, new EnumValueDefinition()); + void Action() => new EnumValue(new EnumValueDefinition()); // assert Assert.Throws(Action); diff --git a/src/HotChocolate/Data/src/Data/Projections/ProjectionTypeInterceptor.cs b/src/HotChocolate/Data/src/Data/Projections/ProjectionTypeInterceptor.cs index fb0303515a3..32ba2bec3c5 100644 --- a/src/HotChocolate/Data/src/Data/Projections/ProjectionTypeInterceptor.cs +++ b/src/HotChocolate/Data/src/Data/Projections/ProjectionTypeInterceptor.cs @@ -22,7 +22,7 @@ public override void OnAfterResolveRootType( } } - public override void OnAfterCompleteType( + public override void OnAfterMakeExecutable( ITypeCompletionContext completionContext, DefinitionBase definition) { From f1ce1d5832db97314cccdbd64c210e3f93ac3525 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Fri, 24 Jan 2025 23:43:35 +0100 Subject: [PATCH 149/154] Fixed issue with required. --- global.json | 2 +- .../Extensions/PagingQueryableExtensions.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/global.json b/global.json index 2bc13e80ad8..93e6dbdd448 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.100", + "version": "9.0.101", "rollForward": "latestMinor" } } diff --git a/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs index 0ffcb382154..91b9265b49a 100644 --- a/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs +++ b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs @@ -461,8 +461,8 @@ private static Expression, CountResult>> Ge private class CountResult { - public required TKey Key { get; set; } - public required int Count { get; set; } + public TKey Key { get; set; } = default!; + public int Count { get; set; } } private static Page CreatePage( From 191571d128c487ea79f6f431da837d2c55b7f5bc Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Tue, 28 Jan 2025 22:41:19 +0000 Subject: [PATCH 150/154] Allow for error tracing on background tasks. (#7952) --- src/GreenDonut/src/Core/AutoBatchScheduler.cs | 16 ++- .../Subscriptions/OperationSession.cs | 7 +- .../Warmup/ExecutorWarmupService.cs | 3 +- .../GraphQLHttpEventStreamProcessor.cs | 2 +- .../GraphQLOverWebSocketProtocolHandler.cs | 55 ++++---- .../Transport.Sockets.Client/SocketClient.cs | 2 +- .../AutoUpdateRequestExecutorProxy.cs | 6 +- .../Processing/DeferredExecutionTask.cs | 24 ++-- .../Processing/Tasks/StreamHelper.cs | 3 +- .../src/Execution/RequestExecutorResolver.cs | 34 +++-- .../EventStreamResultFormatter.cs | 2 +- .../Subscriptions.Postgres/ContinuousTask.cs | 5 +- .../RabbitMQConnection.cs | 67 ++++----- .../Core/src/Subscriptions/DefaultTopic.cs | 5 +- .../HotChocolate.Subscriptions.csproj | 1 + .../GatewayConfigurationFileObserver.cs | 27 ++-- .../StaticGatewayConfigurationFileObserver.cs | 32 +++-- .../BackgroundTaskErrorInterceptor.cs | 22 +++ .../Utilities/FireAndForgetTaskExtensions.cs | 128 ++++++++++++++++++ .../Messaging/MessageProcessor.cs | 6 +- .../Messaging/MessageReceiver.cs | 6 +- 21 files changed, 299 insertions(+), 154 deletions(-) create mode 100644 src/HotChocolate/Utilities/src/Utilities/BackgroundTaskErrorInterceptor.cs create mode 100644 src/HotChocolate/Utilities/src/Utilities/FireAndForgetTaskExtensions.cs diff --git a/src/GreenDonut/src/Core/AutoBatchScheduler.cs b/src/GreenDonut/src/Core/AutoBatchScheduler.cs index 6f31a3f6ac6..fda4efd2682 100644 --- a/src/GreenDonut/src/Core/AutoBatchScheduler.cs +++ b/src/GreenDonut/src/Core/AutoBatchScheduler.cs @@ -15,11 +15,17 @@ public void Schedule(Func dispatch) => BeginDispatch(dispatch); private static void BeginDispatch(Func dispatch) - => Task.Factory.StartNew( - async () => await dispatch().ConfigureAwait(false), - default, - TaskCreationOptions.DenyChildAttach, - TaskScheduler.Default); + => Task.Run(async () => + { + try + { + await dispatch().ConfigureAwait(false); + } + catch + { + // we do ignore any potential exceptions here + } + }); /// /// Gets the default instance if the . diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore/Subscriptions/OperationSession.cs b/src/HotChocolate/AspNetCore/src/AspNetCore/Subscriptions/OperationSession.cs index 6bc08aa6582..2ff5521dce7 100644 --- a/src/HotChocolate/AspNetCore/src/AspNetCore/Subscriptions/OperationSession.cs +++ b/src/HotChocolate/AspNetCore/src/AspNetCore/Subscriptions/OperationSession.cs @@ -1,4 +1,5 @@ using HotChocolate.Language; +using HotChocolate.Utilities; namespace HotChocolate.AspNetCore.Subscriptions; @@ -34,11 +35,7 @@ public OperationSession( public bool IsCompleted { get; private set; } public void BeginExecute(GraphQLRequest request, CancellationToken cancellationToken) - => Task.Factory.StartNew( - () => SendResultsAsync(request, cancellationToken), - default, - TaskCreationOptions.None, - TaskScheduler.Default); + => SendResultsAsync(request, cancellationToken).FireAndForget(); private async Task SendResultsAsync(GraphQLRequest request, CancellationToken cancellationToken) { diff --git a/src/HotChocolate/AspNetCore/src/AspNetCore/Warmup/ExecutorWarmupService.cs b/src/HotChocolate/AspNetCore/src/AspNetCore/Warmup/ExecutorWarmupService.cs index 5b211f8782f..c518e7f8f11 100644 --- a/src/HotChocolate/AspNetCore/src/AspNetCore/Warmup/ExecutorWarmupService.cs +++ b/src/HotChocolate/AspNetCore/src/AspNetCore/Warmup/ExecutorWarmupService.cs @@ -1,3 +1,4 @@ +using HotChocolate.Utilities; using Microsoft.Extensions.Hosting; namespace HotChocolate.AspNetCore.Warmup; @@ -46,7 +47,7 @@ private void BeginWarmup(string schemaName) { if (_tasks.TryGetValue(schemaName, out var value) && value.Any(t => t.KeepWarm)) { - Task.Factory.StartNew(() => WarmupAsync(schemaName, value, _stopping), _stopping); + WarmupAsync(schemaName, value, _stopping).FireAndForget(); } } diff --git a/src/HotChocolate/AspNetCore/src/Transport.Http/GraphQLHttpEventStreamProcessor.cs b/src/HotChocolate/AspNetCore/src/Transport.Http/GraphQLHttpEventStreamProcessor.cs index cc0f15fd231..2c3e42e5af4 100644 --- a/src/HotChocolate/AspNetCore/src/Transport.Http/GraphQLHttpEventStreamProcessor.cs +++ b/src/HotChocolate/AspNetCore/src/Transport.Http/GraphQLHttpEventStreamProcessor.cs @@ -19,7 +19,7 @@ public static IAsyncEnumerable ReadStream(Stream stream, Cancel var reader = pipe.Reader; var writer = pipe.Writer; - Task.Run(async () => await ReadFromTransportAsync(stream, writer, ct).ConfigureAwait(false), ct); + ReadFromTransportAsync(stream, writer, ct).FireAndForget(); return ReadMessagesPipeAsync(reader, ct); } diff --git a/src/HotChocolate/AspNetCore/src/Transport.Sockets.Client/Protocols/GraphQLOverWebSocket/GraphQLOverWebSocketProtocolHandler.cs b/src/HotChocolate/AspNetCore/src/Transport.Sockets.Client/Protocols/GraphQLOverWebSocket/GraphQLOverWebSocketProtocolHandler.cs index 32d2867a8a2..f3730f592e5 100644 --- a/src/HotChocolate/AspNetCore/src/Transport.Sockets.Client/Protocols/GraphQLOverWebSocket/GraphQLOverWebSocketProtocolHandler.cs +++ b/src/HotChocolate/AspNetCore/src/Transport.Sockets.Client/Protocols/GraphQLOverWebSocket/GraphQLOverWebSocketProtocolHandler.cs @@ -2,6 +2,7 @@ using System.Net.WebSockets; using System.Text.Json; using HotChocolate.Transport.Sockets.Client.Protocols.GraphQLOverWebSocket.Messages; +using HotChocolate.Utilities; namespace HotChocolate.Transport.Sockets.Client.Protocols.GraphQLOverWebSocket; @@ -153,39 +154,37 @@ public void TrySendCompleteMessage() { if (!_completed) { - Task.Factory.StartNew( - async () => - { - using var cts = new CancellationTokenSource(2000); - - try - { - if (socket.IsOpen()) - { - await socket.SendCompleteMessageAsync(id, cts.Token); - } - } - catch - { - // if we cannot send the complete message we will just abort the socket. - try - { - socket.Abort(); - } - catch - { - // ignore - } - } - }, - CancellationToken.None, - TaskCreationOptions.None, - TaskScheduler.Default); + TrySendCompleteMessageInternalAsync(socket, id).FireAndForget(); _completed = true; } } } + private static async Task TrySendCompleteMessageInternalAsync(WebSocket socket, string id) + { + using var cts = new CancellationTokenSource(2000); + + try + { + if (socket.IsOpen()) + { + await socket.SendCompleteMessageAsync(id, cts.Token); + } + } + catch + { + // if we cannot send the complete message we will just abort the socket. + try + { + socket.Abort(); + } + catch + { + // ignore + } + } + } + private enum MessageType { None, diff --git a/src/HotChocolate/AspNetCore/src/Transport.Sockets.Client/SocketClient.cs b/src/HotChocolate/AspNetCore/src/Transport.Sockets.Client/SocketClient.cs index 6dc2c4595d5..f9313450ce7 100644 --- a/src/HotChocolate/AspNetCore/src/Transport.Sockets.Client/SocketClient.cs +++ b/src/HotChocolate/AspNetCore/src/Transport.Sockets.Client/SocketClient.cs @@ -76,7 +76,7 @@ private ValueTask InitializeAsync(T payload, CancellationToken cancellationTo } private void BeginRunPipeline() - => Task.Factory.StartNew(() => _pipeline.RunAsync(_ct), _ct); + => _pipeline.RunAsync(_ct).FireAndForget(); public ValueTask ExecuteAsync( OperationRequest request, diff --git a/src/HotChocolate/Core/src/Execution/AutoUpdateRequestExecutorProxy.cs b/src/HotChocolate/Core/src/Execution/AutoUpdateRequestExecutorProxy.cs index 268129d727b..a7c2af5ee57 100644 --- a/src/HotChocolate/Core/src/Execution/AutoUpdateRequestExecutorProxy.cs +++ b/src/HotChocolate/Core/src/Execution/AutoUpdateRequestExecutorProxy.cs @@ -1,3 +1,5 @@ +using HotChocolate.Utilities; + namespace HotChocolate.Execution; /// @@ -143,7 +145,7 @@ public Task ExecuteBatchAsync( => _executor.ExecuteBatchAsync(requestBatch, cancellationToken); private void BeginUpdateExecutor() - => Task.Run(UpdateExecutorAsync); + => UpdateExecutorAsync().FireAndForget(); private async ValueTask UpdateExecutorAsync() { @@ -152,7 +154,7 @@ private async ValueTask UpdateExecutorAsync() try { var executor = await _executorProxy - .GetRequestExecutorAsync(default) + .GetRequestExecutorAsync(CancellationToken.None) .ConfigureAwait(false); _executor = executor; } diff --git a/src/HotChocolate/Core/src/Execution/Processing/DeferredExecutionTask.cs b/src/HotChocolate/Core/src/Execution/Processing/DeferredExecutionTask.cs index 747c21d4c77..7427e43dd89 100644 --- a/src/HotChocolate/Core/src/Execution/Processing/DeferredExecutionTask.cs +++ b/src/HotChocolate/Core/src/Execution/Processing/DeferredExecutionTask.cs @@ -1,4 +1,5 @@ using System.Collections.Immutable; +using HotChocolate.Utilities; using static HotChocolate.WellKnownContextData; namespace HotChocolate.Execution.Processing; @@ -48,25 +49,16 @@ public void Begin(OperationContextOwner operationContextOwner, uint resultId, ui var capturedContext = ExecutionContext.Capture(); if (capturedContext is null) { - Task.Factory.StartNew( - () => ExecuteAsync(operationContextOwner, resultId, parentResultId, patchId), - default, - TaskCreationOptions.None, - TaskScheduler.Default); + ExecuteAsync(operationContextOwner, resultId, parentResultId, patchId).FireAndForget(); } else { - Task.Factory.StartNew( - () => - { - ExecutionContext.Run( - capturedContext, - _ => ExecuteAsync(operationContextOwner, resultId, parentResultId, patchId), - null); - }, - default, - TaskCreationOptions.None, - TaskScheduler.Default); + var execute = () => + ExecutionContext.Run( + capturedContext, + _ => ExecuteAsync(operationContextOwner, resultId, parentResultId, patchId), + null); + execute.FireAndForget(); } } diff --git a/src/HotChocolate/Core/src/Execution/Processing/Tasks/StreamHelper.cs b/src/HotChocolate/Core/src/Execution/Processing/Tasks/StreamHelper.cs index eaaba6bd69a..3dad1498f71 100644 --- a/src/HotChocolate/Core/src/Execution/Processing/Tasks/StreamHelper.cs +++ b/src/HotChocolate/Core/src/Execution/Processing/Tasks/StreamHelper.cs @@ -136,7 +136,8 @@ public async ValueTask MoveNextAsync() () => _enumerator.ToList(), _cancellationToken, TaskCreationOptions.None, - TaskScheduler.Default); + TaskScheduler.Default) + .ConfigureAwait(false); if (_index >= _list.Count) { diff --git a/src/HotChocolate/Core/src/Execution/RequestExecutorResolver.cs b/src/HotChocolate/Core/src/Execution/RequestExecutorResolver.cs index 3c7a134a35b..10834f89d88 100644 --- a/src/HotChocolate/Core/src/Execution/RequestExecutorResolver.cs +++ b/src/HotChocolate/Core/src/Execution/RequestExecutorResolver.cs @@ -192,24 +192,22 @@ private void EvictAllRequestExecutors() #endif private static void BeginRunEvictionEvents(RegisteredExecutor registeredExecutor) - => Task.Factory.StartNew( - async () => - { - try - { - await OnRequestExecutorEvictedAsync(registeredExecutor); - } - finally - { - // we will give the request executor some grace period to finish all request - // in the pipeline - await Task.Delay(TimeSpan.FromMinutes(5)); - registeredExecutor.Dispose(); - } - }, - default, - TaskCreationOptions.DenyChildAttach, - TaskScheduler.Default); + => RunEvictionEvents(registeredExecutor).FireAndForget(); + + private static async Task RunEvictionEvents(RegisteredExecutor registeredExecutor) + { + try + { + await OnRequestExecutorEvictedAsync(registeredExecutor); + } + finally + { + // we will give the request executor some grace period to finish all request + // in the pipeline + await Task.Delay(TimeSpan.FromMinutes(5)); + registeredExecutor.Dispose(); + } + } private async Task CreateSchemaServicesAsync( ConfigurationContext context, diff --git a/src/HotChocolate/Core/src/Execution/Serialization/EventStreamResultFormatter.cs b/src/HotChocolate/Core/src/Execution/Serialization/EventStreamResultFormatter.cs index 05e81483aa0..95feceb8c91 100644 --- a/src/HotChocolate/Core/src/Execution/Serialization/EventStreamResultFormatter.cs +++ b/src/HotChocolate/Core/src/Execution/Serialization/EventStreamResultFormatter.cs @@ -236,7 +236,7 @@ private void EnsureKeepAlive() { if (DateTime.UtcNow - _lastWriteTime >= _keepAlivePeriod) { - Task.Run(WriteKeepAliveAsync); + WriteKeepAliveAsync().FireAndForget(); } return; diff --git a/src/HotChocolate/Core/src/Subscriptions.Postgres/ContinuousTask.cs b/src/HotChocolate/Core/src/Subscriptions.Postgres/ContinuousTask.cs index 960a3ee1927..3b2b26f0610 100644 --- a/src/HotChocolate/Core/src/Subscriptions.Postgres/ContinuousTask.cs +++ b/src/HotChocolate/Core/src/Subscriptions.Postgres/ContinuousTask.cs @@ -1,3 +1,5 @@ +using HotChocolate.Utilities; + namespace HotChocolate.Subscriptions.Postgres; internal sealed class ContinuousTask : IAsyncDisposable @@ -7,7 +9,6 @@ internal sealed class ContinuousTask : IAsyncDisposable private readonly CancellationTokenSource _completion = new(); private readonly Func _handler; private readonly TimeProvider _timeProvider; - private readonly Task _task; private bool _disposed; public ContinuousTask(Func handler, TimeProvider timeProvider) @@ -17,7 +18,7 @@ public ContinuousTask(Func handler, TimeProvider timePr // We do not use Task.Factory.StartNew here because RunContinuously is an async method and // the LongRunning flag only works until the first await. - _task = RunContinuously(); + RunContinuously().FireAndForget(); } public CancellationToken Completion => _completion.Token; diff --git a/src/HotChocolate/Core/src/Subscriptions.RabbitMQ/RabbitMQConnection.cs b/src/HotChocolate/Core/src/Subscriptions.RabbitMQ/RabbitMQConnection.cs index a4fdcf10acc..43ac6d5572f 100644 --- a/src/HotChocolate/Core/src/Subscriptions.RabbitMQ/RabbitMQConnection.cs +++ b/src/HotChocolate/Core/src/Subscriptions.RabbitMQ/RabbitMQConnection.cs @@ -1,4 +1,5 @@ using HotChocolate.Subscriptions.Diagnostics; +using HotChocolate.Utilities; using RabbitMQ.Client; using RabbitMQ.Client.Exceptions; using static HotChocolate.Subscriptions.RabbitMQ.RabbitMQResources; @@ -74,49 +75,49 @@ private void ThrowIfDisposed() } private void InitializeConnection(ConnectionFactory connectionFactory) + => InitializeConnectionAsync(connectionFactory).FireAndForget(); + + private async Task InitializeConnectionAsync(ConnectionFactory connectionFactory) { - Task.Run(async () => - { - connectionFactory.AutomaticRecoveryEnabled = true; - connectionFactory.DispatchConsumersAsync = true; - var connectionAttempt = 0; + connectionFactory.AutomaticRecoveryEnabled = true; + connectionFactory.DispatchConsumersAsync = true; + var connectionAttempt = 0; - while (connectionAttempt < _retryCount) + while (connectionAttempt < _retryCount) + { + try { - try - { - var connection = connectionFactory.CreateConnection(); + var connection = connectionFactory.CreateConnection(); - if (_completionSource.TrySetResult(connection)) - { - return; - } - - throw new InvalidOperationException( - RabbitMQConnection_InitializeConnection_ConnectionSucceededButFailedUnexpectedly); - } - catch (BrokerUnreachableException) + if (_completionSource.TrySetResult(connection)) { - connectionAttempt++; - _diagnosticEvents.ProviderInfo(string.Format( - RabbitMQConnection_InitializeConnection_ConnectionAttemptFailed, - connectionAttempt)); + return; } - if (connectionAttempt < _retryCount) - { - await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, connectionAttempt))).ConfigureAwait(false); - } + throw new InvalidOperationException( + RabbitMQConnection_InitializeConnection_ConnectionSucceededButFailedUnexpectedly); + } + catch (BrokerUnreachableException) + { + connectionAttempt++; + _diagnosticEvents.ProviderInfo(string.Format( + RabbitMQConnection_InitializeConnection_ConnectionAttemptFailed, + connectionAttempt)); } - _diagnosticEvents.ProviderInfo(string.Format( - RabbitMQConnection_InitializeConnection_ConnectionFailedAfterRetry, - connectionAttempt)); - - if (!_completionSource.TrySetException(new RabbitMQConnectionFailedException(connectionAttempt))) + if (connectionAttempt < _retryCount) { - throw new InvalidOperationException(RabbitMQConnection_InitializeConnection_ConnectionFailedUnexpectedly); + await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, connectionAttempt))).ConfigureAwait(false); } - }); + } + + _diagnosticEvents.ProviderInfo(string.Format( + RabbitMQConnection_InitializeConnection_ConnectionFailedAfterRetry, + connectionAttempt)); + + if (!_completionSource.TrySetException(new RabbitMQConnectionFailedException(connectionAttempt))) + { + throw new InvalidOperationException(RabbitMQConnection_InitializeConnection_ConnectionFailedUnexpectedly); + } } } diff --git a/src/HotChocolate/Core/src/Subscriptions/DefaultTopic.cs b/src/HotChocolate/Core/src/Subscriptions/DefaultTopic.cs index 32b794a0e23..0515b3e4d30 100644 --- a/src/HotChocolate/Core/src/Subscriptions/DefaultTopic.cs +++ b/src/HotChocolate/Core/src/Subscriptions/DefaultTopic.cs @@ -3,6 +3,7 @@ using System.Threading.Channels; using HotChocolate.Execution; using HotChocolate.Subscriptions.Diagnostics; +using HotChocolate.Utilities; using static System.Runtime.InteropServices.CollectionsMarshal; using static System.Threading.Channels.Channel; @@ -193,9 +194,7 @@ internal async ValueTask ConnectAsync(CancellationToken ct = default) } private void BeginProcessing(IDisposable session) - => Task.Factory.StartNew( - async s => await ProcessMessagesSessionAsync((IDisposable)s!).ConfigureAwait(false), - session); + => ProcessMessagesSessionAsync(session).FireAndForget(); private async Task ProcessMessagesSessionAsync(IDisposable session) { diff --git a/src/HotChocolate/Core/src/Subscriptions/HotChocolate.Subscriptions.csproj b/src/HotChocolate/Core/src/Subscriptions/HotChocolate.Subscriptions.csproj index f52f4e42b59..3ab323dddf9 100644 --- a/src/HotChocolate/Core/src/Subscriptions/HotChocolate.Subscriptions.csproj +++ b/src/HotChocolate/Core/src/Subscriptions/HotChocolate.Subscriptions.csproj @@ -16,6 +16,7 @@ + diff --git a/src/HotChocolate/Fusion/src/Core/DependencyInjection/GatewayConfigurationFileObserver.cs b/src/HotChocolate/Fusion/src/Core/DependencyInjection/GatewayConfigurationFileObserver.cs index 79f6cda778f..10f6fd40b2a 100644 --- a/src/HotChocolate/Fusion/src/Core/DependencyInjection/GatewayConfigurationFileObserver.cs +++ b/src/HotChocolate/Fusion/src/Core/DependencyInjection/GatewayConfigurationFileObserver.cs @@ -78,19 +78,20 @@ public FileConfigurationSession(IObserver observer, string } private void BeginLoadConfig() - => Task.Run( - async () => - { - try - { - var document = await GatewayConfigurationFileUtils.LoadDocumentAsync(_fileName, default); - _observer.OnNext(new GatewayConfiguration(document)); - } - catch(Exception ex) - { - _observer.OnError(ex); - } - }); + => LoadConfig().FireAndForget(); + + private async Task LoadConfig() + { + try + { + var document = await GatewayConfigurationFileUtils.LoadDocumentAsync(_fileName, CancellationToken.None); + _observer.OnNext(new GatewayConfiguration(document)); + } + catch(Exception ex) + { + _observer.OnError(ex); + } + } public void Dispose() => _watcher.Dispose(); diff --git a/src/HotChocolate/Fusion/src/Core/DependencyInjection/StaticGatewayConfigurationFileObserver.cs b/src/HotChocolate/Fusion/src/Core/DependencyInjection/StaticGatewayConfigurationFileObserver.cs index 6c09abee59b..121fbc6e439 100644 --- a/src/HotChocolate/Fusion/src/Core/DependencyInjection/StaticGatewayConfigurationFileObserver.cs +++ b/src/HotChocolate/Fusion/src/Core/DependencyInjection/StaticGatewayConfigurationFileObserver.cs @@ -1,3 +1,4 @@ +using HotChocolate.Utilities; using static Microsoft.Extensions.DependencyInjection.GatewayConfigurationFileUtils; namespace Microsoft.Extensions.DependencyInjection; @@ -25,20 +26,23 @@ private class FileConfigurationSession : IDisposable { public FileConfigurationSession(IObserver observer, string filename) { - Task.Run( - async () => - { - try - { - var document = await LoadDocumentAsync(filename, default); - observer.OnNext(new GatewayConfiguration(document)); - } - catch(Exception ex) - { - observer.OnError(ex); - observer.OnCompleted(); - } - }); + ObserveFileChangesAsync(filename, observer).FireAndForget(); + } + + private static async Task ObserveFileChangesAsync( + string filename, + IObserver observer) + { + try + { + var document = await LoadDocumentAsync(filename, CancellationToken.None); + observer.OnNext(new GatewayConfiguration(document)); + } + catch(Exception ex) + { + observer.OnError(ex); + observer.OnCompleted(); + } } public void Dispose() diff --git a/src/HotChocolate/Utilities/src/Utilities/BackgroundTaskErrorInterceptor.cs b/src/HotChocolate/Utilities/src/Utilities/BackgroundTaskErrorInterceptor.cs new file mode 100644 index 00000000000..4908955bc84 --- /dev/null +++ b/src/HotChocolate/Utilities/src/Utilities/BackgroundTaskErrorInterceptor.cs @@ -0,0 +1,22 @@ +namespace HotChocolate.Utilities; + +public abstract class BackgroundTaskErrorInterceptor : IDisposable +{ + private bool _disposed; + + protected BackgroundTaskErrorInterceptor() + { + FireAndForgetTaskExtensions.SubscribeToErrors(this); + } + + public abstract void OnError(Exception exception); + + public void Dispose() + { + if (!_disposed) + { + FireAndForgetTaskExtensions.UnsubscribeFromErrors(this); + _disposed = true; + } + } +} diff --git a/src/HotChocolate/Utilities/src/Utilities/FireAndForgetTaskExtensions.cs b/src/HotChocolate/Utilities/src/Utilities/FireAndForgetTaskExtensions.cs new file mode 100644 index 00000000000..b84b839f210 --- /dev/null +++ b/src/HotChocolate/Utilities/src/Utilities/FireAndForgetTaskExtensions.cs @@ -0,0 +1,128 @@ +using System.Collections.Concurrent; +using System.Collections.Immutable; + +namespace HotChocolate.Utilities; + +public static class FireAndForgetTaskExtensions +{ + private static ImmutableArray _interceptors = + ImmutableArray.Empty; + + public static void FireAndForget( + this Task task, + Action? onComplete = null, + Action? onError = null) + { + if (task is null) + { + throw new ArgumentNullException(nameof(task)); + } + + _ = FireAndForgetInternal(task, onComplete, onError); + + static async Task FireAndForgetInternal( + Task task, + Action? onComplete, + Action? onError) + { + try + { + await task.ConfigureAwait(false); + onComplete?.Invoke(); + } + catch (Exception ex) + { + var interceptors = _interceptors; + foreach (var interceptor in interceptors) + { + interceptor.OnError(ex); + } + + onError?.Invoke(ex); + } + } + } + + public static void FireAndForget( + this ValueTask task, + Action? onComplete = null, + Action? onError = null) + { + _ = FireAndForgetInternal(task, onComplete, onError); + + static async ValueTask FireAndForgetInternal( + ValueTask task, + Action? onComplete, + Action? onError) + { + try + { + await task.ConfigureAwait(false); + onComplete?.Invoke(); + } + catch (Exception ex) + { + var interceptors = _interceptors; + foreach (var interceptor in interceptors) + { + interceptor.OnError(ex); + } + + onError?.Invoke(ex); + } + } + } + + public static void FireAndForget( + this Action action, + Action? onComplete = null, + Action? onError = null) + { + _ = FireAndForgetInternal( + Task.Run(action), + onComplete, + onError); + + static async Task FireAndForgetInternal( + Task task, + Action? onComplete, + Action? onError) + { + try + { + await task.ConfigureAwait(false); + onComplete?.Invoke(); + } + catch (Exception ex) + { + var interceptors = _interceptors; + foreach (var interceptor in interceptors) + { + interceptor.OnError(ex); + } + + onError?.Invoke(ex); + } + } + } + + internal static void SubscribeToErrors(BackgroundTaskErrorInterceptor interceptor) + { + if (interceptor is null) + { + throw new ArgumentNullException(nameof(interceptor)); + } + + ImmutableInterlocked.Update(ref _interceptors, x => x.Add(interceptor)); + } + + internal static void UnsubscribeFromErrors(BackgroundTaskErrorInterceptor interceptor) + { + if (interceptor is null) + { + throw new ArgumentNullException(nameof(interceptor)); + } + + ImmutableInterlocked.Update(ref _interceptors, x => x.Remove(interceptor)); + } +} diff --git a/src/StrawberryShake/Client/src/Transport.WebSockets/Messaging/MessageProcessor.cs b/src/StrawberryShake/Client/src/Transport.WebSockets/Messaging/MessageProcessor.cs index 364c25169b0..867e6947019 100644 --- a/src/StrawberryShake/Client/src/Transport.WebSockets/Messaging/MessageProcessor.cs +++ b/src/StrawberryShake/Client/src/Transport.WebSockets/Messaging/MessageProcessor.cs @@ -41,11 +41,7 @@ public static Task Start( PipeReader reader, ProcessAsync process, CancellationToken cancellationToken) - => Task.Factory.StartNew( - () => ProcessMessagesAsync(reader, process, cancellationToken), - cancellationToken, - TaskCreationOptions.LongRunning, - TaskScheduler.Default); + => ProcessMessagesAsync(reader, process, cancellationToken); private static async Task ProcessMessagesAsync( PipeReader reader, diff --git a/src/StrawberryShake/Client/src/Transport.WebSockets/Messaging/MessageReceiver.cs b/src/StrawberryShake/Client/src/Transport.WebSockets/Messaging/MessageReceiver.cs index fd1087db52b..ce9448e4328 100644 --- a/src/StrawberryShake/Client/src/Transport.WebSockets/Messaging/MessageReceiver.cs +++ b/src/StrawberryShake/Client/src/Transport.WebSockets/Messaging/MessageReceiver.cs @@ -22,11 +22,7 @@ public static Task Start( PipeWriter writer, ISocketClient client, CancellationToken cancellationToken) - => Task.Factory.StartNew( - () => ReceiveAsync(client, writer, cancellationToken), - cancellationToken, - TaskCreationOptions.LongRunning, - TaskScheduler.Default); + => ReceiveAsync(client, writer, cancellationToken); private static async Task ReceiveAsync( ISocketClient client, From 44f8b0ac51e45f329133e1fb6245e08307aa5ec3 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Tue, 28 Jan 2025 23:11:42 +0000 Subject: [PATCH 151/154] Added DataContext for simpler access of selections, filters and sorting. (#7957) --- src/GreenDonut/GreenDonut.sln | 7 + .../src/Core/Data/DataLoaderStateHelper.cs | 107 +++++ .../src/Core/Data/DataLoaderStateKeys.cs | 8 + .../src/Core/Data/DefaultPredicateBuilder.cs | 85 ++++ .../src/Core/Data/DefaultSelectorBuilder.cs | 85 ++++ .../src/Core/Data/ExpressionHasher.cs | 347 +++++++++++++++++ .../src/Core/Data/ExpressionHasherPool.cs | 9 + ...GreenDonutPredicateDataLoaderExtensions.cs | 151 +++++++ ...enDonutQueryContextDataLoaderExtensions.cs | 136 +++++++ .../Data/GreenDonutQueryableExtensions.cs | 367 ++++++++++++++++++ ...GreenDonutSelectionDataLoaderExtensions.cs | 222 +++++++++++ .../GreenDonutSortingDataLoaderExtensions.cs | 130 +++++++ .../{Predicates => Data}/IPredicateBuilder.cs | 6 +- .../IQueryDataLoader.cs} | 10 +- .../{Selectors => Data}/ISelectorBuilder.cs | 8 +- .../{Selectors => Data}/KeyValueResult.cs | 2 +- .../QueryDataLoader.cs} | 10 +- src/GreenDonut/src/Core/Data/QueryState.cs | 3 + src/GreenDonut/src/Core/DataLoaderBase.cs | 2 +- .../src/Core/DataLoaderFetchContext.cs | 91 ++++- src/GreenDonut/src/Core/Experiments.cs | 7 - src/GreenDonut/src/Core/GreenDonut.csproj | 4 + .../Predicates/DefaultPredicateBuilder.cs | 58 --- .../PredicateDataLoaderExtensions.cs | 119 ------ .../Core/Selectors/DefaultSelectorBuilder.cs | 58 --- .../Core/Selectors/ISelectionDataLoader.cs | 27 -- .../src/Core/Selectors/SelectionDataLoader.cs | 36 -- .../SelectionDataLoaderExtensions.cs | 287 -------------- .../GreenDonut.Data.Primitives.csproj | 10 + src/GreenDonut/src/Data.Primitives/ISortBy.cs | 44 +++ .../src/Data.Primitives/QueryContext.cs | 31 ++ src/GreenDonut/src/Data.Primitives/SortBy.cs | 82 ++++ .../src/Data.Primitives/SortDefinition.cs | 79 ++++ .../test/Core.Tests/ExpressionHasherTests.cs | 123 ++++++ .../Configuration/ConfigurationContext.cs | 20 + .../RequestExecutorBuilderExtensions.cs | 25 ++ .../Core/src/Execution/Experiments.cs | 7 - ...tChocolateExecutionDataLoaderExtensions.cs | 71 +--- ...otChocolateExecutionSelectionExtensions.cs | 8 +- .../src/Execution/RequestExecutorResolver.cs | 2 +- .../FileBuilders/DataLoaderFileBuilder.cs | 58 ++- .../FileBuilders/IOutputTypeFileBuilder.cs | 2 + .../InterfaceTypeExtensionFileBuilder.cs | 4 +- .../FileBuilders/ModuleFileBuilder.cs | 185 +-------- .../ObjectTypeExtensionFileBuilder.cs | 197 +++++++--- .../FileBuilders/ResolverFileBuilder.cs | 167 ++++---- .../Generators/TypeModuleSyntaxGenerator.cs | 74 ++-- .../Generators/TypesSyntaxGenerator.cs | 6 +- .../Helpers/DataLoaderAttributeHelper.cs | 2 +- .../Helpers/SymbolExtensions.cs | 57 ++- .../Inspectors/DataLoaderInspector.cs | 1 - .../ObjectTypeExtensionInfoInspector.cs | 132 +++++-- .../Inspectors/TypeAttributeInspector.cs | 18 + .../Types.Analyzers/Models/DataLoaderInfo.cs | 44 ++- .../Models/DataLoaderParameterKind.cs | 4 +- .../Types.Analyzers/Models/IOutputTypeInfo.cs | 5 +- .../Models/InterfaceTypeExtensionInfo.cs | 2 + .../Models/ObjectTypeExtensionInfo.cs | 2 + .../Models/ResolverParameter.cs | 20 + .../Models/ResolverParameterKind.cs | 3 +- .../Models/RootTypeExtensionInfo.cs | 45 +++ .../src/Types.Analyzers/WellKnownTypes.cs | 8 +- .../Conventions/DescriptorContext.cs | 3 + .../Conventions/IDescriptorContext.cs | 5 + .../Types/Descriptors/TypeConfiguration.cs | 111 ++++++ .../Core/src/Types/Types/InterfaceType~1.cs | 2 + .../Types/Types/ObjectType.Initialization.cs | 6 + .../Core/src/Types/Types/ObjectType~1.cs | 2 + .../src/Types/Types/TypeSystemObjectBase.cs | 25 +- .../Projections/ProjectableDataLoaderTests.cs | 2 +- .../Types.Analyzers.Tests/DataLoaderTests.cs | 58 ++- .../HotChocolate.Types.Analyzers.Tests.csproj | 1 + .../test/Types.Analyzers.Tests/Models/Book.cs | 7 +- .../Types.Analyzers.Tests/Models/IEntity.cs | 2 +- .../Types.Analyzers.Tests/OperationTests.cs | 67 ++++ .../Types.Analyzers.Tests/ResolverTests.cs | 99 +++++ .../test/Types.Analyzers.Tests/TestHelper.cs | 9 +- .../Core/test/Types.Analyzers.Tests/TestMe.cs | 68 ---- .../Types.Analyzers.Tests/Types/BookNode.cs | 8 +- .../Types/BookOperations.cs | 3 + .../Types/DataLoaders.cs | 2 +- ...er_With_PagingArguments_MatchesSnapshot.md | 2 +- ...r_With_PredicateBuilder_MatchesSnapshot.md | 4 +- ...er_With_SelectorBuilder_MatchesSnapshot.md | 4 +- ...aLoaderTests.Generate_With_QueryContext.md | 110 ++++++ ...oaderTests.Generate_With_SortDefinition.md | 102 +++++ ...aLoaderTests.Generate_Without_Interface.md | 4 +- ...eSource_BatchDataLoader_MatchesSnapshot.md | 39 +- .../OperationTests.Instance_QueryType.md | 32 ++ ...OperationTests.Partial_Static_QueryType.md | 168 ++++++++ .../OperationTests.Static_QueryType.md | 32 ++ ...esolverTests.Ensure_Entity_Becomes_Node.md | 266 +++++++++++++ ...y_Becomes_Node_With_Query_Node_Resolver.md | 188 +++++++++ ...WithGlobalStateArgument_MatchesSnapshot.md | 39 +- ...alStateSetStateArgument_MatchesSnapshot.md | 39 +- ...rWithLocalStateArgument_MatchesSnapshot.md | 39 +- ...alStateSetStateArgument_MatchesSnapshot.md | 39 +- ...WithScopedStateArgument_MatchesSnapshot.md | 39 +- ...edStateSetStateArgument_MatchesSnapshot.md | 39 +- .../ResolverTests.Inject_QueryContext.md | 166 ++++++++ ...urce_TypeModuleOrdering_MatchesSnapshot.md | 44 +-- .../_remove/SomeQuery.txt | 107 ----- .../_remove/SpecialObjectType.txt | 6 - ...otChocolateDataRequestBuilderExtensions.cs | 51 ++- ...=> HotChocolateDataQueryableExtensions.cs} | 43 +- ...tChocolateExecutionDataLoaderExtensions.cs | 156 ++++++++ ...eExecutionPredicateDataLoaderExtensions.cs | 70 ---- .../Data/src/Data/HotChocolate.Data.csproj | 1 + .../QueryContextParameterExpressionBuilder.cs | 89 +++++ .../Data/Sorting/Context/ISortingContext.cs | 13 + .../Data/Sorting/Context/SortingContext.cs | 109 ++++++ .../Data.AutoMapper.Tests/TestExtensions.cs | 2 +- .../DataLoaderTests.cs | 2 +- .../DataLoaderTests.Filter_With_Filtering.md | 3 +- .../Data/test/Data.Tests/IntegrationTests.cs | 89 ++++- ...tionTests.AsSortDefinition_Descending.snap | 12 + ...SortDefinition_Descending_DataContext.snap | 12 + ...ortDefinition_Descending_QueryContext.snap | 12 + src/HotChocolate/Directory.Build.props | 2 +- .../src/Data/Driver/MongoDbSortDefinition.cs | 6 +- .../Data/Driver/SortDefinitionExtensions.cs | 4 +- .../Data/Extensions/BsonDocumentExtensions.cs | 2 +- ...PaginationBatchingDataLoaderExtensions.cs} | 189 +++++++-- ...aginationBatchingDataLoaderFetchContext.cs | 3 +- ...onBatchingDataLoaderPredicateExtensions.cs | 77 ---- .../Pagination.Batching/IPagingDataLoader.cs | 28 -- .../Pagination.Batching/PagingDataLoader.cs | 64 --- .../Pagination.Batching/PagingStateKeys.cs | 6 + .../Extensions/PagingQueryableExtensions.cs | 6 +- .../InterfaceIntegrationTests.cs | 6 +- .../PagingHelperIntegrationTests.cs | 7 +- .../Primitives/HotChocolate.Primitives.sln | 7 + .../Primitives/HotChocolate.Primitives.csproj | 5 - .../HotChocolate.Primitives.Tests.csproj | 2 +- .../test/Primitives.Tests/NameUtilsTests.cs | 2 +- 135 files changed, 5098 insertions(+), 1890 deletions(-) create mode 100644 src/GreenDonut/src/Core/Data/DataLoaderStateHelper.cs create mode 100644 src/GreenDonut/src/Core/Data/DataLoaderStateKeys.cs create mode 100644 src/GreenDonut/src/Core/Data/DefaultPredicateBuilder.cs create mode 100644 src/GreenDonut/src/Core/Data/DefaultSelectorBuilder.cs create mode 100644 src/GreenDonut/src/Core/Data/ExpressionHasher.cs create mode 100644 src/GreenDonut/src/Core/Data/ExpressionHasherPool.cs create mode 100644 src/GreenDonut/src/Core/Data/GreenDonutPredicateDataLoaderExtensions.cs create mode 100644 src/GreenDonut/src/Core/Data/GreenDonutQueryContextDataLoaderExtensions.cs create mode 100644 src/GreenDonut/src/Core/Data/GreenDonutQueryableExtensions.cs create mode 100644 src/GreenDonut/src/Core/Data/GreenDonutSelectionDataLoaderExtensions.cs create mode 100644 src/GreenDonut/src/Core/Data/GreenDonutSortingDataLoaderExtensions.cs rename src/GreenDonut/src/Core/{Predicates => Data}/IPredicateBuilder.cs (87%) rename src/GreenDonut/src/Core/{Predicates/IPredicateDataLoader.cs => Data/IQueryDataLoader.cs} (51%) rename src/GreenDonut/src/Core/{Selectors => Data}/ISelectorBuilder.cs (86%) rename src/GreenDonut/src/Core/{Selectors => Data}/KeyValueResult.cs (88%) rename src/GreenDonut/src/Core/{Predicates/PredicateDataLoader.cs => Data/QueryDataLoader.cs} (79%) create mode 100644 src/GreenDonut/src/Core/Data/QueryState.cs delete mode 100644 src/GreenDonut/src/Core/Experiments.cs delete mode 100644 src/GreenDonut/src/Core/Predicates/DefaultPredicateBuilder.cs delete mode 100644 src/GreenDonut/src/Core/Predicates/PredicateDataLoaderExtensions.cs delete mode 100644 src/GreenDonut/src/Core/Selectors/DefaultSelectorBuilder.cs delete mode 100644 src/GreenDonut/src/Core/Selectors/ISelectionDataLoader.cs delete mode 100644 src/GreenDonut/src/Core/Selectors/SelectionDataLoader.cs delete mode 100644 src/GreenDonut/src/Core/Selectors/SelectionDataLoaderExtensions.cs create mode 100644 src/GreenDonut/src/Data.Primitives/GreenDonut.Data.Primitives.csproj create mode 100644 src/GreenDonut/src/Data.Primitives/ISortBy.cs create mode 100644 src/GreenDonut/src/Data.Primitives/QueryContext.cs create mode 100644 src/GreenDonut/src/Data.Primitives/SortBy.cs create mode 100644 src/GreenDonut/src/Data.Primitives/SortDefinition.cs create mode 100644 src/GreenDonut/test/Core.Tests/ExpressionHasherTests.cs delete mode 100644 src/HotChocolate/Core/src/Execution/Experiments.cs create mode 100644 src/HotChocolate/Core/src/Types.Analyzers/Models/RootTypeExtensionInfo.cs create mode 100644 src/HotChocolate/Core/src/Types/Types/Descriptors/TypeConfiguration.cs create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/OperationTests.cs delete mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/TestMe.cs create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.Generate_With_QueryContext.md create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.Generate_With_SortDefinition.md create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/OperationTests.Instance_QueryType.md create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/OperationTests.Partial_Static_QueryType.md create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/OperationTests.Static_QueryType.md create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.Ensure_Entity_Becomes_Node.md create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.Ensure_Entity_Becomes_Node_With_Query_Node_Resolver.md create mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.Inject_QueryContext.md delete mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/_remove/SomeQuery.txt delete mode 100644 src/HotChocolate/Core/test/Types.Analyzers.Tests/_remove/SpecialObjectType.txt rename src/HotChocolate/Data/src/Data/Filters/Extensions/{QueryableExtensions.cs => HotChocolateDataQueryableExtensions.cs} (65%) create mode 100644 src/HotChocolate/Data/src/Data/Filters/Extensions/HotChocolateExecutionDataLoaderExtensions.cs delete mode 100644 src/HotChocolate/Data/src/Data/Filters/Extensions/HotChocolateExecutionPredicateDataLoaderExtensions.cs create mode 100644 src/HotChocolate/Data/src/Data/QueryContextParameterExpressionBuilder.cs create mode 100644 src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.AsSortDefinition_Descending.snap create mode 100644 src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.AsSortDefinition_Descending_DataContext.snap create mode 100644 src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.AsSortDefinition_Descending_QueryContext.snap rename src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/{HotChocolatePaginationBatchingDataLoaderSelectorExtensions.cs => HotChocolatePaginationBatchingDataLoaderExtensions.cs} (50%) delete mode 100644 src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderPredicateExtensions.cs delete mode 100644 src/HotChocolate/Pagination/src/Pagination.Batching/IPagingDataLoader.cs delete mode 100644 src/HotChocolate/Pagination/src/Pagination.Batching/PagingDataLoader.cs create mode 100644 src/HotChocolate/Pagination/src/Pagination.Batching/PagingStateKeys.cs diff --git a/src/GreenDonut/GreenDonut.sln b/src/GreenDonut/GreenDonut.sln index ed68188b654..bda9e43b037 100644 --- a/src/GreenDonut/GreenDonut.sln +++ b/src/GreenDonut/GreenDonut.sln @@ -17,6 +17,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GreenDonut", "src\Core\Gree EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GreenDonut.Tests", "test\Core.Tests\GreenDonut.Tests.csproj", "{1F96BB47-B053-4E9C-865A-5B6FB613CB0D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GreenDonut.Data.Primitives", "src\Data.Primitives\GreenDonut.Data.Primitives.csproj", "{D30715C8-C763-4B36-B502-2991EBF76758}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -31,6 +33,10 @@ Global {1F96BB47-B053-4E9C-865A-5B6FB613CB0D}.Debug|Any CPU.Build.0 = Debug|Any CPU {1F96BB47-B053-4E9C-865A-5B6FB613CB0D}.Release|Any CPU.ActiveCfg = Release|Any CPU {1F96BB47-B053-4E9C-865A-5B6FB613CB0D}.Release|Any CPU.Build.0 = Release|Any CPU + {D30715C8-C763-4B36-B502-2991EBF76758}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D30715C8-C763-4B36-B502-2991EBF76758}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D30715C8-C763-4B36-B502-2991EBF76758}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D30715C8-C763-4B36-B502-2991EBF76758}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -38,6 +44,7 @@ Global GlobalSection(NestedProjects) = preSolution {FD764A95-F950-40F3-9577-E3168BBC330D} = {251185F7-7E3B-4BB3-B500-56C90564EE8C} {1F96BB47-B053-4E9C-865A-5B6FB613CB0D} = {A829F8F0-4468-405C-8A26-3E5921F7324A} + {D30715C8-C763-4B36-B502-2991EBF76758} = {251185F7-7E3B-4BB3-B500-56C90564EE8C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {CC1769AF-BDF5-4EF2-AED8-1719C180F131} diff --git a/src/GreenDonut/src/Core/Data/DataLoaderStateHelper.cs b/src/GreenDonut/src/Core/Data/DataLoaderStateHelper.cs new file mode 100644 index 00000000000..79a1b305fb1 --- /dev/null +++ b/src/GreenDonut/src/Core/Data/DataLoaderStateHelper.cs @@ -0,0 +1,107 @@ +using System.Linq.Expressions; + +namespace GreenDonut.Data; + +internal static class DataLoaderStateHelper +{ + internal static IDataLoader CreateBranch( + string branchKey, + IDataLoader dataLoader, + QueryState state) + where TKey : notnull + { + var branch = new QueryDataLoader( + (DataLoaderBase)dataLoader, + branchKey); + branch.SetState(state.Key, state.Value); + return branch; + } + + internal static IDataLoader CreateBranch( + string branchKey, + IDataLoader dataLoader, + QueryContext state) + where TKey : notnull + { + var branch = new QueryDataLoader( + (DataLoaderBase)dataLoader, + branchKey); + + if (state.Selector is not null) + { + branch.SetState(DataLoaderStateKeys.Selector, state.Selector); + } + + if (state.Predicate is not null) + { + branch.SetState(DataLoaderStateKeys.Predicate, state.Predicate); + } + + if (state.Sorting is not null) + { + branch.SetState(DataLoaderStateKeys.Sorting, state.Sorting); + } + + return branch; + } + + internal static string ComputeHash(this QueryContext state) + { + var hasher = ExpressionHasherPool.Shared.Get(); + + if (state.Selector is not null) + { + hasher.Add(state.Selector); + } + + if (state.Predicate is not null) + { + hasher.Add(state.Predicate); + } + + if (state.Sorting is not null) + { + hasher.Add(state.Sorting); + } + + var hash = hasher.Compute(); + ExpressionHasherPool.Shared.Return(hasher); + return hash; + } + + internal static ExpressionHasher Add(this ExpressionHasher hasher, QueryContext state) + { + if (state.Selector is not null) + { + hasher.Add(state.Selector); + } + + if (state.Predicate is not null) + { + hasher.Add(state.Predicate); + } + + if (state.Sorting is not null) + { + hasher.Add(state.Sorting); + } + + return hasher; + } + + public static string ComputeHash(this Expression expression) + { + var hasher = ExpressionHasherPool.Shared.Get(); + var branchKey = hasher.Add(expression).Compute(); + ExpressionHasherPool.Shared.Return(hasher); + return branchKey; + } + + public static string ComputeHash(this SortDefinition sortDefinition) + { + var hasher = ExpressionHasherPool.Shared.Get(); + var branchKey = hasher.Add(sortDefinition).Compute(); + ExpressionHasherPool.Shared.Return(hasher); + return branchKey; + } +} diff --git a/src/GreenDonut/src/Core/Data/DataLoaderStateKeys.cs b/src/GreenDonut/src/Core/Data/DataLoaderStateKeys.cs new file mode 100644 index 00000000000..7ea6e4da208 --- /dev/null +++ b/src/GreenDonut/src/Core/Data/DataLoaderStateKeys.cs @@ -0,0 +1,8 @@ +namespace GreenDonut.Data; + +internal static class DataLoaderStateKeys +{ + public const string Selector = "GreenDonut.Data.Selector"; + public const string Predicate = "GreenDonut.Data.Predicate"; + public const string Sorting = "GreenDonut.Data.Sorting"; +} diff --git a/src/GreenDonut/src/Core/Data/DefaultPredicateBuilder.cs b/src/GreenDonut/src/Core/Data/DefaultPredicateBuilder.cs new file mode 100644 index 00000000000..a0551c2a495 --- /dev/null +++ b/src/GreenDonut/src/Core/Data/DefaultPredicateBuilder.cs @@ -0,0 +1,85 @@ +using System.Collections.Immutable; +using System.Linq.Expressions; + +namespace GreenDonut.Data; + +/// +/// A default implementation of the . +/// +public sealed class DefaultPredicateBuilder : IPredicateBuilder +{ + private ImmutableArray _predicates = ImmutableArray.Empty; + + /// + /// Initializes a new instance of . + /// + /// + /// The initial predicate to add. + /// + public DefaultPredicateBuilder(LambdaExpression? initialPredicate) + { + if(initialPredicate is not null) + { + _predicates = _predicates.Add(initialPredicate); + } + } + + private DefaultPredicateBuilder(ImmutableArray predicates) + { + _predicates = predicates; + } + + /// + public void Add(Expression> selector) + { + if (!_predicates.Contains(selector)) + { + _predicates = _predicates.Add(selector); + } + } + + /// + public Expression>? TryCompile() + { + if (_predicates.Length == 0) + { + return null; + } + + if (_predicates.Length == 1) + { + return (Expression>)_predicates[0]; + } + + if (_predicates.Length == 2) + { + return ExpressionHelpers.And( + (Expression>)_predicates[0], + (Expression>)_predicates[1]); + } + + var expression = (Expression>)_predicates[0]; + for (var i = 1; i < _predicates.Length; i++) + { + expression = ExpressionHelpers.And( + expression, + (Expression>)_predicates[i]); + } + + return expression; + } + + /// + /// Creates a new that branches off the current builder. + /// + /// + /// Returns a new . + /// + public DefaultPredicateBuilder Branch() + => new(_predicates); + + /// + /// Gets an empty . + /// + public static DefaultPredicateBuilder Empty { get; } = new(ImmutableArray.Empty); +} diff --git a/src/GreenDonut/src/Core/Data/DefaultSelectorBuilder.cs b/src/GreenDonut/src/Core/Data/DefaultSelectorBuilder.cs new file mode 100644 index 00000000000..2608d8b3eca --- /dev/null +++ b/src/GreenDonut/src/Core/Data/DefaultSelectorBuilder.cs @@ -0,0 +1,85 @@ +using System.Collections.Immutable; +using System.Linq.Expressions; + +namespace GreenDonut.Data; + +/// +/// A default implementation of the . +/// +public sealed class DefaultSelectorBuilder : ISelectorBuilder +{ + private ImmutableArray _selectors = ImmutableArray.Empty; + + /// + /// Initializes a new instance of . + /// + /// + /// The initial selector to add. + /// + public DefaultSelectorBuilder(LambdaExpression? initialSelector = null) + { + if (initialSelector is not null) + { + _selectors = _selectors.Add(initialSelector); + } + } + + private DefaultSelectorBuilder(ImmutableArray selectors) + { + _selectors = selectors; + } + + /// + public void Add(Expression> selector) + { + if (!_selectors.Contains(selector)) + { + _selectors = _selectors.Add(selector); + } + } + + /// + public Expression>? TryCompile() + { + if (_selectors.Length == 0) + { + return null; + } + + if (_selectors.Length == 1) + { + return (Expression>)_selectors[0]; + } + + if (_selectors.Length == 2) + { + return ExpressionHelpers.Combine( + (Expression>)_selectors[0], + (Expression>)_selectors[1]); + } + + var expression = (Expression>)_selectors[0]; + for (var i = 1; i < _selectors.Length; i++) + { + expression = ExpressionHelpers.Combine( + expression, + (Expression>)_selectors[i]); + } + + return expression; + } + + /// + /// Creates a new that branches off the current builder. + /// + /// + /// Returns a new . + /// + public DefaultSelectorBuilder Branch() + => new(_selectors); + + /// + /// Gets an empty . + /// + public static DefaultSelectorBuilder Empty { get; } = new(ImmutableArray.Empty); +} diff --git a/src/GreenDonut/src/Core/Data/ExpressionHasher.cs b/src/GreenDonut/src/Core/Data/ExpressionHasher.cs new file mode 100644 index 00000000000..b633cf9e98a --- /dev/null +++ b/src/GreenDonut/src/Core/Data/ExpressionHasher.cs @@ -0,0 +1,347 @@ +using System.Buffers; +using System.Buffers.Text; +using System.Linq.Expressions; +using System.Reflection; +using System.Security.Cryptography; +using System.Text; + +namespace GreenDonut.Data; + +internal sealed class ExpressionHasher : ExpressionVisitor +{ + private ReadOnlySpan Lambda => "Lambda|"u8; + private ReadOnlySpan Binary => "Binary:"u8; + private ReadOnlySpan Member => "Member:"u8; + private ReadOnlySpan Parameter => "Parameter:"u8; + private ReadOnlySpan Unary => "Unary:"u8; + private ReadOnlySpan MethodCall => "MethodCall:"u8; + private ReadOnlySpan New => "New:"u8; + private ReadOnlySpan MemberInit => "MemberInit|"u8; + private ReadOnlySpan Binding => "Binding:"u8; + + + private byte[] _buffer; + private readonly int _initialSize; + private int _start; + + public ExpressionHasher() + { + _buffer = ArrayPool.Shared.Rent(1024); + _initialSize = _buffer.Length; + } + + public ExpressionHasher Add(Expression expression) + { + Visit(expression); + Append(';'); + return this; + } + + public ExpressionHasher Add(SortDefinition sortDefinition) + { + foreach (var operation in sortDefinition.Operations) + { + Visit(operation.KeySelector); + Append('-', '>'); + Append(operation.Ascending ? (byte)1 : (byte)0); + Append(';'); + } + return this; + } + + public ExpressionHasher Add(char c) + { + Append(c); + Append(';'); + return this; + } + + public ExpressionHasher Add(ReadOnlySpan span) + { + Append(span); + Append(';'); + return this; + } + + public ExpressionHasher Add(ReadOnlySpan span) + { + Append(span); + Append(';'); + return this; + } + + public string Compute() + { + var hashBytes = MD5.HashData(_buffer.AsSpan().Slice(0, _start)); + var hashString = Convert.ToHexString(hashBytes).ToLowerInvariant(); + + _buffer.AsSpan().Slice(0, _start).Clear(); + + if (_buffer.Length > _initialSize) + { + ArrayPool.Shared.Return(_buffer); + _buffer = ArrayPool.Shared.Rent(_initialSize); + } + + _start = 0; + return hashString; + } + + protected override Expression VisitLambda(Expression node) + { + Append(Lambda); + foreach (var parameter in node.Parameters) + { + Visit(parameter); + } + Visit(node.Body); + return node; + } + + protected override Expression VisitBinary(BinaryExpression node) + { + Append(Binary); + Append((int)node.NodeType); + Append('|'); + Visit(node.Left); + Visit(node.Right); + return node; + } + + protected override Expression VisitMember(MemberExpression node) + { + Append(Member); + Append(node.Member); + Append('|'); + Visit(node.Expression); + return node; + } + + protected override Expression VisitParameter(ParameterExpression node) + { + Append(Parameter); + Append(node.Name ?? string.Empty); + Append('|'); + return node; + } + + protected override Expression VisitUnary(UnaryExpression node) + { + Append(Unary); + Append((int)node.NodeType); + Append('|'); + Visit(node.Operand); + return node; + } + + protected override Expression VisitMethodCall(MethodCallExpression node) + { + Append(MethodCall); + Append(node.Method); + Append('|'); + Visit(node.Object); + foreach (var argument in node.Arguments) + { + Visit(argument); + } + return node; + } + + protected override Expression VisitNew(NewExpression node) + { + Append(New); + Append(node.Constructor); + Append('|'); + foreach (var argument in node.Arguments) + { + Visit(argument); + } + return node; + } + + protected override Expression VisitMemberInit(MemberInitExpression node) + { + Append(MemberInit); + Visit(node.NewExpression); + foreach (var binding in node.Bindings) + { + VisitMemberBindingInternal(binding); + } + return node; + } + + private void VisitMemberBindingInternal(MemberBinding binding) + { + Append(Binding); + Append((int)binding.BindingType); + Append('|'); + Append(binding.Member); + Append('|'); + + switch (binding) + { + case MemberAssignment assignment: + Visit(assignment.Expression); + break; + + case MemberMemberBinding memberBinding: + foreach (var subBinding in memberBinding.Bindings) + { + VisitMemberBindingInternal(subBinding); + } + break; + + case MemberListBinding listBinding: + foreach (var initializer in listBinding.Initializers) + { + foreach (var argument in initializer.Arguments) + { + Visit(argument); + } + } + break; + } + } + + private void Append(int i) + { + int written; + + while (!Utf8Formatter.TryFormat(i, _buffer.AsSpan().Slice(_start), out written)) + { + var newBuffer = ArrayPool.Shared.Rent(_buffer.Length * 2); + _buffer.AsSpan().Slice(0, _start).CopyTo(newBuffer); + ArrayPool.Shared.Return(_buffer); + _buffer = newBuffer; + } + + _start += written; + } + + private void Append(ConstructorInfo? constructorInfo) + { + if (constructorInfo is null) + { + return; + } + + var type = constructorInfo.ReflectedType?.FullName + ?? constructorInfo.ReflectedType?.Name + ?? constructorInfo.DeclaringType?.FullName + ?? constructorInfo.DeclaringType?.Name + ?? "global"; + + Append(type); + Append('.'); + Append("$ctor"); + } + + private void Append(MemberInfo? member) + { + if (member is null) + { + return; + } + + var type = member.ReflectedType?.FullName + ?? member.ReflectedType?.Name + ?? member.DeclaringType?.FullName + ?? member.DeclaringType?.Name + ?? "global"; + + Append(type); + Append('.'); + Append(member.Name); + } + + private void Append(char s) + { + if (_start == _buffer.Length) + { + var newBuffer = ArrayPool.Shared.Rent(_buffer.Length * 2); + _buffer.AsSpan().CopyTo(newBuffer); + ArrayPool.Shared.Return(_buffer); + _buffer = newBuffer; + } + + _buffer[_start++] = (byte)s; + } + + private void Append(char a, char b) + { + if (_start + 1 == _buffer.Length) + { + var newBuffer = ArrayPool.Shared.Rent(_buffer.Length * 2); + _buffer.AsSpan().CopyTo(newBuffer); + ArrayPool.Shared.Return(_buffer); + _buffer = newBuffer; + } + + _buffer[_start++] = (byte)a; + _buffer[_start++] = (byte)b; + } + + private void Append(string s) + { + var span = _buffer.AsSpan().Slice(_start); + var chars = s.AsSpan(); + int written; + + while (!Encoding.UTF8.TryGetBytes(chars, span, out written)) + { + var newBuffer = ArrayPool.Shared.Rent(_buffer.Length * 2); + _buffer.AsSpan().Slice(0, _start).CopyTo(newBuffer); + ArrayPool.Shared.Return(_buffer); + _buffer = newBuffer; + span = _buffer.AsSpan().Slice(_start); + } + + _start += written; + } + + private void Append(ReadOnlySpan s) + { + var span = _buffer.AsSpan().Slice(_start); + int written; + + while (!Encoding.UTF8.TryGetBytes(s, span, out written)) + { + var newBuffer = ArrayPool.Shared.Rent(_buffer.Length * 2); + _buffer.AsSpan().Slice(0, _start).CopyTo(newBuffer); + ArrayPool.Shared.Return(_buffer); + _buffer = newBuffer; + span = _buffer.AsSpan().Slice(_start); + } + + _start += written; + } + + private void Append(byte b) + { + if (_start == _buffer.Length) + { + var newBuffer = ArrayPool.Shared.Rent(_buffer.Length * 2); + _buffer.AsSpan().CopyTo(newBuffer); + ArrayPool.Shared.Return(_buffer); + _buffer = newBuffer; + } + + _buffer[_start++] = b; + } + + private void Append(ReadOnlySpan span) + { + var bufferSpan = _buffer.AsSpan().Slice(_start); + + while (!span.TryCopyTo(bufferSpan)) + { + var newBuffer = ArrayPool.Shared.Rent(_buffer.Length * 2); + _buffer.AsSpan().Slice(0, _start).CopyTo(newBuffer); + ArrayPool.Shared.Return(_buffer); + _buffer = newBuffer; + bufferSpan = _buffer.AsSpan().Slice(_start); + } + + _start += span.Length; + } +} diff --git a/src/GreenDonut/src/Core/Data/ExpressionHasherPool.cs b/src/GreenDonut/src/Core/Data/ExpressionHasherPool.cs new file mode 100644 index 00000000000..05f6312a52a --- /dev/null +++ b/src/GreenDonut/src/Core/Data/ExpressionHasherPool.cs @@ -0,0 +1,9 @@ +using Microsoft.Extensions.ObjectPool; + +namespace GreenDonut.Data; + +internal sealed class ExpressionHasherPool(int maximumRetained = 256) + : DefaultObjectPool(new DefaultPooledObjectPolicy(), maximumRetained) +{ + public static ExpressionHasherPool Shared { get; } = new(); +} diff --git a/src/GreenDonut/src/Core/Data/GreenDonutPredicateDataLoaderExtensions.cs b/src/GreenDonut/src/Core/Data/GreenDonutPredicateDataLoaderExtensions.cs new file mode 100644 index 00000000000..4fd716df7b9 --- /dev/null +++ b/src/GreenDonut/src/Core/Data/GreenDonutPredicateDataLoaderExtensions.cs @@ -0,0 +1,151 @@ +using System.Collections.Immutable; +using System.Linq.Expressions; + +namespace GreenDonut.Data; + +/// +/// Data loader extensions for predicates. +/// +public static class GreenDonutPredicateDataLoaderExtensions +{ + /// + /// Branches a DataLoader and applies a predicate to filter the data. + /// + /// + /// The DataLoader to branch. + /// + /// + /// The data predicate. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns a branched DataLoader with the predicate applied. + /// + /// + /// Throws if is null. + /// + public static IDataLoader Where( + this IDataLoader dataLoader, + Expression>? predicate) + where TKey : notnull + { + if (dataLoader is null) + { + throw new ArgumentNullException(nameof(dataLoader)); + } + + if (predicate is null) + { + return dataLoader; + } + + var branchKey = predicate.ComputeHash(); + var state = new QueryState(DataLoaderStateKeys.Predicate, GetOrCreateBuilder(dataLoader.ContextData, predicate)); + return (IQueryDataLoader)dataLoader.Branch(branchKey, DataLoaderStateHelper.CreateBranch, state); + } + + /// + /// Branches a DataLoader and applies a predicate to filter the data. + /// + /// + /// The DataLoader to branch. + /// + /// + /// The data predicate. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns a branched DataLoader with the predicate applied. + /// + /// + /// Throws if is null. + /// + public static IDataLoader Where( + this IDataLoader dataLoader, + Expression>? predicate) + where TKey : notnull + { + if (dataLoader is null) + { + throw new ArgumentNullException(nameof(dataLoader)); + } + + if (predicate is null) + { + return dataLoader; + } + + var branchKey = predicate.ComputeHash(); + var state = new QueryState(DataLoaderStateKeys.Predicate, GetOrCreateBuilder(dataLoader.ContextData, predicate)); + return (IQueryDataLoader)dataLoader.Branch(branchKey, DataLoaderStateHelper.CreateBranch, state); + } + + /// + /// Branches a DataLoader and applies a predicate to filter the data. + /// + /// + /// The DataLoader to branch. + /// + /// + /// The data predicate. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns a branched DataLoader with the predicate applied. + /// + /// + /// Throws if is null. + /// + public static IDataLoader> Where( + this IDataLoader> dataLoader, + Expression>? predicate) + where TKey : notnull + { + if (dataLoader is null) + { + throw new ArgumentNullException(nameof(dataLoader)); + } + + if (predicate is null) + { + return dataLoader; + } + var branchKey = predicate.ComputeHash(); + var state = new QueryState(DataLoaderStateKeys.Predicate, GetOrCreateBuilder(dataLoader.ContextData, predicate)); + return (IQueryDataLoader>)dataLoader.Branch(branchKey, DataLoaderStateHelper.CreateBranch, state); + } + + internal static DefaultPredicateBuilder GetOrCreateBuilder( + IImmutableDictionary contextData, + Expression> predicate) + { + DefaultPredicateBuilder? builder; + if (contextData.TryGetValue(DataLoaderStateKeys.Predicate, out var value)) + { + builder = (DefaultPredicateBuilder)value!; + builder = builder.Branch(); + builder.Add(predicate); + } + else + { + builder = new DefaultPredicateBuilder(predicate); + } + + return builder; + } +} diff --git a/src/GreenDonut/src/Core/Data/GreenDonutQueryContextDataLoaderExtensions.cs b/src/GreenDonut/src/Core/Data/GreenDonutQueryContextDataLoaderExtensions.cs new file mode 100644 index 00000000000..fd81b4c0ad0 --- /dev/null +++ b/src/GreenDonut/src/Core/Data/GreenDonutQueryContextDataLoaderExtensions.cs @@ -0,0 +1,136 @@ +namespace GreenDonut.Data; + +/// +/// Provides DataLoader extension methods for . +/// +public static class GreenDonutQueryContextDataLoaderExtensions +{ + /// + /// Branches a DataLoader and adds a query context to the DataLoader state. + /// + /// + /// The data loader. + /// + /// + /// The query context that shall be added to the DataLoader state. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns a new data loader that applies the query context. + /// + /// + /// Throws if is null. + /// + public static IDataLoader With( + this IDataLoader dataLoader, + QueryContext? context) + where TKey : notnull + { + if (dataLoader is null) + { + throw new ArgumentNullException(nameof(dataLoader)); + } + + if (context is null + || (context.Selector is null + && context.Predicate is null + && context.Sorting is null)) + { + return dataLoader; + } + + var branchKey = context.ComputeHash(); + return (IQueryDataLoader)dataLoader.Branch(branchKey, DataLoaderStateHelper.CreateBranch, context); + } + + /// + /// Branches a DataLoader and adds a query context to the DataLoader state. + /// + /// + /// The data loader. + /// + /// + /// The query context that shall be added to the DataLoader state. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns a new data loader that applies the query context. + /// + /// + /// Throws if is null. + /// + public static IDataLoader With( + this IDataLoader dataLoader, + QueryContext? context) + where TKey : notnull + { + if (dataLoader is null) + { + throw new ArgumentNullException(nameof(dataLoader)); + } + + if (context is null + || (context.Selector is null + && context.Predicate is null + && context.Sorting is null)) + { + return dataLoader; + } + + var branchKey = context.ComputeHash(); + return (IQueryDataLoader)dataLoader.Branch(branchKey, DataLoaderStateHelper.CreateBranch, context); + } + + /// + /// Branches a DataLoader and adds a query context to the DataLoader state. + /// + /// + /// The data loader. + /// + /// + /// The query context that shall be added to the DataLoader state. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns a new data loader that applies the query context. + /// + /// + /// Throws if is null. + /// + public static IDataLoader> With( + this IDataLoader> dataLoader, + QueryContext? context) + where TKey : notnull + { + if (dataLoader is null) + { + throw new ArgumentNullException(nameof(dataLoader)); + } + + if (context is null + || (context.Selector is null + && context.Predicate is null + && context.Sorting is null)) + { + return dataLoader; + } + + var branchKey = context.ComputeHash(); + return (IQueryDataLoader>)dataLoader.Branch(branchKey, DataLoaderStateHelper.CreateBranch, context); + } +} diff --git a/src/GreenDonut/src/Core/Data/GreenDonutQueryableExtensions.cs b/src/GreenDonut/src/Core/Data/GreenDonutQueryableExtensions.cs new file mode 100644 index 00000000000..1fc99843d2f --- /dev/null +++ b/src/GreenDonut/src/Core/Data/GreenDonutQueryableExtensions.cs @@ -0,0 +1,367 @@ +using System.Linq.Expressions; +using System.Reflection; +using GreenDonut.Data; +using static GreenDonut.ExpressionHelpers; + +// ReSharper disable once CheckNamespace +namespace System.Linq; + +/// +/// Provides extension methods to integrate +/// for and . +/// +public static class GreenDonutQueryableExtensions +{ + private static readonly MethodInfo _selectMethod = + typeof(Enumerable) + .GetMethods() + .Where(m => m.Name == nameof(Enumerable.Select) && m.GetParameters().Length == 2) + .First(m => m.GetParameters()[1].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>)); + + /// + /// Applies the selector from the DataLoader state to a queryable. + /// + /// + /// The queryable to apply the selector to. + /// + /// + /// The DataLoader key. + /// + /// + /// The selector builder. + /// + /// + /// The queryable type. + /// + /// + /// Returns a selector query on which a key must be applied to fetch the data. + /// + /// + /// Throws if is null. + /// + public static IQueryable Select( + this IQueryable query, + Expression> keySelector, + ISelectorBuilder builder) + { + if (query is null) + { + throw new ArgumentNullException(nameof(query)); + } + + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + var selector = builder.TryCompile(); + + if (selector is not null) + { + query = query.Select(Combine(selector, Rewrite(keySelector))); + } + + return query; + } + + /// + /// Applies the selector from the DataLoader state to a queryable. + /// + /// + /// The queryable type. + /// + /// + /// The queryable to apply the selector to. + /// + /// + /// The DataLoader key. + /// + /// + /// The selector. + /// + /// + /// Returns the query with the selector applied. + /// + public static IQueryable Select( + this IQueryable query, + Expression> keySelector, + Expression>? selector) + { + if (query is null) + { + throw new ArgumentNullException(nameof(query)); + } + + if (selector is not null) + { + query = query.Select(Combine(selector, Rewrite(keySelector))); + } + + return query; + } + + /// + /// Applies the selector from the DataLoader state to a queryable. + /// + /// + /// The queryable to apply the selector to. + /// + /// + /// The DataLoader key. + /// + /// + /// The list selector. + /// + /// + /// The element selector. + /// + /// + /// The queryable type. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns a selector query on which a key must be applied to fetch the data. + /// + public static IQueryable>> Select( + this IQueryable query, + Expression> key, + Expression>> list, + ISelectorBuilder elementSelector) + { + // we first create a new parameter expression for the root as we need + // a unified parameter for both expressions (key and list) + var parameter = Expression.Parameter(typeof(T), "root"); + + // next we replace the parameter within the key and list selectors with the + // unified parameter. + var rewrittenKey = ReplaceParameter(key, key.Parameters[0], parameter); + var rewrittenList = ReplaceParameter(list, list.Parameters[0], parameter); + + // next we try to compile an element selector expression. + var elementSelectorExpr = elementSelector.TryCompile(); + + // if we have an element selector to project properties on the list expression + // we will need to combine this into the list expression. + if (elementSelectorExpr is not null) + { + var selectMethod = _selectMethod.MakeGenericMethod(typeof(TValue), typeof(TValue)); + + rewrittenList = Expression.Lambda>>( + Expression.Call( + selectMethod, + rewrittenList.Body, + elementSelectorExpr), + parameter); + } + + // finally we combine key and list expression into a single selector expression + var keyValueSelectorExpr = Expression.Lambda>>>( + Expression.MemberInit( + Expression.New(typeof(KeyValueResult>)), + Expression.Bind( + typeof(KeyValueResult>).GetProperty( + nameof(KeyValueResult>.Key))!, + rewrittenKey.Body), + Expression.Bind( + typeof(KeyValueResult>).GetProperty( + nameof(KeyValueResult>.Value))!, + rewrittenList.Body)), + parameter); + + // lastly we apply the selector expression to the queryable. + return query.Select(keyValueSelectorExpr); + } + + /// + /// Applies the predicate from the DataLoader state to a queryable. + /// + /// + /// The queryable to apply the predicate to. + /// + /// + /// The predicate builder. + /// + /// + /// The queryable type. + /// + /// + /// Returns a query with the predicate applied, ready to fetch data with the key. + /// + /// + /// Throws if is null. + /// + public static IQueryable Where( + this IQueryable query, + IPredicateBuilder builder) + { + if (query is null) + { + throw new ArgumentNullException(nameof(query)); + } + + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + var predicate = builder.TryCompile(); + + if (predicate is not null) + { + query = query.Where(predicate); + } + + return query; + } + + /// + /// Applies the to the queryable (if its not null) + /// and returns an ordered queryable. + /// + /// + /// The queryable that shall be ordered. + /// + /// + /// The sort definition that shall be applied to the queryable. + /// + /// + /// The type of the queryable. + /// + /// + /// Returns an ordered queryable. + /// + /// + /// Throws if is null. + /// + public static IQueryable OrderBy(this IQueryable queryable, SortDefinition? sortDefinition) + { + if (queryable is null) + { + throw new ArgumentNullException(nameof(queryable)); + } + + if (sortDefinition is null || sortDefinition.Operations.Length == 0) + { + return queryable; + } + + var first = sortDefinition.Operations[0]; + var query = first.ApplyOrderBy(queryable); + + for (var i = 1; i < sortDefinition.Operations.Length; i++) + { + query = sortDefinition.Operations[i].ApplyThenBy(query); + } + + return query; + } + + /// + /// Applies the to the queryable (if its not null) + /// and returns an ordered queryable. + /// + /// + /// The queryable that shall be ordered. + /// + /// + /// The sort definition that shall be applied to the queryable. + /// + /// + /// The type of the queryable. + /// + /// + /// Returns an ordered queryable. + /// + /// + /// Throws if is null. + /// + public static IOrderedQueryable ThenBy(this IOrderedQueryable queryable, SortDefinition? sortDefinition) + { + if (queryable is null) + { + throw new ArgumentNullException(nameof(queryable)); + } + + if (sortDefinition is null || sortDefinition.Operations.Length == 0) + { + return queryable; + } + + for (var i = 0; i < sortDefinition.Operations.Length; i++) + { + queryable = sortDefinition.Operations[i].ApplyThenBy(queryable); + } + + return queryable; + } + + /// + /// Applies a data context to the queryable. + /// + /// + /// The queryable that shall be projected, filtered and sorted. + /// + /// + /// The data context that shall be applied to the queryable. + /// + /// + /// The type of the queryable. + /// + /// + /// Returns a queryable that has the data context applied. + /// + /// + /// Throws if is null or if is null. + /// + public static IQueryable Apply(this IQueryable queryable, QueryContext? queryContext) + { + if (queryable is null) + { + throw new ArgumentNullException(nameof(queryable)); + } + + if (queryContext is null) + { + return queryable; + } + + if (queryContext.Predicate is not null) + { + queryable = queryable.Where(queryContext.Predicate); + } + + if (queryContext.Sorting is not null) + { + queryable = queryable.OrderBy(queryContext.Sorting); + } + + if (queryContext.Selector is not null) + { + queryable = queryable.Select(queryContext.Selector); + } + + return queryable; + } + + private static Expression ReplaceParameter( + Expression expression, + ParameterExpression oldParameter, + ParameterExpression newParameter) + => (Expression)new ReplaceParameterVisitor(oldParameter, newParameter).Visit(expression); +} + +file sealed class ReplaceParameterVisitor( + ParameterExpression oldParameter, + ParameterExpression newParameter) + : ExpressionVisitor +{ + protected override Expression VisitParameter(ParameterExpression node) + => node == oldParameter + ? newParameter + : base.VisitParameter(node); +} diff --git a/src/GreenDonut/src/Core/Data/GreenDonutSelectionDataLoaderExtensions.cs b/src/GreenDonut/src/Core/Data/GreenDonutSelectionDataLoaderExtensions.cs new file mode 100644 index 00000000000..8a41194c7b7 --- /dev/null +++ b/src/GreenDonut/src/Core/Data/GreenDonutSelectionDataLoaderExtensions.cs @@ -0,0 +1,222 @@ +using System.Linq.Expressions; +using System.Reflection; +using static GreenDonut.ExpressionHelpers; + +namespace GreenDonut.Data; + +/// +/// Data loader extensions for projections. +/// +public static class GreenDonutSelectionDataLoaderExtensions +{ + /// + /// Branches a DataLoader and applies a selector to load the data. + /// + /// + /// The DataLoader to branch. + /// + /// + /// The data selector. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns a branched DataLoader with the selector applied. + /// + /// + /// Throws if is null. + /// + public static IDataLoader Select( + this IDataLoader dataLoader, + Expression>? selector) + where TKey : notnull + { + if (dataLoader is null) + { + throw new ArgumentNullException(nameof(dataLoader)); + } + + if (selector is null) + { + return dataLoader; + } + + if (dataLoader.ContextData.TryGetValue(DataLoaderStateKeys.Selector, out var value)) + { + var context = (DefaultSelectorBuilder)value!; + context.Add(selector); + return dataLoader; + } + + var branchKey = selector.ComputeHash(); + var state = new QueryState(DataLoaderStateKeys.Selector, new DefaultSelectorBuilder(selector)); + return (IQueryDataLoader)dataLoader.Branch(branchKey, DataLoaderStateHelper.CreateBranch, state); + } + + /// + /// Branches a DataLoader and applies a selector to load the data. + /// + /// + /// The DataLoader to branch. + /// + /// + /// The data selector. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns a branched DataLoader with the selector applied. + /// + /// + /// Throws if is null. + /// + public static IDataLoader Select( + this IDataLoader dataLoader, + Expression>? selector) + where TKey : notnull + { + if (dataLoader is null) + { + throw new ArgumentNullException(nameof(dataLoader)); + } + + if (selector is null) + { + return dataLoader; + } + + if (dataLoader.ContextData.TryGetValue(DataLoaderStateKeys.Selector, out var value)) + { + var context = (DefaultSelectorBuilder)value!; + context.Add(selector); + return dataLoader; + } + + var branchKey = selector.ComputeHash(); + var state = new QueryState(DataLoaderStateKeys.Selector, new DefaultSelectorBuilder(selector)); + return (IQueryDataLoader)dataLoader.Branch(branchKey, DataLoaderStateHelper.CreateBranch, state); + } + + /// + /// Branches a DataLoader and applies a selector to load the data. + /// + /// + /// The DataLoader to branch. + /// + /// + /// The data selector. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns a branched DataLoader with the selector applied. + /// + /// + /// Throws if is null. + /// + public static IDataLoader> Select( + this IDataLoader> dataLoader, + Expression>? selector) + where TKey : notnull + { + if (dataLoader is null) + { + throw new ArgumentNullException(nameof(dataLoader)); + } + + if (selector is null) + { + return dataLoader; + } + + if (dataLoader.ContextData.TryGetValue(DataLoaderStateKeys.Selector, out var value)) + { + var context = (DefaultSelectorBuilder)value!; + context.Add(selector); + return dataLoader; + } + + var branchKey = selector.ComputeHash(); + var state = new QueryState(DataLoaderStateKeys.Selector, new DefaultSelectorBuilder(selector)); + return (IQueryDataLoader>)dataLoader.Branch(branchKey, DataLoaderStateHelper.CreateBranch, state); + } + + /// + /// Includes a property in the query. + /// + /// + /// The DataLoader to include the property in. + /// + /// + /// The property selector. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns the DataLoader with the property included. + /// + /// + /// Throws if is null. + /// + /// + /// Throws if the include selector is not a property selector. + /// + public static IDataLoader Include( + this IDataLoader dataLoader, + Expression> includeSelector) + where TKey : notnull + { + if (dataLoader is null) + { + throw new ArgumentNullException(nameof(dataLoader)); + } + + if(!dataLoader.ContextData.ContainsKey(DataLoaderStateKeys.Selector)) + { + throw new InvalidOperationException( + "The Include method must be called after the Select method."); + } + + if (includeSelector is null) + { + throw new ArgumentNullException(nameof(includeSelector)); + } + + if (includeSelector is not LambdaExpression lambda) + { + throw new ArgumentException( + "The include selector must be a lambda expression.", + nameof(includeSelector)); + } + + if (lambda.Body is not MemberExpression member + || member.Member.MemberType != MemberTypes.Property) + { + throw new ArgumentException( + "The include selector must be a property selector.", + nameof(includeSelector)); + } + + var context = dataLoader.GetOrSetState( + DataLoaderStateKeys.Selector, + _ => new DefaultSelectorBuilder()); + context.Add(Rewrite(includeSelector)); + return dataLoader; + } +} diff --git a/src/GreenDonut/src/Core/Data/GreenDonutSortingDataLoaderExtensions.cs b/src/GreenDonut/src/Core/Data/GreenDonutSortingDataLoaderExtensions.cs new file mode 100644 index 00000000000..a9d423e43cd --- /dev/null +++ b/src/GreenDonut/src/Core/Data/GreenDonutSortingDataLoaderExtensions.cs @@ -0,0 +1,130 @@ +namespace GreenDonut.Data; + +/// +/// Provides DataLoader extension methods for . +/// +public static class GreenDonutSortingDataLoaderExtensions +{ + /// + /// Branches a DataLoader and adds a sort definition to the DataLoader state. + /// + /// + /// The data loader. + /// + /// + /// The sort definition that shall be added to the DataLoader state. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns a new data loader that applies the sort definition. + /// + /// + /// Throws if is null. + /// + public static IDataLoader OrderBy( + this IDataLoader dataLoader, + SortDefinition? sortDefinition) + where TKey : notnull + { + if (dataLoader is null) + { + throw new ArgumentNullException(nameof(dataLoader)); + } + + if (sortDefinition is null) + { + return dataLoader; + } + + var branchKey = sortDefinition.ComputeHash(); + var state = new QueryState(DataLoaderStateKeys.Sorting, sortDefinition); + return (IQueryDataLoader)dataLoader.Branch(branchKey, DataLoaderStateHelper.CreateBranch, state); + } + + /// + /// Branches a DataLoader and adds a sort definition to the DataLoader state. + /// + /// + /// The data loader. + /// + /// + /// The sort definition that shall be added to the DataLoader state. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns a new data loader that applies the sort definition. + /// + /// + /// Throws if is null. + /// + public static IDataLoader OrderBy( + this IDataLoader dataLoader, + SortDefinition? sortDefinition) + where TKey : notnull + { + if (dataLoader is null) + { + throw new ArgumentNullException(nameof(dataLoader)); + } + + if (sortDefinition is null) + { + return dataLoader; + } + + var branchKey = sortDefinition.ComputeHash(); + var state = new QueryState(DataLoaderStateKeys.Sorting, sortDefinition); + return (IQueryDataLoader)dataLoader.Branch(branchKey, DataLoaderStateHelper.CreateBranch, state); + } + + /// + /// Branches a DataLoader and adds a sort definition to the DataLoader state. + /// + /// + /// The data loader. + /// + /// + /// The sort definition that shall be added to the DataLoader state. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns a new data loader that applies the sort definition. + /// + /// + /// Throws if is null. + /// + public static IDataLoader> OrderBy( + this IDataLoader> dataLoader, + SortDefinition? sortDefinition) + where TKey : notnull + { + if (dataLoader is null) + { + throw new ArgumentNullException(nameof(dataLoader)); + } + + if (sortDefinition is null) + { + return dataLoader; + } + + var branchKey = sortDefinition.ComputeHash(); + var state = new QueryState(DataLoaderStateKeys.Sorting, sortDefinition); + return (IQueryDataLoader>)dataLoader.Branch(branchKey, DataLoaderStateHelper.CreateBranch, state); + } +} diff --git a/src/GreenDonut/src/Core/Predicates/IPredicateBuilder.cs b/src/GreenDonut/src/Core/Data/IPredicateBuilder.cs similarity index 87% rename from src/GreenDonut/src/Core/Predicates/IPredicateBuilder.cs rename to src/GreenDonut/src/Core/Data/IPredicateBuilder.cs index 8997eba7812..d21f4f45fc7 100644 --- a/src/GreenDonut/src/Core/Predicates/IPredicateBuilder.cs +++ b/src/GreenDonut/src/Core/Data/IPredicateBuilder.cs @@ -1,15 +1,11 @@ -using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; -namespace GreenDonut.Predicates; +namespace GreenDonut.Data; /// /// The predicate builder helps you create a combined predicate expression /// by adding multiple expressions together. /// -#if NET8_0_OR_GREATER -[Experimental(Experiments.Predicates)] -#endif public interface IPredicateBuilder { /// diff --git a/src/GreenDonut/src/Core/Predicates/IPredicateDataLoader.cs b/src/GreenDonut/src/Core/Data/IQueryDataLoader.cs similarity index 51% rename from src/GreenDonut/src/Core/Predicates/IPredicateDataLoader.cs rename to src/GreenDonut/src/Core/Data/IQueryDataLoader.cs index 7514e47396b..18e6d00cc4b 100644 --- a/src/GreenDonut/src/Core/Predicates/IPredicateDataLoader.cs +++ b/src/GreenDonut/src/Core/Data/IQueryDataLoader.cs @@ -1,10 +1,8 @@ -namespace GreenDonut.Predicates; +namespace GreenDonut.Data; /// -/// A predicate DataLoader is a specialized version of a DataLoader that -/// selects a subset of data based on a given predicate from the original DataLoader. -/// The data retrieved by this DataLoader is not shared with other DataLoaders and -/// remains isolated within this instance. +/// A query DataLoader is a specialized version of a DataLoader that has query context which +/// allows to manipulate the database request. /// /// /// The type of the key. @@ -12,7 +10,7 @@ namespace GreenDonut.Predicates; /// /// The type of the value. /// -public interface IPredicateDataLoader +public interface IQueryDataLoader : IDataLoader where TKey : notnull { diff --git a/src/GreenDonut/src/Core/Selectors/ISelectorBuilder.cs b/src/GreenDonut/src/Core/Data/ISelectorBuilder.cs similarity index 86% rename from src/GreenDonut/src/Core/Selectors/ISelectorBuilder.cs rename to src/GreenDonut/src/Core/Data/ISelectorBuilder.cs index 010274b01d1..95420651835 100644 --- a/src/GreenDonut/src/Core/Selectors/ISelectorBuilder.cs +++ b/src/GreenDonut/src/Core/Data/ISelectorBuilder.cs @@ -1,17 +1,12 @@ -#if NET6_0_OR_GREATER -using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; -namespace GreenDonut.Selectors; +namespace GreenDonut.Data; /// /// The selector builder allows to build up a selector expression /// by adding expressions that will be merged into a /// single selector expression. /// -#if NET8_0_OR_GREATER -[Experimental(Experiments.Selectors)] -#endif public interface ISelectorBuilder { /// @@ -41,4 +36,3 @@ public interface ISelectorBuilder /// Expression>? TryCompile(); } -#endif diff --git a/src/GreenDonut/src/Core/Selectors/KeyValueResult.cs b/src/GreenDonut/src/Core/Data/KeyValueResult.cs similarity index 88% rename from src/GreenDonut/src/Core/Selectors/KeyValueResult.cs rename to src/GreenDonut/src/Core/Data/KeyValueResult.cs index 1902bec371e..12fe8713d2a 100644 --- a/src/GreenDonut/src/Core/Selectors/KeyValueResult.cs +++ b/src/GreenDonut/src/Core/Data/KeyValueResult.cs @@ -1,4 +1,4 @@ -namespace GreenDonut.Selectors; +namespace GreenDonut.Data; /// /// This class is a helper that is used to project a key value pair. diff --git a/src/GreenDonut/src/Core/Predicates/PredicateDataLoader.cs b/src/GreenDonut/src/Core/Data/QueryDataLoader.cs similarity index 79% rename from src/GreenDonut/src/Core/Predicates/PredicateDataLoader.cs rename to src/GreenDonut/src/Core/Data/QueryDataLoader.cs index 0c0a03241a0..7288373c0e7 100644 --- a/src/GreenDonut/src/Core/Predicates/PredicateDataLoader.cs +++ b/src/GreenDonut/src/Core/Data/QueryDataLoader.cs @@ -1,13 +1,13 @@ -namespace GreenDonut.Predicates; +namespace GreenDonut.Data; -internal sealed class PredicateDataLoader +internal sealed class QueryDataLoader : DataLoaderBase - , IPredicateDataLoader + , IQueryDataLoader where TKey : notnull { private readonly DataLoaderBase _root; - public PredicateDataLoader( + public QueryDataLoader( DataLoaderBase root, string predicateKey) : base(root.BatchScheduler, root.Options) @@ -23,7 +23,7 @@ public PredicateDataLoader( protected override bool AllowCachePropagation => false; - protected override bool AllowBranching => false; + protected override bool AllowBranching => true; protected internal override ValueTask FetchAsync( IReadOnlyList keys, diff --git a/src/GreenDonut/src/Core/Data/QueryState.cs b/src/GreenDonut/src/Core/Data/QueryState.cs new file mode 100644 index 00000000000..be0db5fc53c --- /dev/null +++ b/src/GreenDonut/src/Core/Data/QueryState.cs @@ -0,0 +1,3 @@ +namespace GreenDonut.Data; + +internal readonly record struct QueryState(string Key, object Value); diff --git a/src/GreenDonut/src/Core/DataLoaderBase.cs b/src/GreenDonut/src/Core/DataLoaderBase.cs index 86bc9776d3b..7d8b452a0ef 100644 --- a/src/GreenDonut/src/Core/DataLoaderBase.cs +++ b/src/GreenDonut/src/Core/DataLoaderBase.cs @@ -228,7 +228,7 @@ void Initialize() ct); } } - + /// public void Remove(TKey key) { diff --git a/src/GreenDonut/src/Core/DataLoaderFetchContext.cs b/src/GreenDonut/src/Core/DataLoaderFetchContext.cs index cc250fe428b..60cc6eea378 100644 --- a/src/GreenDonut/src/Core/DataLoaderFetchContext.cs +++ b/src/GreenDonut/src/Core/DataLoaderFetchContext.cs @@ -1,11 +1,5 @@ using System.Collections.Immutable; -#if NET8_0_OR_GREATER -using System.Diagnostics.CodeAnalysis; -#endif -#if NET6_0_OR_GREATER -using GreenDonut.Selectors; -using GreenDonut.Predicates; -#endif +using GreenDonut.Data; namespace GreenDonut; @@ -147,12 +141,9 @@ public TState GetStateOrDefault(string key, TState defaultValue) /// /// Returns the selector builder if it exists. /// -#if NET8_0_OR_GREATER - [Experimental(Experiments.Selectors)] -#endif public ISelectorBuilder GetSelector() { - if (ContextData.TryGetValue(typeof(ISelectorBuilder).FullName!, out var value) + if (ContextData.TryGetValue(DataLoaderStateKeys.Selector, out var value) && value is ISelectorBuilder casted) { return casted; @@ -170,20 +161,86 @@ public ISelectorBuilder GetSelector() /// /// Returns the predicate builder if it exists. /// -#if NET8_0_OR_GREATER - [Experimental(Experiments.Predicates)] -#endif public IPredicateBuilder GetPredicate() { - if (ContextData.TryGetValue(typeof(IPredicateBuilder).FullName!, out var value) - && value is DefaultPredicateBuilder casted) + if (ContextData.TryGetValue(DataLoaderStateKeys.Predicate, out var value) + && value is IPredicateBuilder casted) { return casted; } // if no predicate was found we will just return // a new default predicate builder. - return new DefaultPredicateBuilder(); + return DefaultPredicateBuilder.Empty; + } + + /// + /// Gets the sorting definition from the DataLoader state snapshot. + /// + /// + /// The entity type. + /// + /// + /// Returns the sorting definition if it exists. + /// + public SortDefinition GetSorting() + { + if (ContextData.TryGetValue(DataLoaderStateKeys.Sorting, out var value) + && value is SortDefinition casted) + { + return casted; + } + + return SortDefinition.Empty; + } + + /// + /// Gets the query context from the DataLoader state snapshot. + /// + /// + /// The entity type. + /// + /// + /// Returns the query context if it exists, otherwise am empty query context. + /// + public QueryContext GetQueryContext() + { + ISelectorBuilder? selector = null; + IPredicateBuilder? predicate = null; + SortDefinition? sorting = null; + + if (ContextData.TryGetValue(DataLoaderStateKeys.Selector, out var value) + && value is ISelectorBuilder casted1) + { + selector = casted1; + } + + if (ContextData.TryGetValue(DataLoaderStateKeys.Predicate, out value) + && value is IPredicateBuilder casted2) + { + predicate = casted2; + } + + if (ContextData.TryGetValue(DataLoaderStateKeys.Sorting, out value) + && value is SortDefinition casted3) + { + sorting = casted3; + } + + var selectorExpression = selector?.TryCompile(); + var predicateExpression = predicate?.TryCompile(); + + if (selectorExpression is null + && predicateExpression is null + && sorting is null) + { + return QueryContext.Empty; + } + + return new QueryContext( + selectorExpression, + predicateExpression, + sorting); } #endif } diff --git a/src/GreenDonut/src/Core/Experiments.cs b/src/GreenDonut/src/Core/Experiments.cs deleted file mode 100644 index d2e1fb348ff..00000000000 --- a/src/GreenDonut/src/Core/Experiments.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace GreenDonut; - -internal static class Experiments -{ - public const string Selectors = "GD0001"; - public const string Predicates = "GD0002"; -} diff --git a/src/GreenDonut/src/Core/GreenDonut.csproj b/src/GreenDonut/src/Core/GreenDonut.csproj index a638c155bbf..652558f4ba7 100644 --- a/src/GreenDonut/src/Core/GreenDonut.csproj +++ b/src/GreenDonut/src/Core/GreenDonut.csproj @@ -31,4 +31,8 @@ + + + + diff --git a/src/GreenDonut/src/Core/Predicates/DefaultPredicateBuilder.cs b/src/GreenDonut/src/Core/Predicates/DefaultPredicateBuilder.cs deleted file mode 100644 index 715a5cdf253..00000000000 --- a/src/GreenDonut/src/Core/Predicates/DefaultPredicateBuilder.cs +++ /dev/null @@ -1,58 +0,0 @@ -#if NET6_0_OR_GREATER -using System.Diagnostics.CodeAnalysis; -using System.Linq.Expressions; - -namespace GreenDonut.Predicates; - -/// -/// A default implementation of the . -/// -#if NET8_0_OR_GREATER -[Experimental(Experiments.Predicates)] -#endif -public sealed class DefaultPredicateBuilder : IPredicateBuilder -{ - private List? _predicates; - - /// - public void Add(Expression> selector) - { - _predicates ??= new List(); - if (!_predicates.Contains(selector)) - { - _predicates.Add(selector); - } - } - - /// - public Expression>? TryCompile() - { - if (_predicates is null) - { - return null; - } - - if (_predicates.Count == 1) - { - return (Expression>)_predicates[0]; - } - - if (_predicates.Count == 2) - { - return ExpressionHelpers.And( - (Expression>)_predicates[0], - (Expression>)_predicates[1]); - } - - var expression = (Expression>)_predicates[0]; - for (var i = 1; i < _predicates.Count; i++) - { - expression = ExpressionHelpers.And( - expression, - (Expression>)_predicates[i]); - } - - return expression; - } -} -#endif diff --git a/src/GreenDonut/src/Core/Predicates/PredicateDataLoaderExtensions.cs b/src/GreenDonut/src/Core/Predicates/PredicateDataLoaderExtensions.cs deleted file mode 100644 index 63b13ce4306..00000000000 --- a/src/GreenDonut/src/Core/Predicates/PredicateDataLoaderExtensions.cs +++ /dev/null @@ -1,119 +0,0 @@ -#if NET6_0_OR_GREATER -using System.Diagnostics.CodeAnalysis; -using System.Linq.Expressions; - -namespace GreenDonut.Predicates; - -/// -/// Data loader extensions for predicates. -/// -#if NET8_0_OR_GREATER -[Experimental(Experiments.Predicates)] -#endif -public static class PredicateDataLoaderExtensions -{ - /// - /// Branches a DataLoader and applies a predicate to filter the data. - /// - /// - /// The DataLoader to branch. - /// - /// - /// The data predicate. - /// - /// - /// The key type. - /// - /// - /// The value type. - /// - /// - /// Returns a branched DataLoader with the predicate applied. - /// - /// - /// Throws if is null. - /// - public static IDataLoader Where( - this IDataLoader dataLoader, - Expression>? predicate) - where TKey : notnull - { - if (dataLoader is null) - { - throw new ArgumentNullException(nameof(dataLoader)); - } - - if (predicate is null) - { - return dataLoader; - } - - if (dataLoader.ContextData.TryGetValue(typeof(IPredicateBuilder).FullName!, out var value)) - { - var context = (DefaultPredicateBuilder)value!; - context.Add(predicate); - return dataLoader; - } - - var branchKey = predicate.ToString(); - return (IDataLoader)dataLoader.Branch(branchKey, CreateBranch, predicate); - - static IDataLoader CreateBranch( - string key, - IDataLoader dataLoader, - Expression> predicate) - { - var branch = new PredicateDataLoader( - (DataLoaderBase)dataLoader, - key); - var context = new DefaultPredicateBuilder(); - branch.ContextData = - branch.ContextData.SetItem(typeof(IPredicateBuilder).FullName!, context); - context.Add(predicate); - return branch; - } - } - - /// - /// Applies the predicate from the DataLoader state to a queryable. - /// - /// - /// The queryable to apply the predicate to. - /// - /// - /// The predicate builder. - /// - /// - /// The queryable type. - /// - /// - /// Returns a query with the predicate applied, ready to fetch data with the key. - /// - /// - /// Throws if is null. - /// - public static IQueryable Where( - this IQueryable query, - IPredicateBuilder builder) - { - if (query is null) - { - throw new ArgumentNullException(nameof(query)); - } - - if (builder is null) - { - throw new ArgumentNullException(nameof(builder)); - } - - var predicate = builder.TryCompile(); - - if (predicate is not null) - { - query = query.Where(predicate); - } - - return query; - } -} -#endif diff --git a/src/GreenDonut/src/Core/Selectors/DefaultSelectorBuilder.cs b/src/GreenDonut/src/Core/Selectors/DefaultSelectorBuilder.cs deleted file mode 100644 index 2857ce89172..00000000000 --- a/src/GreenDonut/src/Core/Selectors/DefaultSelectorBuilder.cs +++ /dev/null @@ -1,58 +0,0 @@ -#if NET6_0_OR_GREATER -using System.Diagnostics.CodeAnalysis; -using System.Linq.Expressions; - -namespace GreenDonut.Selectors; - -/// -/// A default implementation of the . -/// -#if NET8_0_OR_GREATER -[Experimental(Experiments.Selectors)] -#endif -public sealed class DefaultSelectorBuilder : ISelectorBuilder -{ - private List? _selectors; - - /// - public void Add(Expression> selector) - { - _selectors ??= new List(); - if (!_selectors.Contains(selector)) - { - _selectors.Add(selector); - } - } - - /// - public Expression>? TryCompile() - { - if (_selectors is null) - { - return null; - } - - if (_selectors.Count == 1) - { - return (Expression>)_selectors[0]; - } - - if (_selectors.Count == 2) - { - return ExpressionHelpers.Combine( - (Expression>)_selectors[0], - (Expression>)_selectors[1]); - } - - var expression = (Expression>)_selectors[0]; - for (var i = 1; i < _selectors.Count; i++) - { - expression = ExpressionHelpers.Combine( - expression, - (Expression>)_selectors[i]); - } - - return expression; - } -} -#endif diff --git a/src/GreenDonut/src/Core/Selectors/ISelectionDataLoader.cs b/src/GreenDonut/src/Core/Selectors/ISelectionDataLoader.cs deleted file mode 100644 index 156b3e737a7..00000000000 --- a/src/GreenDonut/src/Core/Selectors/ISelectionDataLoader.cs +++ /dev/null @@ -1,27 +0,0 @@ -#if NET6_0_OR_GREATER -namespace GreenDonut.Selectors; - -/// -/// A selection DataLoader is a version of a DataLoader that -/// selects a different shape of data of the original DataLoader. -/// The data that is fetched with this DataLoader version is -/// not propagated to other DataLoader and is isolated within the -/// DataLoader instance. This allows to fetch the data in an optimized -/// way for specific uses cases. -/// -/// -/// The type of the key. -/// -/// -/// The type of the value. -/// -public interface ISelectionDataLoader - : IDataLoader - where TKey : notnull -{ - /// - /// Gets the root DataLoader instance from which this instance was branched off. - /// - IDataLoader Root { get; } -} -#endif diff --git a/src/GreenDonut/src/Core/Selectors/SelectionDataLoader.cs b/src/GreenDonut/src/Core/Selectors/SelectionDataLoader.cs deleted file mode 100644 index 2be4d5effa4..00000000000 --- a/src/GreenDonut/src/Core/Selectors/SelectionDataLoader.cs +++ /dev/null @@ -1,36 +0,0 @@ -#if NET6_0_OR_GREATER -namespace GreenDonut.Selectors; - -internal sealed class SelectionDataLoader - : DataLoaderBase - , ISelectionDataLoader - where TKey : notnull -{ - private readonly DataLoaderBase _root; - - public SelectionDataLoader( - DataLoaderBase root, - string selectionKey) - : base(root.BatchScheduler, root.Options) - { - _root = root; - CacheKeyType = $"{root.CacheKeyType}:{selectionKey}"; - ContextData = root.ContextData; - } - - public IDataLoader Root => _root; - - protected internal override string CacheKeyType { get; } - - protected override bool AllowCachePropagation => false; - - protected override bool AllowBranching => true; - - protected internal override ValueTask FetchAsync( - IReadOnlyList keys, - Memory> results, - DataLoaderFetchContext context, - CancellationToken cancellationToken) - => _root.FetchAsync(keys, results, context, cancellationToken); -} -#endif diff --git a/src/GreenDonut/src/Core/Selectors/SelectionDataLoaderExtensions.cs b/src/GreenDonut/src/Core/Selectors/SelectionDataLoaderExtensions.cs deleted file mode 100644 index f2518dacb53..00000000000 --- a/src/GreenDonut/src/Core/Selectors/SelectionDataLoaderExtensions.cs +++ /dev/null @@ -1,287 +0,0 @@ -#if NET6_0_OR_GREATER -using System.Diagnostics.CodeAnalysis; -using System.Linq.Expressions; -using System.Reflection; -using static GreenDonut.ExpressionHelpers; - -namespace GreenDonut.Selectors; - -/// -/// Data loader extensions for projections. -/// -#if NET8_0_OR_GREATER -[Experimental(Experiments.Selectors)] -#endif -public static class SelectionDataLoaderExtensions -{ - private static readonly MethodInfo _selectMethod = - typeof(Enumerable) - .GetMethods() - .Where(m => m.Name == nameof(Enumerable.Select) && m.GetParameters().Length == 2) - .First(m => m.GetParameters()[1].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>)); - - /// - /// Branches a DataLoader and applies a selector to load the data. - /// - /// - /// The DataLoader to branch. - /// - /// - /// The data selector. - /// - /// - /// The key type. - /// - /// - /// The value type. - /// - /// - /// The element type. - /// - /// - /// Returns a branched DataLoader with the selector applied. - /// - /// - /// Throws if is null. - /// - public static IDataLoader Select( - this IDataLoader dataLoader, - Expression>? selector) - where TKey : notnull - { - if (dataLoader is null) - { - throw new ArgumentNullException(nameof(dataLoader)); - } - - if (selector is null) - { - return dataLoader; - } - - if (dataLoader.ContextData.TryGetValue(typeof(ISelectorBuilder).FullName!, out var value)) - { - var context = (DefaultSelectorBuilder)value!; - context.Add(selector); - return dataLoader; - } - - var branchKey = selector.ToString(); - return (ISelectionDataLoader)dataLoader.Branch(branchKey, CreateBranch, selector); - - static IDataLoader CreateBranch( - string key, - IDataLoader dataLoader, - Expression> selector) - { - var branch = new SelectionDataLoader( - (DataLoaderBase)dataLoader, - key); - var context = new DefaultSelectorBuilder(); - branch.ContextData = branch.ContextData.SetItem(typeof(ISelectorBuilder).FullName!, context); - context.Add(selector); - return branch; - } - } - - /// - /// Includes a property in the query. - /// - /// - /// The DataLoader to include the property in. - /// - /// - /// The property selector. - /// - /// - /// The key type. - /// - /// - /// The value type. - /// - /// - /// Returns the DataLoader with the property included. - /// - /// - /// Throws if is null. - /// - /// - /// Throws if the include selector is not a property selector. - /// - public static IDataLoader Include( - this IDataLoader dataLoader, - Expression> includeSelector) - where TKey : notnull - { - if (dataLoader is null) - { - throw new ArgumentNullException(nameof(dataLoader)); - } - - if (includeSelector is null) - { - throw new ArgumentNullException(nameof(includeSelector)); - } - - if (includeSelector is not LambdaExpression lambda) - { - throw new ArgumentException( - "The include selector must be a lambda expression.", - nameof(includeSelector)); - } - - if (lambda.Body is not MemberExpression member - || member.Member.MemberType != MemberTypes.Property) - { - throw new ArgumentException( - "The include selector must be a property selector.", - nameof(includeSelector)); - } - - var context = dataLoader.GetOrSetState( - typeof(ISelectorBuilder).FullName!, - _ => new DefaultSelectorBuilder()); - context.Add(Rewrite(includeSelector)); - return dataLoader; - } - - /// - /// Applies the selector from the DataLoader state to a queryable. - /// - /// - /// The queryable to apply the selector to. - /// - /// - /// The DataLoader key. - /// - /// - /// The selector builder. - /// - /// - /// The queryable type. - /// - /// - /// Returns a selector query on which a key must be applied to fetch the data. - /// - /// - /// Throws if is null. - /// - public static IQueryable Select(this IQueryable query, - Expression> key, - ISelectorBuilder builder) - { - if (query is null) - { - throw new ArgumentNullException(nameof(query)); - } - - if (builder is null) - { - throw new ArgumentNullException(nameof(builder)); - } - - var selector = builder.TryCompile(); - - if (selector is not null) - { - query = query.Select(Combine(selector, Rewrite(key))); - } - - return query; - } - - /// - /// Applies the selector from the DataLoader state to a queryable. - /// - /// - /// The queryable to apply the selector to. - /// - /// - /// The DataLoader key. - /// - /// - /// The list selector. - /// - /// - /// The element selector. - /// - /// - /// The queryable type. - /// - /// - /// The key type. - /// - /// - /// The value type. - /// - /// - /// Returns a selector query on which a key must be applied to fetch the data. - /// - public static IQueryable>> Select( - this IQueryable query, - Expression> key, - Expression>> list, - ISelectorBuilder elementSelector) - { - // we first create a new parameter expression for the root as we need - // a unified parameter for both expressions (key and list) - var parameter = Expression.Parameter(typeof(T), "root"); - - // next we replace the parameter within the key and list selectors with the - // unified parameter. - var rewrittenKey = ReplaceParameter(key, key.Parameters[0], parameter); - var rewrittenList = ReplaceParameter(list, list.Parameters[0], parameter); - - // next we try to compile an element selector expression. - var elementSelectorExpr = elementSelector.TryCompile(); - - // if we have a element selector to project properties on the list expression - // we will need to combine this into the list expression. - if (elementSelectorExpr is not null) - { - var selectMethod = _selectMethod.MakeGenericMethod(typeof(TValue), typeof(TValue)); - - rewrittenList = Expression.Lambda>>( - Expression.Call( - selectMethod, - rewrittenList.Body, - elementSelectorExpr), - parameter); - } - - // finally we combine key and list expression into a single selector expression - var keyValueSelectorExpr = Expression.Lambda>>>( - Expression.MemberInit( - Expression.New(typeof(KeyValueResult>)), - Expression.Bind( - typeof(KeyValueResult>).GetProperty( - nameof(KeyValueResult>.Key))!, - rewrittenKey.Body), - Expression.Bind( - typeof(KeyValueResult>).GetProperty( - nameof(KeyValueResult>.Value))!, - rewrittenList.Body)), - parameter); - - // lastly we apply the selector expression to the queryable. - return query.Select(keyValueSelectorExpr); - } - - private static Expression ReplaceParameter( - Expression expression, - ParameterExpression oldParameter, - ParameterExpression newParameter) - => (Expression)new ReplaceParameterVisitor(oldParameter, newParameter).Visit(expression); -} - -file sealed class ReplaceParameterVisitor( - ParameterExpression oldParameter, - ParameterExpression newParameter) - : ExpressionVisitor -{ - protected override Expression VisitParameter(ParameterExpression node) - => node == oldParameter - ? newParameter - : base.VisitParameter(node); -} -#endif diff --git a/src/GreenDonut/src/Data.Primitives/GreenDonut.Data.Primitives.csproj b/src/GreenDonut/src/Data.Primitives/GreenDonut.Data.Primitives.csproj new file mode 100644 index 00000000000..346b4c28d6c --- /dev/null +++ b/src/GreenDonut/src/Data.Primitives/GreenDonut.Data.Primitives.csproj @@ -0,0 +1,10 @@ + + + + GreenDonut.Data.Primitives + GreenDonut.Data.Primitives + GreenDonut.Data + This package contains the basic building blocks of the DataLoader linq query integration. + + + diff --git a/src/GreenDonut/src/Data.Primitives/ISortBy.cs b/src/GreenDonut/src/Data.Primitives/ISortBy.cs new file mode 100644 index 00000000000..707958ad9b7 --- /dev/null +++ b/src/GreenDonut/src/Data.Primitives/ISortBy.cs @@ -0,0 +1,44 @@ +using System.Linq.Expressions; + +namespace GreenDonut.Data; + +/// +/// Represents a sort operation on a field. +/// +/// +/// The entity type on which the sort operation is applied. +/// +public interface ISortBy +{ + /// + /// Gets the field on which the sort operation is applied. + /// + LambdaExpression KeySelector { get; } + + /// + /// Gets the sort direction. + /// + bool Ascending { get; } + + /// + /// Applies the sort operation to the queryable. + /// + /// + /// The queryable to which the sort operation is applied. + /// + /// + /// The queryable with the sort operation applied. + /// + IOrderedQueryable ApplyOrderBy(IQueryable queryable); + + /// + /// Applies the sort operation to the queryable. + /// + /// + /// The queryable to which the sort operation is applied. + /// + /// + /// The queryable with the sort operation applied. + /// + IOrderedQueryable ApplyThenBy(IOrderedQueryable queryable); +} diff --git a/src/GreenDonut/src/Data.Primitives/QueryContext.cs b/src/GreenDonut/src/Data.Primitives/QueryContext.cs new file mode 100644 index 00000000000..dd4821c9e82 --- /dev/null +++ b/src/GreenDonut/src/Data.Primitives/QueryContext.cs @@ -0,0 +1,31 @@ +using System.Linq.Expressions; + +namespace GreenDonut.Data; + +/// +/// Represents the context for constructing queries against a data source for . +/// This record contains the projection (selector), filtering (predicate), and sorting instructions +/// () for building a query. +/// +/// +/// The entity type associated with the query context. +/// +/// +/// An expression that defines what data shall be selected from . +/// +/// +/// An expression that defines the filtering condition for . +/// +/// +/// The sorting instructions (see ) for . +/// +public record QueryContext( + Expression>? Selector = null, + Expression>? Predicate = null, + SortDefinition? Sorting = null) +{ + /// + /// An empty query context. + /// + public static QueryContext Empty { get; } = new(); +} diff --git a/src/GreenDonut/src/Data.Primitives/SortBy.cs b/src/GreenDonut/src/Data.Primitives/SortBy.cs new file mode 100644 index 00000000000..b61f478b73d --- /dev/null +++ b/src/GreenDonut/src/Data.Primitives/SortBy.cs @@ -0,0 +1,82 @@ +using System.Linq.Expressions; + +namespace GreenDonut.Data; + +/// +/// Represents a sort operation on a field. +/// +public sealed class SortBy : ISortBy +{ + /// + /// Initializes a new instance of . + /// + /// + /// The field on which the sort operation is applied. + /// + /// + /// Specifies the sort directive. + /// + /// + /// is null. + /// + public SortBy(Expression> keySelector, bool ascending = true) + { + KeySelector = keySelector ?? throw new ArgumentNullException(nameof(keySelector)); + Ascending = ascending; + } + + /// + /// Gets the field on which the sort operation is applied. + /// + public Expression> KeySelector { get; init; } + + LambdaExpression ISortBy.KeySelector => KeySelector; + + /// + /// Gets the sort direction. + /// + public bool Ascending { get => field; init; } + + /// + /// Applies the sort operation to the queryable. + /// + /// + /// The queryable to which the sort operation is applied. + /// + /// + /// The queryable with the sort operation applied. + /// + public IOrderedQueryable ApplyOrderBy(IQueryable queryable) + { + if (queryable is null) + { + throw new ArgumentNullException(nameof(queryable)); + } + + if (Ascending) + { + return queryable.OrderBy(KeySelector); + } + + return queryable.OrderByDescending(KeySelector); + } + + /// + /// Applies the sort operation to the queryable. + /// + /// + /// The queryable to which the sort operation is applied. + /// + /// + /// The queryable with the sort operation applied. + /// + public IOrderedQueryable ApplyThenBy(IOrderedQueryable queryable) + { + if (Ascending) + { + return queryable.ThenBy(KeySelector); + } + + return queryable.ThenByDescending(KeySelector); + } +} diff --git a/src/GreenDonut/src/Data.Primitives/SortDefinition.cs b/src/GreenDonut/src/Data.Primitives/SortDefinition.cs new file mode 100644 index 00000000000..4a061782811 --- /dev/null +++ b/src/GreenDonut/src/Data.Primitives/SortDefinition.cs @@ -0,0 +1,79 @@ +using System.Collections.Immutable; +using System.Text; + +namespace GreenDonut.Data; + +/// +/// Represents all sort operations applied to an entity type. +/// +/// +/// The entity type on which the sort operations are applied. +/// +public sealed record SortDefinition +{ + /// + /// Initializes a new instance of . + /// + /// + /// The sort operations. + /// + public SortDefinition(params ISortBy[] operations) + { + Operations = [..operations]; + } + + /// + /// Initializes a new instance of . + /// + /// + /// The sort operations. + /// + public SortDefinition(IEnumerable> operations) + { + Operations = [..operations]; + } + + /// + /// The sort operations. + /// + public ImmutableArray> Operations { get; init; } + + /// + /// Deconstructs the sort operations. + /// + /// + /// The sort operations. + /// + public void Deconstruct(out ImmutableArray> operations) + => operations = Operations; + + public override string ToString() + { + if (Operations.Length == 0) + { + return "{}"; + } + + var next = false; + var sb = new StringBuilder(); + sb.Append('{'); + + foreach (var operation in Operations) + { + if (next) + { + sb.Append(','); + } + + sb.Append(operation.KeySelector); + sb.Append(':'); + sb.Append(operation.Ascending ? "ASC" : "DESC"); + next = true; + } + + sb.Append('}'); + return sb.ToString(); + } + + public static SortDefinition Empty { get; } = new(); +} diff --git a/src/GreenDonut/test/Core.Tests/ExpressionHasherTests.cs b/src/GreenDonut/test/Core.Tests/ExpressionHasherTests.cs new file mode 100644 index 00000000000..91533ad1e06 --- /dev/null +++ b/src/GreenDonut/test/Core.Tests/ExpressionHasherTests.cs @@ -0,0 +1,123 @@ +using System.Linq.Expressions; +using GreenDonut.Data; +using Xunit; + +namespace GreenDonut; + +public static class ExpressionHasherTests +{ + [Fact] + public static void Simple_Field_Selector() + { + // arrange + var hasher = new ExpressionHasher(); + Expression> selector = x => x.Name; + + // act + var hash = hasher.Add(selector).Compute(); + + // assert + Assert.Equal("3bbe9acfc37825818d75547df2802080", hash); + } + + [Fact] + public static void Selector_With_Interface() + { + // arrange + var hasher = new ExpressionHasher(); + Expression> selector1 = + x => new Entity1 { Entity = new Entity2 { Name = ((Entity2)x.Entity).Name } }; + Expression> selector2 = + x => new Entity1 { Entity = new Entity3 { Name = ((Entity3)x.Entity).Name } }; + + // act + var hash1 = hasher.Add(selector1).Compute(); + var hash2 = hasher.Add(selector2).Compute(); + + // assert + Assert.Equal("dbf2f60aa7fa00fe4856894cb4ebe8fc", hash1); + Assert.Equal("3662ca2896134511f1dc130120d5005f", hash2); + Assert.NotEqual(hash1, hash2); + } + + [Fact] + public static void Selector_With_Interface_And_Combine() + { + // arrange + var hasher = new ExpressionHasher(); + Expression> selector1 = + x => new Entity1 { Entity = new Entity2 { Name = ((Entity2)x.Entity).Name } }; + Expression> selector2 = + x => new Entity1 { Entity = new Entity3 { Name = ((Entity3)x.Entity).Name } }; + + // act + var hash1 = hasher.Add(selector1).Compute(); + var hash2 = hasher.Add(selector2).Compute(); + var hash3 = hasher.Add(selector1).Add(selector2).Compute(); + + // assert + Assert.Equal("dbf2f60aa7fa00fe4856894cb4ebe8fc", hash1); + Assert.Equal("3662ca2896134511f1dc130120d5005f", hash2); + Assert.Equal("3dc320227bb0fe57f35be799bad69975", hash3); + Assert.NotEqual(hash1, hash2); + Assert.NotEqual(hash1, hash3); + } + + [Fact] + public static void Selector_With_List() + { + // arrange + var hasher = new ExpressionHasher(); + Expression> selector = + x => new Entity1 + { + Entities = new List( + x.Entities + .OfType() + .Select(t => new Entity2 { Name = t.Name } )) + }; + + // act + var hash = hasher.Add(selector).Compute(); + + // assert + Assert.Equal("bcee7ce84e8e24974afa2d4e121c2116", hash); + } + + [Fact] + public static void Simple_Predicate() + { + // arrange + var hasher = new ExpressionHasher(); + Expression> selector = x => x.Name == "abc"; + + // act + var hash = hasher.Add(selector).Compute(); + + // assert + Assert.Equal("4ca93a1aed69ddc7269106fd807109b1", hash); + } + + public class Entity1 + { + public string Name { get; set; } = default!; + + public string? Description { get; set; } + + public IEntity Entity { get; set; } = default!; + + public List Entities { get; set; } = default!; + } + + public interface IEntity; + + public class Entity2 : IEntity + { + public string Name { get; set; } = default!; + } + + public class Entity3 : IEntity + { + public string Name { get; set; } = default!; + } +} diff --git a/src/HotChocolate/Core/src/Execution/Configuration/ConfigurationContext.cs b/src/HotChocolate/Core/src/Execution/Configuration/ConfigurationContext.cs index 1c1bd93b347..fa305344050 100644 --- a/src/HotChocolate/Core/src/Execution/Configuration/ConfigurationContext.cs +++ b/src/HotChocolate/Core/src/Execution/Configuration/ConfigurationContext.cs @@ -1,3 +1,5 @@ +using HotChocolate.Types.Descriptors; + namespace HotChocolate.Execution.Configuration; /// @@ -5,6 +7,8 @@ namespace HotChocolate.Execution.Configuration; /// public sealed class ConfigurationContext : IHasContextData { + private IDescriptorContext? _descriptorContext; + /// /// Initializes a new instance of . /// @@ -52,4 +56,20 @@ public ConfigurationContext( /// Gets the configuration context data which can be used by hooks to store arbitrary state. /// public IDictionary ContextData { get; } = new Dictionary(); + + /// + /// Gets the descriptor context. + /// + public IDescriptorContext DescriptorContext + { + get + { + if(_descriptorContext is null) + { + _descriptorContext = SchemaBuilder.CreateContext(); + } + + return _descriptorContext; + } + } } diff --git a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.cs b/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.cs index 94690ac8da6..80e50131255 100644 --- a/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.cs +++ b/src/HotChocolate/Core/src/Execution/DependencyInjection/RequestExecutorBuilderExtensions.cs @@ -2,6 +2,7 @@ using HotChocolate.Execution; using HotChocolate.Execution.Configuration; using HotChocolate.Execution.Options; +using HotChocolate.Types.Descriptors; using Microsoft.Extensions.Options; // ReSharper disable once CheckNamespace @@ -118,6 +119,30 @@ public static IRequestExecutorBuilder ConfigureSchema( (ctx, sp) => configureSchema(sp, ctx.SchemaBuilder)))); } + /// + /// Adds a delegate that will be used to configure the descriptor context. + /// + public static IRequestExecutorBuilder ConfigureDescriptorContext( + this IRequestExecutorBuilder builder, + Action configure) + { + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (configure is null) + { + throw new ArgumentNullException(nameof(configure)); + } + + return Configure( + builder, + options => options.OnConfigureSchemaBuilderHooks.Add( + new OnConfigureSchemaBuilderAction( + (ctx, _) => configure(ctx.DescriptorContext)))); + } + /// /// Adds a delegate that will be used to configure a named . /// diff --git a/src/HotChocolate/Core/src/Execution/Experiments.cs b/src/HotChocolate/Core/src/Execution/Experiments.cs deleted file mode 100644 index cf9f4f64f53..00000000000 --- a/src/HotChocolate/Core/src/Execution/Experiments.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace HotChocolate.Execution; - -internal static class Experiments -{ - public const string Selectors = "GD0001"; - public const string Predicates = "GD0002"; -} diff --git a/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs b/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs index 6ef63a532dc..9b501ee9f5c 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs @@ -1,20 +1,12 @@ -#if NET6_0_OR_GREATER -#nullable enable - -using System.Buffers; -using System.Diagnostics.CodeAnalysis; using HotChocolate.Execution.Processing; using HotChocolate.Pagination; // ReSharper disable once CheckNamespace -namespace GreenDonut.Selectors; +namespace GreenDonut.Data; /// /// Provides extension methods for projection on DataLoader. /// -#if NET8_0_OR_GREATER -[Experimental(Experiments.Selectors)] -#endif public static class HotChocolateExecutionDataLoaderExtensions { /// @@ -82,62 +74,6 @@ public static IDataLoader Select( return dataLoader.Select(expression); } - /// - /// Selects the fields that where selected in the GraphQL selection tree. - /// - /// - /// The data loader. - /// - /// - /// The selection that shall be applied to the data loader. - /// - /// - /// The key type. - /// - /// - /// The value type. - /// - /// - /// Returns a new data loader that applies the selection. - /// - public static IDataLoader> Select( - this IDataLoader> dataLoader, - ISelection selection) - where TKey : notnull - where TValue : notnull - { - var expression = selection.AsSelector(); - return dataLoader.Select(expression); - } - - /// - /// Selects the fields that where selected in the GraphQL selection tree. - /// - /// - /// The data loader. - /// - /// - /// The selection that shall be applied to the data loader. - /// - /// - /// The key type. - /// - /// - /// The value type. - /// - /// - /// Returns a new data loader that applies the selection. - /// - public static IDataLoader> Select( - this IDataLoader> dataLoader, - ISelection selection) - where TKey : notnull - where TValue : notnull - { - var expression = selection.AsSelector(); - return dataLoader.Select(expression); - } - /// /// Selects the fields that where selected in the GraphQL selection tree. /// @@ -184,8 +120,8 @@ public static IDataLoader> Select( /// /// Returns a new data loader that applies the selection. /// - public static IPagingDataLoader> Select( - this IPagingDataLoader> dataLoader, + public static IDataLoader> Select( + this IDataLoader> dataLoader, ISelection selection) where TKey : notnull { @@ -203,4 +139,3 @@ public static IPagingDataLoader> Select( return dataLoader.Select(expression); } } -#endif diff --git a/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionSelectionExtensions.cs b/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionSelectionExtensions.cs index 95f39a30ff9..52d005d7faf 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionSelectionExtensions.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionSelectionExtensions.cs @@ -5,7 +5,7 @@ using System.Linq.Expressions; using System.Text; using System.Runtime.CompilerServices; -using GreenDonut.Selectors; +using GreenDonut.Data; using HotChocolate.Execution.Projections; using HotChocolate.Types; using HotChocolate.Types.Descriptors.Definitions; @@ -33,9 +33,6 @@ public static class HotChocolateExecutionSelectionExtensions /// /// Returns a selector expression that can be used for data projections. /// -#if NET8_0_OR_GREATER - [Experimental(Experiments.Selectors)] -#endif public static Expression> AsSelector( this ISelection selection) { @@ -93,9 +90,6 @@ private static Expression> GetOrCreateExpression( static (_, ctx) => ctx._builder.BuildExpression(ctx.selection), (_builder, selection)); -#if NET8_0_OR_GREATER - [Experimental(Experiments.Selectors)] -#endif private static Expression> GetOrCreateExpression( ISelection selection, ISelectorBuilder builder) diff --git a/src/HotChocolate/Core/src/Execution/RequestExecutorResolver.cs b/src/HotChocolate/Core/src/Execution/RequestExecutorResolver.cs index 10834f89d88..5843cc482a2 100644 --- a/src/HotChocolate/Core/src/Execution/RequestExecutorResolver.cs +++ b/src/HotChocolate/Core/src/Execution/RequestExecutorResolver.cs @@ -386,7 +386,7 @@ private static async ValueTask CreateSchemaAsync( .AddServices(schemaServices) .SetContextData(typeof(RequestExecutorOptions).FullName!, executorOptions); - var descriptorContext = context.SchemaBuilder.CreateContext(); + var descriptorContext = context.DescriptorContext; await foreach (var member in typeModuleChangeMonitor.CreateTypesAsync(descriptorContext) diff --git a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs index 0ea4192ec67..6c7d88ea20d 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/DataLoaderFileBuilder.cs @@ -1,5 +1,4 @@ using System.Collections.Immutable; -using System.Security.Cryptography.X509Certificates; using System.Text; using HotChocolate.Types.Analyzers.Helpers; using HotChocolate.Types.Analyzers.Inspectors; @@ -268,7 +267,7 @@ public void WriteDataLoaderLoadMethod( parameter.StateKey); _writer.IncreaseIndent(); _writer.WriteIndentedLine( - "?? new global::GreenDonut.Selectors.DefaultSelectorBuilder();"); + "?? global::GreenDonut.Data.DefaultSelectorBuilder.Empty;"); _writer.DecreaseIndent(); } else if (parameter.Kind is DataLoaderParameterKind.PredicateBuilder) @@ -280,9 +279,62 @@ public void WriteDataLoaderLoadMethod( parameter.StateKey); _writer.IncreaseIndent(); _writer.WriteIndentedLine( - "?? new global::GreenDonut.Predicates.DefaultPredicateBuilder();"); + "?? global::GreenDonut.Data.DefaultPredicateBuilder.Empty;"); _writer.DecreaseIndent(); } + else if (parameter.Kind is DataLoaderParameterKind.SortDefinition) + { + _writer.WriteIndentedLine( + "var {0} = context.GetState<{1}>(\"{2}\")", + parameter.VariableName, + parameter.Type.ToFullyQualified(), + parameter.StateKey); + _writer.IncreaseIndent(); + _writer.WriteIndentedLine( + "?? {0}.Empty;", + parameter.Type.ToFullyQualified()); + _writer.DecreaseIndent(); + } + else if (parameter.Kind is DataLoaderParameterKind.QueryContext) + { + _writer.WriteIndentedLine( + "var {0}_selector = context.GetState(\"{2}\")?.TryCompile<{3}>();", + parameter.VariableName, + WellKnownTypes.SelectorBuilder, + DataLoaderInfo.Selector, + ((INamedTypeSymbol)parameter.Type).TypeArguments[0].ToFullyQualified()); + _writer.WriteIndentedLine( + "var {0}_predicate = context.GetState(\"{2}\")?.TryCompile<{3}>();", + parameter.VariableName, + WellKnownTypes.PredicateBuilder, + DataLoaderInfo.Predicate, + ((INamedTypeSymbol)parameter.Type).TypeArguments[0].ToFullyQualified()); + _writer.WriteIndentedLine( + "var {0}_sortDefinition = context.GetState>(\"{3}\");", + parameter.VariableName, + WellKnownTypes.SortDefinition, + ((INamedTypeSymbol)parameter.Type).TypeArguments[0].ToFullyQualified(), + DataLoaderInfo.Sorting); + _writer.WriteLine(); + _writer.WriteIndentedLine( + "if({0}_selector is null && {0}_predicate is null && {0}_sortDefinition is null)"); + _writer.WriteIndentedLine("{"); + _writer.IncreaseIndent(); + _writer.WriteIndentedLine( + "var {0} = global::{1}<{2}>.Empty;", + parameter.VariableName, + WellKnownTypes.QueryContext, + ((INamedTypeSymbol)parameter.Type).TypeArguments[0].ToFullyQualified()); + _writer.DecreaseIndent(); + _writer.WriteIndentedLine("}"); + _writer.WriteLine(); + _writer.WriteIndentedLine( + "var {0} = new global::{1}<{2}>({0}_selector?, " + + "{0}_predicate, {0}_sortDefinition);", + parameter.VariableName, + WellKnownTypes.QueryContext, + ((INamedTypeSymbol)parameter.Type).TypeArguments[0].ToFullyQualified()); + } else if (parameter.Kind is DataLoaderParameterKind.PagingArguments) { _writer.WriteIndentedLine( diff --git a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/IOutputTypeFileBuilder.cs b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/IOutputTypeFileBuilder.cs index fe2cd3909ef..ec7f0e8ff12 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/IOutputTypeFileBuilder.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/IOutputTypeFileBuilder.cs @@ -7,9 +7,11 @@ public interface IOutputTypeFileBuilder void WriteHeader(); void WriteBeginNamespace(); + void WriteEndNamespace(); string WriteBeginClass(string typeName); + void WriteEndClass(); void WriteInitializeMethod(IOutputTypeInfo typeInfo); diff --git a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/InterfaceTypeExtensionFileBuilder.cs b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/InterfaceTypeExtensionFileBuilder.cs index f3bf0db7d63..fadf106e960 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/InterfaceTypeExtensionFileBuilder.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/InterfaceTypeExtensionFileBuilder.cs @@ -47,7 +47,7 @@ public void WriteInitializeMethod(IOutputTypeInfo typeInfo) { _writer.WriteIndentedLine( "internal static void Initialize(global::HotChocolate.Types.IInterfaceTypeDescriptor<{0}> descriptor)", - typeInfo.RuntimeType.ToFullyQualified()); + typeInfo.RuntimeType!.ToFullyQualified()); _writer.WriteIndentedLine("{"); using (_writer.IncreaseIndent()) @@ -127,6 +127,6 @@ public void WriteConfigureMethod(IOutputTypeInfo typeInfo) { _writer.WriteIndentedLine( "static partial void Configure(global::HotChocolate.Types.IInterfaceTypeDescriptor<{0}> descriptor);", - typeInfo.RuntimeType.ToFullyQualified()); + typeInfo.RuntimeType!.ToFullyQualified()); } } diff --git a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ModuleFileBuilder.cs b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ModuleFileBuilder.cs index 6dd4e21890e..c77bd810ddd 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ModuleFileBuilder.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ModuleFileBuilder.cs @@ -1,6 +1,7 @@ using System.Text; using HotChocolate.Types.Analyzers.Helpers; using HotChocolate.Types.Analyzers.Models; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; namespace HotChocolate.Types.Analyzers.FileBuilders; @@ -79,188 +80,34 @@ public void WriteRegisterTypeExtension(string typeName, bool staticType) : "builder.AddTypeExtension();", typeName); - public void WriteRegisterObjectTypeExtension(string runtimeTypeName, string extensionType) - { - _writer.WriteIndentedLine( - "AddObjectTypeExtension_8734371<{0}>(builder, {1}.Initialize);", - runtimeTypeName, - extensionType); - } + public void WriteEnsureObjectTypeExtensionIsRegistered(string runtimeTypeName) + => _writer.WriteIndentedLine("builder.AddType>();", runtimeTypeName); - public void WriteRegisterInterfaceTypeExtension(string runtimeTypeName, string extensionType) - { - _writer.WriteIndentedLine( - "AddInterfaceTypeExtension_8734371<{0}>(builder, {1}.Initialize);", - runtimeTypeName, - extensionType); - } + public void WriteEnsureInterfaceTypeExtensionIsRegistered(string runtimeTypeName) + => _writer.WriteIndentedLine("builder.AddType>();", runtimeTypeName); - public void WriteRegisterObjectTypeExtensionHelpers() + public void WriteRegisterTypeExtension(string key, string runtimeTypeName, string extensionType) { - _writer.WriteLine(); - _writer.WriteIndentedLine("private static void AddObjectTypeExtension_8734371("); - - using (_writer.IncreaseIndent()) - { - _writer.WriteIndentedLine("global::HotChocolate.Execution.Configuration.IRequestExecutorBuilder builder,"); - _writer.WriteIndentedLine("Action> initialize)"); - } - - _writer.WriteIndentedLine("{"); - + _writer.WriteIndentedLine( + "builder.ConfigureDescriptorContext(ctx => ctx.TypeConfiguration.TryAdd<{0}>(", + runtimeTypeName); using (_writer.IncreaseIndent()) { - _writer.WriteIndentedLine("builder.ConfigureSchema(sb =>"); - _writer.WriteIndentedLine("{"); - - using (_writer.IncreaseIndent()) - { - _writer.WriteIndentedLine("string typeName = typeof(T).FullName!;"); - _writer.WriteIndentedLine("string typeKey = $\"8734371_Type_ObjectType<{typeName}>\";"); - _writer.WriteIndentedLine("string hooksKey = $\"8734371_Hooks_ObjectType<{typeName}>\";"); - _writer.WriteLine(); - _writer.WriteIndentedLine("if (!sb.ContextData.ContainsKey(typeKey))"); - _writer.WriteIndentedLine("{"); - - using (_writer.IncreaseIndent()) - { - _writer.WriteIndentedLine("sb.AddObjectType("); - using (_writer.IncreaseIndent()) - { - _writer.WriteIndentedLine("descriptor =>"); - _writer.WriteIndentedLine("{"); - - using (_writer.IncreaseIndent()) - { - _writer.WriteIndentedLine( - "var hooks = (global::System.Collections.Generic.List<" - + "Action>>)" - + "descriptor.Extend().Context.ContextData[hooksKey]!;"); - _writer.WriteIndentedLine("foreach (var configure in hooks)"); - _writer.WriteIndentedLine("{"); - - using (_writer.IncreaseIndent()) - { - _writer.WriteIndentedLine("configure(descriptor);"); - } - - _writer.WriteIndentedLine("};"); - } - - _writer.WriteIndentedLine("});"); - } - - _writer.WriteIndentedLine("sb.ContextData.Add(typeKey, null);"); - } - - _writer.WriteIndentedLine("}"); - _writer.WriteLine(); - - _writer.WriteIndentedLine("if (!sb.ContextData.TryGetValue(hooksKey, out var value))"); - _writer.WriteIndentedLine("{"); - - using (_writer.IncreaseIndent()) - { - _writer.WriteIndentedLine( - "value = new System.Collections.Generic.List>>();"); - _writer.WriteIndentedLine("sb.ContextData.Add(hooksKey, value);"); - } - - _writer.WriteIndentedLine("}"); - _writer.WriteLine(); - _writer.WriteIndentedLine( - "((System.Collections.Generic.List>>)value!)" - + ".Add(initialize);"); - } - - _writer.WriteIndentedLine("});"); + _writer.WriteIndentedLine("\"{0}\",", key); + _writer.WriteIndentedLine("() => {0}.Initialize));", extensionType); } - - _writer.WriteIndentedLine("}"); } - public void WriteRegisterInterfaceTypeExtensionHelpers() + public void WriteRegisterRootTypeExtension(string key, OperationType operation, string extensionType) { - _writer.WriteLine(); - _writer.WriteIndentedLine("private static void AddInterfaceTypeExtension_8734371("); + _writer.WriteIndentedLine("builder.ConfigureDescriptorContext(ctx => ctx.TypeConfiguration.TryAdd("); using (_writer.IncreaseIndent()) { - _writer.WriteIndentedLine("global::HotChocolate.Execution.Configuration.IRequestExecutorBuilder builder,"); - _writer.WriteIndentedLine("Action> initialize)"); - } - - _writer.WriteIndentedLine("{"); - - using (_writer.IncreaseIndent()) - { - _writer.WriteIndentedLine("builder.ConfigureSchema(sb =>"); - _writer.WriteIndentedLine("{"); - - using (_writer.IncreaseIndent()) - { - _writer.WriteIndentedLine("string typeName = typeof(T).FullName!;"); - _writer.WriteIndentedLine("string typeKey = $\"8734371_Type_InterfaceType<{typeName}>\";"); - _writer.WriteIndentedLine("string hooksKey = $\"8734371_Hooks_InterfaceType<{typeName}>\";"); - _writer.WriteLine(); - _writer.WriteIndentedLine("if (!sb.ContextData.ContainsKey(typeKey))"); - _writer.WriteIndentedLine("{"); - - using (_writer.IncreaseIndent()) - { - _writer.WriteIndentedLine("sb.AddInterfaceType("); - using (_writer.IncreaseIndent()) - { - _writer.WriteIndentedLine("descriptor =>"); - _writer.WriteIndentedLine("{"); - - using (_writer.IncreaseIndent()) - { - _writer.WriteIndentedLine( - "var hooks = (global::System.Collections.Generic.List<" - + "Action>>)" - + "descriptor.Extend().Context.ContextData[hooksKey]!;"); - _writer.WriteIndentedLine("foreach (var configure in hooks)"); - _writer.WriteIndentedLine("{"); - - using (_writer.IncreaseIndent()) - { - _writer.WriteIndentedLine("configure(descriptor);"); - } - - _writer.WriteIndentedLine("};"); - } - - _writer.WriteIndentedLine("});"); - } - - _writer.WriteIndentedLine("sb.ContextData.Add(typeKey, null);"); - } - - _writer.WriteIndentedLine("}"); - _writer.WriteLine(); - - _writer.WriteIndentedLine("if (!sb.ContextData.TryGetValue(hooksKey, out var value))"); - _writer.WriteIndentedLine("{"); - - using (_writer.IncreaseIndent()) - { - _writer.WriteIndentedLine( - "value = new System.Collections.Generic.List>>();"); - _writer.WriteIndentedLine("sb.ContextData.Add(hooksKey, value);"); - } - - _writer.WriteIndentedLine("}"); - _writer.WriteLine(); - _writer.WriteIndentedLine( - "((System.Collections.Generic.List>>)value!)" - + ".Add(initialize);"); - } - - _writer.WriteIndentedLine("});"); + _writer.WriteIndentedLine("\"{0}\",", key); + _writer.WriteIndentedLine("global::HotChocolate.Types.OperationType.{0},", operation); + _writer.WriteIndentedLine("() => {0}.Initialize));", extensionType); } - - _writer.WriteIndentedLine("}"); } public void WriteRegisterDataLoader(string typeName) diff --git a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ObjectTypeExtensionFileBuilder.cs b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ObjectTypeExtensionFileBuilder.cs index 24263cc6f8f..dcdda490b42 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ObjectTypeExtensionFileBuilder.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ObjectTypeExtensionFileBuilder.cs @@ -44,15 +44,40 @@ public void WriteEndClass() } public void WriteInitializeMethod(IOutputTypeInfo typeInfo) + { + if (typeInfo is ObjectTypeExtensionInfo objectTypeExtension) + { + WriteObjectTypeInitializeMethod(objectTypeExtension); + } + else if (typeInfo is RootTypeExtensionInfo rootTypeExtension) + { + WriteRootTypeInitializeMethod(rootTypeExtension); + } + else + { + throw new NotSupportedException(); + } + } + + public void WriteObjectTypeInitializeMethod(IOutputTypeInfo typeInfo) { if (typeInfo is not ObjectTypeExtensionInfo objectTypeExtension) { return; } - _writer.WriteIndentedLine( - "internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor<{0}> descriptor)", - objectTypeExtension.RuntimeType.ToFullyQualified()); + if (typeInfo.IsRootType) + { + _writer.WriteIndentedLine( + "internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor descriptor)"); + } + else + { + _writer.WriteIndentedLine( + "internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor<{0}> descriptor)", + objectTypeExtension.RuntimeType.ToFullyQualified()); + } + _writer.WriteIndentedLine("{"); using (_writer.IncreaseIndent()) @@ -93,12 +118,13 @@ public void WriteInitializeMethod(IOutputTypeInfo typeInfo) _writer.WriteIndentedLine( "var thisType = typeof({0});", objectTypeExtension.Type.ToFullyQualified()); - if(hasRuntimeBindings) + if (hasRuntimeBindings) { _writer.WriteIndentedLine( "var runtimeType = typeof({0});", objectTypeExtension.RuntimeType.ToFullyQualified()); } + _writer.WriteIndentedLine( "var bindingResolver = descriptor.Extend().Context.ParameterBindingResolver;"); _writer.WriteIndentedLine( @@ -121,82 +147,143 @@ public void WriteInitializeMethod(IOutputTypeInfo typeInfo) } } - if (objectTypeExtension.Resolvers.Length > 0) + WriteResolverBindings(objectTypeExtension); + + _writer.WriteLine(); + _writer.WriteIndentedLine("Configure(descriptor);"); + } + + _writer.WriteIndentedLine("}"); + } + + public void WriteRootTypeInitializeMethod(IOutputTypeInfo typeInfo) + { + if (typeInfo is not RootTypeExtensionInfo rootTypeExtension) + { + return; + } + + _writer.WriteIndentedLine( + "internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor descriptor)"); + + _writer.WriteIndentedLine("{"); + + using (_writer.IncreaseIndent()) + { + if (rootTypeExtension.Resolvers.Length > 0) { - foreach (var resolver in objectTypeExtension.Resolvers) + _writer.WriteIndentedLine("const global::{0} bindingFlags =", WellKnownTypes.BindingFlags); + using (_writer.IncreaseIndent()) { - _writer.WriteLine(); - _writer.WriteIndentedLine("descriptor"); + _writer.WriteIndentedLine("global::{0}.Public", WellKnownTypes.BindingFlags); + using (_writer.IncreaseIndent()) + { + _writer.WriteIndentedLine("| global::{0}.NonPublic", WellKnownTypes.BindingFlags); + _writer.WriteIndentedLine("| global::{0}.Static;", WellKnownTypes.BindingFlags); + } + } + + _writer.WriteLine(); + _writer.WriteIndentedLine( + "var thisType = typeof({0});", + rootTypeExtension.Type.ToFullyQualified()); + _writer.WriteIndentedLine( + "var bindingResolver = descriptor.Extend().Context.ParameterBindingResolver;"); + _writer.WriteIndentedLine( + "global::{0}Resolvers.InitializeBindings(bindingResolver);", + rootTypeExtension.Type.ToDisplayString()); + } + + WriteResolverBindings(rootTypeExtension); + + _writer.WriteLine(); + _writer.WriteIndentedLine("Configure(descriptor);"); + } + + _writer.WriteIndentedLine("}"); + } + + private void WriteResolverBindings(IOutputTypeInfo typeInfo) + { + if (typeInfo.Resolvers.Length > 0) + { + foreach (var resolver in typeInfo.Resolvers) + { + _writer.WriteLine(); + _writer.WriteIndentedLine("descriptor"); + + using (_writer.IncreaseIndent()) + { + _writer.WriteIndentedLine( + ".Field(thisType.GetMember(\"{0}\", bindingFlags)[0])", + resolver.Member.Name); + + _writer.WriteIndentedLine(".ExtendWith(c =>"); + _writer.WriteIndentedLine("{"); using (_writer.IncreaseIndent()) { + _writer.WriteIndentedLine("c.Definition.SetSourceGeneratorFlags();"); _writer.WriteIndentedLine( - ".Field(thisType.GetMember(\"{0}\", bindingFlags)[0])", + "c.Definition.Resolvers = {0}Resolvers.{1}_{2}();", + typeInfo.Type.ToFullyQualified(), + typeInfo.Type.Name, resolver.Member.Name); - _writer.WriteIndentedLine(".ExtendWith(c =>"); - _writer.WriteIndentedLine("{"); - using (_writer.IncreaseIndent()) + if (resolver.ResultKind is not ResolverResultKind.Pure + && !resolver.Member.HasPostProcessorAttribute() + && resolver.Member.IsListType(out var elementType)) { - _writer.WriteIndentedLine("c.Definition.SetSourceGeneratorFlags();"); _writer.WriteIndentedLine( - "c.Definition.Resolvers = {0}Resolvers.{1}_{2}();", - objectTypeExtension.Type.ToFullyQualified(), - objectTypeExtension.Type.Name, - resolver.Member.Name); - - if (resolver.ResultKind is not ResolverResultKind.Pure - && !resolver.Member.HasPostProcessorAttribute() - && resolver.Member.IsListType(out var elementType)) - { - _writer.WriteIndentedLine( - "c.Definition.ResultPostProcessor = global::{0}<{1}>.Default;", - WellKnownTypes.ListPostProcessor, - elementType); - } + "c.Definition.ResultPostProcessor = global::{0}<{1}>.Default;", + WellKnownTypes.ListPostProcessor, + elementType); } - - _writer.WriteIndentedLine("});"); } - if (resolver.Bindings.Length > 0) + _writer.WriteIndentedLine("});"); + } + + if (resolver.Bindings.Length > 0) + { + foreach (var binding in resolver.Bindings) { - foreach (var binding in resolver.Bindings) - { - _writer.WriteLine(); - _writer.WriteIndentedLine("descriptor"); + _writer.WriteLine(); + _writer.WriteIndentedLine("descriptor"); - using (_writer.IncreaseIndent()) + using (_writer.IncreaseIndent()) + { + if (binding.Kind is MemberBindingKind.Property) + { + _writer.WriteIndentedLine( + ".Field(runtimeType.GetMember(\"{0}\", runtimeBindingFlags)[0])", + binding.Name); + _writer.WriteIndentedLine(".Ignore();"); + } + else if (binding.Kind is MemberBindingKind.Property) { - if (binding.Kind is MemberBindingKind.Property) - { - _writer.WriteIndentedLine( - ".Field(runtimeType.GetMember(\"{0}\", runtimeBindingFlags)[0])", - binding.Name); - _writer.WriteIndentedLine(".Ignore();"); - } - else if (binding.Kind is MemberBindingKind.Property) - { - _writer.WriteIndentedLine(".Field(\"{0}\")", binding.Name); - _writer.WriteIndentedLine(".Ignore();"); - } + _writer.WriteIndentedLine(".Field(\"{0}\")", binding.Name); + _writer.WriteIndentedLine(".Ignore();"); } } } } } - - _writer.WriteLine(); - _writer.WriteIndentedLine("Configure(descriptor);"); } - - _writer.WriteIndentedLine("}"); } public void WriteConfigureMethod(IOutputTypeInfo typeInfo) { - _writer.WriteIndentedLine( - "static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor<{0}> descriptor);", - typeInfo.RuntimeType.ToFullyQualified()); + if (typeInfo.RuntimeType is null) + { + _writer.WriteIndentedLine( + "static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor);"); + } + else + { + _writer.WriteIndentedLine( + "static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor<{0}> descriptor);", + typeInfo.RuntimeType.ToFullyQualified()); + } } } diff --git a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ResolverFileBuilder.cs b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ResolverFileBuilder.cs index 9382db06371..2d213790aa9 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ResolverFileBuilder.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/FileBuilders/ResolverFileBuilder.cs @@ -240,7 +240,8 @@ private static string ToFullyQualifiedString( IMethodSymbol resolverMethod, ILocalTypeLookup typeLookup) { - if (type.TypeKind is TypeKind.Error && typeLookup.TryGetTypeName(type, resolverMethod, out var typeDisplayName)) + if (type.TypeKind is TypeKind.Error + && typeLookup.TryGetTypeName(type, resolverMethod, out var typeDisplayName)) { return typeDisplayName; } @@ -528,15 +529,18 @@ private void AddResolverArguments(Resolver resolver, IMethodSymbol resolverMetho case ResolverParameterKind.CancellationToken: _writer.WriteIndentedLine("var args{0} = context.RequestAborted;", i); break; + case ResolverParameterKind.ClaimsPrincipal: _writer.WriteIndentedLine( "var args{0} = context.GetGlobalState<{1}>(\"ClaimsPrincipal\");", i, WellKnownTypes.ClaimsPrincipal); break; + case ResolverParameterKind.DocumentNode: _writer.WriteIndentedLine("var args{0} = context.Operation.Document;", i); break; + case ResolverParameterKind.EventMessage: _writer.WriteIndentedLine( "var args{0} = context.GetScopedState<{1}>(" @@ -544,68 +548,73 @@ private void AddResolverArguments(Resolver resolver, IMethodSymbol resolverMetho i, parameter.Type.ToFullyQualified()); break; + case ResolverParameterKind.FieldNode: _writer.WriteIndentedLine( "var args{0} = context.Selection.SyntaxNode", i, parameter.Type.ToFullyQualified()); break; + case ResolverParameterKind.OutputField: _writer.WriteIndentedLine( "var args{0} = context.Selection.Field", i, parameter.Type.ToFullyQualified()); break; + case ResolverParameterKind.HttpContext: _writer.WriteIndentedLine( "var args{0} = context.GetGlobalState(nameof(global::{1}))!;", i, WellKnownTypes.HttpContext); break; + case ResolverParameterKind.HttpRequest: _writer.WriteIndentedLine( "var args{0} = context.GetGlobalState(nameof(global::{1}))?.Request!;", i, WellKnownTypes.HttpContext); break; + case ResolverParameterKind.HttpResponse: _writer.WriteIndentedLine( "var args{0} = context.GetGlobalState(nameof(global::{1}))?.Response!;", i, WellKnownTypes.HttpContext); break; + case ResolverParameterKind.GetGlobalState when parameter.Parameter.HasExplicitDefaultValue: - { - var defaultValue = parameter.Parameter.ExplicitDefaultValue; - var defaultValueString = GeneratorUtils.ConvertDefaultValueToString(defaultValue, parameter.Type); + { + var defaultValue = parameter.Parameter.ExplicitDefaultValue; + var defaultValueString = GeneratorUtils.ConvertDefaultValueToString(defaultValue, parameter.Type); + + _writer.WriteIndentedLine( + "var args{0} = context.GetGlobalStateOrDefault<{1}{2}>(\"{3}\", {4});", + i, + parameter.Type.ToFullyQualified(), + parameter.Type.IsNullableRefType() ? "?" : string.Empty, + parameter.Key, + defaultValueString); + break; + } - _writer.WriteIndentedLine( - "var args{0} = context.GetGlobalStateOrDefault<{1}{2}>(\"{3}\", {4});", - i, - parameter.Type.ToFullyQualified(), - parameter.Type.IsNullableRefType() ? "?" : string.Empty, - parameter.Key, - defaultValueString); - break; - } case ResolverParameterKind.GetGlobalState when !parameter.IsNullable: - { _writer.WriteIndentedLine( - "var args{0} = context.GetGlobalState<{1}>(\"{2}\");", - i, - parameter.Type.ToFullyQualified(), - parameter.Key); + "var args{0} = context.GetGlobalState<{1}>(\"{2}\");", + i, + parameter.Type.ToFullyQualified(), + parameter.Key); break; - } + case ResolverParameterKind.GetGlobalState: - { _writer.WriteIndentedLine( "var args{0} = context.GetGlobalStateOrDefault<{1}>(\"{2}\");", i, parameter.Type.ToFullyQualified(), parameter.Key); break; - } + case ResolverParameterKind.SetGlobalState: _writer.WriteIndentedLine( "var args{0} = new HotChocolate.SetState<{1}>(" @@ -614,38 +623,38 @@ private void AddResolverArguments(Resolver resolver, IMethodSymbol resolverMetho ((INamedTypeSymbol)parameter.Type).TypeArguments[0].ToFullyQualified(), parameter.Key); break; + case ResolverParameterKind.GetScopedState when parameter.Parameter.HasExplicitDefaultValue: - { - var defaultValue = parameter.Parameter.ExplicitDefaultValue; - var defaultValueString = GeneratorUtils.ConvertDefaultValueToString(defaultValue, parameter.Type); + { + var defaultValue = parameter.Parameter.ExplicitDefaultValue; + var defaultValueString = GeneratorUtils.ConvertDefaultValueToString(defaultValue, parameter.Type); + + _writer.WriteIndentedLine( + "var args{0} = context.GetScopedStateOrDefault<{1}{2}>(\"{3}\", {4});", + i, + parameter.Type.ToFullyQualified(), + parameter.Type.IsNullableRefType() ? "?" : string.Empty, + parameter.Key, + defaultValueString); + break; + } - _writer.WriteIndentedLine( - "var args{0} = context.GetScopedStateOrDefault<{1}{2}>(\"{3}\", {4});", - i, - parameter.Type.ToFullyQualified(), - parameter.Type.IsNullableRefType() ? "?" : string.Empty, - parameter.Key, - defaultValueString); - break; - } case ResolverParameterKind.GetScopedState when !parameter.IsNullable: - { _writer.WriteIndentedLine( - "var args{0} = context.GetScopedState<{1}>(\"{2}\");", - i, - parameter.Type.ToFullyQualified(), - parameter.Key); + "var args{0} = context.GetScopedState<{1}>(\"{2}\");", + i, + parameter.Type.ToFullyQualified(), + parameter.Key); break; - } + case ResolverParameterKind.GetScopedState: - { _writer.WriteIndentedLine( - "var args{0} = context.GetScopedStateOrDefault<{1}>(\"{2}\");", - i, - parameter.Type.ToFullyQualified(), - parameter.Key); + "var args{0} = context.GetScopedStateOrDefault<{1}>(\"{2}\");", + i, + parameter.Type.ToFullyQualified(), + parameter.Key); break; - } + case ResolverParameterKind.SetScopedState: _writer.WriteIndentedLine( "var args{0} = new HotChocolate.SetState<{1}>(" @@ -654,38 +663,38 @@ private void AddResolverArguments(Resolver resolver, IMethodSymbol resolverMetho ((INamedTypeSymbol)parameter.Type).TypeArguments[0].ToFullyQualified(), parameter.Key); break; + case ResolverParameterKind.GetLocalState when parameter.Parameter.HasExplicitDefaultValue: - { - var defaultValue = parameter.Parameter.ExplicitDefaultValue; - var defaultValueString = GeneratorUtils.ConvertDefaultValueToString(defaultValue, parameter.Type); + { + var defaultValue = parameter.Parameter.ExplicitDefaultValue; + var defaultValueString = GeneratorUtils.ConvertDefaultValueToString(defaultValue, parameter.Type); + + _writer.WriteIndentedLine( + "var args{0} = context.GetLocalStateOrDefault<{1}{2}>(\"{3}\", {4});", + i, + parameter.Type.ToFullyQualified(), + parameter.Type.IsNullableRefType() ? "?" : string.Empty, + parameter.Key, + defaultValueString); + break; + } - _writer.WriteIndentedLine( - "var args{0} = context.GetLocalStateOrDefault<{1}{2}>(\"{3}\", {4});", - i, - parameter.Type.ToFullyQualified(), - parameter.Type.IsNullableRefType() ? "?" : string.Empty, - parameter.Key, - defaultValueString); - break; - } case ResolverParameterKind.GetLocalState when !parameter.IsNullable: - { _writer.WriteIndentedLine( - "var args{0} = context.GetLocalState<{1}>(\"{2}\");", - i, - parameter.Type.ToFullyQualified(), - parameter.Key); + "var args{0} = context.GetLocalState<{1}>(\"{2}\");", + i, + parameter.Type.ToFullyQualified(), + parameter.Key); break; - } + case ResolverParameterKind.GetLocalState: - { _writer.WriteIndentedLine( - "var args{0} = context.GetLocalStateOrDefault<{1}>(\"{2}\");", - i, - parameter.Type.ToFullyQualified(), - parameter.Key); + "var args{0} = context.GetLocalStateOrDefault<{1}>(\"{2}\");", + i, + parameter.Type.ToFullyQualified(), + parameter.Key); break; - } + case ResolverParameterKind.SetLocalState: _writer.WriteIndentedLine( "var args{0} = new HotChocolate.SetState<{1}>(" @@ -705,6 +714,26 @@ private void AddResolverArguments(Resolver resolver, IMethodSymbol resolverMetho resolver.Member.Name, ToFullyQualifiedString(parameter.Type, resolverMethod, typeLookup)); break; + + case ResolverParameterKind.QueryContext: + var entityType = parameter.TypeParameters[0].ToFullyQualified(); + _writer.WriteIndentedLine("var args{0}_selection = context.Selection;", i); + _writer.WriteIndentedLine("var args{0}_filter = context.GetFilterContext();", i); + _writer.WriteIndentedLine("var args{0}_sorting = context.GetSortingContext();", i); + _writer.WriteIndentedLine( + "var args{0} = new global::{1}<{2}>(", + i, + WellKnownTypes.QueryContext, + entityType); + using (_writer.IncreaseIndent()) + { + _writer.WriteIndentedLine("args{0}_selection.AsSelector<{1}>(),", i, entityType); + _writer.WriteIndentedLine("args{0}_filter?.AsPredicate<{1}>(),", i, entityType); + _writer.WriteIndentedLine("args{0}_sorting?.AsSortDefinition<{1}>());", i, entityType); + } + + break; + default: throw new ArgumentOutOfRangeException(); } diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Generators/TypeModuleSyntaxGenerator.cs b/src/HotChocolate/Core/src/Types.Analyzers/Generators/TypeModuleSyntaxGenerator.cs index 1e263527958..e5af14f16b0 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Generators/TypeModuleSyntaxGenerator.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Generators/TypeModuleSyntaxGenerator.cs @@ -28,7 +28,7 @@ private static void Execute( var module = syntaxInfos.GetModuleInfo(compilation.AssemblyName, out var defaultModule); // the generator is disabled. - if(module.Options == ModuleOptions.Disabled) + if (module.Options == ModuleOptions.Disabled) { return; } @@ -59,13 +59,13 @@ private static void WriteConfiguration( generator.WriteBeginRegistrationMethod(); var operations = OperationType.No; - var hasObjectTypeExtensions = false; - var hasInterfaceTypes = false; var hasConfigurations = false; + List? _objectTypeExtensions = null; + List? _interfaceTypeExtensions = null; foreach (var syntaxInfo in syntaxInfos.OrderBy(s => s.OrderByKey)) { - if(syntaxInfo.Diagnostics.Length > 0) + if (syntaxInfo.Diagnostics.Length > 0) { continue; } @@ -78,7 +78,6 @@ private static void WriteConfiguration( generator.WriteRegisterType(type.Name); hasConfigurations = true; } - break; case TypeExtensionInfo extension: @@ -92,7 +91,6 @@ private static void WriteConfiguration( operations |= extension.Type; } } - break; case RegisterDataLoaderInfo dataLoader: @@ -101,7 +99,6 @@ private static void WriteConfiguration( generator.WriteRegisterDataLoader(dataLoader.Name); hasConfigurations = true; } - break; case DataLoaderInfo dataLoader: @@ -116,7 +113,7 @@ private static void WriteConfiguration( dataLoaderDefaults.GenerateInterfaces); hasConfigurations = true; - if(dataLoader.Groups.Count > 0) + if (dataLoader.Groups.Count > 0) { groups ??= []; foreach (var groupName in dataLoader.Groups) @@ -127,7 +124,6 @@ private static void WriteConfiguration( } } } - break; case OperationRegistrationInfo operation: @@ -141,33 +137,55 @@ private static void WriteConfiguration( operations |= operation.Type; } } - break; case ObjectTypeExtensionInfo objectTypeExtension: if ((module.Options & ModuleOptions.RegisterTypes) == ModuleOptions.RegisterTypes && objectTypeExtension.Diagnostics.Length == 0) { - hasObjectTypeExtensions = true; - generator.WriteRegisterObjectTypeExtension( + _objectTypeExtensions ??= []; + _objectTypeExtensions.Add(objectTypeExtension.RuntimeType.ToFullyQualified()); + + generator.WriteRegisterTypeExtension( + GetAssemblyQualifiedName(objectTypeExtension.Type), objectTypeExtension.RuntimeType.ToFullyQualified(), objectTypeExtension.Type.ToFullyQualified()); hasConfigurations = true; } - break; case InterfaceTypeExtensionInfo interfaceType: if ((module.Options & ModuleOptions.RegisterTypes) == ModuleOptions.RegisterTypes && interfaceType.Diagnostics.Length == 0) { - hasInterfaceTypes = true; - generator.WriteRegisterInterfaceTypeExtension( + _interfaceTypeExtensions ??= []; + _interfaceTypeExtensions.Add(interfaceType.RuntimeType.ToFullyQualified()); + + generator.WriteRegisterTypeExtension( + GetAssemblyQualifiedName(interfaceType.Type), interfaceType.RuntimeType.ToFullyQualified(), interfaceType.Type.ToFullyQualified()); hasConfigurations = true; } + break; + + case RootTypeExtensionInfo rootType: + if ((module.Options & ModuleOptions.RegisterTypes) == ModuleOptions.RegisterTypes + && rootType.Diagnostics.Length == 0) + { + var operationType = rootType.OperationType; + generator.WriteRegisterRootTypeExtension( + GetAssemblyQualifiedName(rootType.Type), + operationType, + rootType.Type.ToFullyQualified()); + hasConfigurations = true; + + if (operationType is not OperationType.No && (operations & operationType) != operationType) + { + operations |= operationType; + } + } break; } } @@ -198,20 +216,23 @@ private static void WriteConfiguration( } } - generator.WriteEndRegistrationMethod(); - - if (hasObjectTypeExtensions) + if (_objectTypeExtensions is not null) { - generator.WriteRegisterObjectTypeExtensionHelpers(); - hasConfigurations = true; + foreach (var type in _objectTypeExtensions) + { + generator.WriteEnsureObjectTypeExtensionIsRegistered(type); + } } - if (hasInterfaceTypes) + if (_interfaceTypeExtensions is not null) { - generator.WriteRegisterInterfaceTypeExtensionHelpers(); - hasConfigurations = true; + foreach (var type in _interfaceTypeExtensions) + { + generator.WriteEnsureInterfaceTypeExtensionIsRegistered(type); + } } + generator.WriteEndRegistrationMethod(); generator.WriteEndClass(); generator.WriteEndNamespace(); @@ -262,4 +283,11 @@ private static void WriteOperationTypes( context.AddSource(WellKnownFileNames.RootTypesFile, generator.ToSourceText()); } + + public static string GetAssemblyQualifiedName(ITypeSymbol typeSymbol) + { + var assemblyName = typeSymbol.ContainingAssembly?.Name ?? "UnknownAssembly"; + var typeFullName = typeSymbol.ToDisplayString(); + return $"{assemblyName}::{typeFullName}"; + } } diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Generators/TypesSyntaxGenerator.cs b/src/HotChocolate/Core/src/Types.Analyzers/Generators/TypesSyntaxGenerator.cs index d663acd7fd2..a105692e469 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Generators/TypesSyntaxGenerator.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Generators/TypesSyntaxGenerator.cs @@ -67,9 +67,9 @@ private static void WriteTypes( continue; } - var classGenerator = typeInfo is ObjectTypeExtensionInfo - ? (IOutputTypeFileBuilder)new ObjectTypeExtensionFileBuilder(sb, group.Key) - : new InterfaceTypeExtensionFileBuilder(sb, group.Key); + var classGenerator = typeInfo is InterfaceTypeExtensionInfo + ? new InterfaceTypeExtensionFileBuilder(sb, group.Key) + : (IOutputTypeFileBuilder)new ObjectTypeExtensionFileBuilder(sb, group.Key); if (!firstClass) { diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Helpers/DataLoaderAttributeHelper.cs b/src/HotChocolate/Core/src/Types.Analyzers/Helpers/DataLoaderAttributeHelper.cs index 2c2112e216a..1edafd92cbb 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Helpers/DataLoaderAttributeHelper.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Helpers/DataLoaderAttributeHelper.cs @@ -69,7 +69,7 @@ static bool IsDataLoaderGroupAttribute(INamedTypeSymbol? attributeClass) { foreach (var attributeData in parameter.GetAttributes()) { - if (!IsTypeName(attributeData.AttributeClass, "GreenDonut", "DataLoaderStateAttribute")) + if (!attributeData.AttributeClass.IsOrInheritsFrom("GreenDonut.DataLoaderStateAttribute")) { continue; } diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Helpers/SymbolExtensions.cs b/src/HotChocolate/Core/src/Types.Analyzers/Helpers/SymbolExtensions.cs index 1e9fae52939..28efd6e2b47 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Helpers/SymbolExtensions.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Helpers/SymbolExtensions.cs @@ -110,6 +110,18 @@ public static bool IsSetState(this IParameterSymbol parameter) return false; } + public static bool IsQueryContext(this IParameterSymbol parameter) + { + if (parameter.Type is INamedTypeSymbol namedTypeSymbol + && namedTypeSymbol is { IsGenericType: true, TypeArguments.Length: 1 } + && namedTypeSymbol.ToDisplayString().StartsWith(WellKnownTypes.QueryContextGeneric)) + { + return true; + } + + return false; + } + public static bool IsGlobalState( this IParameterSymbol parameter, [NotNullWhen(true)] out string? key) @@ -118,7 +130,7 @@ public static bool IsGlobalState( foreach (var attributeData in parameter.GetAttributes()) { - if (attributeData.AttributeClass?.ToDisplayString() == "HotChocolate.GlobalStateAttribute") + if (IsOrInheritsFrom(attributeData.AttributeClass, "HotChocolate.GlobalStateAttribute")) { if (attributeData.ConstructorArguments.Length == 1 && attributeData.ConstructorArguments[0].Kind == TypedConstantKind.Primitive && @@ -153,7 +165,7 @@ public static bool IsScopedState( foreach (var attributeData in parameter.GetAttributes()) { - if (attributeData.AttributeClass?.ToDisplayString() == "HotChocolate.ScopedStateAttribute") + if (IsOrInheritsFrom(attributeData.AttributeClass, "HotChocolate.ScopedStateAttribute")) { if (attributeData.ConstructorArguments.Length == 1 && attributeData.ConstructorArguments[0].Kind == TypedConstantKind.Primitive && @@ -188,7 +200,7 @@ public static bool IsLocalState( foreach (var attributeData in parameter.GetAttributes()) { - if (attributeData.AttributeClass?.ToDisplayString() == "HotChocolate.LocalStateAttribute") + if (IsOrInheritsFrom(attributeData.AttributeClass, "HotChocolate.LocalStateAttribute")) { if (attributeData.ConstructorArguments.Length == 1 && attributeData.ConstructorArguments[0].Kind == TypedConstantKind.Primitive && @@ -395,7 +407,7 @@ public static bool IsListType(this ITypeSymbol typeSymbol, [NotNullWhen(true)] o return true; } - if(typeDefinition.Equals(WellKnownTypes.EnumerableDefinition, StringComparison.Ordinal)) + if (typeDefinition.Equals(WellKnownTypes.EnumerableDefinition, StringComparison.Ordinal)) { elementType = namedTypeSymbol.TypeArguments[0].ToFullyQualified(); return true; @@ -479,4 +491,41 @@ private static bool IsPostProcessorAttribute(INamedTypeSymbol? attributeClass) return false; } + + public static bool IsOrInheritsFrom(this ITypeSymbol? attributeClass, params string[] fullTypeName) + { + var current = attributeClass; + + while (current != null) + { + foreach(var typeName in fullTypeName) + { + if (current.ToDisplayString() == typeName) + { + return true; + } + } + + current = current.BaseType; + } + + return false; + } + + public static bool IsOrInheritsFrom(this ITypeSymbol? attributeClass, string fullTypeName) + { + var current = attributeClass; + + while (current != null) + { + if (current.ToDisplayString() == fullTypeName) + { + return true; + } + + current = current.BaseType; + } + + return false; + } } diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Inspectors/DataLoaderInspector.cs b/src/HotChocolate/Core/src/Types.Analyzers/Inspectors/DataLoaderInspector.cs index 73389a6af85..2e3890c65d2 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Inspectors/DataLoaderInspector.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Inspectors/DataLoaderInspector.cs @@ -1,6 +1,5 @@ using System.Diagnostics.CodeAnalysis; using HotChocolate.Types.Analyzers.Filters; -using HotChocolate.Types.Analyzers.Helpers; using HotChocolate.Types.Analyzers.Models; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Inspectors/ObjectTypeExtensionInfoInspector.cs b/src/HotChocolate/Core/src/Types.Analyzers/Inspectors/ObjectTypeExtensionInfoInspector.cs index 7adf2b3b32e..24f55caa6c1 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Inspectors/ObjectTypeExtensionInfoInspector.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Inspectors/ObjectTypeExtensionInfoInspector.cs @@ -19,7 +19,9 @@ public bool TryHandle(GeneratorSyntaxContext context, [NotNullWhen(true)] out Sy { var diagnostics = ImmutableArray.Empty; - if (!IsObjectTypeExtension(context, out var possibleType, out var classSymbol, out var runtimeType)) + OperationType? operationType = null; + if (!IsObjectTypeExtension(context, out var possibleType, out var classSymbol, out var runtimeType) + && !IsOperationType(context, out possibleType, out classSymbol, out operationType)) { syntaxInfo = null; return false; @@ -85,10 +87,27 @@ public bool TryHandle(GeneratorSyntaxContext context, [NotNullWhen(true)] out Sy Array.Resize(ref resolvers, i); } - syntaxInfo = new ObjectTypeExtensionInfo( + if (runtimeType is not null) + { + syntaxInfo = new ObjectTypeExtensionInfo( + classSymbol, + runtimeType, + nodeResolver, + possibleType, + i == 0 + ? ImmutableArray.Empty + : resolvers.ToImmutableArray()); + + if (diagnostics.Length > 0) + { + syntaxInfo.AddDiagnosticRange(diagnostics); + } + return true; + } + + syntaxInfo = new RootTypeExtensionInfo( classSymbol, - runtimeType, - nodeResolver, + operationType!.Value, possibleType, i == 0 ? ImmutableArray.Empty @@ -98,7 +117,6 @@ public bool TryHandle(GeneratorSyntaxContext context, [NotNullWhen(true)] out Sy { syntaxInfo.AddDiagnosticRange(diagnostics); } - return true; } @@ -146,6 +164,64 @@ attributeContainingTypeSymbol.TypeArguments[0] is INamedTypeSymbol rt && return false; } + private static bool IsOperationType( + GeneratorSyntaxContext context, + [NotNullWhen(true)] out ClassDeclarationSyntax? resolverTypeSyntax, + [NotNullWhen(true)] out INamedTypeSymbol? resolverTypeSymbol, + [NotNullWhen(true)] out OperationType? operationType) + { + if (context.Node is ClassDeclarationSyntax { AttributeLists.Count: > 0, } possibleType) + { + foreach (var attributeListSyntax in possibleType.AttributeLists) + { + foreach (var attributeSyntax in attributeListSyntax.Attributes) + { + var symbol = ModelExtensions.GetSymbolInfo(context.SemanticModel, attributeSyntax).Symbol; + + if (symbol is not IMethodSymbol attributeSymbol) + { + continue; + } + + var attributeContainingTypeSymbol = attributeSymbol.ContainingType; + var fullName = attributeContainingTypeSymbol.ToDisplayString(); + + if (fullName.StartsWith(QueryTypeAttribute, Ordinal) && + ModelExtensions.GetDeclaredSymbol(context.SemanticModel, possibleType) is INamedTypeSymbol rtsq) + { + resolverTypeSyntax = possibleType; + resolverTypeSymbol = rtsq; + operationType = OperationType.Query; + return true; + } + + if (fullName.StartsWith(MutationTypeAttribute, Ordinal) && + ModelExtensions.GetDeclaredSymbol(context.SemanticModel, possibleType) is INamedTypeSymbol rtsm) + { + resolverTypeSyntax = possibleType; + resolverTypeSymbol = rtsm; + operationType = OperationType.Mutation; + return true; + } + + if (fullName.StartsWith(SubscriptionTypeAttribute, Ordinal) && + ModelExtensions.GetDeclaredSymbol(context.SemanticModel, possibleType) is INamedTypeSymbol rtss) + { + resolverTypeSyntax = possibleType; + resolverTypeSymbol = rtss; + operationType = OperationType.Subscription; + return true; + } + } + } + } + + resolverTypeSyntax = null; + resolverTypeSymbol = null; + operationType = null; + return false; + } + private static Resolver CreateResolver( GeneratorSyntaxContext context, INamedTypeSymbol resolverType, @@ -226,32 +302,34 @@ private static Resolver CreateNodeResolver( file static class Extensions { public static bool IsNodeResolver(this IMethodSymbol methodSymbol) - => methodSymbol - .GetAttributes() - .Any(t => t.AttributeClass?.ToDisplayString().Equals(NodeResolverAttribute, Ordinal) ?? false); - - public static bool Skip(this IMethodSymbol methodSymbol) - => methodSymbol - .GetAttributes() - .Any(t => + { + foreach (var attribute in methodSymbol.GetAttributes()) + { + if (attribute.AttributeClass.IsOrInheritsFrom(NodeResolverAttribute)) { - var name = t.AttributeClass?.ToDisplayString(); + return true; + } + } - if (name is null) - { - return false; - } + return false; + } - if (name.Equals(DataLoaderAttribute, Ordinal) || - name.Equals(QueryAttribute, Ordinal) || - name.Equals(MutationAttribute, Ordinal) || - name.Equals(SubscriptionAttribute, Ordinal)) - { - return true; - } + public static bool Skip(this IMethodSymbol methodSymbol) + { + foreach (var attribute in methodSymbol.GetAttributes()) + { + if (attribute.AttributeClass.IsOrInheritsFrom( + DataLoaderAttribute, + QueryAttribute, + MutationAttribute, + SubscriptionAttribute)) + { + return true; + } + } - return false; - }); + return false; + } public static ImmutableArray GetMemberBindings(this ISymbol member) { diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Inspectors/TypeAttributeInspector.cs b/src/HotChocolate/Core/src/Types.Analyzers/Inspectors/TypeAttributeInspector.cs index b86f7b35b48..1ee6233201b 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Inspectors/TypeAttributeInspector.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Inspectors/TypeAttributeInspector.cs @@ -51,6 +51,12 @@ public bool TryHandle( { if (fullName.Equals(QueryTypeAttribute)) { + if (type.IsStatic && possibleType.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword))) + { + syntaxInfo = null; + return false; + } + syntaxInfo = new TypeExtensionInfo( type.ToDisplayString(), possibleType.Modifiers.Any(t => t.IsKind(SyntaxKind.StaticKeyword)), @@ -60,6 +66,12 @@ public bool TryHandle( if (fullName.Equals(MutationTypeAttribute)) { + if (type.IsStatic && possibleType.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword))) + { + syntaxInfo = null; + return false; + } + syntaxInfo = new TypeExtensionInfo( type.ToDisplayString(), possibleType.Modifiers.Any(t => t.IsKind(SyntaxKind.StaticKeyword)), @@ -69,6 +81,12 @@ public bool TryHandle( if (fullName.Equals(SubscriptionTypeAttribute)) { + if (type.IsStatic && possibleType.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword))) + { + syntaxInfo = null; + return false; + } + syntaxInfo = new TypeExtensionInfo( type.ToDisplayString(), possibleType.Modifiers.Any(t => t.IsKind(SyntaxKind.StaticKeyword)), diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderInfo.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderInfo.cs index 17f1287d808..523cd529d81 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderInfo.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderInfo.cs @@ -179,7 +179,7 @@ private static ImmutableArray CreateParameters(IMethodS $"p{i}", parameter, DataLoaderParameterKind.SelectorBuilder, - WellKnownTypes.SelectorBuilder)); + Selector)); continue; } @@ -191,7 +191,7 @@ private static ImmutableArray CreateParameters(IMethodS $"p{i}", parameter, DataLoaderParameterKind.PredicateBuilder, - WellKnownTypes.PredicateBuilder)); + Predicate)); continue; } @@ -202,7 +202,28 @@ private static ImmutableArray CreateParameters(IMethodS $"p{i}", parameter, DataLoaderParameterKind.PagingArguments, - WellKnownTypes.PagingArguments)); + PagingArgs)); + continue; + } + + if (IsSortDefinition(parameter)) + { + builder.Add( + new DataLoaderParameterInfo( + $"p{i}", + parameter, + DataLoaderParameterKind.SortDefinition, + Sorting)); + continue; + } + + if (IsQueryContext(parameter)) + { + builder.Add( + new DataLoaderParameterInfo( + $"p{i}", + parameter, + DataLoaderParameterKind.QueryContext)); continue; } @@ -255,6 +276,18 @@ private static bool IsPagingArguments(IParameterSymbol parameter) return string.Equals(typeName, WellKnownTypes.PagingArguments, StringComparison.Ordinal); } + private static bool IsSortDefinition(IParameterSymbol parameter) + { + var typeName = parameter.Type.ToDisplayString(); + return typeName.StartsWith(WellKnownTypes.SortDefinitionGeneric, StringComparison.Ordinal); + } + + private static bool IsQueryContext(IParameterSymbol parameter) + { + var typeName = parameter.Type.ToDisplayString(); + return typeName.StartsWith(WellKnownTypes.QueryContextGeneric, StringComparison.Ordinal); + } + public static bool IsKeyValuePair(ITypeSymbol returnTypeSymbol, ITypeSymbol keyType, ITypeSymbol valueType) { if (returnTypeSymbol is INamedTypeSymbol namedTypeSymbol @@ -305,4 +338,9 @@ private static string GetDataLoaderName(string name, AttributeData attribute) ? name.Substring(0, name.Length - 10) : name; } + + public const string Selector = "GreenDonut.Data.Selector"; + public const string Predicate = "GreenDonut.Data.Predicate"; + public const string Sorting = "GreenDonut.Data.Sorting"; + public const string PagingArgs = "HotChocolate.Pagination.PagingArgs"; } diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderParameterKind.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderParameterKind.cs index 6481bdbb9e3..7354f5ded97 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderParameterKind.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/DataLoaderParameterKind.cs @@ -8,5 +8,7 @@ public enum DataLoaderParameterKind CancellationToken = 3, SelectorBuilder = 4, PagingArguments = 5, - PredicateBuilder = 6 + PredicateBuilder = 6, + SortDefinition = 7, + QueryContext = 8 } diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/IOutputTypeInfo.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/IOutputTypeInfo.cs index ac3c4b69879..fd83b9696a1 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/IOutputTypeInfo.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/IOutputTypeInfo.cs @@ -1,4 +1,5 @@ using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -8,9 +9,11 @@ public interface IOutputTypeInfo { string Name { get; } + bool IsRootType { get; } + INamedTypeSymbol Type { get; } - INamedTypeSymbol RuntimeType { get; } + INamedTypeSymbol? RuntimeType { get; } ClassDeclarationSyntax ClassDeclarationSyntax { get; } diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/InterfaceTypeExtensionInfo.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/InterfaceTypeExtensionInfo.cs index fd6cf1481d7..f91b242fedc 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/InterfaceTypeExtensionInfo.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/InterfaceTypeExtensionInfo.cs @@ -15,6 +15,8 @@ public sealed class InterfaceTypeExtensionInfo( { public string Name { get; } = type.ToFullyQualified(); + public bool IsRootType => false; + public INamedTypeSymbol Type { get; } = type; public INamedTypeSymbol RuntimeType { get; } = runtimeType; diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/ObjectTypeExtensionInfo.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/ObjectTypeExtensionInfo.cs index d9d6e532541..c1b56b363c8 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/ObjectTypeExtensionInfo.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/ObjectTypeExtensionInfo.cs @@ -16,6 +16,8 @@ public sealed class ObjectTypeExtensionInfo( { public string Name { get; } = type.ToFullyQualified(); + public bool IsRootType => false; + public INamedTypeSymbol Type { get; } = type; public INamedTypeSymbol RuntimeType { get; } = runtimeType; diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/ResolverParameter.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/ResolverParameter.cs index 007016cef5f..26b27e36fe2 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/ResolverParameter.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/ResolverParameter.cs @@ -1,3 +1,4 @@ +using System.Collections.Immutable; using HotChocolate.Types.Analyzers.Helpers; using Microsoft.CodeAnalysis; @@ -20,6 +21,9 @@ public ResolverParameter(IParameterSymbol parameter, string? key, ResolverParame public ITypeSymbol Type => Parameter.Type; + public ImmutableArray TypeParameters + => GetGenericTypeArgument(Type); + public IParameterSymbol Parameter { get; } public ResolverParameterKind Kind { get; } @@ -136,6 +140,22 @@ private static ResolverParameterKind GetParameterKind( return ResolverParameterKind.Argument; } + if (parameter.IsQueryContext()) + { + return ResolverParameterKind.QueryContext; + } + return ResolverParameterKind.Unknown; } + + private static ImmutableArray GetGenericTypeArgument(ITypeSymbol typeSymbol) + { + if (typeSymbol is INamedTypeSymbol namedTypeSymbol && namedTypeSymbol.IsGenericType) + { + return namedTypeSymbol.TypeArguments; + } + + // Return null if it's not a generic type or index is out of bounds + return ImmutableArray.Empty; + } } diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/ResolverParameterKind.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/ResolverParameterKind.cs index de93f0858e5..11dcdf9ffd8 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/Models/ResolverParameterKind.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/ResolverParameterKind.cs @@ -20,5 +20,6 @@ public enum ResolverParameterKind GetLocalState, SetLocalState, Service, - Argument + Argument, + QueryContext } diff --git a/src/HotChocolate/Core/src/Types.Analyzers/Models/RootTypeExtensionInfo.cs b/src/HotChocolate/Core/src/Types.Analyzers/Models/RootTypeExtensionInfo.cs new file mode 100644 index 00000000000..1f9afd1ac42 --- /dev/null +++ b/src/HotChocolate/Core/src/Types.Analyzers/Models/RootTypeExtensionInfo.cs @@ -0,0 +1,45 @@ +using System.Collections.Immutable; +using HotChocolate.Types.Analyzers.Helpers; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace HotChocolate.Types.Analyzers.Models; + +public sealed class RootTypeExtensionInfo( + INamedTypeSymbol type, + OperationType operationType, + ClassDeclarationSyntax classDeclarationSyntax, + ImmutableArray resolvers) + : SyntaxInfo + , IOutputTypeInfo +{ + public string Name { get; } = type.ToFullyQualified(); + + public bool IsRootType => true; + + public OperationType OperationType => operationType; + + public INamedTypeSymbol Type { get; } = type; + + public INamedTypeSymbol? RuntimeType => null; + + public ClassDeclarationSyntax ClassDeclarationSyntax { get; } = classDeclarationSyntax; + + public ImmutableArray Resolvers { get; } = resolvers; + + public override string OrderByKey => Name; + + public override bool Equals(object? obj) + => obj is ObjectTypeExtensionInfo other && Equals(other); + + public override bool Equals(SyntaxInfo obj) + => obj is ObjectTypeExtensionInfo other && Equals(other); + + private bool Equals(ObjectTypeExtensionInfo other) + => string.Equals(Name, other.Name, StringComparison.Ordinal) && + ClassDeclarationSyntax.SyntaxTree.IsEquivalentTo( + other.ClassDeclarationSyntax.SyntaxTree); + + public override int GetHashCode() + => HashCode.Combine(Name, ClassDeclarationSyntax); +} diff --git a/src/HotChocolate/Core/src/Types.Analyzers/WellKnownTypes.cs b/src/HotChocolate/Core/src/Types.Analyzers/WellKnownTypes.cs index 550cab59229..69c0bd6a9e4 100644 --- a/src/HotChocolate/Core/src/Types.Analyzers/WellKnownTypes.cs +++ b/src/HotChocolate/Core/src/Types.Analyzers/WellKnownTypes.cs @@ -66,9 +66,13 @@ public static class WellKnownTypes public const string PromiseCacheObserver = "GreenDonut.PromiseCacheObserver"; public const string KeyValuePair = "System.Collections.Generic.KeyValuePair"; public const string EnumerableExtensions = "System.Linq.Enumerable"; - public const string SelectorBuilder = "GreenDonut.Selectors.ISelectorBuilder"; - public const string PredicateBuilder = "GreenDonut.Predicates.IPredicateBuilder"; + public const string SelectorBuilder = "GreenDonut.Data.ISelectorBuilder"; + public const string PredicateBuilder = "GreenDonut.Data.IPredicateBuilder"; public const string PagingArguments = "HotChocolate.Pagination.PagingArguments"; + public const string QueryContext = "GreenDonut.Data.QueryContext"; + public const string QueryContextGeneric = QueryContext + "<"; + public const string SortDefinition = "GreenDonut.Data.SortDefinition"; + public const string SortDefinitionGeneric = SortDefinition + "<"; public static HashSet TypeClass { get; } = [ diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/DescriptorContext.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/DescriptorContext.cs index 70bb2627036..a6380d93c7e 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/DescriptorContext.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/DescriptorContext.cs @@ -130,6 +130,9 @@ public IParameterBindingResolver ParameterBindingResolver /// public IDictionary ContextData { get; } + /// + public TypeConfigurationContainer TypeConfiguration { get; } = new(); + /// public ReadOnlySpan GetTypeDiscoveryHandlers() => _typeDiscoveryHandlers ??= CreateTypeDiscoveryHandlers(this); diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/IDescriptorContext.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/IDescriptorContext.cs index 34d83776700..79b5b89e253 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/IDescriptorContext.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Conventions/IDescriptorContext.cs @@ -76,6 +76,11 @@ public interface IDescriptorContext : IHasContextData, IDisposable /// IParameterBindingResolver ParameterBindingResolver { get; } + /// + /// Gets the type configuration container. + /// + TypeConfigurationContainer TypeConfiguration { get; } + /// /// Gets the registered type discovery handlers. /// diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeConfiguration.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeConfiguration.cs new file mode 100644 index 00000000000..35205555a23 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeConfiguration.cs @@ -0,0 +1,111 @@ +using System.Collections.Concurrent; +using System.ComponentModel; +using HotChocolate.Types; + +namespace HotChocolate.Types.Descriptors; + +/// +/// The type configuration container is a helper that allows us to store +/// type configurations that are applied to a type during the type initialization. +/// +public sealed class TypeConfigurationContainer +{ + private readonly object _sync = new(); + private readonly HashSet _created = []; + private readonly Dictionary> _configurations = []; + private readonly Dictionary> _namedConfigurations = []; + + public void TryAdd( + string key, + Func>> configureFactory) + => TryAddInternal(key, typeof(TRuntimeType), configureFactory); + + public void TryAdd( + string key, + string typeName, + Func> configureFactory) + => TryAddInternal(key, typeName, configureFactory); + + public void TryAdd( + string key, + Func>> configureFactory) + => TryAddInternal(key, typeof(TRuntimeType), configureFactory); + + private void TryAddInternal(string key, Type runtimeType, Func configureFactory) + { + lock (_sync) + { + if (!_created.Add(key)) + { + return; + } + + if (!_configurations.TryGetValue(runtimeType, out var list)) + { + list = new List(); + _configurations[runtimeType] = list; + } + + list.Add(configureFactory()); + } + } + + private void TryAddInternal(string key, string typeName, Func configureFactory) + { + lock (_sync) + { + if (!_created.Add(key)) + { + return; + } + + if (!_namedConfigurations.TryGetValue(typeName, out var list)) + { + list = new List(); + _namedConfigurations[typeName] = list; + } + + list.Add(configureFactory()); + } + } + + internal void Apply( + Type runtimeType, + TDescriptor descriptor) + where TDescriptor : IDescriptor + { + lock (_sync) + { + if (_configurations.TryGetValue(runtimeType, out var list)) + { + foreach (var item in list) + { + if (item is Action configure) + { + configure(descriptor); + } + } + } + } + } + + internal void Apply( + string typeName, + TDescriptor descriptor) + where TDescriptor : IDescriptor + { + lock (_sync) + { + if (_namedConfigurations.TryGetValue(typeName, out var list)) + { + foreach (var item in list) + { + if (item is Action configure) + { + configure(descriptor); + } + } + } + } + } +} diff --git a/src/HotChocolate/Core/src/Types/Types/InterfaceType~1.cs b/src/HotChocolate/Core/src/Types/Types/InterfaceType~1.cs index ca0cdcdefdb..0d8bc1d4869 100644 --- a/src/HotChocolate/Core/src/Types/Types/InterfaceType~1.cs +++ b/src/HotChocolate/Core/src/Types/Types/InterfaceType~1.cs @@ -24,6 +24,8 @@ protected override InterfaceTypeDefinition CreateDefinition(ITypeDiscoveryContex _configure!(descriptor); _configure = null; + context.DescriptorContext.TypeConfiguration.Apply(typeof(T), descriptor); + return descriptor.CreateDefinition(); } diff --git a/src/HotChocolate/Core/src/Types/Types/ObjectType.Initialization.cs b/src/HotChocolate/Core/src/Types/Types/ObjectType.Initialization.cs index a8140733497..fe445f94dfa 100644 --- a/src/HotChocolate/Core/src/Types/Types/ObjectType.Initialization.cs +++ b/src/HotChocolate/Core/src/Types/Types/ObjectType.Initialization.cs @@ -30,6 +30,12 @@ protected override ObjectTypeDefinition CreateDefinition( context.DescriptorContext, GetType()); _configure!.Invoke(descriptor); + + if (!descriptor.Definition.NeedsNameCompletion) + { + context.DescriptorContext.TypeConfiguration.Apply(descriptor.Definition.Name, descriptor); + } + return descriptor.CreateDefinition(); } diff --git a/src/HotChocolate/Core/src/Types/Types/ObjectType~1.cs b/src/HotChocolate/Core/src/Types/Types/ObjectType~1.cs index 674801c1a28..e36607a4018 100644 --- a/src/HotChocolate/Core/src/Types/Types/ObjectType~1.cs +++ b/src/HotChocolate/Core/src/Types/Types/ObjectType~1.cs @@ -49,6 +49,8 @@ protected override ObjectTypeDefinition CreateDefinition( _configure!(descriptor); _configure = null; + context.DescriptorContext.TypeConfiguration.Apply>(typeof(T), descriptor); + return descriptor.CreateDefinition(); } diff --git a/src/HotChocolate/Core/src/Types/Types/TypeSystemObjectBase.cs b/src/HotChocolate/Core/src/Types/Types/TypeSystemObjectBase.cs index e75d1392981..5298be8878c 100644 --- a/src/HotChocolate/Core/src/Types/Types/TypeSystemObjectBase.cs +++ b/src/HotChocolate/Core/src/Types/Types/TypeSystemObjectBase.cs @@ -68,19 +68,34 @@ protected set public abstract IReadOnlyDictionary ContextData { get; } protected internal bool IsInitialized - => _status is TypeStatus.Initialized or TypeStatus.Named or TypeStatus.Completed; + => _status is TypeStatus.Initialized + or TypeStatus.Named + or TypeStatus.Completed + or TypeStatus.MetadataCompleted + or TypeStatus.Executable + or TypeStatus.Finalized; protected internal bool IsNamed - => _status is TypeStatus.Named or TypeStatus.Completed; + => _status is TypeStatus.Named + or TypeStatus.Completed + or TypeStatus.MetadataCompleted + or TypeStatus.Executable + or TypeStatus.Finalized; protected internal bool IsCompleted - => _status is TypeStatus.Completed; + => _status is TypeStatus.Completed + or TypeStatus.MetadataCompleted + or TypeStatus.Executable + or TypeStatus.Finalized; protected internal bool IsMetadataCompleted - => _status is TypeStatus.MetadataCompleted; + => _status is TypeStatus.MetadataCompleted + or TypeStatus.Executable + or TypeStatus.Finalized; protected internal bool IsExecutable - => _status is TypeStatus.Executable; + => _status is TypeStatus.Executable + or TypeStatus.Finalized; protected internal bool IsSealed => _status is TypeStatus.Finalized; diff --git a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs index 91563e37f0e..30491fe6a9a 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Projections/ProjectableDataLoaderTests.cs @@ -2,7 +2,7 @@ using System.Linq.Expressions; using CookieCrumble; using GreenDonut; -using GreenDonut.Selectors; +using GreenDonut.Data; using HotChocolate.Execution.Processing; using HotChocolate.Execution.TestContext; using HotChocolate.Types; diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/DataLoaderTests.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/DataLoaderTests.cs index 81ec13224eb..ef0cc3a357d 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/DataLoaderTests.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/DataLoaderTests.cs @@ -469,7 +469,7 @@ internal static class TestClass [DataLoader] public static Task> GetEntityByIdAsync( IReadOnlyList entityIds, - GreenDonut.Selectors.ISelectorBuilder selector, + GreenDonut.Data.ISelectorBuilder selector, CancellationToken cancellationToken) => default!; } @@ -494,7 +494,7 @@ internal static class TestClass [DataLoader] public static Task> GetEntityByIdAsync( IReadOnlyList entityIds, - GreenDonut.Predicates.IPredicateBuilder predicate, + GreenDonut.Data.IPredicateBuilder predicate, CancellationToken cancellationToken) => default!; } @@ -521,7 +521,59 @@ internal static class TestClass [DataLoader] public static Task> GetEntityByIdAsync( IReadOnlyList entityIds, - GreenDonut.Predicates.IPredicateBuilder predicate, + GreenDonut.Data.IPredicateBuilder predicate, + CancellationToken cancellationToken) + => default!; + } + """).MatchMarkdownAsync(); + } + + [Fact] + public async Task Generate_With_QueryContext() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using HotChocolate; + using GreenDonut; + using GreenDonut.Data; + + namespace TestNamespace; + + internal static class TestClass + { + [DataLoader] + public static Task> GetEntityByIdAsync( + IReadOnlyList entityIds, + QueryContext queryContext, + CancellationToken cancellationToken) + => default!; + } + """).MatchMarkdownAsync(); + } + + [Fact] + public async Task Generate_With_SortDefinition() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + using HotChocolate; + using GreenDonut; + using GreenDonut.Data; + + namespace TestNamespace; + + internal static class TestClass + { + [DataLoader] + public static Task> GetEntityByIdAsync( + IReadOnlyList entityIds, + SortDefinition queryContext, CancellationToken cancellationToken) => default!; } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/HotChocolate.Types.Analyzers.Tests.csproj b/src/HotChocolate/Core/test/Types.Analyzers.Tests/HotChocolate.Types.Analyzers.Tests.csproj index fbc902ecaeb..2369fa7b31b 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/HotChocolate.Types.Analyzers.Tests.csproj +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/HotChocolate.Types.Analyzers.Tests.csproj @@ -20,6 +20,7 @@ + diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/Models/Book.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/Models/Book.cs index 9cf4cb81bdc..7ecf3bbe41a 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/Models/Book.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/Models/Book.cs @@ -1,3 +1,8 @@ namespace HotChocolate.Types; -public sealed record Book(int Id, string Title, int AuthorId, Genre Genre) : IEntity; +public sealed record Book( + int Id, + string Title, + int AuthorId, + Genre Genre) + : IEntity; diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/Models/IEntity.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/Models/IEntity.cs index aeca6d90b67..c2575d1941a 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/Models/IEntity.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/Models/IEntity.cs @@ -15,5 +15,5 @@ static partial void Configure(IInterfaceTypeDescriptor descriptor) descriptor.Name("Entity"); } - public static string IdString([HotChocolate.Parent] IEntity entity) => entity.Id.ToString(); + public static string IdString([Parent] IEntity entity) => entity.Id.ToString(); } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/OperationTests.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/OperationTests.cs new file mode 100644 index 00000000000..848fa67d7ac --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/OperationTests.cs @@ -0,0 +1,67 @@ +namespace HotChocolate.Types; + +public class OperationTests +{ + [Fact] + public async Task Partial_Static_QueryType() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using HotChocolate; + using HotChocolate.Types; + + namespace TestNamespace; + + [QueryType] + public static partial class Query + { + public static int GetTest(string arg) + { + return arg.Length; + } + } + """).MatchMarkdownAsync(); + } + + [Fact] + public async Task Static_QueryType() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using HotChocolate; + using HotChocolate.Types; + + namespace TestNamespace; + + [QueryType] + public static class Query + { + public static int GetTest(string arg) + { + return arg.Length; + } + } + """).MatchMarkdownAsync(); + } + + [Fact] + public async Task Instance_QueryType() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using HotChocolate; + using HotChocolate.Types; + + namespace TestNamespace; + + [QueryType] + public class Query + { + public static int GetTest(string arg) + { + return arg.Length; + } + } + """).MatchMarkdownAsync(); + } +} diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/ResolverTests.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/ResolverTests.cs index 09fe7c8d33b..89a2f72bb99 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/ResolverTests.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/ResolverTests.cs @@ -142,4 +142,103 @@ public static int GetTest([GlobalState] SetState test) internal class Test { } """).MatchMarkdownAsync(); } + + [Fact] + public async Task Inject_QueryContext() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using HotChocolate; + using HotChocolate.Types; + using GreenDonut.Data; + using System.Linq; + + namespace TestNamespace; + + [ObjectType] + internal static partial class TestType + { + public static IQueryable GetTest(QueryContext test) + { + return default; + } + } + + internal class Test; + + internal class Entity; + """).MatchMarkdownAsync(); + } + + [Fact] + public async Task Ensure_Entity_Becomes_Node() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using HotChocolate; + using HotChocolate.Types; + using HotChocolate.Types.Relay; + using System.Threading.Tasks; + + namespace TestNamespace; + + [QueryType] + internal static partial class Query + { + public static Task GetTestById([ID] int id) + => Task.FromResult(null); + } + + [ObjectType] + internal static partial class TestType + { + [NodeResolver] + public static Task GetTest(int id) + => Task.FromResult(null); + } + + internal class Test + { + public int Id { get; set; } + + public string Name { get; set; } + } + """).MatchMarkdownAsync(); + } + + [Fact] + public async Task Ensure_Entity_Becomes_Node_With_Query_Node_Resolver() + { + await TestHelper.GetGeneratedSourceSnapshot( + """ + using HotChocolate; + using HotChocolate.Types; + using HotChocolate.Types.Relay; + using System.Threading.Tasks; + + namespace TestNamespace; + + [QueryType] + internal static partial class Query + { + [NodeResolver] + public static Task GetTestById(int id) + => Task.FromResult(null); + } + + [ObjectType] + internal static partial class TestType + { + public static Task GetTest(int id) + => Task.FromResult(null); + } + + internal class Test + { + public int Id { get; set; } + + public string Name { get; set; } + } + """).MatchMarkdownAsync(); + } } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs index ca406c98ad6..ba812b22db9 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestHelper.cs @@ -7,6 +7,9 @@ using Basic.Reference.Assemblies; using CookieCrumble; using GreenDonut; +using GreenDonut.Data; +using HotChocolate.Data; +using HotChocolate.Data.Filters; using HotChocolate.Pagination; using HotChocolate.Types.Analyzers; using Microsoft.CodeAnalysis; @@ -44,7 +47,11 @@ public static Snapshot GetGeneratedSourceSnapshot(string[] sourceTexts, string? MetadataReference.CreateFromFile(typeof(PagingArguments).Assembly.Location), // GreenDonut - MetadataReference.CreateFromFile(typeof(DataLoaderAttribute).Assembly.Location) + MetadataReference.CreateFromFile(typeof(DataLoaderAttribute).Assembly.Location), + + // HotChocolate.Data + MetadataReference.CreateFromFile(typeof(QueryContext<>).Assembly.Location), + MetadataReference.CreateFromFile(typeof(IFilterContext).Assembly.Location) ]; // Create a Roslyn compilation for the syntax tree. diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestMe.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestMe.cs deleted file mode 100644 index 763fdb81dea..00000000000 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/TestMe.cs +++ /dev/null @@ -1,68 +0,0 @@ -// - -#nullable enable -#pragma warning disable - -using System; -using System.Runtime.CompilerServices; -using HotChocolate; -using HotChocolate.Types; -using HotChocolate.Execution.Configuration; -using HotChocolate.Internal; - -namespace HotChocolate.Types -{ - internal static class EntityInterfaceResolvers2 - { - private static readonly object _sync = new object(); - private static bool _bindingsInitialized; - private readonly static global::HotChocolate.Internal.IParameterBinding[] _args_EntityInterface_IdString = new global::HotChocolate.Internal.IParameterBinding[1]; - - public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver) - { - if (!_bindingsInitialized) - { - lock (_sync) - { - if (!_bindingsInitialized) - { - const global::System.Reflection.BindingFlags bindingFlags = - global::System.Reflection.BindingFlags.Public - | global::System.Reflection.BindingFlags.NonPublic - | global::System.Reflection.BindingFlags.Static; - - var type = typeof(global::HotChocolate.Types.EntityInterface); - global::System.Reflection.MethodInfo resolver = default!; - global::System.Reflection.ParameterInfo[] parameters = default!; - - resolver = type.GetMethod( - "IdString", - bindingFlags, - new global::System.Type[] { typeof(global::HotChocolate.Types.IEntity) })!; - parameters = resolver.GetParameters(); - _args_EntityInterface_IdString[0] = bindingResolver.GetBinding(parameters[0]); - - _bindingsInitialized = true; - } - } - } - } - - public static HotChocolate.Resolvers.FieldResolverDelegates EntityInterface_IdString() - { - if(!_bindingsInitialized) - { - throw new global::System.InvalidOperationException("The bindings must be initialized before the resolvers can be created."); - } - return new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: EntityInterface_IdString_Resolver); - } - - private static global::System.Object? EntityInterface_IdString_Resolver(global::HotChocolate.Resolvers.IResolverContext context) - { - var args0 = context.Parent(); - var result = global::HotChocolate.Types.EntityInterface.IdString(args0); - return result; - } - } -} - diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/Types/BookNode.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/Types/BookNode.cs index bda3fd343d7..48915421cdf 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/Types/BookNode.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/Types/BookNode.cs @@ -28,10 +28,14 @@ public static async Task> GetChapterAsync( CancellationToken cancellationToken) => await repository.GetChaptersAsync(book.Id, cancellationToken); - public static string IdAndTitle([HotChocolate.Parent] Book book) + public static string IdAndTitle( + [HotChocolate.Parent] Book book) => $"{book.Id}: {book.Title}"; - public static string GetBookUri([HotChocolate.Parent] Book book, HttpContext context, [LocalState] string? foo = null) + public static string GetBookUri( + [HotChocolate.Parent] Book book, + HttpContext context, + [LocalState] string? foo = null) => context.Request.Path + $"/{book.Id}"; [NodeResolver] diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/Types/BookOperations.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/Types/BookOperations.cs index e33623dd26e..5c5845505ec 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/Types/BookOperations.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/Types/BookOperations.cs @@ -1,3 +1,6 @@ +using HotChocolate.Execution.Configuration; +using Microsoft.Extensions.DependencyInjection; + namespace HotChocolate.Types; public static class BookOperations diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/Types/DataLoaders.cs b/src/HotChocolate/Core/test/Types.Analyzers.Tests/Types/DataLoaders.cs index ecb4875e251..df3dd3b7f1b 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/Types/DataLoaders.cs +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/Types/DataLoaders.cs @@ -1,5 +1,5 @@ using GreenDonut; -using GreenDonut.Selectors; +using GreenDonut.Data; using HotChocolate.Pagination; namespace HotChocolate.Types; diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PagingArguments_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PagingArguments_MatchesSnapshot.md index 874273d38be..9356649b2f2 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PagingArguments_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PagingArguments_MatchesSnapshot.md @@ -42,7 +42,7 @@ namespace TestNamespace global::GreenDonut.DataLoaderFetchContext context, global::System.Threading.CancellationToken ct) { - var p1 = context.GetRequiredState("HotChocolate.Pagination.PagingArguments"); + var p1 = context.GetRequiredState("HotChocolate.Pagination.PagingArgs"); var temp = await global::TestNamespace.TestClass.GetEntityByIdAsync(keys, p1, ct).ConfigureAwait(false); CopyResults(keys, results.Span, temp); } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PredicateBuilder_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PredicateBuilder_MatchesSnapshot.md index 540e99be34d..320e97ffded 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PredicateBuilder_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_PredicateBuilder_MatchesSnapshot.md @@ -42,8 +42,8 @@ namespace TestNamespace global::GreenDonut.DataLoaderFetchContext context, global::System.Threading.CancellationToken ct) { - var p1 = context.GetState("GreenDonut.Predicates.IPredicateBuilder") - ?? new global::GreenDonut.Predicates.DefaultPredicateBuilder(); + var p1 = context.GetState("GreenDonut.Data.Predicate") + ?? global::GreenDonut.Data.DefaultPredicateBuilder.Empty; var temp = await global::TestNamespace.TestClass.GetEntityByIdAsync(keys, p1, ct).ConfigureAwait(false); CopyResults(keys, results.Span, temp); } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_SelectorBuilder_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_SelectorBuilder_MatchesSnapshot.md index da24ba18f11..4bda1fd67d0 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_SelectorBuilder_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.GenerateSource_BatchDataLoader_With_SelectorBuilder_MatchesSnapshot.md @@ -42,8 +42,8 @@ namespace TestNamespace global::GreenDonut.DataLoaderFetchContext context, global::System.Threading.CancellationToken ct) { - var p1 = context.GetState("GreenDonut.Selectors.ISelectorBuilder") - ?? new global::GreenDonut.Selectors.DefaultSelectorBuilder(); + var p1 = context.GetState("GreenDonut.Data.Selector") + ?? global::GreenDonut.Data.DefaultSelectorBuilder.Empty; var temp = await global::TestNamespace.TestClass.GetEntityByIdAsync(keys, p1, ct).ConfigureAwait(false); CopyResults(keys, results.Span, temp); } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.Generate_With_QueryContext.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.Generate_With_QueryContext.md new file mode 100644 index 00000000000..c1ce0178dcc --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.Generate_With_QueryContext.md @@ -0,0 +1,110 @@ +# Generate_With_QueryContext + +## GreenDonutDataLoader.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using Microsoft.Extensions.DependencyInjection; +using GreenDonut; + +namespace TestNamespace +{ + public interface IEntityByIdDataLoader + : global::GreenDonut.IDataLoader + { + } + + public sealed partial class EntityByIdDataLoader + : global::GreenDonut.DataLoaderBase + , IEntityByIdDataLoader + { + private readonly global::System.IServiceProvider _services; + + public EntityByIdDataLoader( + global::System.IServiceProvider services, + global::GreenDonut.IBatchScheduler batchScheduler, + global::GreenDonut.DataLoaderOptions options) + : base(batchScheduler, options) + { + _services = services ?? + throw new global::System.ArgumentNullException(nameof(services)); + } + + protected override async global::System.Threading.Tasks.ValueTask FetchAsync( + global::System.Collections.Generic.IReadOnlyList keys, + global::System.Memory> results, + global::GreenDonut.DataLoaderFetchContext context, + global::System.Threading.CancellationToken ct) + { + var p1_selector = context.GetState("GreenDonut.Data.Selector")?.TryCompile(); + var p1_predicate = context.GetState("GreenDonut.Data.Predicate")?.TryCompile(); + var p1_sortDefinition = context.GetState>("GreenDonut.Data.Sorting"); + + if({0}_selector is null && {0}_predicate is null && {0}_sortDefinition is null) + { + var p1 = global::GreenDonut.Data.QueryContext.Empty; + } + + var p1 = new global::GreenDonut.Data.QueryContext(p1_selector?, p1_predicate, p1_sortDefinition); + var temp = await global::TestNamespace.TestClass.GetEntityByIdAsync(keys, p1, ct).ConfigureAwait(false); + CopyResults(keys, results.Span, temp); + } + + private void CopyResults( + global::System.Collections.Generic.IReadOnlyList keys, + global::System.Span> results, + global::System.Collections.Generic.IDictionary resultMap) + { + for (var i = 0; i < keys.Count; i++) + { + var key = keys[i]; + if (resultMap.TryGetValue(key, out var value)) + { + results[i] = global::GreenDonut.Result.Resolve(value); + } + else + { + results[i] = global::GreenDonut.Result.Resolve(default(string)); + } + } + } + } +} + + +``` + +## HotChocolateTypeModule.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + builder.AddDataLoader(); + return builder; + } + } +} + +``` + diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.Generate_With_SortDefinition.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.Generate_With_SortDefinition.md new file mode 100644 index 00000000000..87f762e2bbc --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.Generate_With_SortDefinition.md @@ -0,0 +1,102 @@ +# Generate_With_SortDefinition + +## GreenDonutDataLoader.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using Microsoft.Extensions.DependencyInjection; +using GreenDonut; + +namespace TestNamespace +{ + public interface IEntityByIdDataLoader + : global::GreenDonut.IDataLoader + { + } + + public sealed partial class EntityByIdDataLoader + : global::GreenDonut.DataLoaderBase + , IEntityByIdDataLoader + { + private readonly global::System.IServiceProvider _services; + + public EntityByIdDataLoader( + global::System.IServiceProvider services, + global::GreenDonut.IBatchScheduler batchScheduler, + global::GreenDonut.DataLoaderOptions options) + : base(batchScheduler, options) + { + _services = services ?? + throw new global::System.ArgumentNullException(nameof(services)); + } + + protected override async global::System.Threading.Tasks.ValueTask FetchAsync( + global::System.Collections.Generic.IReadOnlyList keys, + global::System.Memory> results, + global::GreenDonut.DataLoaderFetchContext context, + global::System.Threading.CancellationToken ct) + { + var p1 = context.GetState>("GreenDonut.Data.Sorting") + ?? global::GreenDonut.Data.SortDefinition.Empty; + var temp = await global::TestNamespace.TestClass.GetEntityByIdAsync(keys, p1, ct).ConfigureAwait(false); + CopyResults(keys, results.Span, temp); + } + + private void CopyResults( + global::System.Collections.Generic.IReadOnlyList keys, + global::System.Span> results, + global::System.Collections.Generic.IDictionary resultMap) + { + for (var i = 0; i < keys.Count; i++) + { + var key = keys[i]; + if (resultMap.TryGetValue(key, out var value)) + { + results[i] = global::GreenDonut.Result.Resolve(value); + } + else + { + results[i] = global::GreenDonut.Result.Resolve(default(string)); + } + } + } + } +} + + +``` + +## HotChocolateTypeModule.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + builder.AddDataLoader(); + return builder; + } + } +} + +``` + diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.Generate_Without_Interface.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.Generate_Without_Interface.md index bc544f391ca..e180a1e4a96 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.Generate_Without_Interface.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/DataLoaderTests.Generate_Without_Interface.md @@ -36,8 +36,8 @@ namespace TestNamespace global::GreenDonut.DataLoaderFetchContext context, global::System.Threading.CancellationToken ct) { - var p1 = context.GetState("GreenDonut.Predicates.IPredicateBuilder") - ?? new global::GreenDonut.Predicates.DefaultPredicateBuilder(); + var p1 = context.GetState("GreenDonut.Data.Predicate") + ?? global::GreenDonut.Data.DefaultPredicateBuilder.Empty; var temp = await global::TestNamespace.TestClass.GetEntityByIdAsync(keys, p1, ct).ConfigureAwait(false); CopyResults(keys, results.Span, temp); } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeTests.GenerateSource_BatchDataLoader_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeTests.GenerateSource_BatchDataLoader_MatchesSnapshot.md index ea2aebec076..d81c8cc9c8c 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeTests.GenerateSource_BatchDataLoader_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ObjectTypeTests.GenerateSource_BatchDataLoader_MatchesSnapshot.md @@ -101,43 +101,12 @@ namespace Microsoft.Extensions.DependencyInjection { public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) { - AddObjectTypeExtension_8734371(builder, global::TestNamespace.BookNode.Initialize); + builder.ConfigureDescriptorContext(ctx => ctx.TypeConfiguration.TryAdd( + "Tests::TestNamespace.BookNode", + () => global::TestNamespace.BookNode.Initialize)); + builder.AddType>(); return builder; } - - private static void AddObjectTypeExtension_8734371( - global::HotChocolate.Execution.Configuration.IRequestExecutorBuilder builder, - Action> initialize) - { - builder.ConfigureSchema(sb => - { - string typeName = typeof(T).FullName!; - string typeKey = $"8734371_Type_ObjectType<{typeName}>"; - string hooksKey = $"8734371_Hooks_ObjectType<{typeName}>"; - - if (!sb.ContextData.ContainsKey(typeKey)) - { - sb.AddObjectType( - descriptor => - { - var hooks = (global::System.Collections.Generic.List>>)descriptor.Extend().Context.ContextData[hooksKey]!; - foreach (var configure in hooks) - { - configure(descriptor); - }; - }); - sb.ContextData.Add(typeKey, null); - } - - if (!sb.ContextData.TryGetValue(hooksKey, out var value)) - { - value = new System.Collections.Generic.List>>(); - sb.ContextData.Add(hooksKey, value); - } - - ((System.Collections.Generic.List>>)value!).Add(initialize); - }); - } } } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/OperationTests.Instance_QueryType.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/OperationTests.Instance_QueryType.md new file mode 100644 index 00000000000..d1c7f56ffa1 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/OperationTests.Instance_QueryType.md @@ -0,0 +1,32 @@ +# Instance_QueryType + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + builder.AddTypeExtension(); + builder.ConfigureSchema( + b => b.TryAddRootType( + () => new global::HotChocolate.Types.ObjectType( + d => d.Name(global::HotChocolate.Types.OperationTypeNames.Query)), + HotChocolate.Language.OperationType.Query)); + return builder; + } + } +} + +``` diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/OperationTests.Partial_Static_QueryType.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/OperationTests.Partial_Static_QueryType.md new file mode 100644 index 00000000000..ec611cd3b4c --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/OperationTests.Partial_Static_QueryType.md @@ -0,0 +1,168 @@ +# Partial_Static_QueryType + +## HotChocolateResolvers.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using HotChocolate.Internal; + +namespace TestNamespace +{ + internal static class QueryResolvers + { + private static readonly object _sync = new object(); + private static bool _bindingsInitialized; + private readonly static global::HotChocolate.Internal.IParameterBinding[] _args_Query_GetTest = new global::HotChocolate.Internal.IParameterBinding[1]; + + public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver) + { + if (!_bindingsInitialized) + { + lock (_sync) + { + if (!_bindingsInitialized) + { + + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var type = typeof(global::TestNamespace.Query); + global::System.Reflection.MethodInfo resolver = default!; + global::System.Reflection.ParameterInfo[] parameters = default!; + _bindingsInitialized = true; + + resolver = type.GetMethod( + "GetTest", + bindingFlags, + new global::System.Type[] + { + typeof(string) + })!; + parameters = resolver.GetParameters(); + _args_Query_GetTest[0] = bindingResolver.GetBinding(parameters[0]); + } + } + } + } + + public static HotChocolate.Resolvers.FieldResolverDelegates Query_GetTest() + { + if(!_bindingsInitialized) + { + throw new global::System.InvalidOperationException("The bindings must be initialized before the resolvers can be created."); + } + + var isPure = _args_Query_GetTest[0].IsPure; + + return isPure + ? new global::HotChocolate.Resolvers.FieldResolverDelegates(pureResolver: Query_GetTest_Resolver) + : new global::HotChocolate.Resolvers.FieldResolverDelegates(resolver: c => new(Query_GetTest_Resolver(c))); + } + + private static global::System.Object? Query_GetTest_Resolver(global::HotChocolate.Resolvers.IResolverContext context) + { + var args0 = _args_Query_GetTest[0].Execute(context); + var result = global::TestNamespace.Query.GetTest(args0); + return result; + } + } +} + + +``` + +## HotChocolateTypeModule.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + builder.ConfigureDescriptorContext(ctx => ctx.TypeConfiguration.TryAdd( + "Tests::TestNamespace.Query", + global::HotChocolate.Types.OperationType.Query, + () => global::TestNamespace.Query.Initialize)); + builder.ConfigureSchema( + b => b.TryAddRootType( + () => new global::HotChocolate.Types.ObjectType( + d => d.Name(global::HotChocolate.Types.OperationTypeNames.Query)), + HotChocolate.Language.OperationType.Query)); + return builder; + } + } +} + +``` + +## HotChocolateTypes.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace TestNamespace +{ +public static partial class Query +{ + internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor descriptor) + { + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var thisType = typeof(global::TestNamespace.Query); + var bindingResolver = descriptor.Extend().Context.ParameterBindingResolver; + global::TestNamespace.QueryResolvers.InitializeBindings(bindingResolver); + + descriptor + .Field(thisType.GetMember("GetTest", bindingFlags)[0]) + .ExtendWith(c => + { + c.Definition.SetSourceGeneratorFlags(); + c.Definition.Resolvers = global::TestNamespace.QueryResolvers.Query_GetTest(); + }); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); +} +} + + +``` + diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/OperationTests.Static_QueryType.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/OperationTests.Static_QueryType.md new file mode 100644 index 00000000000..a1be279e547 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/OperationTests.Static_QueryType.md @@ -0,0 +1,32 @@ +# Static_QueryType + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + builder.AddTypeExtension(typeof(global::TestNamespace.Query)); + builder.ConfigureSchema( + b => b.TryAddRootType( + () => new global::HotChocolate.Types.ObjectType( + d => d.Name(global::HotChocolate.Types.OperationTypeNames.Query)), + HotChocolate.Language.OperationType.Query)); + return builder; + } + } +} + +``` diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.Ensure_Entity_Becomes_Node.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.Ensure_Entity_Becomes_Node.md new file mode 100644 index 00000000000..e2752ffdc06 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.Ensure_Entity_Becomes_Node.md @@ -0,0 +1,266 @@ +# Ensure_Entity_Becomes_Node + +## HotChocolateResolvers.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using HotChocolate.Internal; + +namespace TestNamespace +{ + internal static class QueryResolvers + { + private static readonly object _sync = new object(); + private static bool _bindingsInitialized; + private readonly static global::HotChocolate.Internal.IParameterBinding[] _args_Query_GetTestById = new global::HotChocolate.Internal.IParameterBinding[1]; + + public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver) + { + if (!_bindingsInitialized) + { + lock (_sync) + { + if (!_bindingsInitialized) + { + + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var type = typeof(global::TestNamespace.Query); + global::System.Reflection.MethodInfo resolver = default!; + global::System.Reflection.ParameterInfo[] parameters = default!; + _bindingsInitialized = true; + + resolver = type.GetMethod( + "GetTestById", + bindingFlags, + new global::System.Type[] + { + typeof(int) + })!; + parameters = resolver.GetParameters(); + _args_Query_GetTestById[0] = bindingResolver.GetBinding(parameters[0]); + } + } + } + } + + public static HotChocolate.Resolvers.FieldResolverDelegates Query_GetTestById() + { + if(!_bindingsInitialized) + { + throw new global::System.InvalidOperationException("The bindings must be initialized before the resolvers can be created."); + } + + return new global::HotChocolate.Resolvers.FieldResolverDelegates(resolver: Query_GetTestById_Resolver); + } + + private static async global::System.Threading.Tasks.ValueTask Query_GetTestById_Resolver(global::HotChocolate.Resolvers.IResolverContext context) + { + var args0 = _args_Query_GetTestById[0].Execute(context); + var result = await global::TestNamespace.Query.GetTestById(args0); + return result; + } + } + + internal static class TestTypeResolvers + { + private static readonly object _sync = new object(); + private static bool _bindingsInitialized; + private readonly static global::HotChocolate.Internal.IParameterBinding[] _args_TestType_GetTest = new global::HotChocolate.Internal.IParameterBinding[1]; + + public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver) + { + if (!_bindingsInitialized) + { + lock (_sync) + { + if (!_bindingsInitialized) + { + + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var type = typeof(global::TestNamespace.TestType); + global::System.Reflection.MethodInfo resolver = default!; + global::System.Reflection.ParameterInfo[] parameters = default!; + _bindingsInitialized = true; + + resolver = type.GetMethod( + "GetTest", + bindingFlags, + new global::System.Type[] + { + typeof(int) + })!; + parameters = resolver.GetParameters(); + _args_TestType_GetTest[0] = bindingResolver.GetBinding(parameters[0]); + + int argumentCount = 0; + + foreach(var binding in _args_TestType_GetTest) + { + if(binding.Kind == global::HotChocolate.Internal.ArgumentKind.Argument) + { + argumentCount++; + } + } + + if(argumentCount > 1) + { + throw new global::HotChocolate.SchemaException( + global::HotChocolate.SchemaErrorBuilder.New() + .SetMessage("The node resolver `TestNamespace.TestType.GetTest` mustn't have more than one argument. Node resolvers can only have a single argument called `id`.") + .Build()); + } + } + } + } + } + + public static HotChocolate.Resolvers.FieldResolverDelegates TestType_GetTest() + { + if(!_bindingsInitialized) + { + throw new global::System.InvalidOperationException("The bindings must be initialized before the resolvers can be created."); + } + + return new global::HotChocolate.Resolvers.FieldResolverDelegates(resolver: TestType_GetTest_Resolver); + } + + private static async global::System.Threading.Tasks.ValueTask TestType_GetTest_Resolver(global::HotChocolate.Resolvers.IResolverContext context) + { + var args0 = context.GetLocalState(global::HotChocolate.WellKnownContextData.InternalId); + var result = await global::TestNamespace.TestType.GetTest(args0); + return result; + } + } +} + + +``` + +## HotChocolateTypeModule.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + builder.ConfigureDescriptorContext(ctx => ctx.TypeConfiguration.TryAdd( + "Tests::TestNamespace.Query", + global::HotChocolate.Types.OperationType.Query, + () => global::TestNamespace.Query.Initialize)); + builder.ConfigureDescriptorContext(ctx => ctx.TypeConfiguration.TryAdd( + "Tests::TestNamespace.TestType", + () => global::TestNamespace.TestType.Initialize)); + builder.ConfigureSchema( + b => b.TryAddRootType( + () => new global::HotChocolate.Types.ObjectType( + d => d.Name(global::HotChocolate.Types.OperationTypeNames.Query)), + HotChocolate.Language.OperationType.Query)); + builder.AddType>(); + return builder; + } + } +} + +``` + +## HotChocolateTypes.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace TestNamespace +{ +public static partial class Query +{ + internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor descriptor) + { + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var thisType = typeof(global::TestNamespace.Query); + var bindingResolver = descriptor.Extend().Context.ParameterBindingResolver; + global::TestNamespace.QueryResolvers.InitializeBindings(bindingResolver); + + descriptor + .Field(thisType.GetMember("GetTestById", bindingFlags)[0]) + .ExtendWith(c => + { + c.Definition.SetSourceGeneratorFlags(); + c.Definition.Resolvers = global::TestNamespace.QueryResolvers.Query_GetTestById(); + }); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); +} + +public static partial class TestType +{ + internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor descriptor) + { + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var thisType = typeof(global::TestNamespace.TestType); + var bindingResolver = descriptor.Extend().Context.ParameterBindingResolver; + global::TestNamespace.TestTypeResolvers.InitializeBindings(bindingResolver); + + descriptor + .ImplementsNode() + .ResolveNode(global::TestNamespace.TestTypeResolvers.TestType_GetTest().Resolver!); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); +} +} + + +``` + diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.Ensure_Entity_Becomes_Node_With_Query_Node_Resolver.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.Ensure_Entity_Becomes_Node_With_Query_Node_Resolver.md new file mode 100644 index 00000000000..90ebb7dc82a --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.Ensure_Entity_Becomes_Node_With_Query_Node_Resolver.md @@ -0,0 +1,188 @@ +# Ensure_Entity_Becomes_Node_With_Query_Node_Resolver + +## HotChocolateResolvers.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using HotChocolate.Internal; + +namespace TestNamespace +{ + internal static class QueryResolvers + { + private static readonly object _sync = new object(); + private static bool _bindingsInitialized; + public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver) + { + } + } + + internal static class TestTypeResolvers + { + private static readonly object _sync = new object(); + private static bool _bindingsInitialized; + private readonly static global::HotChocolate.Internal.IParameterBinding[] _args_TestType_GetTest = new global::HotChocolate.Internal.IParameterBinding[1]; + + public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver) + { + if (!_bindingsInitialized) + { + lock (_sync) + { + if (!_bindingsInitialized) + { + + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var type = typeof(global::TestNamespace.TestType); + global::System.Reflection.MethodInfo resolver = default!; + global::System.Reflection.ParameterInfo[] parameters = default!; + _bindingsInitialized = true; + + resolver = type.GetMethod( + "GetTest", + bindingFlags, + new global::System.Type[] + { + typeof(int) + })!; + parameters = resolver.GetParameters(); + _args_TestType_GetTest[0] = bindingResolver.GetBinding(parameters[0]); + } + } + } + } + + public static HotChocolate.Resolvers.FieldResolverDelegates TestType_GetTest() + { + if(!_bindingsInitialized) + { + throw new global::System.InvalidOperationException("The bindings must be initialized before the resolvers can be created."); + } + + return new global::HotChocolate.Resolvers.FieldResolverDelegates(resolver: TestType_GetTest_Resolver); + } + + private static async global::System.Threading.Tasks.ValueTask TestType_GetTest_Resolver(global::HotChocolate.Resolvers.IResolverContext context) + { + var args0 = _args_TestType_GetTest[0].Execute(context); + var result = await global::TestNamespace.TestType.GetTest(args0); + return result; + } + } +} + + +``` + +## HotChocolateTypeModule.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + builder.ConfigureDescriptorContext(ctx => ctx.TypeConfiguration.TryAdd( + "Tests::TestNamespace.Query", + global::HotChocolate.Types.OperationType.Query, + () => global::TestNamespace.Query.Initialize)); + builder.ConfigureDescriptorContext(ctx => ctx.TypeConfiguration.TryAdd( + "Tests::TestNamespace.TestType", + () => global::TestNamespace.TestType.Initialize)); + builder.ConfigureSchema( + b => b.TryAddRootType( + () => new global::HotChocolate.Types.ObjectType( + d => d.Name(global::HotChocolate.Types.OperationTypeNames.Query)), + HotChocolate.Language.OperationType.Query)); + builder.AddType>(); + return builder; + } + } +} + +``` + +## HotChocolateTypes.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace TestNamespace +{ +public static partial class Query +{ + internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor descriptor) + { + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); +} + +public static partial class TestType +{ + internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor descriptor) + { + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var thisType = typeof(global::TestNamespace.TestType); + var bindingResolver = descriptor.Extend().Context.ParameterBindingResolver; + global::TestNamespace.TestTypeResolvers.InitializeBindings(bindingResolver); + + descriptor + .Field(thisType.GetMember("GetTest", bindingFlags)[0]) + .ExtendWith(c => + { + c.Definition.SetSourceGeneratorFlags(); + c.Definition.Resolvers = global::TestNamespace.TestTypeResolvers.TestType_GetTest(); + }); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); +} +} + + +``` + diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithGlobalStateArgument_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithGlobalStateArgument_MatchesSnapshot.md index 13a37042bf9..4900c126701 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithGlobalStateArgument_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithGlobalStateArgument_MatchesSnapshot.md @@ -97,43 +97,12 @@ namespace Microsoft.Extensions.DependencyInjection { public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) { - AddObjectTypeExtension_8734371(builder, global::TestNamespace.TestType.Initialize); + builder.ConfigureDescriptorContext(ctx => ctx.TypeConfiguration.TryAdd( + "Tests::TestNamespace.TestType", + () => global::TestNamespace.TestType.Initialize)); + builder.AddType>(); return builder; } - - private static void AddObjectTypeExtension_8734371( - global::HotChocolate.Execution.Configuration.IRequestExecutorBuilder builder, - Action> initialize) - { - builder.ConfigureSchema(sb => - { - string typeName = typeof(T).FullName!; - string typeKey = $"8734371_Type_ObjectType<{typeName}>"; - string hooksKey = $"8734371_Hooks_ObjectType<{typeName}>"; - - if (!sb.ContextData.ContainsKey(typeKey)) - { - sb.AddObjectType( - descriptor => - { - var hooks = (global::System.Collections.Generic.List>>)descriptor.Extend().Context.ContextData[hooksKey]!; - foreach (var configure in hooks) - { - configure(descriptor); - }; - }); - sb.ContextData.Add(typeKey, null); - } - - if (!sb.ContextData.TryGetValue(hooksKey, out var value)) - { - value = new System.Collections.Generic.List>>(); - sb.ContextData.Add(hooksKey, value); - } - - ((System.Collections.Generic.List>>)value!).Add(initialize); - }); - } } } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithGlobalStateSetStateArgument_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithGlobalStateSetStateArgument_MatchesSnapshot.md index b196f075236..7503a878e3d 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithGlobalStateSetStateArgument_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithGlobalStateSetStateArgument_MatchesSnapshot.md @@ -97,43 +97,12 @@ namespace Microsoft.Extensions.DependencyInjection { public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) { - AddObjectTypeExtension_8734371(builder, global::TestNamespace.TestType.Initialize); + builder.ConfigureDescriptorContext(ctx => ctx.TypeConfiguration.TryAdd( + "Tests::TestNamespace.TestType", + () => global::TestNamespace.TestType.Initialize)); + builder.AddType>(); return builder; } - - private static void AddObjectTypeExtension_8734371( - global::HotChocolate.Execution.Configuration.IRequestExecutorBuilder builder, - Action> initialize) - { - builder.ConfigureSchema(sb => - { - string typeName = typeof(T).FullName!; - string typeKey = $"8734371_Type_ObjectType<{typeName}>"; - string hooksKey = $"8734371_Hooks_ObjectType<{typeName}>"; - - if (!sb.ContextData.ContainsKey(typeKey)) - { - sb.AddObjectType( - descriptor => - { - var hooks = (global::System.Collections.Generic.List>>)descriptor.Extend().Context.ContextData[hooksKey]!; - foreach (var configure in hooks) - { - configure(descriptor); - }; - }); - sb.ContextData.Add(typeKey, null); - } - - if (!sb.ContextData.TryGetValue(hooksKey, out var value)) - { - value = new System.Collections.Generic.List>>(); - sb.ContextData.Add(hooksKey, value); - } - - ((System.Collections.Generic.List>>)value!).Add(initialize); - }); - } } } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithLocalStateArgument_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithLocalStateArgument_MatchesSnapshot.md index efd3b27a04a..09c99d25246 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithLocalStateArgument_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithLocalStateArgument_MatchesSnapshot.md @@ -97,43 +97,12 @@ namespace Microsoft.Extensions.DependencyInjection { public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) { - AddObjectTypeExtension_8734371(builder, global::TestNamespace.TestType.Initialize); + builder.ConfigureDescriptorContext(ctx => ctx.TypeConfiguration.TryAdd( + "Tests::TestNamespace.TestType", + () => global::TestNamespace.TestType.Initialize)); + builder.AddType>(); return builder; } - - private static void AddObjectTypeExtension_8734371( - global::HotChocolate.Execution.Configuration.IRequestExecutorBuilder builder, - Action> initialize) - { - builder.ConfigureSchema(sb => - { - string typeName = typeof(T).FullName!; - string typeKey = $"8734371_Type_ObjectType<{typeName}>"; - string hooksKey = $"8734371_Hooks_ObjectType<{typeName}>"; - - if (!sb.ContextData.ContainsKey(typeKey)) - { - sb.AddObjectType( - descriptor => - { - var hooks = (global::System.Collections.Generic.List>>)descriptor.Extend().Context.ContextData[hooksKey]!; - foreach (var configure in hooks) - { - configure(descriptor); - }; - }); - sb.ContextData.Add(typeKey, null); - } - - if (!sb.ContextData.TryGetValue(hooksKey, out var value)) - { - value = new System.Collections.Generic.List>>(); - sb.ContextData.Add(hooksKey, value); - } - - ((System.Collections.Generic.List>>)value!).Add(initialize); - }); - } } } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithLocalStateSetStateArgument_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithLocalStateSetStateArgument_MatchesSnapshot.md index 74e82253f53..d9be0e92284 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithLocalStateSetStateArgument_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithLocalStateSetStateArgument_MatchesSnapshot.md @@ -97,43 +97,12 @@ namespace Microsoft.Extensions.DependencyInjection { public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) { - AddObjectTypeExtension_8734371(builder, global::TestNamespace.TestType.Initialize); + builder.ConfigureDescriptorContext(ctx => ctx.TypeConfiguration.TryAdd( + "Tests::TestNamespace.TestType", + () => global::TestNamespace.TestType.Initialize)); + builder.AddType>(); return builder; } - - private static void AddObjectTypeExtension_8734371( - global::HotChocolate.Execution.Configuration.IRequestExecutorBuilder builder, - Action> initialize) - { - builder.ConfigureSchema(sb => - { - string typeName = typeof(T).FullName!; - string typeKey = $"8734371_Type_ObjectType<{typeName}>"; - string hooksKey = $"8734371_Hooks_ObjectType<{typeName}>"; - - if (!sb.ContextData.ContainsKey(typeKey)) - { - sb.AddObjectType( - descriptor => - { - var hooks = (global::System.Collections.Generic.List>>)descriptor.Extend().Context.ContextData[hooksKey]!; - foreach (var configure in hooks) - { - configure(descriptor); - }; - }); - sb.ContextData.Add(typeKey, null); - } - - if (!sb.ContextData.TryGetValue(hooksKey, out var value)) - { - value = new System.Collections.Generic.List>>(); - sb.ContextData.Add(hooksKey, value); - } - - ((System.Collections.Generic.List>>)value!).Add(initialize); - }); - } } } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithScopedStateArgument_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithScopedStateArgument_MatchesSnapshot.md index 119f8ec2954..67ed96b4b2a 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithScopedStateArgument_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithScopedStateArgument_MatchesSnapshot.md @@ -97,43 +97,12 @@ namespace Microsoft.Extensions.DependencyInjection { public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) { - AddObjectTypeExtension_8734371(builder, global::TestNamespace.TestType.Initialize); + builder.ConfigureDescriptorContext(ctx => ctx.TypeConfiguration.TryAdd( + "Tests::TestNamespace.TestType", + () => global::TestNamespace.TestType.Initialize)); + builder.AddType>(); return builder; } - - private static void AddObjectTypeExtension_8734371( - global::HotChocolate.Execution.Configuration.IRequestExecutorBuilder builder, - Action> initialize) - { - builder.ConfigureSchema(sb => - { - string typeName = typeof(T).FullName!; - string typeKey = $"8734371_Type_ObjectType<{typeName}>"; - string hooksKey = $"8734371_Hooks_ObjectType<{typeName}>"; - - if (!sb.ContextData.ContainsKey(typeKey)) - { - sb.AddObjectType( - descriptor => - { - var hooks = (global::System.Collections.Generic.List>>)descriptor.Extend().Context.ContextData[hooksKey]!; - foreach (var configure in hooks) - { - configure(descriptor); - }; - }); - sb.ContextData.Add(typeKey, null); - } - - if (!sb.ContextData.TryGetValue(hooksKey, out var value)) - { - value = new System.Collections.Generic.List>>(); - sb.ContextData.Add(hooksKey, value); - } - - ((System.Collections.Generic.List>>)value!).Add(initialize); - }); - } } } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithScopedStateSetStateArgument_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithScopedStateSetStateArgument_MatchesSnapshot.md index a224d91f3e8..6e9a3a6c117 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithScopedStateSetStateArgument_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.GenerateSource_ResolverWithScopedStateSetStateArgument_MatchesSnapshot.md @@ -97,43 +97,12 @@ namespace Microsoft.Extensions.DependencyInjection { public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) { - AddObjectTypeExtension_8734371(builder, global::TestNamespace.TestType.Initialize); + builder.ConfigureDescriptorContext(ctx => ctx.TypeConfiguration.TryAdd( + "Tests::TestNamespace.TestType", + () => global::TestNamespace.TestType.Initialize)); + builder.AddType>(); return builder; } - - private static void AddObjectTypeExtension_8734371( - global::HotChocolate.Execution.Configuration.IRequestExecutorBuilder builder, - Action> initialize) - { - builder.ConfigureSchema(sb => - { - string typeName = typeof(T).FullName!; - string typeKey = $"8734371_Type_ObjectType<{typeName}>"; - string hooksKey = $"8734371_Hooks_ObjectType<{typeName}>"; - - if (!sb.ContextData.ContainsKey(typeKey)) - { - sb.AddObjectType( - descriptor => - { - var hooks = (global::System.Collections.Generic.List>>)descriptor.Extend().Context.ContextData[hooksKey]!; - foreach (var configure in hooks) - { - configure(descriptor); - }; - }); - sb.ContextData.Add(typeKey, null); - } - - if (!sb.ContextData.TryGetValue(hooksKey, out var value)) - { - value = new System.Collections.Generic.List>>(); - sb.ContextData.Add(hooksKey, value); - } - - ((System.Collections.Generic.List>>)value!).Add(initialize); - }); - } } } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.Inject_QueryContext.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.Inject_QueryContext.md new file mode 100644 index 00000000000..2e2c708268a --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/ResolverTests.Inject_QueryContext.md @@ -0,0 +1,166 @@ +# Inject_QueryContext + +## HotChocolateResolvers.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using HotChocolate.Internal; + +namespace TestNamespace +{ + internal static class TestTypeResolvers + { + private static readonly object _sync = new object(); + private static bool _bindingsInitialized; + private readonly static global::HotChocolate.Internal.IParameterBinding[] _args_TestType_GetTest = new global::HotChocolate.Internal.IParameterBinding[1]; + + public static void InitializeBindings(global::HotChocolate.Internal.IParameterBindingResolver bindingResolver) + { + if (!_bindingsInitialized) + { + lock (_sync) + { + if (!_bindingsInitialized) + { + + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var type = typeof(global::TestNamespace.TestType); + global::System.Reflection.MethodInfo resolver = default!; + global::System.Reflection.ParameterInfo[] parameters = default!; + _bindingsInitialized = true; + + resolver = type.GetMethod( + "GetTest", + bindingFlags, + new global::System.Type[] + { + typeof(global::GreenDonut.Data.QueryContext) + })!; + parameters = resolver.GetParameters(); + _args_TestType_GetTest[0] = bindingResolver.GetBinding(parameters[0]); + } + } + } + } + + public static HotChocolate.Resolvers.FieldResolverDelegates TestType_GetTest() + { + if(!_bindingsInitialized) + { + throw new global::System.InvalidOperationException("The bindings must be initialized before the resolvers can be created."); + } + + return new global::HotChocolate.Resolvers.FieldResolverDelegates(resolver: TestType_GetTest_Resolver); + } + + private static global::System.Threading.Tasks.ValueTask TestType_GetTest_Resolver(global::HotChocolate.Resolvers.IResolverContext context) + { + var args0_selection = context.Selection; + var args0_filter = context.GetFilterContext(); + var args0_sorting = context.GetSortingContext(); + var args0 = new global::GreenDonut.Data.QueryContext( + args0_selection.AsSelector(), + args0_filter?.AsPredicate(), + args0_sorting?.AsSortDefinition()); + var result = global::TestNamespace.TestType.GetTest(args0); + return new global::System.Threading.Tasks.ValueTask(result); + } + } +} + + +``` + +## HotChocolateTypeModule.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static partial class TestsTypesRequestExecutorBuilderExtensions + { + public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) + { + builder.ConfigureDescriptorContext(ctx => ctx.TypeConfiguration.TryAdd( + "Tests::TestNamespace.TestType", + () => global::TestNamespace.TestType.Initialize)); + builder.AddType>(); + return builder; + } + } +} + +``` + +## HotChocolateTypes.735550c.g.cs + +```csharp +// + +#nullable enable +#pragma warning disable + +using System; +using System.Runtime.CompilerServices; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Execution.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace TestNamespace +{ +public static partial class TestType +{ + internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor descriptor) + { + const global::System.Reflection.BindingFlags bindingFlags = + global::System.Reflection.BindingFlags.Public + | global::System.Reflection.BindingFlags.NonPublic + | global::System.Reflection.BindingFlags.Static; + + var thisType = typeof(global::TestNamespace.TestType); + var bindingResolver = descriptor.Extend().Context.ParameterBindingResolver; + global::TestNamespace.TestTypeResolvers.InitializeBindings(bindingResolver); + + descriptor + .Field(thisType.GetMember("GetTest", bindingFlags)[0]) + .ExtendWith(c => + { + c.Definition.SetSourceGeneratorFlags(); + c.Definition.Resolvers = global::TestNamespace.TestTypeResolvers.TestType_GetTest(); + c.Definition.ResultPostProcessor = global::HotChocolate.Execution.ListPostProcessor.Default; + }); + + Configure(descriptor); + } + + static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor); +} +} + + +``` + diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/TypeModuleSyntaxGeneratorTests.GenerateSource_TypeModuleOrdering_MatchesSnapshot.md b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/TypeModuleSyntaxGeneratorTests.GenerateSource_TypeModuleOrdering_MatchesSnapshot.md index 9ab52b46170..4fd58af2754 100644 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/TypeModuleSyntaxGeneratorTests.GenerateSource_TypeModuleOrdering_MatchesSnapshot.md +++ b/src/HotChocolate/Core/test/Types.Analyzers.Tests/__snapshots__/TypeModuleSyntaxGeneratorTests.GenerateSource_TypeModuleOrdering_MatchesSnapshot.md @@ -180,8 +180,12 @@ namespace Microsoft.Extensions.DependencyInjection { public static IRequestExecutorBuilder AddTestsTypes(this IRequestExecutorBuilder builder) { - AddObjectTypeExtension_8734371(builder, global::TestNamespace.ATestAAttrType.Initialize); - AddObjectTypeExtension_8734371(builder, global::TestNamespace.ATestBAttrType.Initialize); + builder.ConfigureDescriptorContext(ctx => ctx.TypeConfiguration.TryAdd( + "Tests::TestNamespace.ATestAAttrType", + () => global::TestNamespace.ATestAAttrType.Initialize)); + builder.ConfigureDescriptorContext(ctx => ctx.TypeConfiguration.TryAdd( + "Tests::TestNamespace.ATestBAttrType", + () => global::TestNamespace.ATestBAttrType.Initialize)); builder.AddTypeExtension(); builder.AddTypeExtension(); builder.AddType(); @@ -192,42 +196,10 @@ namespace Microsoft.Extensions.DependencyInjection builder.AddDataLoader(); builder.AddDataLoader(); builder.AddDataLoader(); + builder.AddType>(); + builder.AddType>(); return builder; } - - private static void AddObjectTypeExtension_8734371( - global::HotChocolate.Execution.Configuration.IRequestExecutorBuilder builder, - Action> initialize) - { - builder.ConfigureSchema(sb => - { - string typeName = typeof(T).FullName!; - string typeKey = $"8734371_Type_ObjectType<{typeName}>"; - string hooksKey = $"8734371_Hooks_ObjectType<{typeName}>"; - - if (!sb.ContextData.ContainsKey(typeKey)) - { - sb.AddObjectType( - descriptor => - { - var hooks = (global::System.Collections.Generic.List>>)descriptor.Extend().Context.ContextData[hooksKey]!; - foreach (var configure in hooks) - { - configure(descriptor); - }; - }); - sb.ContextData.Add(typeKey, null); - } - - if (!sb.ContextData.TryGetValue(hooksKey, out var value)) - { - value = new System.Collections.Generic.List>>(); - sb.ContextData.Add(hooksKey, value); - } - - ((System.Collections.Generic.List>>)value!).Add(initialize); - }); - } } } diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/_remove/SomeQuery.txt b/src/HotChocolate/Core/test/Types.Analyzers.Tests/_remove/SomeQuery.txt deleted file mode 100644 index 9b15c5ed68e..00000000000 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/_remove/SomeQuery.txt +++ /dev/null @@ -1,107 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using HotChocolate; - -[assembly: DataLoaderDefaults( - ServiceScope = DataLoaderServiceScope.DataLoaderScope, - AccessModifier = DataLoaderAccessModifier.Public)] - -namespace HotChocolate.Types; - -[QueryType] -public static class SomeQuery -{ - public static IEntity? GetPerson() => new Person(); - - [GraphQLType("CustomEnum")] - public static ValueTask GetEnum() => default; - - public static Book GetBook() => new() { Title = "SomeTitle", }; - - public static Task WithDataLoader( - IFoosByIdDataLoader foosById, - CancellationToken cancellationToken) - { - return foosById.LoadAsync("a", cancellationToken); - } - - [DataLoader] -#pragma warning disable CS1998 - public static async Task> GetFoosById56( -#pragma warning restore CS1998 - IReadOnlyList keys, - SomeService someService, - CancellationToken cancellationToken) - { - return default!; - } - - // should be ignored on the schema - [DataLoader] -#pragma warning disable CS1998 - public static async Task GetFoosById55( -#pragma warning restore CS1998 - string id, - SomeService someService, - CancellationToken cancellationToken) - { - return "abc"; - } -} - -[MutationType] -public static class SomeMutation -{ - public static string DoSomething() => "abc"; -} - -[SubscriptionType] -public static class SomeSubscription -{ - public static string OnSomething() => "abc"; -} - -public static class DataLoaderGen -{ - [DataLoader] - internal static async Task> GetFoosById( - IReadOnlyList ids, - SomeService someService, - CancellationToken cancellationToken) - { - return await Task.FromResult(ids.ToDictionary(t => t, t => t)); - } - - [DataLoader] - public static async Task GetFoosById2( - string id, - SomeService someService, - CancellationToken cancellationToken) - { - return await Task.FromResult("abc"); - } - - [DataLoader(ServiceScope = DataLoaderServiceScope.OriginalScope)] - public static Task> GetFoosById3( - IReadOnlyList ids, - SomeService someService, - CancellationToken cancellationToken) - { - return default!; - } - - [DataLoader] - public static Task GetGenericById( - IReadOnlyList ids, - GenericService> someService, - CancellationToken cancellationToken) - { - return default!; - } -} - -public class SomeService; - -public class GenericService; diff --git a/src/HotChocolate/Core/test/Types.Analyzers.Tests/_remove/SpecialObjectType.txt b/src/HotChocolate/Core/test/Types.Analyzers.Tests/_remove/SpecialObjectType.txt deleted file mode 100644 index c4bbc227b2f..00000000000 --- a/src/HotChocolate/Core/test/Types.Analyzers.Tests/_remove/SpecialObjectType.txt +++ /dev/null @@ -1,6 +0,0 @@ -namespace HotChocolate.Types; - -public class SpecialObjectType : ObjectType -{ - -} diff --git a/src/HotChocolate/Data/src/Data/Extensions/HotChocolateDataRequestBuilderExtensions.cs b/src/HotChocolate/Data/src/Data/Extensions/HotChocolateDataRequestBuilderExtensions.cs index 49d39635f29..2ba3a389140 100644 --- a/src/HotChocolate/Data/src/Data/Extensions/HotChocolateDataRequestBuilderExtensions.cs +++ b/src/HotChocolate/Data/src/Data/Extensions/HotChocolateDataRequestBuilderExtensions.cs @@ -4,7 +4,6 @@ using HotChocolate.Data.Projections; using HotChocolate.Data.Sorting; using HotChocolate.Execution.Configuration; -using HotChocolate.Internal; namespace Microsoft.Extensions.DependencyInjection; @@ -33,9 +32,8 @@ public static IRequestExecutorBuilder AddFiltering( string? name = null, bool compatibilityMode = false) { - builder.Services.AddSingleton( - new FilterContextParameterExpressionBuilder()); - + builder.Services.TryAddParameterExpressionBuilder(); + builder.AddQueryContext(); return builder.ConfigureSchema(s => s.AddFiltering(name, compatibilityMode)); } @@ -59,11 +57,9 @@ public static IRequestExecutorBuilder AddFiltering( Action configure, string? name = null) { - builder.Services.AddSingleton( - new FilterContextParameterExpressionBuilder()); - - return builder - .ConfigureSchema(s => s.AddFiltering(configure, name)); + builder.Services.TryAddParameterExpressionBuilder(); + builder.AddQueryContext(); + return builder.ConfigureSchema(s => s.AddFiltering(configure, name)); } /// @@ -86,9 +82,8 @@ public static IRequestExecutorBuilder AddFiltering( string? name = null) where TConvention : class, IFilterConvention { - builder.Services.AddSingleton( - new FilterContextParameterExpressionBuilder()); - + builder.Services.TryAddParameterExpressionBuilder(); + builder.AddQueryContext(); return builder.ConfigureSchema(s => s.AddFiltering(name)); } @@ -108,8 +103,8 @@ public static IRequestExecutorBuilder AddSorting( this IRequestExecutorBuilder builder, string? name = null) { - builder.Services.AddSingleton( - new SortingContextParameterExpressionBuilder()); + builder.Services.TryAddParameterExpressionBuilder(); + builder.AddQueryContext(); return builder.ConfigureSchema(s => s.AddSorting(name)); } @@ -133,8 +128,8 @@ public static IRequestExecutorBuilder AddSorting( Action configure, string? name = null) { - builder.Services.AddSingleton( - new SortingContextParameterExpressionBuilder()); + builder.Services.TryAddParameterExpressionBuilder(); + builder.AddQueryContext(); return builder.ConfigureSchema(s => s.AddSorting(configure, name)); } @@ -158,8 +153,8 @@ public static IRequestExecutorBuilder AddSorting( string? name = null) where TConvention : class, ISortConvention { - builder.Services.AddSingleton( - new SortingContextParameterExpressionBuilder()); + builder.Services.TryAddParameterExpressionBuilder(); + builder.AddQueryContext(); return builder.ConfigureSchema(s => s.AddSorting(name)); } @@ -177,8 +172,8 @@ public static IRequestExecutorBuilder AddSorting( /// public static IRequestExecutorBuilder AddProjections( this IRequestExecutorBuilder builder, - string? name = null) => - AddProjections(builder, x => x.AddDefaults(), name); + string? name = null) + => AddProjections(builder, x => x.AddDefaults(), name); /// /// Adds projection support. @@ -227,4 +222,20 @@ public static IRequestExecutorBuilder AddProjections( => builder.ConfigureSchema(s => s .TryAddTypeInterceptor() .TryAddConvention(name)); + + /// + /// Adds data context support. + /// + /// + /// The . + /// + /// + /// Returns the . + /// + public static IRequestExecutorBuilder AddQueryContext( + this IRequestExecutorBuilder builder) + { + builder.Services.TryAddParameterExpressionBuilder(); + return builder; + } } diff --git a/src/HotChocolate/Data/src/Data/Filters/Extensions/QueryableExtensions.cs b/src/HotChocolate/Data/src/Data/Filters/Extensions/HotChocolateDataQueryableExtensions.cs similarity index 65% rename from src/HotChocolate/Data/src/Data/Filters/Extensions/QueryableExtensions.cs rename to src/HotChocolate/Data/src/Data/Filters/Extensions/HotChocolateDataQueryableExtensions.cs index 90598cb3628..d1554153861 100644 --- a/src/HotChocolate/Data/src/Data/Filters/Extensions/QueryableExtensions.cs +++ b/src/HotChocolate/Data/src/Data/Filters/Extensions/HotChocolateDataQueryableExtensions.cs @@ -1,4 +1,5 @@ using HotChocolate.Data.Filters; +using HotChocolate.Data.Sorting; using HotChocolate.Execution.Processing; // ReSharper disable once CheckNamespace @@ -8,7 +9,7 @@ namespace System.Linq; /// Provides extension methods to integrate /// with and . /// -public static class QueryableExtensions +public static class HotChocolateDataQueryableExtensions { /// /// Applies a selection to the queryable. @@ -76,4 +77,44 @@ public static IQueryable Where(this IQueryable queryable, IFilterContex var predicate = filter.AsPredicate(); return predicate is null ? queryable : queryable.Where(predicate); } + + /// + /// Applies a sorting context to the queryable. + /// + /// + /// The queryable that shall be sorted. + /// + /// + /// The sorting context that shall be applied to the queryable. + /// + /// + /// The type of the queryable. + /// + /// + /// Returns a queryable that has the sorting applied. + /// + /// + /// Throws if is null or if is null. + /// + public static IQueryable Order(this IQueryable queryable, ISortingContext sorting) + { + if (queryable is null) + { + throw new ArgumentNullException(nameof(queryable)); + } + + if (sorting is null) + { + throw new ArgumentNullException(nameof(sorting)); + } + + var sortDefinition = sorting.AsSortDefinition(); + + if (sortDefinition is null || sortDefinition.Operations.Length == 0) + { + return queryable; + } + + return queryable.OrderBy(sortDefinition); + } } diff --git a/src/HotChocolate/Data/src/Data/Filters/Extensions/HotChocolateExecutionDataLoaderExtensions.cs b/src/HotChocolate/Data/src/Data/Filters/Extensions/HotChocolateExecutionDataLoaderExtensions.cs new file mode 100644 index 00000000000..e0c9c976c99 --- /dev/null +++ b/src/HotChocolate/Data/src/Data/Filters/Extensions/HotChocolateExecutionDataLoaderExtensions.cs @@ -0,0 +1,156 @@ +using HotChocolate.Data.Filters; +using HotChocolate.Data.Sorting; +using HotChocolate.Pagination; + +// ReSharper disable once CheckNamespace +namespace GreenDonut.Data; + +/// +/// Provides extension methods for projection on DataLoader. +/// +public static class HotChocolateExecutionDataLoaderExtensions +{ + /// + /// Applies a filter context to the data loader. + /// + /// + /// The data loader. + /// + /// + /// The filter context that shall be applied to the data loader. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns a new data loader that applies the selection. + /// + public static IDataLoader Where( + this IDataLoader dataLoader, + IFilterContext context) + where TKey : notnull + { + var expression = context.AsPredicate(); + return dataLoader.Where(expression); + } + + /// + /// Applies a filter context to the data loader. + /// + /// + /// The data loader. + /// + /// + /// The filter context that shall be applied to the data loader. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns a new data loader that applies the selection. + /// + public static IDataLoader Where( + this IDataLoader dataLoader, + IFilterContext context) + where TKey : notnull + { + var expression = context.AsPredicate(); + return dataLoader.Where(expression); + } + + /// + /// Applies a filter context to the data loader. + /// + /// + /// The data loader. + /// + /// + /// The filter context that shall be applied to the data loader. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns a new data loader that applies the selection. + /// + public static IDataLoader> Where( + this IDataLoader> dataLoader, + IFilterContext context) + where TKey : notnull + { + var expression = context.AsPredicate(); + return dataLoader.Where(expression); + } + + /// + /// Applies a filter context to the data loader. + /// + /// + /// The data loader. + /// + /// + /// The filter context that shall be applied to the data loader. + /// + /// + /// The key type. + /// + /// + /// The value type. + /// + /// + /// Returns a new data loader that applies the selection. + /// + public static IDataLoader> Where( + this IDataLoader> dataLoader, + IFilterContext context) + where TKey : notnull + { + var expression = context.AsPredicate(); + return dataLoader.Where(expression); + } + + public static IDataLoader Order( + this IDataLoader dataLoader, + ISortingContext context) + where TKey : notnull + { + var definition = context.AsSortDefinition(); + return dataLoader.OrderBy(definition); + } + + public static IDataLoader Order( + this IDataLoader dataLoader, + ISortingContext context) + where TKey : notnull + { + var definition = context.AsSortDefinition(); + return dataLoader.OrderBy(definition); + } + + public static IDataLoader> Order( + this IDataLoader> dataLoader, + ISortingContext context) + where TKey : notnull + { + var definition = context.AsSortDefinition(); + return dataLoader.OrderBy(definition); + } + + public static IDataLoader> Order( + this IDataLoader> dataLoader, + ISortingContext context) + where TKey : notnull + { + var definition = context.AsSortDefinition(); + return dataLoader.Order(definition); + } +} diff --git a/src/HotChocolate/Data/src/Data/Filters/Extensions/HotChocolateExecutionPredicateDataLoaderExtensions.cs b/src/HotChocolate/Data/src/Data/Filters/Extensions/HotChocolateExecutionPredicateDataLoaderExtensions.cs deleted file mode 100644 index cdd683e4829..00000000000 --- a/src/HotChocolate/Data/src/Data/Filters/Extensions/HotChocolateExecutionPredicateDataLoaderExtensions.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using HotChocolate.Data.Filters; -using HotChocolate.Execution; -using HotChocolate.Pagination; - -// ReSharper disable once CheckNamespace -namespace GreenDonut.Predicates; - -/// -/// Provides extension methods for projection on DataLoader. -/// -#if NET8_0_OR_GREATER -[Experimental(Experiments.Predicates)] -#endif -public static class HotChocolateExecutionPredicateDataLoaderExtensions -{ - /// - /// Applies a filter context to the data loader. - /// - /// - /// The data loader. - /// - /// - /// The filter context that shall be applied to the data loader. - /// - /// - /// The key type. - /// - /// - /// The value type. - /// - /// - /// Returns a new data loader that applies the selection. - /// - public static IDataLoader Where( - this IDataLoader dataLoader, - IFilterContext context) - where TKey : notnull - { - var expression = context.AsPredicate(); - return dataLoader.Where(expression); - } - - /// - /// Applies a filter context to the data loader. - /// - /// - /// The data loader. - /// - /// - /// The filter context that shall be applied to the data loader. - /// - /// - /// The key type. - /// - /// - /// The value type. - /// - /// - /// Returns a new data loader that applies the selection. - /// - public static IPagingDataLoader> Where( - this IPagingDataLoader> dataLoader, - IFilterContext context) - where TKey : notnull - { - var expression = context.AsPredicate(); - return dataLoader.Where(expression); - } -} diff --git a/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj b/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj index 4b45a288672..770950976ea 100644 --- a/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj +++ b/src/HotChocolate/Data/src/Data/HotChocolate.Data.csproj @@ -12,6 +12,7 @@ + diff --git a/src/HotChocolate/Data/src/Data/QueryContextParameterExpressionBuilder.cs b/src/HotChocolate/Data/src/Data/QueryContextParameterExpressionBuilder.cs new file mode 100644 index 00000000000..2ae0adece2d --- /dev/null +++ b/src/HotChocolate/Data/src/Data/QueryContextParameterExpressionBuilder.cs @@ -0,0 +1,89 @@ +using System.Collections.Concurrent; +using System.Linq.Expressions; +using System.Reflection; +using GreenDonut.Data; +using HotChocolate.Data.Filters; +using HotChocolate.Data.Sorting; +using HotChocolate.Execution.Processing; +using HotChocolate.Internal; +using HotChocolate.Resolvers; + +namespace HotChocolate.Data; + +internal sealed class QueryContextParameterExpressionBuilder() + : IParameterExpressionBuilder + , IParameterBindingFactory + , IParameterBinding +{ + private static readonly MethodInfo _createQueryContext = + typeof(QueryContextParameterExpressionBuilder) + .GetMethod(nameof(CreateQueryContext), BindingFlags.Static | BindingFlags.NonPublic)!; + private static readonly ConcurrentDictionary _expressionCache = new(); + + public ArgumentKind Kind => ArgumentKind.Custom; + + public bool IsPure => false; + + public bool IsDefaultHandler => false; + + public bool CanHandle(ParameterInfo parameter) + => parameter.ParameterType.IsGenericType && + parameter.ParameterType.GetGenericTypeDefinition() == typeof(QueryContext<>); + + public Expression Build(ParameterExpressionBuilderContext context) + { + var resolverContext = context.ResolverContext; + var parameterType = context.Parameter.ParameterType; + + var factoryCacheEntry = + _expressionCache.GetOrAdd( + parameterType, + type => + { + var entityType = type.GetGenericArguments()[0]; + var factoryMethod = _createQueryContext.MakeGenericMethod(entityType); + var factory = Expression.Call(factoryMethod, resolverContext); + return new FactoryCacheEntry(factoryMethod, factory); + }); + + if(factoryCacheEntry.Factory is not null) + { + return factoryCacheEntry.Factory; + } + + var factory = Expression.Call(factoryCacheEntry.FactoryMethod, resolverContext); + _expressionCache.TryUpdate(parameterType, factoryCacheEntry with { Factory = factory }, factoryCacheEntry); + return factory; + } + + public IParameterBinding Create(ParameterBindingContext context) + => this; + + public T Execute(IResolverContext context) + { + var factoryCacheEntry = + _expressionCache.GetOrAdd( + typeof(T), + type => + { + var entityType = type.GetGenericArguments()[0]; + var factoryMethod = _createQueryContext.MakeGenericMethod(entityType); + return new FactoryCacheEntry(factoryMethod); + }); + return (T)factoryCacheEntry.FactoryMethod.Invoke(null, [context])!; + } + + private static QueryContext CreateQueryContext(IResolverContext context) + { + var selection = context.Selection; + var filterContext = context.GetFilterContext(); + var sortContext = context.GetSortingContext(); + + return new QueryContext( + selection.AsSelector(), + filterContext?.AsPredicate(), + sortContext?.AsSortDefinition()); + } + + private record FactoryCacheEntry(MethodInfo FactoryMethod, Expression? Factory = null); +} diff --git a/src/HotChocolate/Data/src/Data/Sorting/Context/ISortingContext.cs b/src/HotChocolate/Data/src/Data/Sorting/Context/ISortingContext.cs index a52b6565445..180e12dbdad 100644 --- a/src/HotChocolate/Data/src/Data/Sorting/Context/ISortingContext.cs +++ b/src/HotChocolate/Data/src/Data/Sorting/Context/ISortingContext.cs @@ -1,3 +1,5 @@ +using GreenDonut.Data; + namespace HotChocolate.Data.Sorting; /// @@ -38,6 +40,17 @@ public interface ISortingContext /// Returns a collection of sorting operations in the order that they are requested /// IReadOnlyList> GetFields(); + + /// + /// Returns a sort definition that can be used to sort a query. + /// + /// + /// The type of the entity. + /// + /// + /// Returns a sort definition that can be used to sort a query. + /// + SortDefinition? AsSortDefinition(); } public delegate TQuery PostSortingAction(bool userDefinedSorting, TQuery query); diff --git a/src/HotChocolate/Data/src/Data/Sorting/Context/SortingContext.cs b/src/HotChocolate/Data/src/Data/Sorting/Context/SortingContext.cs index dfe068bef23..d371adb2bd3 100644 --- a/src/HotChocolate/Data/src/Data/Sorting/Context/SortingContext.cs +++ b/src/HotChocolate/Data/src/Data/Sorting/Context/SortingContext.cs @@ -1,4 +1,11 @@ +using System.Collections.Concurrent; +using System.Collections.Immutable; +using System.Linq.Expressions; +using System.Reflection; +using GreenDonut.Data; +using HotChocolate.Data.Projections.Expressions.Handlers; using HotChocolate.Language; +using HotChocolate.Language.Visitors; using HotChocolate.Resolvers; using HotChocolate.Types; using static HotChocolate.Data.Sorting.Expressions.QueryableSortProvider; @@ -10,8 +17,14 @@ namespace HotChocolate.Data.Sorting; /// public class SortingContext : ISortingContext { + private static readonly MethodInfo _createSortByMethod = + typeof(SortingContext).GetMethod(nameof(CreateSortBy), BindingFlags.NonPublic | BindingFlags.Static)!; + private static readonly ConcurrentDictionary<(Type, Type), MethodInfo> _sortByFactoryCache = new(); + private static readonly SortDefinitionFormatter _formatter = new(); private readonly IReadOnlyList _value; private readonly IResolverContext _context; + private readonly IType _type; + private readonly IValueNode _valueNode; /// /// Creates a new instance of @@ -28,6 +41,8 @@ public SortingContext( .ToArray() : [new SortingInfo(type, valueNode, inputParser),]; _context = context; + _type = type; + _valueNode = valueNode; } /// @@ -101,4 +116,98 @@ void SerializeAndAssign(string fieldName, ISortingValueNode? value) throw new InvalidOperationException(); } } + + public SortDefinition? AsSortDefinition() + { + if(_valueNode.Kind == SyntaxKind.NullValue + || (_valueNode is ListValueNode listValue && listValue.Items.Count == 0) + || (_valueNode is ObjectValueNode objectValue && objectValue.Fields.Count == 0)) + { + return null; + } + + var builder = ImmutableArray.CreateBuilder>(); + var parameter = Expression.Parameter(typeof(T), "t"); + + foreach (var (selector, ascending, type) in _formatter.Rewrite(_valueNode, _type, parameter)) + { + var factory = _sortByFactoryCache.GetOrAdd( + (typeof(T), type), + static key => _createSortByMethod.MakeGenericMethod(key.Item1, key.Item2)); + var sortBy = (ISortBy)factory.Invoke(null, [parameter, selector, ascending])!; + builder.Add(sortBy); + } + + return new SortDefinition(builder.ToImmutable()); + } + + private static SortBy CreateSortBy( + ParameterExpression parameter, + Expression selector, + bool ascending) + => new SortBy( + Expression.Lambda>(selector, parameter), + ascending); + + private sealed class SortDefinitionFormatter : SyntaxWalker + { + public IEnumerable<(Expression, bool, Type)> Rewrite(IValueNode node, IType type, Expression parameter) + { + var context = new Context(); + context.Types.Push((InputObjectType)type); + context.Parents.Push(parameter); + Visit(node, context); + return context.Completed; + } + + protected override ISyntaxVisitorAction Enter( + ObjectFieldNode node, + Context context) + { + var type = context.Types.Peek(); + var field = (SortField)type.Fields[node.Name.Value]; + var fieldType = field.Type.NamedType(); + + var parent = context.Parents.Peek(); + context.Parents.Push(Expression.Property(parent, (PropertyInfo)field.Member!)); + + if (fieldType.IsInputObjectType()) + { + context.Types.Push((InputObjectType)fieldType); + } + + return base.Leave(node, context); + } + + protected override ISyntaxVisitorAction Leave( + ObjectFieldNode node, + Context context) + { + var type = context.Types.Peek(); + var field = (SortField)type.Fields[node.Name.Value]; + var fieldType = field.Type.NamedType(); + var expression = context.Parents.Pop(); + + if (fieldType.IsInputObjectType()) + { + context.Types.Pop(); + } + else + { + var ascending = node.Value.Value?.Equals("ASC") ?? true; + context.Completed.Add((expression, ascending, field.Member!.GetReturnType())); + } + + return base.Leave(node, context); + } + + public class Context + { + public Stack Types { get; } = new(); + + public Stack Parents { get; } = new(); + + public List<(Expression, bool, Type)> Completed { get; } = new(); + } + } } diff --git a/src/HotChocolate/Data/test/Data.AutoMapper.Tests/TestExtensions.cs b/src/HotChocolate/Data/test/Data.AutoMapper.Tests/TestExtensions.cs index 7d84dc58f8f..71ccd7095f1 100644 --- a/src/HotChocolate/Data/test/Data.AutoMapper.Tests/TestExtensions.cs +++ b/src/HotChocolate/Data/test/Data.AutoMapper.Tests/TestExtensions.cs @@ -4,7 +4,7 @@ namespace HotChocolate.Data.Projections; -public static class TestExtensions +public static class TestBufferHelper { public static void AddSqlFrom( this Snapshot snapshot, diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/DataLoaderTests.cs b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/DataLoaderTests.cs index d9b1eec9917..60e1d6165ab 100644 --- a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/DataLoaderTests.cs +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/DataLoaderTests.cs @@ -2,7 +2,7 @@ using System.Linq.Expressions; using CookieCrumble; using GreenDonut; -using GreenDonut.Predicates; +using GreenDonut.Data; using HotChocolate.Execution; using HotChocolate.Data.Filters; using HotChocolate.Data.TestContext; diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Filtering.md b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Filtering.md index 17e80aa06c1..22aa5e794f8 100644 --- a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Filtering.md +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Filtering.md @@ -4,13 +4,14 @@ ```text .param set @__keys_0 '[1]' +.param set @__p_1_startswith 'Product%' SELECT "p"."Id", "p"."AvailableStock", "p"."BrandId", "p"."Description", "p"."ImageFileName", "p"."MaxStockThreshold", "p"."Name", "p"."OnReorder", "p"."Price", "p"."RestockThreshold", "p"."TypeId" FROM "Products" AS "p" WHERE "p"."BrandId" IN ( SELECT "k"."value" FROM json_each(@__keys_0) AS "k" -) +) AND "p"."Name" LIKE @__p_1_startswith ESCAPE '\' ``` ## Result diff --git a/src/HotChocolate/Data/test/Data.Tests/IntegrationTests.cs b/src/HotChocolate/Data/test/Data.Tests/IntegrationTests.cs index ec14aa7d908..6c3e91b55e3 100644 --- a/src/HotChocolate/Data/test/Data.Tests/IntegrationTests.cs +++ b/src/HotChocolate/Data/test/Data.Tests/IntegrationTests.cs @@ -4,8 +4,9 @@ // ReSharper disable MemberCanBePrivate.Global // ReSharper disable MoveLocalFunctionAfterJumpStatement -using CookieCrumble; +using GreenDonut.Data; using HotChocolate.Data.Filters; +using HotChocolate.Data.Sorting; using HotChocolate.Execution; using HotChocolate.Types; using Microsoft.Extensions.DependencyInjection; @@ -880,6 +881,58 @@ public async Task AsPredicate_With_Filter_Returns_Author_1() result.MatchSnapshot(); } + [Fact] + public async Task AsSortDefinition_Descending() + { + // arrange + var executor = await new ServiceCollection() + .AddGraphQL() + .AddFiltering() + .AddSorting() + .AddProjections() + .AddQueryType() + .BuildRequestExecutorAsync(); + + // act + var result = await executor.ExecuteAsync( + """ + { + authorsSorted(order: { name: DESC }) { + name + } + } + """); + + // assert + result.MatchSnapshot(); + } + + [Fact] + public async Task AsSortDefinition_Descending_QueryContext() + { + // arrange + var executor = await new ServiceCollection() + .AddGraphQL() + .AddFiltering() + .AddSorting() + .AddProjections() + .AddQueryType() + .BuildRequestExecutorAsync(); + + // act + var result = await executor.ExecuteAsync( + """ + { + authorsData(order: { name: DESC }) { + name + } + } + """); + + // assert + result.MatchSnapshot(); + } + [QueryType] public static class StaticQuery { @@ -1083,5 +1136,39 @@ public IQueryable GetAuthors(IFilterContext filter) }, }.AsQueryable() .Where(filter); + + [UseSorting] + public IQueryable GetAuthorsSorted(ISortingContext sorting) + => new[] + { + new Author + { + Name = "Author1", + Books = new List(), + }, + new Author + { + Name = "Author2", + Books = new List() + }, + }.AsQueryable() + .Order(sorting); + + [UseSorting] + public IQueryable GetAuthorsData(QueryContext context) + => new[] + { + new Author + { + Name = "Author1", + Books = new List(), + }, + new Author + { + Name = "Author2", + Books = new List() + }, + }.AsQueryable() + .Apply(context); } } diff --git a/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.AsSortDefinition_Descending.snap b/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.AsSortDefinition_Descending.snap new file mode 100644 index 00000000000..c201ae4992a --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.AsSortDefinition_Descending.snap @@ -0,0 +1,12 @@ +{ + "data": { + "authorsSorted": [ + { + "name": "Author2" + }, + { + "name": "Author1" + } + ] + } +} diff --git a/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.AsSortDefinition_Descending_DataContext.snap b/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.AsSortDefinition_Descending_DataContext.snap new file mode 100644 index 00000000000..c6e103b265f --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.AsSortDefinition_Descending_DataContext.snap @@ -0,0 +1,12 @@ +{ + "data": { + "authorsData": [ + { + "name": "Author2" + }, + { + "name": "Author1" + } + ] + } +} diff --git a/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.AsSortDefinition_Descending_QueryContext.snap b/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.AsSortDefinition_Descending_QueryContext.snap new file mode 100644 index 00000000000..c6e103b265f --- /dev/null +++ b/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.AsSortDefinition_Descending_QueryContext.snap @@ -0,0 +1,12 @@ +{ + "data": { + "authorsData": [ + { + "name": "Author2" + }, + { + "name": "Author1" + } + ] + } +} diff --git a/src/HotChocolate/Directory.Build.props b/src/HotChocolate/Directory.Build.props index 8766174fd84..d94f89d96b1 100644 --- a/src/HotChocolate/Directory.Build.props +++ b/src/HotChocolate/Directory.Build.props @@ -4,6 +4,6 @@ hotchocolate-signet.png - 20.0.2 + 23.0.4 diff --git a/src/HotChocolate/MongoDb/src/Data/Driver/MongoDbSortDefinition.cs b/src/HotChocolate/MongoDb/src/Data/Driver/MongoDbSortDefinition.cs index 38995101458..53d804d34ec 100644 --- a/src/HotChocolate/MongoDb/src/Data/Driver/MongoDbSortDefinition.cs +++ b/src/HotChocolate/MongoDb/src/Data/Driver/MongoDbSortDefinition.cs @@ -4,7 +4,7 @@ namespace HotChocolate.Data.MongoDb; -public abstract class MongoDbSortDefinition : SortDefinition +public abstract class MongoDbSortDefinition : MongoDB.Driver.SortDefinition { public abstract BsonDocument Render( IBsonSerializer documentSerializer, @@ -13,11 +13,11 @@ public abstract BsonDocument Render( public override BsonDocument Render(RenderArgs args) => Render(args.DocumentSerializer, args.SerializerRegistry); - public SortDefinition ToSortDefinition() => new SortDefinitionWrapper(this); + public MongoDB.Driver.SortDefinition ToSortDefinition() => new SortDefinitionWrapper(this); private sealed class SortDefinitionWrapper( MongoDbSortDefinition sort) - : SortDefinition + : MongoDB.Driver.SortDefinition { public override BsonDocument Render(RenderArgs args) => sort.Render(args.DocumentSerializer, args.SerializerRegistry); diff --git a/src/HotChocolate/MongoDb/src/Data/Driver/SortDefinitionExtensions.cs b/src/HotChocolate/MongoDb/src/Data/Driver/SortDefinitionExtensions.cs index b4fac551e1b..8bde1e31f0e 100644 --- a/src/HotChocolate/MongoDb/src/Data/Driver/SortDefinitionExtensions.cs +++ b/src/HotChocolate/MongoDb/src/Data/Driver/SortDefinitionExtensions.cs @@ -7,11 +7,11 @@ namespace HotChocolate.Data.MongoDb; public static class SortDefinitionExtensions { public static MongoDbSortDefinition Wrap( - this SortDefinition sortDefinition) + this MongoDB.Driver.SortDefinition sortDefinition) => new SortDefinitionWrapper(sortDefinition); private sealed class SortDefinitionWrapper( - SortDefinition sort) + MongoDB.Driver.SortDefinition sort) : MongoDbSortDefinition { public override BsonDocument Render( diff --git a/src/HotChocolate/MongoDb/src/Data/Extensions/BsonDocumentExtensions.cs b/src/HotChocolate/MongoDb/src/Data/Extensions/BsonDocumentExtensions.cs index 48931801964..36596d46a9e 100644 --- a/src/HotChocolate/MongoDb/src/Data/Extensions/BsonDocumentExtensions.cs +++ b/src/HotChocolate/MongoDb/src/Data/Extensions/BsonDocumentExtensions.cs @@ -20,7 +20,7 @@ public static BsonDocument DefaultRender( _serializerRegistry)); public static BsonDocument DefaultRender( - this SortDefinition bsonQuery) + this MongoDB.Driver.SortDefinition bsonQuery) => bsonQuery.Render( new RenderArgs( _documentSerializer, diff --git a/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderSelectorExtensions.cs b/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs similarity index 50% rename from src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderSelectorExtensions.cs rename to src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs index 31ae7ace89a..9f9a219c2e7 100644 --- a/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderSelectorExtensions.cs +++ b/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderExtensions.cs @@ -1,16 +1,16 @@ using System.Buffers; -using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Runtime.CompilerServices; using HotChocolate.Pagination; +using static GreenDonut.Data.GreenDonutPredicateDataLoaderExtensions; // ReSharper disable once CheckNamespace -namespace GreenDonut.Selectors; +namespace GreenDonut.Data; /// /// Provides extension methods to pass a pagination context to a DataLoader. /// -public static class HotChocolatePaginationBatchingDataLoaderSelectorExtensions +public static class HotChocolatePaginationBatchingDataLoaderExtensions { /// /// Branches a DataLoader with the provided . @@ -33,29 +33,81 @@ public static class HotChocolatePaginationBatchingDataLoaderSelectorExtensions /// /// Throws if the is null. /// - public static IPagingDataLoader> WithPagingArguments( + [Obsolete("Use With() instead.")] + public static IDataLoader> WithPagingArguments( this IDataLoader> dataLoader, PagingArguments pagingArguments) where TKey : notnull + => With(dataLoader, pagingArguments); + + /// + /// Branches a DataLoader with the provided . + /// + /// + /// The DataLoader that shall be branched. + /// + /// + /// The paging arguments that shall exist as state in the branched DataLoader. + /// + /// + /// The query context that shall exist as state in the branched DataLoader. + /// + /// + /// The key type of the DataLoader. + /// + /// + /// The value type of the DataLoader. + /// + /// + /// Returns a branched DataLoader with the provided . + /// + /// + /// Throws if the is null. + /// + public static IDataLoader> With( + this IDataLoader> dataLoader, + PagingArguments pagingArguments, + QueryContext? context = null) + where TKey : notnull { if (dataLoader is null) { throw new ArgumentNullException(nameof(dataLoader)); } - var branchKey = CreateBranchKey(pagingArguments); - return (IPagingDataLoader>)dataLoader.Branch( - branchKey, - CreatePagingDataLoader, - pagingArguments); + var branchKey = pagingArguments.ComputeHash(context); + var state = new PagingState(pagingArguments, context); + return (IQueryDataLoader>)dataLoader.Branch(branchKey, CreateBranch, state); - static IDataLoader CreatePagingDataLoader( + static IDataLoader CreateBranch( string branchKey, - IDataLoader> root, - PagingArguments pagingArguments) + IDataLoader> dataLoader, + PagingState state) { - var branch = new PagingDataLoader>(root, branchKey); - branch.SetState(pagingArguments); + var branch = new QueryDataLoader>( + (DataLoaderBase>)dataLoader, + branchKey); + + branch.SetState(PagingStateKeys.PagingArgs, state.PagingArgs); + + if (state.Context is not null) + { + if (state.Context.Selector is not null) + { + branch.SetState(DataLoaderStateKeys.Selector, state.Context.Selector); + } + + if (state.Context.Predicate is not null) + { + branch.SetState(DataLoaderStateKeys.Predicate, state.Context.Predicate); + } + + if (state.Context.Sorting is not null) + { + branch.SetState(DataLoaderStateKeys.Sorting, state.Context.Sorting); + } + } + return branch; } } @@ -84,11 +136,8 @@ static IDataLoader CreatePagingDataLoader( /// /// Throws if the is null. /// -#if NET8_0_OR_GREATER - [Experimental(Experiments.Selectors)] -#endif - public static IPagingDataLoader> Select( - this IPagingDataLoader> dataLoader, + public static IDataLoader> Select( + this IDataLoader> dataLoader, Expression>? selector) where TKey : notnull { @@ -102,30 +151,97 @@ public static IPagingDataLoader> Select>)dataLoader.Branch(branchKey, CreateBranch, selector); + var branchKey = selector.ComputeHash(); + var state = new QueryState(DataLoaderStateKeys.Selector, new DefaultSelectorBuilder(selector)); + return (IQueryDataLoader>)dataLoader.Branch(branchKey, DataLoaderStateHelper.CreateBranch, state); + } - static IDataLoader CreateBranch( - string key, - IDataLoader> dataLoader, - Expression> selector) + /// + /// Adds a predicate as state to the DataLoader. + /// + /// + /// The DataLoader. + /// + /// + /// The predicate that shall be added as state to the DataLoader. + /// + /// + /// The key type of the DataLoader. + /// + /// + /// The value type of the DataLoader. + /// + /// + /// Returns the DataLoader with the added projection. + /// + /// + /// Throws if the is null. + /// + public static IDataLoader> Where( + this IDataLoader> dataLoader, + Expression>? predicate) + where TKey : notnull + { + if (dataLoader is null) { - var branch = new PagingDataLoader>(dataLoader, key); - var context = new DefaultSelectorBuilder(); - branch.ContextData = branch.ContextData.SetItem(typeof(ISelectorBuilder).FullName!, context); - context.Add(selector); - return branch; + throw new ArgumentNullException(nameof(dataLoader)); } + + if (predicate is null) + { + return dataLoader; + } + + var branchKey = predicate.ComputeHash(); + var state = new QueryState(DataLoaderStateKeys.Predicate, GetOrCreateBuilder(dataLoader.ContextData, predicate)); + return (IQueryDataLoader>)dataLoader.Branch(branchKey, DataLoaderStateHelper.CreateBranch, state); } - private static string CreateBranchKey( + public static IDataLoader> Order( + this IDataLoader> dataLoader, + SortDefinition? sortDefinition) + where TKey : notnull + { + if (dataLoader is null) + { + throw new ArgumentNullException(nameof(dataLoader)); + } + + if (sortDefinition is null) + { + return dataLoader; + } + + var branchKey = sortDefinition.ComputeHash(); + var state = new QueryState(DataLoaderStateKeys.Sorting, sortDefinition); + return (IQueryDataLoader>)dataLoader.Branch(branchKey, DataLoaderStateHelper.CreateBranch, state); + } + + private static string ComputeHash(this PagingArguments arguments, QueryContext? context = null) + { + var hasher = ExpressionHasherPool.Shared.Get(); + + hasher.Add(arguments); + + if (context is not null) + { + hasher.Add(context); + } + + var s = hasher.Compute(); + ExpressionHasherPool.Shared.Return(hasher); + return s; + } + + private static void Add( + this ExpressionHasher hasher, PagingArguments pagingArguments) { var requiredBufferSize = 1; @@ -147,7 +263,8 @@ private static string CreateBranchKey( if (requiredBufferSize == 1) { - return "-"; + hasher.Add('-'); + return; } char[]? rentedBuffer = null; @@ -212,14 +329,12 @@ private static string CreateBranchKey( written += before.Length; } - var branchKey = new string(buffer.Slice(0, written)); + hasher.Add(buffer.Slice(0, written)); if (rentedBuffer != null) { ArrayPool.Shared.Return(rentedBuffer); } - - return branchKey; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -245,4 +360,6 @@ private static int EstimateIntLength(int? value) return length + 2; } + + internal readonly record struct PagingState(PagingArguments PagingArgs, QueryContext? Context = null); } diff --git a/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderFetchContext.cs b/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderFetchContext.cs index 659504e95fb..c1f7cf6099b 100644 --- a/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderFetchContext.cs +++ b/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderFetchContext.cs @@ -1,5 +1,6 @@ using HotChocolate.Pagination; +// ReSharper disable once CheckNamespace namespace GreenDonut; /// @@ -21,5 +22,5 @@ public static class HotChocolatePaginationBatchingDataLoaderFetchContext /// public static PagingArguments GetPagingArguments( this DataLoaderFetchContext context) - => context.GetRequiredState(); + => context.GetRequiredState(PagingStateKeys.PagingArgs); } diff --git a/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderPredicateExtensions.cs b/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderPredicateExtensions.cs deleted file mode 100644 index 1cd6c8202df..00000000000 --- a/src/HotChocolate/Pagination/src/Pagination.Batching/Extensions/HotChocolatePaginationBatchingDataLoaderPredicateExtensions.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Linq.Expressions; -using HotChocolate.Pagination; - -// ReSharper disable once CheckNamespace -namespace GreenDonut.Predicates; - -/// -/// Provides extension methods to pass a pagination context to a DataLoader. -/// -public static class HotChocolatePaginationBatchingDataLoaderPredicateExtensions -{ - /// - /// Adds a predicate as state to the DataLoader. - /// - /// - /// The DataLoader. - /// - /// - /// The predicate that shall be added as state to the DataLoader. - /// - /// - /// The key type of the DataLoader. - /// - /// - /// The value type of the DataLoader. - /// - /// - /// Returns the DataLoader with the added projection. - /// - /// - /// Throws if the is null. - /// -#if NET8_0_OR_GREATER - [Experimental(Experiments.Predicates)] -#endif - public static IPagingDataLoader> Where( - this IPagingDataLoader> dataLoader, - Expression>? predicate) - where TKey : notnull - { - if (dataLoader is null) - { - throw new ArgumentNullException(nameof(dataLoader)); - } - - if (predicate is null) - { - return dataLoader; - } - - if (dataLoader.ContextData.TryGetValue(typeof(IPredicateBuilder).FullName!, out var value)) - { - var context = (DefaultPredicateBuilder)value!; - context.Add(predicate); - return dataLoader; - } - - var branchKey = predicate.ToString(); - return (IPagingDataLoader>)dataLoader.Branch( - branchKey, - CreateBranch, - predicate); - - static IDataLoader CreateBranch( - string key, - IDataLoader> dataLoader, - Expression> predicate) - { - var branch = new PagingDataLoader>(dataLoader, key); - var context = new DefaultPredicateBuilder(); - branch.ContextData = branch.ContextData.SetItem(typeof(IPredicateBuilder).FullName!, context); - context.Add(predicate); - return branch; - } - } -} diff --git a/src/HotChocolate/Pagination/src/Pagination.Batching/IPagingDataLoader.cs b/src/HotChocolate/Pagination/src/Pagination.Batching/IPagingDataLoader.cs deleted file mode 100644 index 67c50d80053..00000000000 --- a/src/HotChocolate/Pagination/src/Pagination.Batching/IPagingDataLoader.cs +++ /dev/null @@ -1,28 +0,0 @@ -using GreenDonut; - -namespace HotChocolate.Pagination; - -/// -/// A paging DataLoader is a version of a DataLoader that -/// branches per paging arguments. -/// -/// -/// The type of the key. -/// -/// -/// The type of the value. -/// -public interface IPagingDataLoader - : IDataLoader - where TKey : notnull -{ - /// - /// Gets the root DataLoader instance from which this instance was branched off. - /// - IDataLoader Root { get; } - - /// - /// Gets the paging arguments for this DataLoader. - /// - PagingArguments PagingArguments { get; } -} diff --git a/src/HotChocolate/Pagination/src/Pagination.Batching/PagingDataLoader.cs b/src/HotChocolate/Pagination/src/Pagination.Batching/PagingDataLoader.cs deleted file mode 100644 index 9b31c7ef6d4..00000000000 --- a/src/HotChocolate/Pagination/src/Pagination.Batching/PagingDataLoader.cs +++ /dev/null @@ -1,64 +0,0 @@ -using GreenDonut; -using GreenDonut.Internals; - -namespace HotChocolate.Pagination; - -internal sealed class PagingDataLoader - : DataLoaderBase - , IPagingDataLoader - where TKey : notnull -{ - private readonly DataLoaderBase _root; - - public PagingDataLoader( - IDataLoader root, - string pagingKey) - : this(GetRoot(root), pagingKey) - { - } - - private PagingDataLoader( - DataLoaderBase root, - string pagingKey) - : base(DataLoaderHelper.GetBatchScheduler(root), DataLoaderHelper.GetOptions(root)) - { - _root = root; - CacheKeyType = $"{DataLoaderHelper.GetCacheKeyType(root)}:{pagingKey}"; - ContextData = root.ContextData; - } - - public IDataLoader Root => _root; - - public PagingArguments PagingArguments - => (PagingArguments)ContextData[typeof(PagingArguments).FullName!]!; - - protected internal override string CacheKeyType { get; } - - protected override bool AllowCachePropagation => false; - - protected override bool AllowBranching => true; - - protected internal override ValueTask FetchAsync( - IReadOnlyList keys, - Memory> results, - DataLoaderFetchContext context, - CancellationToken cancellationToken) - => DataLoaderHelper.FetchAsync(_root, keys, results, context, cancellationToken); - - private static DataLoaderBase GetRoot(IDataLoader root) - { - if(root is DataLoaderBase baseRoot) - { - return baseRoot; - } - - if(root is PagingDataLoader pagingRoot) - { - return pagingRoot._root; - } - - throw new ArgumentException( - "The root data loader must be a DataLoaderBase.", - nameof(root)); - } -} diff --git a/src/HotChocolate/Pagination/src/Pagination.Batching/PagingStateKeys.cs b/src/HotChocolate/Pagination/src/Pagination.Batching/PagingStateKeys.cs new file mode 100644 index 00000000000..6e960696a03 --- /dev/null +++ b/src/HotChocolate/Pagination/src/Pagination.Batching/PagingStateKeys.cs @@ -0,0 +1,6 @@ +namespace HotChocolate.Pagination; + +internal static class PagingStateKeys +{ + public const string PagingArgs = "HotChocolate.Pagination.PagingArgs"; +} diff --git a/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs index 91b9265b49a..41992b47c31 100644 --- a/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs +++ b/src/HotChocolate/Pagination/src/Pagination.EntityFramework/Extensions/PagingQueryableExtensions.cs @@ -448,9 +448,11 @@ private static Expression, CountResult>> Ge var bindings = new List { - Expression.Bind(typeof(CountResult).GetProperty(nameof(CountResult.Key))!, + Expression.Bind( + typeof(CountResult).GetProperty(nameof(CountResult.Key))!, keyProperty), - Expression.Bind(typeof(CountResult).GetProperty(nameof(CountResult.Count))!, + Expression.Bind( + typeof(CountResult).GetProperty(nameof(CountResult.Count))!, countCall) }; diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/InterfaceIntegrationTests.cs b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/InterfaceIntegrationTests.cs index aa9efb8aebd..1e2bd70a0db 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/InterfaceIntegrationTests.cs +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/InterfaceIntegrationTests.cs @@ -1,7 +1,7 @@ using System.Collections.Immutable; using CookieCrumble; using GreenDonut; -using GreenDonut.Selectors; +using GreenDonut.Data; using HotChocolate.Data.TestContext; using HotChocolate.Execution; using HotChocolate.Execution.Processing; @@ -378,7 +378,7 @@ public static async Task> GetPetsAsync( ISelection selection, CancellationToken cancellationToken) => await animalsByOwner - .WithPagingArguments(pagingArgs) + .With(pagingArgs) .Select(selection) .LoadAsync(owner.Id, cancellationToken) .ToConnectionAsync(); @@ -396,7 +396,7 @@ public static async Task> GetPetsAsync( ISelection selection, CancellationToken cancellationToken) => await animalsByOwner - .WithPagingArguments(pagingArgs) + .With(pagingArgs) .Select(selection) .LoadAsync(owner.Id, cancellationToken) .ToConnectionAsync(); diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs index bf0ef464d97..214bfad2f27 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs @@ -2,7 +2,7 @@ using HotChocolate.Data.TestContext; using CookieCrumble; using GreenDonut; -using GreenDonut.Selectors; +using GreenDonut.Data; using HotChocolate.Execution; using HotChocolate.Execution.Processing; using HotChocolate.Types; @@ -1451,10 +1451,9 @@ public static async Task> GetProducts( [Parent] Brand brand, ProductsByBrandDataLoader dataLoader, PagingArguments arguments, - IResolverContext context, CancellationToken cancellationToken) => await dataLoader - .WithPagingArguments(arguments) + .With(arguments) .LoadAsync(brand.Id, cancellationToken) .ToConnectionAsync(); } @@ -1470,7 +1469,7 @@ public static async Task> GetProducts( PagingArguments arguments, CancellationToken cancellationToken) => await dataLoader - .WithPagingArguments(arguments) + .With(arguments) .Select(selection) .LoadAsync(brand.Id, cancellationToken) .ToConnectionAsync(); diff --git a/src/HotChocolate/Primitives/HotChocolate.Primitives.sln b/src/HotChocolate/Primitives/HotChocolate.Primitives.sln index 8350cdae72b..bfd5242bee2 100644 --- a/src/HotChocolate/Primitives/HotChocolate.Primitives.sln +++ b/src/HotChocolate/Primitives/HotChocolate.Primitives.sln @@ -11,6 +11,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{B598E71D-7 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Primitives.Tests", "test\Primitives.Tests\HotChocolate.Primitives.Tests.csproj", "{F7643027-2CEF-4F06-8DFA-46FFA8C17C4F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Data.Primitives", "src\Primitives.Data\HotChocolate.Data.Primitives.csproj", "{A4522BC2-46E0-4242-A57B-CB4474B3F7C6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -28,9 +30,14 @@ Global {F7643027-2CEF-4F06-8DFA-46FFA8C17C4F}.Debug|Any CPU.Build.0 = Debug|Any CPU {F7643027-2CEF-4F06-8DFA-46FFA8C17C4F}.Release|Any CPU.ActiveCfg = Release|Any CPU {F7643027-2CEF-4F06-8DFA-46FFA8C17C4F}.Release|Any CPU.Build.0 = Release|Any CPU + {A4522BC2-46E0-4242-A57B-CB4474B3F7C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A4522BC2-46E0-4242-A57B-CB4474B3F7C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A4522BC2-46E0-4242-A57B-CB4474B3F7C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A4522BC2-46E0-4242-A57B-CB4474B3F7C6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {F5CEC44C-8AB9-4639-9B38-4F523E3ABF2A} = {42689032-4007-47A4-B045-12D2681BF2CF} {F7643027-2CEF-4F06-8DFA-46FFA8C17C4F} = {B598E71D-74BD-4055-A2F7-10B7DB892ECB} + {A4522BC2-46E0-4242-A57B-CB4474B3F7C6} = {42689032-4007-47A4-B045-12D2681BF2CF} EndGlobalSection EndGlobal diff --git a/src/HotChocolate/Primitives/src/Primitives/HotChocolate.Primitives.csproj b/src/HotChocolate/Primitives/src/Primitives/HotChocolate.Primitives.csproj index 2b4122fde8a..0d3b92ddc23 100644 --- a/src/HotChocolate/Primitives/src/Primitives/HotChocolate.Primitives.csproj +++ b/src/HotChocolate/Primitives/src/Primitives/HotChocolate.Primitives.csproj @@ -21,11 +21,6 @@ True AbstractionResources.resx - - True - True - PrimitivesResources.resx - diff --git a/src/HotChocolate/Primitives/test/Primitives.Tests/HotChocolate.Primitives.Tests.csproj b/src/HotChocolate/Primitives/test/Primitives.Tests/HotChocolate.Primitives.Tests.csproj index 253e466d1b5..8fcf314b8d0 100644 --- a/src/HotChocolate/Primitives/test/Primitives.Tests/HotChocolate.Primitives.Tests.csproj +++ b/src/HotChocolate/Primitives/test/Primitives.Tests/HotChocolate.Primitives.Tests.csproj @@ -2,7 +2,7 @@ HotChocolate.Primitives.Tests - HotChocolate.Primitives + HotChocolate diff --git a/src/HotChocolate/Primitives/test/Primitives.Tests/NameUtilsTests.cs b/src/HotChocolate/Primitives/test/Primitives.Tests/NameUtilsTests.cs index 7a27503f41d..5596f931ade 100644 --- a/src/HotChocolate/Primitives/test/Primitives.Tests/NameUtilsTests.cs +++ b/src/HotChocolate/Primitives/test/Primitives.Tests/NameUtilsTests.cs @@ -1,6 +1,6 @@ using HotChocolate.Utilities; -namespace HotChocolate.Primitives; +namespace HotChocolate; public class NameUtilsTests { From b2ae9252621a55f95a6aa357b02941926a84d9ef Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 29 Jan 2025 00:06:45 +0000 Subject: [PATCH 152/154] Updated Dev Container --- .devcontainer/dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.devcontainer/dockerfile b/.devcontainer/dockerfile index 52ac0fff18f..32ddc5aee69 100644 --- a/.devcontainer/dockerfile +++ b/.devcontainer/dockerfile @@ -30,6 +30,7 @@ RUN wget https://dot.net/v1/dotnet-install.sh -O dotnet-install.sh \ && ./dotnet-install.sh --channel 6.0 --install-dir /usr/share/dotnet \ && ./dotnet-install.sh --channel 7.0 --install-dir /usr/share/dotnet \ && ./dotnet-install.sh --channel 8.0 --install-dir /usr/share/dotnet \ + && ./dotnet-install.sh --channel 9.0 --install-dir /usr/share/dotnet \ && ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet # Install Node.js LTS @@ -55,4 +56,4 @@ RUN curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | # Clean up RUN rm dotnet-install.sh \ && apt-get clean \ - && rm -rf /var/lib/apt/lists/* \ No newline at end of file + && rm -rf /var/lib/apt/lists/* From b55d403355e62e77fd1a7ea0a843a463a637cb59 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 29 Jan 2025 00:48:43 +0000 Subject: [PATCH 153/154] Fixed Compile Issues --- .../src/Core/CreateDataLoaderBranch.cs | 2 - .../src/Core/Data/ExpressionHasher.cs | 48 +++++++++++++++++++ src/GreenDonut/src/Core/Data/QueryState.cs | 13 ++++- src/GreenDonut/src/Core/DataLoaderBase.cs | 6 +-- src/GreenDonut/src/Core/ExpressionHelpers.cs | 2 - src/GreenDonut/src/Core/IDataLoader.cs | 5 -- .../GreenDonut.Data.Primitives.csproj | 4 ++ .../src/Data.Primitives/QueryContext.cs | 33 +++++++++++++ src/GreenDonut/src/Data.Primitives/SortBy.cs | 4 +- .../src/Data.Primitives/SortDefinition.cs | 8 ++-- ...tChocolateExecutionDataLoaderExtensions.cs | 2 + .../Data/test/Data.Tests/IntegrationTests.cs | 1 + .../Primitives/test/Directory.Build.props | 5 -- .../Utilities/HotChocolate.Utilities.csproj | 1 + 14 files changed, 108 insertions(+), 26 deletions(-) diff --git a/src/GreenDonut/src/Core/CreateDataLoaderBranch.cs b/src/GreenDonut/src/Core/CreateDataLoaderBranch.cs index 6dd69ed5177..940688de38c 100644 --- a/src/GreenDonut/src/Core/CreateDataLoaderBranch.cs +++ b/src/GreenDonut/src/Core/CreateDataLoaderBranch.cs @@ -1,4 +1,3 @@ -#if NET6_0_OR_GREATER namespace GreenDonut; /// @@ -18,4 +17,3 @@ public delegate IDataLoader CreateDataLoaderBranch( IDataLoader dataLoader, TState state) where TKey : notnull; -#endif diff --git a/src/GreenDonut/src/Core/Data/ExpressionHasher.cs b/src/GreenDonut/src/Core/Data/ExpressionHasher.cs index b633cf9e98a..52c65f30d3c 100644 --- a/src/GreenDonut/src/Core/Data/ExpressionHasher.cs +++ b/src/GreenDonut/src/Core/Data/ExpressionHasher.cs @@ -72,8 +72,14 @@ public ExpressionHasher Add(ReadOnlySpan span) public string Compute() { +#if NET8_0_OR_GREATER var hashBytes = MD5.HashData(_buffer.AsSpan().Slice(0, _start)); var hashString = Convert.ToHexString(hashBytes).ToLowerInvariant(); +#else + var md5 = MD5.Create(); + var hashBytes = md5.ComputeHash(_buffer, 0, _start); + var hashString = BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant(); +#endif _buffer.AsSpan().Slice(0, _start).Clear(); @@ -281,6 +287,7 @@ private void Append(char a, char b) _buffer[_start++] = (byte)b; } +#if NET8_0_OR_GREATER private void Append(string s) { var span = _buffer.AsSpan().Slice(_start); @@ -316,6 +323,47 @@ private void Append(ReadOnlySpan s) _start += written; } +#else + private void Append(string s) + { + byte[]? utf8Bytes = null; + + try + { + utf8Bytes = Encoding.UTF8.GetBytes(s); + EnsureBufferCapacity(utf8Bytes.Length); + + Buffer.BlockCopy(utf8Bytes, 0, _buffer, _start, utf8Bytes.Length); + _start += utf8Bytes.Length; + } + finally + { + if (utf8Bytes != null) + { + ArrayPool.Shared.Return(utf8Bytes); + } + } + } + + private void Append(ReadOnlySpan s) + { + // Converting ReadOnlySpan to string for .NET Standard 2.0 compatibility + var str = new string(s.ToArray()); + Append(str); + } + + private void EnsureBufferCapacity(int requiredCapacity) + { + while (_buffer.Length - _start < requiredCapacity) + { + var newBuffer = ArrayPool.Shared.Rent(_buffer.Length * 2); + Buffer.BlockCopy(_buffer, 0, newBuffer, 0, _start); + ArrayPool.Shared.Return(_buffer); + _buffer = newBuffer; + } + } +#endif + private void Append(byte b) { if (_start == _buffer.Length) diff --git a/src/GreenDonut/src/Core/Data/QueryState.cs b/src/GreenDonut/src/Core/Data/QueryState.cs index be0db5fc53c..350aaf09fdb 100644 --- a/src/GreenDonut/src/Core/Data/QueryState.cs +++ b/src/GreenDonut/src/Core/Data/QueryState.cs @@ -1,3 +1,14 @@ namespace GreenDonut.Data; -internal readonly record struct QueryState(string Key, object Value); +internal readonly struct QueryState +{ + public QueryState(string key, object value) + { + Key = key; + Value = value; + } + + public string Key { get; } + + public object Value { get; } +} diff --git a/src/GreenDonut/src/Core/DataLoaderBase.cs b/src/GreenDonut/src/Core/DataLoaderBase.cs index 7d8b452a0ef..24624e41094 100644 --- a/src/GreenDonut/src/Core/DataLoaderBase.cs +++ b/src/GreenDonut/src/Core/DataLoaderBase.cs @@ -29,10 +29,8 @@ public abstract partial class DataLoaderBase private readonly IBatchScheduler _batchScheduler; private readonly int _maxBatchSize; private readonly IDataLoaderDiagnosticEvents _diagnosticEvents; -#if NET6_0_OR_GREATER private ImmutableDictionary _branches = ImmutableDictionary.Empty; -#endif private Batch? _currentBatch; /// @@ -228,7 +226,7 @@ void Initialize() ct); } } - + /// public void Remove(TKey key) { @@ -263,7 +261,6 @@ public void Set(TKey key, Task value) Cache.TryAdd(cacheKey, new Promise(value)); } } -#if NET6_0_OR_GREATER /// public IDataLoader Branch( @@ -302,7 +299,6 @@ public IDataLoader Branch( return branch; } -#endif private void BatchOperationFailed( Batch batch, diff --git a/src/GreenDonut/src/Core/ExpressionHelpers.cs b/src/GreenDonut/src/Core/ExpressionHelpers.cs index 69263ce02c3..4893e6f0f96 100644 --- a/src/GreenDonut/src/Core/ExpressionHelpers.cs +++ b/src/GreenDonut/src/Core/ExpressionHelpers.cs @@ -1,4 +1,3 @@ -#if NET6_0_OR_GREATER using System.Linq.Expressions; using System.Reflection; @@ -240,4 +239,3 @@ protected override Expression VisitParameter(ParameterExpression node) => node == toReplace ? replacement : base.VisitParameter(node); } } -#endif diff --git a/src/GreenDonut/src/Core/IDataLoader.cs b/src/GreenDonut/src/Core/IDataLoader.cs index 4cd5538b00a..58fb31b8b02 100644 --- a/src/GreenDonut/src/Core/IDataLoader.cs +++ b/src/GreenDonut/src/Core/IDataLoader.cs @@ -1,7 +1,4 @@ using System.Collections.Immutable; -#if NET8_0_OR_GREATER -using GreenDonut.Selectors; -#endif namespace GreenDonut; @@ -165,7 +162,6 @@ public interface IDataLoader /// void Set(TKey key, Task value); -#if NET6_0_OR_GREATER /// /// Branches the current DataLoader. /// @@ -185,5 +181,4 @@ IDataLoader Branch( string key, CreateDataLoaderBranch createBranch, TState state); -#endif } diff --git a/src/GreenDonut/src/Data.Primitives/GreenDonut.Data.Primitives.csproj b/src/GreenDonut/src/Data.Primitives/GreenDonut.Data.Primitives.csproj index 346b4c28d6c..a3fb68f8df5 100644 --- a/src/GreenDonut/src/Data.Primitives/GreenDonut.Data.Primitives.csproj +++ b/src/GreenDonut/src/Data.Primitives/GreenDonut.Data.Primitives.csproj @@ -7,4 +7,8 @@ This package contains the basic building blocks of the DataLoader linq query integration. + + + + diff --git a/src/GreenDonut/src/Data.Primitives/QueryContext.cs b/src/GreenDonut/src/Data.Primitives/QueryContext.cs index dd4821c9e82..1a1e4808e5b 100644 --- a/src/GreenDonut/src/Data.Primitives/QueryContext.cs +++ b/src/GreenDonut/src/Data.Primitives/QueryContext.cs @@ -2,6 +2,7 @@ namespace GreenDonut.Data; +#if NET6_0_OR_GREATER /// /// Represents the context for constructing queries against a data source for . /// This record contains the projection (selector), filtering (predicate), and sorting instructions @@ -29,3 +30,35 @@ public record QueryContext( /// public static QueryContext Empty { get; } = new(); } +#else +/// +/// Represents the context for constructing queries against a data source for . +/// This record contains the projection (selector), filtering (predicate), and sorting instructions +/// () for building a query. +/// +public class QueryContext( + Expression>? selector = null, + Expression>? predicate = null, + SortDefinition? sorting = null) +{ + /// + /// An expression that defines what data shall be selected from . + /// + public Expression>? Selector { get; } = selector; + + /// + /// An expression that defines the filtering condition for . + /// + public Expression>? Predicate { get; } = predicate; + + /// + /// The sorting instructions (see ) for . + /// + public SortDefinition? Sorting { get; } = sorting; + + /// + /// An empty query context. + /// + public static QueryContext Empty { get; } = new(); +} +#endif diff --git a/src/GreenDonut/src/Data.Primitives/SortBy.cs b/src/GreenDonut/src/Data.Primitives/SortBy.cs index b61f478b73d..a51e60a3735 100644 --- a/src/GreenDonut/src/Data.Primitives/SortBy.cs +++ b/src/GreenDonut/src/Data.Primitives/SortBy.cs @@ -28,14 +28,14 @@ public SortBy(Expression> keySelector, bool ascending = tr /// /// Gets the field on which the sort operation is applied. /// - public Expression> KeySelector { get; init; } + public Expression> KeySelector { get; } LambdaExpression ISortBy.KeySelector => KeySelector; /// /// Gets the sort direction. /// - public bool Ascending { get => field; init; } + public bool Ascending { get; } /// /// Applies the sort operation to the queryable. diff --git a/src/GreenDonut/src/Data.Primitives/SortDefinition.cs b/src/GreenDonut/src/Data.Primitives/SortDefinition.cs index 4a061782811..9077f79678c 100644 --- a/src/GreenDonut/src/Data.Primitives/SortDefinition.cs +++ b/src/GreenDonut/src/Data.Primitives/SortDefinition.cs @@ -9,7 +9,7 @@ namespace GreenDonut.Data; /// /// The entity type on which the sort operations are applied. /// -public sealed record SortDefinition +public sealed class SortDefinition { /// /// Initializes a new instance of . @@ -19,7 +19,7 @@ public sealed record SortDefinition /// public SortDefinition(params ISortBy[] operations) { - Operations = [..operations]; + Operations = ImmutableArray.Create(operations); } /// @@ -30,13 +30,13 @@ public SortDefinition(params ISortBy[] operations) /// public SortDefinition(IEnumerable> operations) { - Operations = [..operations]; + Operations = ImmutableArray.CreateRange(operations); } /// /// The sort operations. /// - public ImmutableArray> Operations { get; init; } + public ImmutableArray> Operations { get; } /// /// Deconstructs the sort operations. diff --git a/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs b/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs index 9b501ee9f5c..56f41e53077 100644 --- a/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs +++ b/src/HotChocolate/Core/src/Execution/Projections/HotChocolateExecutionDataLoaderExtensions.cs @@ -1,3 +1,4 @@ +#if NET6_0_OR_GREATER using HotChocolate.Execution.Processing; using HotChocolate.Pagination; @@ -139,3 +140,4 @@ public static IDataLoader> Select( return dataLoader.Select(expression); } } +#endif diff --git a/src/HotChocolate/Data/test/Data.Tests/IntegrationTests.cs b/src/HotChocolate/Data/test/Data.Tests/IntegrationTests.cs index 6c3e91b55e3..f19a5ca365b 100644 --- a/src/HotChocolate/Data/test/Data.Tests/IntegrationTests.cs +++ b/src/HotChocolate/Data/test/Data.Tests/IntegrationTests.cs @@ -4,6 +4,7 @@ // ReSharper disable MemberCanBePrivate.Global // ReSharper disable MoveLocalFunctionAfterJumpStatement +using CookieCrumble; using GreenDonut.Data; using HotChocolate.Data.Filters; using HotChocolate.Data.Sorting; diff --git a/src/HotChocolate/Primitives/test/Directory.Build.props b/src/HotChocolate/Primitives/test/Directory.Build.props index 8be6d5b6c1e..59f7f79cf82 100644 --- a/src/HotChocolate/Primitives/test/Directory.Build.props +++ b/src/HotChocolate/Primitives/test/Directory.Build.props @@ -10,11 +10,6 @@ - - - - - diff --git a/src/HotChocolate/Utilities/src/Utilities/HotChocolate.Utilities.csproj b/src/HotChocolate/Utilities/src/Utilities/HotChocolate.Utilities.csproj index b4db534551f..27f60db66ba 100644 --- a/src/HotChocolate/Utilities/src/Utilities/HotChocolate.Utilities.csproj +++ b/src/HotChocolate/Utilities/src/Utilities/HotChocolate.Utilities.csproj @@ -39,6 +39,7 @@ + From 6b01035be16149d9da2df5010369b35f948c9ee5 Mon Sep 17 00:00:00 2001 From: Erik O'Leary <969938+onionhammer@users.noreply.github.com> Date: Tue, 28 Jan 2025 17:43:37 -0600 Subject: [PATCH 154/154] Fixed CursorParser to use '\' as escape instead of ':' (#7962) Co-authored-by: Michael Staib --- .../DataLoaderTests.cs | 2 +- .../DataLoaderTests.Filter_With_Filtering.md | 4 +- .../Expressions/CursorParser.cs | 2 +- .../CursorKeySerializerHelper.cs | 2 +- .../CursorFormatterTests.cs | 121 +++++++++++ .../CursorKeySerializerHelperTests.cs | 16 ++ .../PagingHelperIntegrationTests.cs | 4 +- ...grationPagingHelperTests.GetDefaultPage.md | 24 +-- ...rationPagingHelperTests.GetDefaultPage2.md | 24 +-- ...ingHelperTests.GetDefaultPage_With_Deep.md | 20 +- ...sts.GetDefaultPage_With_Deep_SecondPage.md | 4 +- ...elperTests.GetDefaultPage_With_Nullable.md | 44 ++-- ...s.GetDefaultPage_With_Nullable_Fallback.md | 44 ++-- ...tPage_With_Nullable_Fallback_SecondPage.md | 14 +- ...GetDefaultPage_With_Nullable_SecondPage.md | 12 +- ...gHelperTests.GetSecondPage_With_2_Items.md | 8 +- ...erTests.Map_Page_To_Connection_With_Dto.md | 8 +- ...Tests.Map_Page_To_Connection_With_Dto_2.md | 8 +- ...PagingHelperTests.Nested_Paging_First_2.md | 4 +- ...elperTests.Nested_Paging_First_2_NET9_0.md | 4 +- ....Nested_Paging_First_2_With_Projections.md | 4 +- ..._Paging_First_2_With_Projections_NET9_0.md | 4 +- ...gingHelperTests.Paging_Empty_PagingArgs.md | 204 +++++++++--------- ...grationPagingHelperTests.Paging_First_5.md | 14 +- ...gHelperTests.Paging_First_5_After_Id_13.md | 14 +- ...HelperTests.Paging_First_5_Before_Id_96.md | 40 ++-- ...egrationPagingHelperTests.Paging_Last_5.md | 14 +- 27 files changed, 400 insertions(+), 263 deletions(-) create mode 100644 src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/CursorFormatterTests.cs diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/DataLoaderTests.cs b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/DataLoaderTests.cs index 60e1d6165ab..35c7eb5612f 100644 --- a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/DataLoaderTests.cs +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/DataLoaderTests.cs @@ -288,7 +288,7 @@ file static class Extensions { public static Snapshot AddSql(this Snapshot snapshot, List queries) { - snapshot.Add(string.Join("\n", queries), "SQL"); + snapshot.Add(string.Join("\n", queries).Replace("@__p_1_startswith", "@__p_1_rewritten"), "SQL"); return snapshot; } diff --git a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Filtering.md b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Filtering.md index 22aa5e794f8..6f50e2049d2 100644 --- a/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Filtering.md +++ b/src/HotChocolate/Data/test/Data.Filters.SqlServer.Tests/__snapshots__/DataLoaderTests.Filter_With_Filtering.md @@ -4,14 +4,14 @@ ```text .param set @__keys_0 '[1]' -.param set @__p_1_startswith 'Product%' +.param set @__p_1_rewritten 'Product%' SELECT "p"."Id", "p"."AvailableStock", "p"."BrandId", "p"."Description", "p"."ImageFileName", "p"."MaxStockThreshold", "p"."Name", "p"."OnReorder", "p"."Price", "p"."RestockThreshold", "p"."TypeId" FROM "Products" AS "p" WHERE "p"."BrandId" IN ( SELECT "k"."value" FROM json_each(@__keys_0) AS "k" -) AND "p"."Name" LIKE @__p_1_startswith ESCAPE '\' +) AND "p"."Name" LIKE @__p_1_rewritten ESCAPE '\' ``` ## Result diff --git a/src/HotChocolate/Pagination/src/Pagination.Core/Expressions/CursorParser.cs b/src/HotChocolate/Pagination/src/Pagination.Core/Expressions/CursorParser.cs index c98398bbec5..11ad2612de8 100644 --- a/src/HotChocolate/Pagination/src/Pagination.Core/Expressions/CursorParser.cs +++ b/src/HotChocolate/Pagination/src/Pagination.Core/Expressions/CursorParser.cs @@ -9,7 +9,7 @@ namespace HotChocolate.Pagination.Expressions; /// public static class CursorParser { - private const byte _escape = (byte)':'; + private const byte _escape = (byte)'\\'; private const byte _separator = (byte)':'; /// diff --git a/src/HotChocolate/Pagination/src/Pagination.Core/Serialization/CursorKeySerializerHelper.cs b/src/HotChocolate/Pagination/src/Pagination.Core/Serialization/CursorKeySerializerHelper.cs index 9368481b6fb..f684c36a5a3 100644 --- a/src/HotChocolate/Pagination/src/Pagination.Core/Serialization/CursorKeySerializerHelper.cs +++ b/src/HotChocolate/Pagination/src/Pagination.Core/Serialization/CursorKeySerializerHelper.cs @@ -73,7 +73,7 @@ public static class CursorKeySerializerHelper public static bool TryFormat(object? key, ICursorKeySerializer serializer, Span buffer, out int written) { - var success = false; + bool success; if (key is null) { diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/CursorFormatterTests.cs b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/CursorFormatterTests.cs new file mode 100644 index 00000000000..58b22b574a6 --- /dev/null +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/CursorFormatterTests.cs @@ -0,0 +1,121 @@ +using System.Linq.Expressions; +using System.Text; +using System.Text.Unicode; +using HotChocolate.Pagination.Expressions; +using HotChocolate.Pagination.Serialization; + +namespace HotChocolate.Data; + +public class CursorFormatterTests +{ + [Fact] + public void Format_Single_Key() + { + // arrange + var entity = new MyClass { Name = "test" }; + Expression> selector = x => x.Name; + var serializer = new StringCursorKeySerializer(); + + // act + var result = CursorFormatter.Format(entity, [new CursorKey(selector, serializer)]); + + // assert + Assert.Equal("dGVzdA==", result); + Assert.Equal("test", Encoding.UTF8.GetString(Convert.FromBase64String(result))); + } + + [Fact] + public void Format_Single_Key_With_Colon() + { + // arrange + var entity = new MyClass { Name = "test:test" }; + Expression> selector = x => x.Name; + var serializer = new StringCursorKeySerializer(); + + // act + var result = CursorFormatter.Format(entity, [new CursorKey(selector, serializer)]); + + // assert + Assert.Equal("dGVzdFw6dGVzdA==", result); + Assert.Equal("test\\:test", Encoding.UTF8.GetString(Convert.FromBase64String(result))); + } + + [Fact] + public void Format_Two_Keys() + { + // arrange + var entity = new MyClass { Name = "test", Description = "description" }; + Expression> selector1 = x => x.Name; + Expression> selector2 = x => x.Description; + var serializer = new StringCursorKeySerializer(); + + // act + var result = CursorFormatter.Format( + entity, + [ + new CursorKey(selector1, serializer), + new CursorKey(selector2, serializer) + ]); + + // assert + Assert.Equal("dGVzdDpkZXNjcmlwdGlvbg==", result); + Assert.Equal("test:description", Encoding.UTF8.GetString(Convert.FromBase64String(result))); + } + + [Fact] + public void Format_Two_Keys_With_Colon() + { + // arrange + var entity = new MyClass { Name = "test:345", Description = "description:123" }; + Expression> selector1 = x => x.Name; + Expression> selector2 = x => x.Description; + var serializer = new StringCursorKeySerializer(); + + // act + var result = CursorFormatter.Format( + entity, + [ + new CursorKey(selector1, serializer), + new CursorKey(selector2, serializer) + ]); + + // assert + Assert.Equal("dGVzdFw6MzQ1OmRlc2NyaXB0aW9uXDoxMjM=", result); + Assert.Equal("test\\:345:description\\:123", Encoding.UTF8.GetString(Convert.FromBase64String(result))); + } + + [Fact] + public void Format_And_Parse_Two_Keys_With_Colon() + { + // arrange + var entity = new MyClass { Name = "test:345", Description = "description:123" }; + Expression> selector1 = x => x.Name; + Expression> selector2 = x => x.Description; + var serializer = new StringCursorKeySerializer(); + + // act + var formatted = CursorFormatter.Format( + entity, + [ + new CursorKey(selector1, serializer), + new CursorKey(selector2, serializer) + ]); + var parsed =CursorParser.Parse( + formatted, + [ + new CursorKey(selector1, serializer), + new CursorKey(selector2, serializer) + ]); + + // assert + Assert.Equal("test:345", parsed[0]); + Assert.Equal("description:123", parsed[1]); + } + + public class MyClass + { + public string Name { get; set; } = default!; + + public string? Description { get; set; } = default!; + } +} diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/CursorKeySerializerHelperTests.cs b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/CursorKeySerializerHelperTests.cs index 67060a2bae1..4183c972a83 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/CursorKeySerializerHelperTests.cs +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/CursorKeySerializerHelperTests.cs @@ -141,4 +141,20 @@ public static void TryFormat_BufferTooSmall_ReturnsFalse() Assert.False(success); Assert.Equal(0, written); } + + [Fact] + public static void String_With_Colon_Format_And_Parse() + { + // arrange + object key = "part1:part2"; + Span buffer = new byte[1024]; + + // act + CursorKeySerializerHelper.TryFormat(key, _serializer, buffer, out var written); + var parsedString =CursorKeySerializerHelper.Parse(buffer.Slice(0, written), _serializer); + + + // assert + Assert.Equal(key, parsedString); + } } diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs index 214bfad2f27..3b742a9c91d 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/PagingHelperIntegrationTests.cs @@ -376,7 +376,7 @@ public async Task GetDefaultPage_With_Nullable_Fallback_SecondPage() .SetDocument( """ { - brandsNullableFallback(first: 2, after: "QnJhbmQxMToxMg==") { + brandsNullableFallback(first: 2, after: "QnJhbmRcOjExOjEy") { edges { cursor } @@ -1209,7 +1209,7 @@ private static async Task SeedAsync(string connectionString) { var brand = new Brand { - Name = "Brand" + i, + Name = "Brand:" + i, DisplayName = i % 2 == 0 ? "BrandDisplay" + i : null, BrandDetails = new() { Country = new() { Name = "Country" + i } } }; diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage.md index 1ce01edfd9b..a6bcaa61efc 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage.md @@ -25,50 +25,50 @@ LIMIT @__p_0 "nodes": [ { "id": 1, - "name": "Brand0" + "name": "Brand:0" }, { "id": 2, - "name": "Brand1" + "name": "Brand:1" }, { "id": 11, - "name": "Brand10" + "name": "Brand:10" }, { "id": 12, - "name": "Brand11" + "name": "Brand:11" }, { "id": 13, - "name": "Brand12" + "name": "Brand:12" }, { "id": 14, - "name": "Brand13" + "name": "Brand:13" }, { "id": 15, - "name": "Brand14" + "name": "Brand:14" }, { "id": 16, - "name": "Brand15" + "name": "Brand:15" }, { "id": 17, - "name": "Brand16" + "name": "Brand:16" }, { "id": 18, - "name": "Brand17" + "name": "Brand:17" } ], "pageInfo": { "hasNextPage": true, "hasPreviousPage": false, - "startCursor": "QnJhbmQwOjE=", - "endCursor": "QnJhbmQxNzoxOA==" + "startCursor": "QnJhbmRcOjA6MQ==", + "endCursor": "QnJhbmRcOjE3OjE4" } } } diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage2.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage2.md index fc776f00bc5..88416cf7a4e 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage2.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage2.md @@ -25,50 +25,50 @@ LIMIT @__p_0 "nodes": [ { "id": 1, - "name": "Brand0" + "name": "Brand:0" }, { "id": 2, - "name": "Brand1" + "name": "Brand:1" }, { "id": 11, - "name": "Brand10" + "name": "Brand:10" }, { "id": 12, - "name": "Brand11" + "name": "Brand:11" }, { "id": 13, - "name": "Brand12" + "name": "Brand:12" }, { "id": 14, - "name": "Brand13" + "name": "Brand:13" }, { "id": 15, - "name": "Brand14" + "name": "Brand:14" }, { "id": 16, - "name": "Brand15" + "name": "Brand:15" }, { "id": 17, - "name": "Brand16" + "name": "Brand:16" }, { "id": 18, - "name": "Brand17" + "name": "Brand:17" } ], "pageInfo": { "hasNextPage": true, "hasPreviousPage": false, - "startCursor": "QnJhbmQwOjE=", - "endCursor": "QnJhbmQxNzoxOA==" + "startCursor": "QnJhbmRcOjA6MQ==", + "endCursor": "QnJhbmRcOjE3OjE4" } } } diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep.md index 28b88dcb52f..4ecd855ccaf 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep.md @@ -57,7 +57,7 @@ LIMIT @__p_0 "nodes": [ { "id": 1, - "name": "Brand0", + "name": "Brand:0", "displayName": "BrandDisplay0", "brandDetails": { "country": { @@ -67,7 +67,7 @@ LIMIT @__p_0 }, { "id": 2, - "name": "Brand1", + "name": "Brand:1", "displayName": null, "brandDetails": { "country": { @@ -77,7 +77,7 @@ LIMIT @__p_0 }, { "id": 11, - "name": "Brand10", + "name": "Brand:10", "displayName": "BrandDisplay10", "brandDetails": { "country": { @@ -87,7 +87,7 @@ LIMIT @__p_0 }, { "id": 12, - "name": "Brand11", + "name": "Brand:11", "displayName": null, "brandDetails": { "country": { @@ -97,7 +97,7 @@ LIMIT @__p_0 }, { "id": 13, - "name": "Brand12", + "name": "Brand:12", "displayName": "BrandDisplay12", "brandDetails": { "country": { @@ -107,7 +107,7 @@ LIMIT @__p_0 }, { "id": 14, - "name": "Brand13", + "name": "Brand:13", "displayName": null, "brandDetails": { "country": { @@ -117,7 +117,7 @@ LIMIT @__p_0 }, { "id": 15, - "name": "Brand14", + "name": "Brand:14", "displayName": "BrandDisplay14", "brandDetails": { "country": { @@ -127,7 +127,7 @@ LIMIT @__p_0 }, { "id": 16, - "name": "Brand15", + "name": "Brand:15", "displayName": null, "brandDetails": { "country": { @@ -137,7 +137,7 @@ LIMIT @__p_0 }, { "id": 17, - "name": "Brand16", + "name": "Brand:16", "displayName": "BrandDisplay16", "brandDetails": { "country": { @@ -147,7 +147,7 @@ LIMIT @__p_0 }, { "id": 18, - "name": "Brand17", + "name": "Brand:17", "displayName": null, "brandDetails": { "country": { diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_SecondPage.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_SecondPage.md index a0531d99e0e..a74f98d890f 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_SecondPage.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Deep_SecondPage.md @@ -36,7 +36,7 @@ LIMIT @__p_2 "nodes": [ { "id": 11, - "name": "Brand10", + "name": "Brand:10", "displayName": "BrandDisplay10", "brandDetails": { "country": { @@ -46,7 +46,7 @@ LIMIT @__p_2 }, { "id": 12, - "name": "Brand11", + "name": "Brand:11", "displayName": null, "brandDetails": { "country": { diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable.md index 0945c8540c9..dbab174edfe 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable.md @@ -24,40 +24,40 @@ LIMIT @__p_0 "brandsNullable": { "edges": [ { - "cursor": "QnJhbmQwOlxudWxsOjE=" + "cursor": "QnJhbmRcOjA6XG51bGw6MQ==" }, { - "cursor": "QnJhbmQxOlxudWxsOjI=" + "cursor": "QnJhbmRcOjE6XG51bGw6Mg==" }, { - "cursor": "QnJhbmQxMDpcbnVsbDoxMQ==" + "cursor": "QnJhbmRcOjEwOlxudWxsOjEx" }, { - "cursor": "QnJhbmQxMTpcbnVsbDoxMg==" + "cursor": "QnJhbmRcOjExOlxudWxsOjEy" }, { - "cursor": "QnJhbmQxMjpcbnVsbDoxMw==" + "cursor": "QnJhbmRcOjEyOlxudWxsOjEz" }, { - "cursor": "QnJhbmQxMzpcbnVsbDoxNA==" + "cursor": "QnJhbmRcOjEzOlxudWxsOjE0" }, { - "cursor": "QnJhbmQxNDpcbnVsbDoxNQ==" + "cursor": "QnJhbmRcOjE0OlxudWxsOjE1" }, { - "cursor": "QnJhbmQxNTpcbnVsbDoxNg==" + "cursor": "QnJhbmRcOjE1OlxudWxsOjE2" }, { - "cursor": "QnJhbmQxNjpcbnVsbDoxNw==" + "cursor": "QnJhbmRcOjE2OlxudWxsOjE3" }, { - "cursor": "QnJhbmQxNzpcbnVsbDoxOA==" + "cursor": "QnJhbmRcOjE3OlxudWxsOjE4" } ], "nodes": [ { "id": 1, - "name": "Brand0", + "name": "Brand:0", "displayName": "BrandDisplay0", "brandDetails": { "country": { @@ -67,7 +67,7 @@ LIMIT @__p_0 }, { "id": 2, - "name": "Brand1", + "name": "Brand:1", "displayName": null, "brandDetails": { "country": { @@ -77,7 +77,7 @@ LIMIT @__p_0 }, { "id": 11, - "name": "Brand10", + "name": "Brand:10", "displayName": "BrandDisplay10", "brandDetails": { "country": { @@ -87,7 +87,7 @@ LIMIT @__p_0 }, { "id": 12, - "name": "Brand11", + "name": "Brand:11", "displayName": null, "brandDetails": { "country": { @@ -97,7 +97,7 @@ LIMIT @__p_0 }, { "id": 13, - "name": "Brand12", + "name": "Brand:12", "displayName": "BrandDisplay12", "brandDetails": { "country": { @@ -107,7 +107,7 @@ LIMIT @__p_0 }, { "id": 14, - "name": "Brand13", + "name": "Brand:13", "displayName": null, "brandDetails": { "country": { @@ -117,7 +117,7 @@ LIMIT @__p_0 }, { "id": 15, - "name": "Brand14", + "name": "Brand:14", "displayName": "BrandDisplay14", "brandDetails": { "country": { @@ -127,7 +127,7 @@ LIMIT @__p_0 }, { "id": 16, - "name": "Brand15", + "name": "Brand:15", "displayName": null, "brandDetails": { "country": { @@ -137,7 +137,7 @@ LIMIT @__p_0 }, { "id": 17, - "name": "Brand16", + "name": "Brand:16", "displayName": "BrandDisplay16", "brandDetails": { "country": { @@ -147,7 +147,7 @@ LIMIT @__p_0 }, { "id": 18, - "name": "Brand17", + "name": "Brand:17", "displayName": null, "brandDetails": { "country": { @@ -159,8 +159,8 @@ LIMIT @__p_0 "pageInfo": { "hasNextPage": true, "hasPreviousPage": false, - "startCursor": "QnJhbmQwOlxudWxsOjE=", - "endCursor": "QnJhbmQxNzpcbnVsbDoxOA==" + "startCursor": "QnJhbmRcOjA6XG51bGw6MQ==", + "endCursor": "QnJhbmRcOjE3OlxudWxsOjE4" } } } diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback.md index ab5f7965f40..74bf3e72e47 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback.md @@ -24,40 +24,40 @@ LIMIT @__p_0 "brandsNullableFallback": { "edges": [ { - "cursor": "QnJhbmQxOjI=" + "cursor": "QnJhbmRcOjE6Mg==" }, { - "cursor": "QnJhbmQxMToxMg==" + "cursor": "QnJhbmRcOjExOjEy" }, { - "cursor": "QnJhbmQxMzoxNA==" + "cursor": "QnJhbmRcOjEzOjE0" }, { - "cursor": "QnJhbmQxNToxNg==" + "cursor": "QnJhbmRcOjE1OjE2" }, { - "cursor": "QnJhbmQxNzoxOA==" + "cursor": "QnJhbmRcOjE3OjE4" }, { - "cursor": "QnJhbmQxOToyMA==" + "cursor": "QnJhbmRcOjE5OjIw" }, { - "cursor": "QnJhbmQyMToyMg==" + "cursor": "QnJhbmRcOjIxOjIy" }, { - "cursor": "QnJhbmQyMzoyNA==" + "cursor": "QnJhbmRcOjIzOjI0" }, { - "cursor": "QnJhbmQyNToyNg==" + "cursor": "QnJhbmRcOjI1OjI2" }, { - "cursor": "QnJhbmQyNzoyOA==" + "cursor": "QnJhbmRcOjI3OjI4" } ], "nodes": [ { "id": 2, - "name": "Brand1", + "name": "Brand:1", "displayName": null, "brandDetails": { "country": { @@ -67,7 +67,7 @@ LIMIT @__p_0 }, { "id": 12, - "name": "Brand11", + "name": "Brand:11", "displayName": null, "brandDetails": { "country": { @@ -77,7 +77,7 @@ LIMIT @__p_0 }, { "id": 14, - "name": "Brand13", + "name": "Brand:13", "displayName": null, "brandDetails": { "country": { @@ -87,7 +87,7 @@ LIMIT @__p_0 }, { "id": 16, - "name": "Brand15", + "name": "Brand:15", "displayName": null, "brandDetails": { "country": { @@ -97,7 +97,7 @@ LIMIT @__p_0 }, { "id": 18, - "name": "Brand17", + "name": "Brand:17", "displayName": null, "brandDetails": { "country": { @@ -107,7 +107,7 @@ LIMIT @__p_0 }, { "id": 20, - "name": "Brand19", + "name": "Brand:19", "displayName": null, "brandDetails": { "country": { @@ -117,7 +117,7 @@ LIMIT @__p_0 }, { "id": 22, - "name": "Brand21", + "name": "Brand:21", "displayName": null, "brandDetails": { "country": { @@ -127,7 +127,7 @@ LIMIT @__p_0 }, { "id": 24, - "name": "Brand23", + "name": "Brand:23", "displayName": null, "brandDetails": { "country": { @@ -137,7 +137,7 @@ LIMIT @__p_0 }, { "id": 26, - "name": "Brand25", + "name": "Brand:25", "displayName": null, "brandDetails": { "country": { @@ -147,7 +147,7 @@ LIMIT @__p_0 }, { "id": 28, - "name": "Brand27", + "name": "Brand:27", "displayName": null, "brandDetails": { "country": { @@ -159,8 +159,8 @@ LIMIT @__p_0 "pageInfo": { "hasNextPage": true, "hasPreviousPage": false, - "startCursor": "QnJhbmQxOjI=", - "endCursor": "QnJhbmQyNzoyOA==" + "startCursor": "QnJhbmRcOjE6Mg==", + "endCursor": "QnJhbmRcOjI3OjI4" } } } diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_SecondPage.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_SecondPage.md index 482ed6ab460..6c45f9e815a 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_SecondPage.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_Fallback_SecondPage.md @@ -3,7 +3,7 @@ ## SQL 0 ```sql --- @__p_0='Brand11' +-- @__p_0='Brand:11' -- @__p_1='12' -- @__p_2='3' SELECT b."Id", b."AlwaysNull", b."DisplayName", b."Name", b."BrandDetails_Country_Name" @@ -27,16 +27,16 @@ LIMIT @__p_2 "brandsNullableFallback": { "edges": [ { - "cursor": "QnJhbmQxMzoxNA==" + "cursor": "QnJhbmRcOjEzOjE0" }, { - "cursor": "QnJhbmQxNToxNg==" + "cursor": "QnJhbmRcOjE1OjE2" } ], "nodes": [ { "id": 14, - "name": "Brand13", + "name": "Brand:13", "displayName": null, "brandDetails": { "country": { @@ -46,7 +46,7 @@ LIMIT @__p_2 }, { "id": 16, - "name": "Brand15", + "name": "Brand:15", "displayName": null, "brandDetails": { "country": { @@ -58,8 +58,8 @@ LIMIT @__p_2 "pageInfo": { "hasNextPage": true, "hasPreviousPage": true, - "startCursor": "QnJhbmQxMzoxNA==", - "endCursor": "QnJhbmQxNToxNg==" + "startCursor": "QnJhbmRcOjEzOjE0", + "endCursor": "QnJhbmRcOjE1OjE2" } } } diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_SecondPage.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_SecondPage.md index c75b3a87354..f629e9be0be 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_SecondPage.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetDefaultPage_With_Nullable_SecondPage.md @@ -27,16 +27,16 @@ LIMIT @__p_3 "brandsNullable": { "edges": [ { - "cursor": "QnJhbmQxMTpcbnVsbDoxMg==" + "cursor": "QnJhbmRcOjExOlxudWxsOjEy" }, { - "cursor": "QnJhbmQxMjpcbnVsbDoxMw==" + "cursor": "QnJhbmRcOjEyOlxudWxsOjEz" } ], "nodes": [ { "id": 12, - "name": "Brand11", + "name": "Brand:11", "displayName": null, "brandDetails": { "country": { @@ -46,7 +46,7 @@ LIMIT @__p_3 }, { "id": 13, - "name": "Brand12", + "name": "Brand:12", "displayName": "BrandDisplay12", "brandDetails": { "country": { @@ -58,8 +58,8 @@ LIMIT @__p_3 "pageInfo": { "hasNextPage": true, "hasPreviousPage": true, - "startCursor": "QnJhbmQxMTpcbnVsbDoxMg==", - "endCursor": "QnJhbmQxMjpcbnVsbDoxMw==" + "startCursor": "QnJhbmRcOjExOlxudWxsOjEy", + "endCursor": "QnJhbmRcOjEyOlxudWxsOjEz" } } } diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetSecondPage_With_2_Items.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetSecondPage_With_2_Items.md index 59c8beda14f..bd3cfb39b70 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetSecondPage_With_2_Items.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.GetSecondPage_With_2_Items.md @@ -28,18 +28,18 @@ LIMIT @__p_2 "nodes": [ { "id": 19, - "name": "Brand18" + "name": "Brand:18" }, { "id": 20, - "name": "Brand19" + "name": "Brand:19" } ], "pageInfo": { "hasNextPage": true, "hasPreviousPage": true, - "startCursor": "QnJhbmQxODoxOQ==", - "endCursor": "QnJhbmQxOToyMA==" + "startCursor": "QnJhbmRcOjE4OjE5", + "endCursor": "QnJhbmRcOjE5OjIw" } } } diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto.md index 739a6c46e34..8d6ddd40623 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto.md @@ -24,19 +24,19 @@ LIMIT @__p_0 "brands": { "edges": [ { - "cursor": "QnJhbmQwOjE=", + "cursor": "QnJhbmRcOjA6MQ==", "displayName": "BrandDisplay0", "node": { "id": 1, - "name": "Brand0" + "name": "Brand:0" } }, { - "cursor": "QnJhbmQxOjI=", + "cursor": "QnJhbmRcOjE6Mg==", "displayName": null, "node": { "id": 2, - "name": "Brand1" + "name": "Brand:1" } } ] diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_2.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_2.md index 90c0eb3f43f..267c5102285 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_2.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Map_Page_To_Connection_With_Dto_2.md @@ -24,19 +24,19 @@ LIMIT @__p_0 "brands": { "edges": [ { - "cursor": "QnJhbmQwOjE=", + "cursor": "QnJhbmRcOjA6MQ==", "displayName": "BrandDisplay0", "node": { "id": 1, - "name": "Brand0" + "name": "Brand:0" } }, { - "cursor": "QnJhbmQxOjI=", + "cursor": "QnJhbmRcOjE6Mg==", "displayName": null, "node": { "id": 2, - "name": "Brand1" + "name": "Brand:1" } } ] diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2.md index 43893634212..0f838514ec5 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2.md @@ -52,10 +52,10 @@ ORDER BY t."BrandId", t0."BrandId", t0."Name", t0."Id" "brands": { "edges": [ { - "cursor": "QnJhbmQwOjE=" + "cursor": "QnJhbmRcOjA6MQ==" }, { - "cursor": "QnJhbmQxOjI=" + "cursor": "QnJhbmRcOjE6Mg==" } ], "nodes": [ diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_NET9_0.md index 5125369ac0e..8ade6d72a9e 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_NET9_0.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_NET9_0.md @@ -53,10 +53,10 @@ ORDER BY p1."BrandId", p3."BrandId", p3."Name", p3."Id" "brands": { "edges": [ { - "cursor": "QnJhbmQwOjE=" + "cursor": "QnJhbmRcOjA6MQ==" }, { - "cursor": "QnJhbmQxOjI=" + "cursor": "QnJhbmRcOjE6Mg==" } ], "nodes": [ diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections.md index 4e9f3ffd421..456198d42a5 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections.md @@ -52,10 +52,10 @@ ORDER BY t."BrandId", t0."BrandId", t0."Name", t0."Id" "brands": { "edges": [ { - "cursor": "QnJhbmQwOjE=" + "cursor": "QnJhbmRcOjA6MQ==" }, { - "cursor": "QnJhbmQxOjI=" + "cursor": "QnJhbmRcOjE6Mg==" } ], "nodes": [ diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections_NET9_0.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections_NET9_0.md index 3b5162ff3a7..71f5f62f4ce 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections_NET9_0.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Nested_Paging_First_2_With_Projections_NET9_0.md @@ -53,10 +53,10 @@ ORDER BY p1."BrandId", p3."BrandId", p3."Name", p3."Id" "brands": { "edges": [ { - "cursor": "QnJhbmQwOjE=" + "cursor": "QnJhbmRcOjA6MQ==" }, { - "cursor": "QnJhbmQxOjI=" + "cursor": "QnJhbmRcOjE6Mg==" } ], "nodes": [ diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Empty_PagingArgs.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Empty_PagingArgs.md index 4f7b7a29cd5..a9ed7429904 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Empty_PagingArgs.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Empty_PagingArgs.md @@ -21,9 +21,9 @@ ORDER BY b."Name", b."Id" "HasNextPage": false, "HasPreviousPage": false, "First": 1, - "FirstCursor": "QnJhbmQwOjE=", + "FirstCursor": "QnJhbmRcOjA6MQ==", "Last": 100, - "LastCursor": "QnJhbmQ5OToxMDA=" + "LastCursor": "QnJhbmRcOjk5OjEwMA==" } ``` @@ -33,7 +33,7 @@ ORDER BY b."Name", b."Id" [ { "Id": 1, - "Name": "Brand0", + "Name": "Brand:0", "DisplayName": "BrandDisplay0", "AlwaysNull": null, "Products": [], @@ -45,7 +45,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 2, - "Name": "Brand1", + "Name": "Brand:1", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -57,7 +57,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 11, - "Name": "Brand10", + "Name": "Brand:10", "DisplayName": "BrandDisplay10", "AlwaysNull": null, "Products": [], @@ -69,7 +69,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 12, - "Name": "Brand11", + "Name": "Brand:11", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -81,7 +81,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 13, - "Name": "Brand12", + "Name": "Brand:12", "DisplayName": "BrandDisplay12", "AlwaysNull": null, "Products": [], @@ -93,7 +93,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 14, - "Name": "Brand13", + "Name": "Brand:13", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -105,7 +105,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 15, - "Name": "Brand14", + "Name": "Brand:14", "DisplayName": "BrandDisplay14", "AlwaysNull": null, "Products": [], @@ -117,7 +117,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 16, - "Name": "Brand15", + "Name": "Brand:15", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -129,7 +129,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 17, - "Name": "Brand16", + "Name": "Brand:16", "DisplayName": "BrandDisplay16", "AlwaysNull": null, "Products": [], @@ -141,7 +141,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 18, - "Name": "Brand17", + "Name": "Brand:17", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -153,7 +153,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 19, - "Name": "Brand18", + "Name": "Brand:18", "DisplayName": "BrandDisplay18", "AlwaysNull": null, "Products": [], @@ -165,7 +165,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 20, - "Name": "Brand19", + "Name": "Brand:19", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -177,7 +177,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 3, - "Name": "Brand2", + "Name": "Brand:2", "DisplayName": "BrandDisplay2", "AlwaysNull": null, "Products": [], @@ -189,7 +189,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 21, - "Name": "Brand20", + "Name": "Brand:20", "DisplayName": "BrandDisplay20", "AlwaysNull": null, "Products": [], @@ -201,7 +201,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 22, - "Name": "Brand21", + "Name": "Brand:21", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -213,7 +213,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 23, - "Name": "Brand22", + "Name": "Brand:22", "DisplayName": "BrandDisplay22", "AlwaysNull": null, "Products": [], @@ -225,7 +225,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 24, - "Name": "Brand23", + "Name": "Brand:23", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -237,7 +237,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 25, - "Name": "Brand24", + "Name": "Brand:24", "DisplayName": "BrandDisplay24", "AlwaysNull": null, "Products": [], @@ -249,7 +249,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 26, - "Name": "Brand25", + "Name": "Brand:25", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -261,7 +261,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 27, - "Name": "Brand26", + "Name": "Brand:26", "DisplayName": "BrandDisplay26", "AlwaysNull": null, "Products": [], @@ -273,7 +273,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 28, - "Name": "Brand27", + "Name": "Brand:27", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -285,7 +285,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 29, - "Name": "Brand28", + "Name": "Brand:28", "DisplayName": "BrandDisplay28", "AlwaysNull": null, "Products": [], @@ -297,7 +297,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 30, - "Name": "Brand29", + "Name": "Brand:29", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -309,7 +309,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 4, - "Name": "Brand3", + "Name": "Brand:3", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -321,7 +321,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 31, - "Name": "Brand30", + "Name": "Brand:30", "DisplayName": "BrandDisplay30", "AlwaysNull": null, "Products": [], @@ -333,7 +333,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 32, - "Name": "Brand31", + "Name": "Brand:31", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -345,7 +345,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 33, - "Name": "Brand32", + "Name": "Brand:32", "DisplayName": "BrandDisplay32", "AlwaysNull": null, "Products": [], @@ -357,7 +357,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 34, - "Name": "Brand33", + "Name": "Brand:33", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -369,7 +369,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 35, - "Name": "Brand34", + "Name": "Brand:34", "DisplayName": "BrandDisplay34", "AlwaysNull": null, "Products": [], @@ -381,7 +381,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 36, - "Name": "Brand35", + "Name": "Brand:35", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -393,7 +393,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 37, - "Name": "Brand36", + "Name": "Brand:36", "DisplayName": "BrandDisplay36", "AlwaysNull": null, "Products": [], @@ -405,7 +405,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 38, - "Name": "Brand37", + "Name": "Brand:37", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -417,7 +417,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 39, - "Name": "Brand38", + "Name": "Brand:38", "DisplayName": "BrandDisplay38", "AlwaysNull": null, "Products": [], @@ -429,7 +429,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 40, - "Name": "Brand39", + "Name": "Brand:39", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -441,7 +441,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 5, - "Name": "Brand4", + "Name": "Brand:4", "DisplayName": "BrandDisplay4", "AlwaysNull": null, "Products": [], @@ -453,7 +453,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 41, - "Name": "Brand40", + "Name": "Brand:40", "DisplayName": "BrandDisplay40", "AlwaysNull": null, "Products": [], @@ -465,7 +465,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 42, - "Name": "Brand41", + "Name": "Brand:41", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -477,7 +477,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 43, - "Name": "Brand42", + "Name": "Brand:42", "DisplayName": "BrandDisplay42", "AlwaysNull": null, "Products": [], @@ -489,7 +489,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 44, - "Name": "Brand43", + "Name": "Brand:43", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -501,7 +501,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 45, - "Name": "Brand44", + "Name": "Brand:44", "DisplayName": "BrandDisplay44", "AlwaysNull": null, "Products": [], @@ -513,7 +513,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 46, - "Name": "Brand45", + "Name": "Brand:45", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -525,7 +525,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 47, - "Name": "Brand46", + "Name": "Brand:46", "DisplayName": "BrandDisplay46", "AlwaysNull": null, "Products": [], @@ -537,7 +537,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 48, - "Name": "Brand47", + "Name": "Brand:47", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -549,7 +549,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 49, - "Name": "Brand48", + "Name": "Brand:48", "DisplayName": "BrandDisplay48", "AlwaysNull": null, "Products": [], @@ -561,7 +561,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 50, - "Name": "Brand49", + "Name": "Brand:49", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -573,7 +573,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 6, - "Name": "Brand5", + "Name": "Brand:5", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -585,7 +585,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 51, - "Name": "Brand50", + "Name": "Brand:50", "DisplayName": "BrandDisplay50", "AlwaysNull": null, "Products": [], @@ -597,7 +597,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 52, - "Name": "Brand51", + "Name": "Brand:51", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -609,7 +609,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 53, - "Name": "Brand52", + "Name": "Brand:52", "DisplayName": "BrandDisplay52", "AlwaysNull": null, "Products": [], @@ -621,7 +621,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 54, - "Name": "Brand53", + "Name": "Brand:53", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -633,7 +633,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 55, - "Name": "Brand54", + "Name": "Brand:54", "DisplayName": "BrandDisplay54", "AlwaysNull": null, "Products": [], @@ -645,7 +645,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 56, - "Name": "Brand55", + "Name": "Brand:55", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -657,7 +657,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 57, - "Name": "Brand56", + "Name": "Brand:56", "DisplayName": "BrandDisplay56", "AlwaysNull": null, "Products": [], @@ -669,7 +669,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 58, - "Name": "Brand57", + "Name": "Brand:57", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -681,7 +681,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 59, - "Name": "Brand58", + "Name": "Brand:58", "DisplayName": "BrandDisplay58", "AlwaysNull": null, "Products": [], @@ -693,7 +693,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 60, - "Name": "Brand59", + "Name": "Brand:59", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -705,7 +705,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 7, - "Name": "Brand6", + "Name": "Brand:6", "DisplayName": "BrandDisplay6", "AlwaysNull": null, "Products": [], @@ -717,7 +717,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 61, - "Name": "Brand60", + "Name": "Brand:60", "DisplayName": "BrandDisplay60", "AlwaysNull": null, "Products": [], @@ -729,7 +729,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 62, - "Name": "Brand61", + "Name": "Brand:61", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -741,7 +741,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 63, - "Name": "Brand62", + "Name": "Brand:62", "DisplayName": "BrandDisplay62", "AlwaysNull": null, "Products": [], @@ -753,7 +753,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 64, - "Name": "Brand63", + "Name": "Brand:63", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -765,7 +765,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 65, - "Name": "Brand64", + "Name": "Brand:64", "DisplayName": "BrandDisplay64", "AlwaysNull": null, "Products": [], @@ -777,7 +777,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 66, - "Name": "Brand65", + "Name": "Brand:65", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -789,7 +789,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 67, - "Name": "Brand66", + "Name": "Brand:66", "DisplayName": "BrandDisplay66", "AlwaysNull": null, "Products": [], @@ -801,7 +801,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 68, - "Name": "Brand67", + "Name": "Brand:67", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -813,7 +813,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 69, - "Name": "Brand68", + "Name": "Brand:68", "DisplayName": "BrandDisplay68", "AlwaysNull": null, "Products": [], @@ -825,7 +825,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 70, - "Name": "Brand69", + "Name": "Brand:69", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -837,7 +837,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 8, - "Name": "Brand7", + "Name": "Brand:7", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -849,7 +849,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 71, - "Name": "Brand70", + "Name": "Brand:70", "DisplayName": "BrandDisplay70", "AlwaysNull": null, "Products": [], @@ -861,7 +861,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 72, - "Name": "Brand71", + "Name": "Brand:71", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -873,7 +873,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 73, - "Name": "Brand72", + "Name": "Brand:72", "DisplayName": "BrandDisplay72", "AlwaysNull": null, "Products": [], @@ -885,7 +885,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 74, - "Name": "Brand73", + "Name": "Brand:73", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -897,7 +897,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 75, - "Name": "Brand74", + "Name": "Brand:74", "DisplayName": "BrandDisplay74", "AlwaysNull": null, "Products": [], @@ -909,7 +909,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 76, - "Name": "Brand75", + "Name": "Brand:75", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -921,7 +921,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 77, - "Name": "Brand76", + "Name": "Brand:76", "DisplayName": "BrandDisplay76", "AlwaysNull": null, "Products": [], @@ -933,7 +933,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 78, - "Name": "Brand77", + "Name": "Brand:77", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -945,7 +945,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 79, - "Name": "Brand78", + "Name": "Brand:78", "DisplayName": "BrandDisplay78", "AlwaysNull": null, "Products": [], @@ -957,7 +957,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 80, - "Name": "Brand79", + "Name": "Brand:79", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -969,7 +969,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 9, - "Name": "Brand8", + "Name": "Brand:8", "DisplayName": "BrandDisplay8", "AlwaysNull": null, "Products": [], @@ -981,7 +981,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 81, - "Name": "Brand80", + "Name": "Brand:80", "DisplayName": "BrandDisplay80", "AlwaysNull": null, "Products": [], @@ -993,7 +993,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 82, - "Name": "Brand81", + "Name": "Brand:81", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -1005,7 +1005,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 83, - "Name": "Brand82", + "Name": "Brand:82", "DisplayName": "BrandDisplay82", "AlwaysNull": null, "Products": [], @@ -1017,7 +1017,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 84, - "Name": "Brand83", + "Name": "Brand:83", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -1029,7 +1029,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 85, - "Name": "Brand84", + "Name": "Brand:84", "DisplayName": "BrandDisplay84", "AlwaysNull": null, "Products": [], @@ -1041,7 +1041,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 86, - "Name": "Brand85", + "Name": "Brand:85", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -1053,7 +1053,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 87, - "Name": "Brand86", + "Name": "Brand:86", "DisplayName": "BrandDisplay86", "AlwaysNull": null, "Products": [], @@ -1065,7 +1065,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 88, - "Name": "Brand87", + "Name": "Brand:87", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -1077,7 +1077,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 89, - "Name": "Brand88", + "Name": "Brand:88", "DisplayName": "BrandDisplay88", "AlwaysNull": null, "Products": [], @@ -1089,7 +1089,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 90, - "Name": "Brand89", + "Name": "Brand:89", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -1101,7 +1101,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 10, - "Name": "Brand9", + "Name": "Brand:9", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -1113,7 +1113,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 91, - "Name": "Brand90", + "Name": "Brand:90", "DisplayName": "BrandDisplay90", "AlwaysNull": null, "Products": [], @@ -1125,7 +1125,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 92, - "Name": "Brand91", + "Name": "Brand:91", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -1137,7 +1137,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 93, - "Name": "Brand92", + "Name": "Brand:92", "DisplayName": "BrandDisplay92", "AlwaysNull": null, "Products": [], @@ -1149,7 +1149,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 94, - "Name": "Brand93", + "Name": "Brand:93", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -1161,7 +1161,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 95, - "Name": "Brand94", + "Name": "Brand:94", "DisplayName": "BrandDisplay94", "AlwaysNull": null, "Products": [], @@ -1173,7 +1173,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 96, - "Name": "Brand95", + "Name": "Brand:95", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -1185,7 +1185,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 97, - "Name": "Brand96", + "Name": "Brand:96", "DisplayName": "BrandDisplay96", "AlwaysNull": null, "Products": [], @@ -1197,7 +1197,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 98, - "Name": "Brand97", + "Name": "Brand:97", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -1209,7 +1209,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 99, - "Name": "Brand98", + "Name": "Brand:98", "DisplayName": "BrandDisplay98", "AlwaysNull": null, "Products": [], @@ -1221,7 +1221,7 @@ ORDER BY b."Name", b."Id" }, { "Id": 100, - "Name": "Brand99", + "Name": "Brand:99", "DisplayName": null, "AlwaysNull": null, "Products": [], diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5.md index 363f584a816..461376f4890 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5.md @@ -23,9 +23,9 @@ LIMIT @__p_0 "HasNextPage": true, "HasPreviousPage": false, "First": 1, - "FirstCursor": "QnJhbmQwOjE=", + "FirstCursor": "QnJhbmRcOjA6MQ==", "Last": 13, - "LastCursor": "QnJhbmQxMjoxMw==" + "LastCursor": "QnJhbmRcOjEyOjEz" } ``` @@ -35,7 +35,7 @@ LIMIT @__p_0 [ { "Id": 1, - "Name": "Brand0", + "Name": "Brand:0", "DisplayName": "BrandDisplay0", "AlwaysNull": null, "Products": [], @@ -47,7 +47,7 @@ LIMIT @__p_0 }, { "Id": 2, - "Name": "Brand1", + "Name": "Brand:1", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -59,7 +59,7 @@ LIMIT @__p_0 }, { "Id": 11, - "Name": "Brand10", + "Name": "Brand:10", "DisplayName": "BrandDisplay10", "AlwaysNull": null, "Products": [], @@ -71,7 +71,7 @@ LIMIT @__p_0 }, { "Id": 12, - "Name": "Brand11", + "Name": "Brand:11", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -83,7 +83,7 @@ LIMIT @__p_0 }, { "Id": 13, - "Name": "Brand12", + "Name": "Brand:12", "DisplayName": "BrandDisplay12", "AlwaysNull": null, "Products": [], diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_After_Id_13.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_After_Id_13.md index 3f7a63fde55..86f39effe03 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_After_Id_13.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_After_Id_13.md @@ -26,9 +26,9 @@ LIMIT @__p_2 "HasNextPage": true, "HasPreviousPage": true, "First": 14, - "FirstCursor": "QnJhbmQxMzoxNA==", + "FirstCursor": "QnJhbmRcOjEzOjE0", "Last": 18, - "LastCursor": "QnJhbmQxNzoxOA==" + "LastCursor": "QnJhbmRcOjE3OjE4" } ``` @@ -38,7 +38,7 @@ LIMIT @__p_2 [ { "Id": 14, - "Name": "Brand13", + "Name": "Brand:13", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -50,7 +50,7 @@ LIMIT @__p_2 }, { "Id": 15, - "Name": "Brand14", + "Name": "Brand:14", "DisplayName": "BrandDisplay14", "AlwaysNull": null, "Products": [], @@ -62,7 +62,7 @@ LIMIT @__p_2 }, { "Id": 16, - "Name": "Brand15", + "Name": "Brand:15", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -74,7 +74,7 @@ LIMIT @__p_2 }, { "Id": 17, - "Name": "Brand16", + "Name": "Brand:16", "DisplayName": "BrandDisplay16", "AlwaysNull": null, "Products": [], @@ -86,7 +86,7 @@ LIMIT @__p_2 }, { "Id": 18, - "Name": "Brand17", + "Name": "Brand:17", "DisplayName": null, "AlwaysNull": null, "Products": [], diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_Before_Id_96.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_Before_Id_96.md index e87fe49bb26..edb16838fd3 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_Before_Id_96.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_First_5_Before_Id_96.md @@ -25,10 +25,10 @@ LIMIT @__p_2 { "HasNextPage": true, "HasPreviousPage": true, - "First": 91, - "FirstCursor": "QnJhbmQ5MDo5MQ==", - "Last": 95, - "LastCursor": "QnJhbmQ5NDo5NQ==" + "First": 92, + "FirstCursor": "QnJhbmRcOjkxOjky", + "Last": 96, + "LastCursor": "QnJhbmRcOjk1Ojk2" } ``` @@ -36,21 +36,9 @@ LIMIT @__p_2 ```json [ - { - "Id": 91, - "Name": "Brand90", - "DisplayName": "BrandDisplay90", - "AlwaysNull": null, - "Products": [], - "BrandDetails": { - "Country": { - "Name": "Country90" - } - } - }, { "Id": 92, - "Name": "Brand91", + "Name": "Brand:91", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -62,7 +50,7 @@ LIMIT @__p_2 }, { "Id": 93, - "Name": "Brand92", + "Name": "Brand:92", "DisplayName": "BrandDisplay92", "AlwaysNull": null, "Products": [], @@ -74,7 +62,7 @@ LIMIT @__p_2 }, { "Id": 94, - "Name": "Brand93", + "Name": "Brand:93", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -86,7 +74,7 @@ LIMIT @__p_2 }, { "Id": 95, - "Name": "Brand94", + "Name": "Brand:94", "DisplayName": "BrandDisplay94", "AlwaysNull": null, "Products": [], @@ -95,6 +83,18 @@ LIMIT @__p_2 "Name": "Country94" } } + }, + { + "Id": 96, + "Name": "Brand:95", + "DisplayName": null, + "AlwaysNull": null, + "Products": [], + "BrandDetails": { + "Country": { + "Name": "Country95" + } + } } ] ``` diff --git a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Last_5.md b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Last_5.md index 9906664fc7e..13f2c7552cb 100644 --- a/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Last_5.md +++ b/src/HotChocolate/Pagination/test/Pagination.EntityFramework.Tests/__snapshots__/IntegrationPagingHelperTests.Paging_Last_5.md @@ -23,9 +23,9 @@ LIMIT @__p_0 "HasNextPage": false, "HasPreviousPage": true, "First": 96, - "FirstCursor": "QnJhbmQ5NTo5Ng==", + "FirstCursor": "QnJhbmRcOjk1Ojk2", "Last": 100, - "LastCursor": "QnJhbmQ5OToxMDA=" + "LastCursor": "QnJhbmRcOjk5OjEwMA==" } ``` @@ -35,7 +35,7 @@ LIMIT @__p_0 [ { "Id": 96, - "Name": "Brand95", + "Name": "Brand:95", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -47,7 +47,7 @@ LIMIT @__p_0 }, { "Id": 97, - "Name": "Brand96", + "Name": "Brand:96", "DisplayName": "BrandDisplay96", "AlwaysNull": null, "Products": [], @@ -59,7 +59,7 @@ LIMIT @__p_0 }, { "Id": 98, - "Name": "Brand97", + "Name": "Brand:97", "DisplayName": null, "AlwaysNull": null, "Products": [], @@ -71,7 +71,7 @@ LIMIT @__p_0 }, { "Id": 99, - "Name": "Brand98", + "Name": "Brand:98", "DisplayName": "BrandDisplay98", "AlwaysNull": null, "Products": [], @@ -83,7 +83,7 @@ LIMIT @__p_0 }, { "Id": 100, - "Name": "Brand99", + "Name": "Brand:99", "DisplayName": null, "AlwaysNull": null, "Products": [],