From 15da3b046634c33c3757976f9c4fceea90595ae5 Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Wed, 18 Aug 2021 18:50:03 +0200 Subject: [PATCH] Add more capabilities to control how the connection name is created. (#4081) --- ...aMiddlewareTests.Download_GraphQL_SDL.snap | 28 +- .../FieldCollectorBenchmarks.cs | 8 +- .../VariableCoercionBenchmarks.cs | 2 +- .../Options/ComplexityAnalyzerSettings.cs | 4 +- .../ObjectFieldDataLoaderExtensions.cs | 29 +- .../ConnectionCountType.cs | 46 -- .../Types.CursorPagination/ConnectionType.cs | 240 ++++++--- .../src/Types.CursorPagination/EdgeType.cs | 141 ++++-- ...orPagingRequestExecutorBuilderExtension.cs | 137 ++++++ .../Extensions/GetCursorPagingProvider.cs | 6 +- .../PagingObjectFieldDescriptorExtensions.cs | 199 +++++--- .../Extensions/UsePagingAttribute.cs | 57 ++- .../Types.CursorPagination/IConnectionType.cs | 18 + .../src/Types.CursorPagination/IEdgeType.cs | 21 +- .../src/Types.CursorPagination/NameHelper.cs | 11 + .../PagingProviderEntry.cs | 15 + .../PublicAPI.Shipped.txt | 23 - .../PublicAPI.Unshipped.txt | 47 +- .../QueryableCursorPagingProvider.cs | 3 +- .../Extensions/GetOffsetPagingProvider.cs | 6 +- ...etPagingObjectFieldDescriptorExtensions.cs | 32 +- ...etPagingRequestExecutorBuilderExtension.cs | 137 ++++++ .../Extensions/UseOffsetPagingAttribute.cs | 24 +- ...HotChocolate.Types.OffsetPagination.csproj | 8 +- .../PagingProviderEntry.cs | 15 + .../PublicAPI.Unshipped.txt | 10 +- .../Configuration/AggregateTypeInterceptor.cs | 68 ++- .../Contracts/ITypeCompletionContext.cs | 4 +- .../Contracts/ITypeDiscoveryContext.cs | 2 +- .../ITypeInitilizationInterceptor.cs | 16 + .../DelegateTypeInitializationInterceptor.cs | 7 +- ...DelegateTypeInitializationInterceptor~1.cs | 8 +- .../DependantFactoryTypeReferenceHandler.cs | 34 ++ .../ExtendedTypeReferenceHandler.cs | 63 ++- .../FactoryTypeReferenceHandler.cs | 34 ++ .../src/Types/Configuration/ITypeRegistrar.cs | 7 +- .../Configuration/ITypeRegistrarHandler.cs | 24 +- .../src/Types/Configuration/ListExtensions.cs | 15 + .../RegisteredType.CompletionContext.cs | 147 ++++++ ....cs => RegisteredType.DiscoveryContext.cs} | 192 +++----- .../src/Types/Configuration/RegisteredType.cs | 88 ++-- .../SchemaTypeReferenceHandler.cs | 36 +- .../SyntaxTypeReferenceHandler.cs | 41 +- .../Configuration/TypeCompletionContext.cs | 152 ------ .../src/Types/Configuration/TypeDiscoverer.cs | 86 +++- .../TypeInitializationInterceptor.cs | 5 + .../Types/Configuration/TypeInitializer.cs | 82 ++-- .../src/Types/Configuration/TypeLookup.cs | 5 + .../Configuration/TypeReferenceResolver.cs | 9 +- .../src/Types/Configuration/TypeRegistrar.cs | 82 ++-- .../src/Types/Configuration/TypeRegistry.cs | 12 +- .../src/Types/Internal/TypeExtensionHelper.cs | 22 +- .../Core/src/Types/InternalsVisibleTo.cs | 2 +- .../Properties/TypeResources.Designer.cs | 54 +++ .../src/Types/Properties/TypeResources.resx | 27 ++ ...ellationTokenParameterExpressionBuilder.cs | 4 +- .../src/Types/Resolvers/IResolverContext.cs | 9 +- .../CompletedDependencyDescriptor~1.cs | 7 +- .../Contracts/ConfigurationKind.cs | 1 + .../Definitions/CompleteConfiguration.cs | 113 +++++ .../Definitions/CreateConfiguration.cs | 43 ++ .../Descriptors/Definitions/DefinitionBase.cs | 35 +- .../Definitions/DirectiveTypeDefinition.cs | 4 +- .../Definitions/EnumTypeDefinition.cs | 4 +- .../Descriptors/Definitions/IDefinition.cs | 12 +- ...n.cs => ITypeSystemMemberConfiguration.cs} | 24 +- .../Definitions/InputObjectTypeDefinition.cs | 5 +- .../Definitions/InterfaceTypeDefinition.cs | 4 +- .../Definitions/ObjectTypeDefinition.cs | 26 +- .../Descriptors/DependencyDescriptorBase~1.cs | 13 +- .../Types/Descriptors/DescriptorBase~1.cs | 106 ++-- .../LazyTypeConfigurationBuilder.cs | 11 - .../LazyTypeConfigurationBuilder~1.cs | 90 ---- .../NamedDependencyDescriptor~1.cs | 7 +- .../Types/Descriptors/TypeConfiguration.cs | 60 --- .../DependantFactoryTypeReference.cs | 120 +++++ .../TypeReferences/ExtendedTypeReference.cs | 2 +- .../TypeReferences/ITypeReference.cs | 20 +- .../TypeReferences/SchemaTypeReference.cs | 2 +- .../TypeReferences/SyntaxTypeReference.cs | 46 +- .../TypeReferences/TypeReference.cs | 42 +- .../TypeReferences/TypeReferenceKind.cs | 36 ++ .../Core/src/Types/Types/DirectiveType.cs | 2 +- .../Types/Directives/CostTypeInterceptor.cs | 5 + .../Core/src/Types/Types/ObjectType.cs | 2 +- .../Types/Pagination/GetPagingProvider.cs | 9 +- .../Types/Types/Pagination/IPagingProvider.cs | 14 +- .../Types/Types/Pagination/PagingDefaults.cs | 2 + .../Types/Types/Pagination/PagingHelper.cs | 45 +- .../Types/Types/Pagination/PagingOptions.cs | 19 +- .../src/Types/Types/TypeSystemObjectBase~1.cs | 12 +- .../StarWarsCodeFirstTests.cs | 2 +- ...sts.Ensure_Benchmark_Query_LargeQuery.snap | 20 +- .../StarWarsCodeFirstTests.Schema.snap | 28 +- .../Pipeline/ComplexityAnalyzerTests.cs | 6 +- ...alyzerTests.Apply_Complexity_Defaults.snap | 10 +- ...erationCompilerTests.Nested_Fragments.snap | 56 +-- ...perationCompilerTests.Reuse_Selection.snap | 4 +- .../GetHeroWithFriendsQuery.graphql | 2 +- .../GetTwoHerosWithFriendsQuery.graphql | 2 +- .../__resources__/LargeQuery.graphql | 2 +- ...ectionTests.DefaultValueIsInputObject.snap | 8 +- ...eIntrospection_AllDirectives_Internal.snap | 20 +- ...iveIntrospection_AllDirectives_Public.snap | 110 ++--- ...Introspection_SomeDirectives_Internal.snap | 102 ++-- ...sts.ExecuteGraphiQLIntrospectionQuery.snap | 8 +- ...cuteGraphiQLIntrospectionQuery_ToJson.snap | 8 +- .../IntegrationTests.cs | 173 ++++++- .../MockExecutable.cs | 39 ++ ...hema_Correctly_When_Connection_IsUsed.snap | 38 +- ...Attribute_Interface_With_Paging_Field.snap | 68 ++- ...ts.Attribute_Simple_StringList_Schema.snap | 66 ++- ...onTests.Deactivate_BackwardPagination.snap | 66 ++- ...activate_BackwardPagination_Interface.snap | 68 ++- ...egrationTests.Explicit_ConnectionName.snap | 81 ++++ ...Tests.Infer_ConnectionName_From_Field.snap | 43 ++ ...tionTests.Interface_With_Paging_Field.snap | 68 ++- .../IntegrationTests.No_Boundaries_Set.snap | 2 +- ...ntegrationTests.SelectDefaultProvider.snap | 9 + ...IntegrationTests.SelectProviderByName.snap | 9 + ...grationTests.Simple_StringList_Schema.snap | 66 ++- ...ts.Ensure_Attributes_Are_Applied_Once.snap | 8 +- ...eTests.UsePagingAttribute_Infer_Types.snap | 8 +- ...ingAttribute_Infer_Types_On_Interface.snap | 8 +- ...ingAttribute_On_Extension_Infer_Types.snap | 8 +- .../Configuration/SchemaTypeDiscoveryTests.cs | 2 +- .../TypeInitializationOrderTests.cs | 20 +- .../Configuration/TypeInitializerTests.cs | 75 ++- .../TypeScopeInterceptorTests.cs | 42 +- ...sts.Register_ClrType_InferSchemaTypes.snap | 18 +- ...sts.Register_SchemaType_ClrTypeExists.snap | 18 +- ...erTests.Upgrade_Type_From_GenericType.snap | 18 +- ...DiscoveryTests.InferDateTimeFromModel.snap | 32 +- ...AndNameRefsAreRecognizedAsTheSameType.snap | 9 + ...zerTests.InitializeFactoryTypeRefOnce.snap | 9 + ...rstTests.DescriptionsAreCorrectlyRead.snap | 456 +++++++++--------- ...rrectly_Exposed_Through_Introspection.snap | 250 +++++----- .../__resources__/StarWars_Request.graphql | 2 +- .../FilterObjectFieldDescriptorExtensions.cs | 41 +- ...electionObjectFieldDescriptorExtensions.cs | 27 +- ...rDefaultObjectFieldDescriptorExtensions.cs | 32 +- .../SortObjectFieldDescriptorExtensions.cs | 41 +- ...s.Infer_Schema_From_IQueryable_Fields.snap | 8 +- ...er_Schema_From_IQueryable_Task_Fields.snap | 8 +- ...hema_From_IQueryable_ValueTask_Fields.snap | 8 +- ....CreateSchema_OnDifferentScope_Schema.snap | 8 +- ...asIsSameAsAlwaysProjectedField_Schema.snap | 8 +- ...ProjectedField_With_Extensions_Schema.snap | 8 +- ...sts.Execute_And_OnRoot_Reverse_Schema.snap | 8 +- ...rationTests.Execute_And_OnRoot_Schema.snap | 8 +- ...rectlyOrderedMiddlewarePassValidation.snap | 22 +- .../FilterObjectFieldDescriptorExtensions.cs | 52 +- .../SortObjectFieldDescriptorExtensions.cs | 44 +- .../MongoDbDataRequestBuilderExtensions.cs | 42 ++ .../Extensions/SchemaBuilderExtensions.cs | 3 + .../Paging/AggregateFluentPagingContainer.cs | 2 +- ...goPagingObjectFieldDescriptorExtensions.cs | 129 ----- .../Paging/UseMongoDbOffsetPagingAttribute.cs | 81 ---- .../Data/Paging/UseMongoDbPagingAttribute.cs | 83 ---- ...bProjectionProviderDescriptorExtensions.cs | 1 - .../MongoDb/src/Data/PublicAPI.Shipped.txt | 26 - .../MongoDb/src/Data/PublicAPI.Unshipped.txt | 27 ++ ...MongoDbCursorPagingAggregateFluentTests.cs | 2 +- .../MongoDbCursorPagingFindFluentTests.cs | 2 +- .../MongoDbOffsetPagingAggregateTests.cs | 97 ++-- .../MongoDbOffsetPagingFindFluentTests.cs | 6 +- .../MongoDbProjectionVisitorPagingTests.cs | 2 +- .../ProjectionVisitorTestBase.cs | 5 +- .../SchemaCache.cs | 5 +- .../Neo4JDataRequestBuilderExtensions.cs | 34 ++ ...4JPagingObjectFieldDescriptorExtensions.cs | 72 --- .../Paging/UseNeo4JOffsetPagingAttribute.cs | 82 ---- .../Neo4JFixture.cs | 3 +- .../BaseTests.AutoMerge_Schema.snap | 8 +- .../SchemaTests.CustomerSchemaSnapshot.snap | 8 +- ...ectionClientTests.Download_Schema_AST.snap | 12 +- ...ectionClientTests.Download_Schema_SDL.snap | 12 +- .../Integration/MultiProfileTest.Client.cs | 40 +- .../StarWarsGetFriendsNoStoreTest.Client.cs | 34 +- .../StarWarsGetFriendsTest.Client.cs | 40 +- .../Integration/StarWarsGetFriendsTest.cs | 3 +- .../StarWarsUnionListTest.Client.cs | 30 +- ...st.Execute_StarWarsIntrospection_Test.snap | 40 +- .../__resources__/Schema.graphql | 45 +- ...e_With_Fragment_Definition_Two_Models.snap | 34 +- ...e_With_Fragment_Definition_Two_Models.snap | 40 +- .../Mappers/DataTypeMapperTests.cs | 6 +- .../api-reference/migrate-from-11-to-12.md | 60 +++ 188 files changed, 4175 insertions(+), 2978 deletions(-) delete mode 100644 src/HotChocolate/Core/src/Types.CursorPagination/ConnectionCountType.cs create mode 100644 src/HotChocolate/Core/src/Types.CursorPagination/Extensions/CursorPagingRequestExecutorBuilderExtension.cs create mode 100644 src/HotChocolate/Core/src/Types.CursorPagination/IConnectionType.cs create mode 100644 src/HotChocolate/Core/src/Types.CursorPagination/NameHelper.cs create mode 100644 src/HotChocolate/Core/src/Types.CursorPagination/PagingProviderEntry.cs create mode 100644 src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/OffsetPagingRequestExecutorBuilderExtension.cs create mode 100644 src/HotChocolate/Core/src/Types.OffsetPagination/PagingProviderEntry.cs create mode 100644 src/HotChocolate/Core/src/Types/Configuration/DependantFactoryTypeReferenceHandler.cs create mode 100644 src/HotChocolate/Core/src/Types/Configuration/FactoryTypeReferenceHandler.cs create mode 100644 src/HotChocolate/Core/src/Types/Configuration/ListExtensions.cs create mode 100644 src/HotChocolate/Core/src/Types/Configuration/RegisteredType.CompletionContext.cs rename src/HotChocolate/Core/src/Types/Configuration/{TypeDiscoveryContext.cs => RegisteredType.DiscoveryContext.cs} (64%) delete mode 100644 src/HotChocolate/Core/src/Types/Configuration/TypeCompletionContext.cs create mode 100644 src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/CompleteConfiguration.cs create mode 100644 src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/CreateConfiguration.cs rename src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/{ITypeConfigration.cs => ITypeSystemMemberConfiguration.cs} (55%) delete mode 100644 src/HotChocolate/Core/src/Types/Types/Descriptors/LazyTypeConfigurationBuilder.cs delete mode 100644 src/HotChocolate/Core/src/Types/Types/Descriptors/LazyTypeConfigurationBuilder~1.cs delete mode 100644 src/HotChocolate/Core/src/Types/Types/Descriptors/TypeConfiguration.cs create mode 100644 src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/DependantFactoryTypeReference.cs create mode 100644 src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/TypeReferenceKind.cs create mode 100644 src/HotChocolate/Core/test/Types.CursorPagination.Tests/MockExecutable.cs create mode 100644 src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Explicit_ConnectionName.snap create mode 100644 src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Infer_ConnectionName_From_Field.snap create mode 100644 src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.SelectDefaultProvider.snap create mode 100644 src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.SelectProviderByName.snap create mode 100644 src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeInitializerTests.FactoryAndNameRefsAreRecognizedAsTheSameType.snap create mode 100644 src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeInitializerTests.InitializeFactoryTypeRefOnce.snap delete mode 100644 src/HotChocolate/MongoDb/src/Data/Paging/MongoPagingObjectFieldDescriptorExtensions.cs delete mode 100644 src/HotChocolate/MongoDb/src/Data/Paging/UseMongoDbOffsetPagingAttribute.cs delete mode 100644 src/HotChocolate/MongoDb/src/Data/Paging/UseMongoDbPagingAttribute.cs delete mode 100644 src/HotChocolate/Neo4J/src/Data/Paging/Neo4JPagingObjectFieldDescriptorExtensions.cs delete mode 100644 src/HotChocolate/Neo4J/src/Data/Paging/UseNeo4JOffsetPagingAttribute.cs 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 985ead13403..4d8d505ace0 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 @@ -7,43 +7,43 @@ 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): CharacterConnection + 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] 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 +} + "A connection to a list of items." -type CharacterConnection { +type FriendsConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [CharacterEdge!] + edges: [FriendsEdge!] "A flattened list of the nodes." nodes: [Character] } "An edge in a connection." -type CharacterEdge { +type FriendsEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." node: Character } -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): CharacterConnection - height(unit: Unit): Float - primaryFunction: String -} - 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): CharacterConnection + 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 diff --git a/src/HotChocolate/Core/benchmark/Execution.Benchmarks/FieldCollectorBenchmarks.cs b/src/HotChocolate/Core/benchmark/Execution.Benchmarks/FieldCollectorBenchmarks.cs index 82380a94691..6992fead6f5 100644 --- a/src/HotChocolate/Core/benchmark/Execution.Benchmarks/FieldCollectorBenchmarks.cs +++ b/src/HotChocolate/Core/benchmark/Execution.Benchmarks/FieldCollectorBenchmarks.cs @@ -2,12 +2,14 @@ using HotChocolate.Execution.Processing; using HotChocolate.Language; using HotChocolate.StarWars; +using HotChocolate.Types; namespace HotChocolate.Execution.Benchmarks { [RPlotExporter, CategoriesColumn, RankColumn, MeanColumn, MedianColumn, MemoryDiagnoser] public class FieldCollectorBenchmarks { + private readonly InputParser _inputParser = new(); private readonly ISchema _schema; private readonly DocumentNode _introspectionQuery; private readonly FragmentCollection _introspectionFragments; @@ -39,7 +41,8 @@ public object PrepareSelectionSets_Introspection() _introspectionQuery, _introspectionOperation, _schema, - _schema.QueryType); + _schema.QueryType, + _inputParser); } [Benchmark] @@ -50,7 +53,8 @@ public object PrepareSelectionSets_StarWars() _starWarsQuery, _starWarsOperation, _schema, - _schema.QueryType); + _schema.QueryType, + _inputParser); } } } diff --git a/src/HotChocolate/Core/benchmark/Execution.Benchmarks/VariableCoercionBenchmarks.cs b/src/HotChocolate/Core/benchmark/Execution.Benchmarks/VariableCoercionBenchmarks.cs index 425bc2f1704..bcebf2613e2 100644 --- a/src/HotChocolate/Core/benchmark/Execution.Benchmarks/VariableCoercionBenchmarks.cs +++ b/src/HotChocolate/Core/benchmark/Execution.Benchmarks/VariableCoercionBenchmarks.cs @@ -57,7 +57,7 @@ public class VariableCoercionBenchmarks public VariableCoercionBenchmarks() { - _variableCoercionHelper = new VariableCoercionHelper(); + _variableCoercionHelper = new VariableCoercionHelper(new(), new()); _schema = SchemaBuilder.New().AddStarWarsTypes().Create(); } diff --git a/src/HotChocolate/Core/src/Execution/Options/ComplexityAnalyzerSettings.cs b/src/HotChocolate/Core/src/Execution/Options/ComplexityAnalyzerSettings.cs index 35d2b5c3d9e..86890466d19 100644 --- a/src/HotChocolate/Core/src/Execution/Options/ComplexityAnalyzerSettings.cs +++ b/src/HotChocolate/Core/src/Execution/Options/ComplexityAnalyzerSettings.cs @@ -63,7 +63,7 @@ public static int DefaultCalculation(ComplexityContext context) } var cost = context.Complexity + context.ChildComplexity; - bool needsDefaultMultiplier = true; + var needsDefaultMultiplier = true; foreach (MultiplierPathString multiplier in context.Multipliers) { @@ -74,7 +74,7 @@ public static int DefaultCalculation(ComplexityContext context) } } - if(needsDefaultMultiplier && context.DefaultMultiplier.HasValue) + if(needsDefaultMultiplier && context.DefaultMultiplier.HasValue) { cost *= context.DefaultMultiplier.Value; } diff --git a/src/HotChocolate/Core/src/Fetching/Extensions/ObjectFieldDataLoaderExtensions.cs b/src/HotChocolate/Core/src/Fetching/Extensions/ObjectFieldDataLoaderExtensions.cs index 8bef97dd2de..0c9cd663481 100644 --- a/src/HotChocolate/Core/src/Fetching/Extensions/ObjectFieldDataLoaderExtensions.cs +++ b/src/HotChocolate/Core/src/Fetching/Extensions/ObjectFieldDataLoaderExtensions.cs @@ -56,21 +56,18 @@ public static IObjectFieldDescriptor UseDataloader( definition.Type = TypeReference.Create(schemaType, TypeContext.Output); definition.Configurations.Add( - LazyTypeConfigurationBuilder - .New() - .Definition(definition) - .Configure( - (_, def) => - { - CompileMiddleware( - def, - placeholder, - keyType, - valueType, - dataLoaderType); - }) - .On(ApplyConfigurationOn.Completion) - .Build()); + new CompleteConfiguration( + (_, def) => + { + CompileMiddleware( + def, + placeholder, + keyType, + valueType, + dataLoaderType); + }, + definition, + ApplyConfigurationOn.Completion)); }); return descriptor; @@ -111,7 +108,7 @@ private static bool TryGetDataLoaderTypes( { if (interfaceType.IsGenericType) { - Type? typeDefinition = interfaceType.GetGenericTypeDefinition(); + Type typeDefinition = interfaceType.GetGenericTypeDefinition(); if (typeof(IDataLoader<,>) == typeDefinition) { key = interfaceType.GetGenericArguments()[0]; diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/ConnectionCountType.cs b/src/HotChocolate/Core/src/Types.CursorPagination/ConnectionCountType.cs deleted file mode 100644 index 74ddc83b58a..00000000000 --- a/src/HotChocolate/Core/src/Types.CursorPagination/ConnectionCountType.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace HotChocolate.Types.Pagination -{ - public class ConnectionCountType - : ConnectionType - where T : class, IOutputType - { - public ConnectionCountType() - { - } - - public ConnectionCountType( - Action> configure) - : base(descriptor => - { - ApplyConfig(descriptor); - configure(descriptor); - }) - { - } - - protected override void Configure(IObjectTypeDescriptor descriptor) => - ApplyConfig(descriptor); - - protected static new void ApplyConfig(IObjectTypeDescriptor descriptor) - { - ConnectionType.ApplyConfig(descriptor); - - descriptor - .Field("totalCount") - .Type>() - .ResolveWith(t => t.GetTotalCount(default!, default)); - } - - private sealed class Resolvers - { - public ValueTask GetTotalCount( - [Parent] Connection connection, - CancellationToken cancellationToken) => - connection.GetTotalCountAsync(cancellationToken); - } - } -} diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/ConnectionType.cs b/src/HotChocolate/Core/src/Types.CursorPagination/ConnectionType.cs index 34b0ded6f40..4faef5b63e8 100644 --- a/src/HotChocolate/Core/src/Types.CursorPagination/ConnectionType.cs +++ b/src/HotChocolate/Core/src/Types.CursorPagination/ConnectionType.cs @@ -1,87 +1,215 @@ using System; +using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using HotChocolate.Configuration; +using HotChocolate.Resolvers; +using HotChocolate.Types.Descriptors; using HotChocolate.Types.Descriptors.Definitions; +using static HotChocolate.Properties.TypeResources; namespace HotChocolate.Types.Pagination { - public class ConnectionType - : ObjectType + /// + /// The connection type. + /// + internal class ConnectionType + : ObjectType + , IConnectionType , IPageType - where T : class, IOutputType { - public ConnectionType() + internal ConnectionType( + NameString connectionName, + ITypeReference nodeType, + bool withTotalCount) { + if (nodeType is null) + { + throw new ArgumentNullException(nameof(nodeType)); + } + + ConnectionName = connectionName.EnsureNotEmpty(nameof(connectionName)); + NameString edgeTypeName = NameHelper.CreateEdgeName(connectionName); + + SyntaxTypeReference edgesType = + TypeReference.Parse( + $"[{edgeTypeName}!]", + TypeContext.Output, + factory: _ => new EdgeType(connectionName, nodeType)); + + Definition = CreateTypeDefinition(withTotalCount, edgesType); + Definition.Name = NameHelper.CreateConnectionName(connectionName); + Definition.Dependencies.Add(new(nodeType)); + Definition.Configurations.Add( + new CompleteConfiguration( + (c, d) => + { + var definition = (ObjectTypeDefinition)d; + ObjectFieldDefinition nodes = definition.Fields.First(IsNodesField); + nodes.Type = TypeReference.Parse( + $"[{c.GetType(nodeType).Print()}]", + TypeContext.Output); + }, + Definition, + ApplyConfigurationOn.Naming, + nodeType, + TypeDependencyKind.Named)); + Definition.Configurations.Add( + new CompleteConfiguration( + (c, _) => EdgeType = c.GetType(TypeReference.Create(edgeTypeName)), + Definition, + ApplyConfigurationOn.Completion)); } - public ConnectionType( - Action> configure) - : base(descriptor => - { - ApplyConfig(descriptor); - configure(descriptor); - }) + internal ConnectionType(ITypeReference nodeType, bool withTotalCount) { + if (nodeType is null) + { + throw new ArgumentNullException(nameof(nodeType)); + } + + DependantFactoryTypeReference edgeType = + TypeReference.Create( + ContextDataKeys.EdgeType, + nodeType, + _ => new EdgeType(nodeType), + TypeContext.Output); + + Definition = CreateTypeDefinition(withTotalCount); + Definition.Dependencies.Add(new(nodeType)); + Definition.Dependencies.Add(new(edgeType)); + Definition.NeedsNameCompletion = true; + + Definition.Configurations.Add( + new CompleteConfiguration( + (c, d) => + { + IType type = c.GetType(nodeType); + ConnectionName = type.NamedType().Name; + + var definition = (ObjectTypeDefinition)d; + ObjectFieldDefinition edges = definition.Fields.First(IsEdgesField); + ObjectFieldDefinition nodes = definition.Fields.First(IsNodesField); + + definition.Name = NameHelper.CreateConnectionName(ConnectionName); + edges.Type = TypeReference.Parse( + $"[{NameHelper.CreateEdgeName(ConnectionName)}!]", + TypeContext.Output); + + nodes.Type = TypeReference.Parse( + $"[{type.Print()}]", + TypeContext.Output); + }, + Definition, + ApplyConfigurationOn.Naming, + nodeType, + TypeDependencyKind.Named)); + Definition.Configurations.Add( + new CompleteConfiguration( + (c, _) => + { + EdgeType = c.GetType(edgeType); + }, + Definition, + ApplyConfigurationOn.Completion)); } + /// + /// Gets the connection name of this connection type. + /// + public NameString ConnectionName { get; private set; } + + /// + /// Gets the edge type of this connection. + /// public IEdgeType EdgeType { get; private set; } = default!; IOutputType IPageType.ItemType => EdgeType; - protected override void Configure(IObjectTypeDescriptor descriptor) => - ApplyConfig(descriptor); - - protected static void ApplyConfig(IObjectTypeDescriptor descriptor) + protected override void OnBeforeRegisterDependencies( + ITypeDiscoveryContext context, + DefinitionBase definition, + IDictionary contextData) { - descriptor - .Name(dependency => $"{dependency.Name}Connection") - .DependsOn() - .Description("A connection to a list of items.") - .BindFields(BindingBehavior.Explicit); - - descriptor - .Field(t => t.Info) - .Name("pageInfo") - .Description("Information to aid in pagination.") - .Type>(); - - descriptor - .Field(t => t.Edges) - .Name("edges") - .Description("A list of edges.") - .Type>>>(); - - descriptor - .Field(t => t.Edges.Select(t => t.Node)) - .Name("nodes") - .Description("A flattened list of the nodes.") - .Type>() - .Resolve(ctx => ctx.Parent().Edges.Select(t => t.Node)) - .Extend() - .OnBeforeCreate( - d => d.PureResolver = - ctx => ctx.Parent().Edges.Select(t => t.Node)); + context.RegisterDependency( + context.TypeInspector.GetOutputTypeRef(typeof(PageInfoType)), + TypeDependencyKind.Default); + + base.OnBeforeRegisterDependencies(context, definition, contextData); } - protected override void OnRegisterDependencies( - ITypeDiscoveryContext context, - ObjectTypeDefinition definition) + private static ObjectTypeDefinition CreateTypeDefinition( + bool withTotalCount, + ITypeReference? edgesType = null) { - base.OnRegisterDependencies(context, definition); + var definition = new ObjectTypeDefinition( + default, + ConnectionType_Description, + typeof(Connection)); - context.RegisterDependency( - context.TypeInspector.GetTypeRef(typeof(EdgeType)), - TypeDependencyKind.Default); + definition.Fields.Add(new( + Names.PageInfo, + ConnectionType_PageInfo_Description, + TypeReference.Parse("PageInfo!"), + pureResolver: GetPagingInfo)); + + definition.Fields.Add(new( + Names.Edges, + ConnectionType_Edges_Description, + edgesType, + pureResolver: GetEdges) + { CustomSettings = { ContextDataKeys.Edges } }); + + definition.Fields.Add(new( + Names.Nodes, + ConnectionType_Nodes_Description, + pureResolver: GetNodes) + { CustomSettings = { ContextDataKeys.Nodes } }); + + if (withTotalCount) + { + definition.Fields.Add(new( + Names.TotalCount, + type: TypeReference.Parse($"{ScalarNames.Int}!"), + resolver: GetTotalCountAsync)); + } + + return definition; } - protected override void OnCompleteType( - ITypeCompletionContext context, - ObjectTypeDefinition definition) + private static bool IsEdgesField(ObjectFieldDefinition field) + => field.CustomSettings.Count > 0 && + field.CustomSettings[0].Equals(ContextDataKeys.Edges); + + private static bool IsNodesField(ObjectFieldDefinition field) + => field.CustomSettings.Count > 0 && + field.CustomSettings[0].Equals(ContextDataKeys.Nodes); + + private static IPageInfo GetPagingInfo(IPureResolverContext context) + => context.Parent().Info; + + private static IReadOnlyCollection GetEdges(IPureResolverContext context) + => context.Parent().Edges; + + private static IEnumerable GetNodes(IPureResolverContext context) + => context.Parent().Edges.Select(t => t.Node); + + private static async ValueTask GetTotalCountAsync(IResolverContext context) + => await context.Parent().GetTotalCountAsync(context.RequestAborted); + + private static class Names { - base.OnCompleteType(context, definition); + public const string PageInfo = "pageInfo"; + public const string Edges = "edges"; + public const string Nodes = "nodes"; + public const string TotalCount = "totalCount"; + } - EdgeType = context.GetType>( - context.TypeInspector.GetTypeRef(typeof(EdgeType))); + private static class ContextDataKeys + { + public const string EdgeType = "HotChocolate_Types_Edge"; + public const string Edges = "HotChocolate.Types.Connection.Edges"; + public const string Nodes = "HotChocolate.Types.Connection.Nodes"; } } } diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/EdgeType.cs b/src/HotChocolate/Core/src/Types.CursorPagination/EdgeType.cs index a93e6ff8d3c..d72c4f090a4 100644 --- a/src/HotChocolate/Core/src/Types.CursorPagination/EdgeType.cs +++ b/src/HotChocolate/Core/src/Types.CursorPagination/EdgeType.cs @@ -1,45 +1,124 @@ -using HotChocolate.Configuration; +using System; +using HotChocolate.Resolvers; +using HotChocolate.Types.Descriptors; using HotChocolate.Types.Descriptors.Definitions; +using static HotChocolate.Properties.TypeResources; namespace HotChocolate.Types.Pagination { - public class EdgeType - : ObjectType - , IEdgeType - where T : class, IOutputType + /// + /// Represents an edge in a connection. + /// + internal sealed class EdgeType : ObjectType, IEdgeType { - public IOutputType EntityType { get; private set; } = default!; + internal EdgeType( + NameString connectionName, + ITypeReference nodeType) + { + if (nodeType is null) + { + throw new ArgumentNullException(nameof(nodeType)); + } + + ConnectionName = connectionName.EnsureNotEmpty(nameof(connectionName)); + Definition = CreateTypeDefinition(nodeType); + Definition.Name = NameHelper.CreateEdgeName(connectionName); + Definition.Configurations.Add( + new CompleteConfiguration( + (c, _) => NodeType = c.GetType(nodeType), + Definition, + ApplyConfigurationOn.Completion)); + } - protected override void Configure( - IObjectTypeDescriptor descriptor) + internal EdgeType(ITypeReference nodeType) { - descriptor - .Name(dependency => dependency.Name + "Edge") - .DependsOn() - .Description("An edge in a connection.") - .BindFields(BindingBehavior.Explicit); - - descriptor - .Field(t => t.Cursor) - .Name("cursor") - .Description("A cursor for use in pagination.") - .Type>(); - - descriptor - .Field(t => t.Node) - .Name("node") - .Description("The item at the end of the edge.") - .Type(); + if (nodeType is null) + { + throw new ArgumentNullException(nameof(nodeType)); + } + + Definition = CreateTypeDefinition(nodeType); + Definition.Configurations.Add( + new CompleteConfiguration( + (c, d) => + { + IType type = c.GetType(nodeType); + ConnectionName = type.NamedType().Name; + ((ObjectTypeDefinition)d).Name = NameHelper.CreateEdgeName(ConnectionName); + }, + Definition, + ApplyConfigurationOn.Naming, + nodeType, + TypeDependencyKind.Named)); + Definition.Configurations.Add( + new CompleteConfiguration( + (c, _) => NodeType = c.GetType(nodeType), + Definition, + ApplyConfigurationOn.Completion)); } - protected override void OnCompleteType( - ITypeCompletionContext context, - ObjectTypeDefinition definition) + /// + /// Gets the connection name of this connection type. + /// + public NameString ConnectionName { get; private set; } + + /// + public IOutputType NodeType { get; private set; } = default!; + + /// + [Obsolete("Use NodeType.")] + public IOutputType EntityType => NodeType; + + /// + public override bool IsInstanceOfType(IResolverContext context, object resolverResult) { - base.OnCompleteType(context, definition); + if (resolverResult is IEdge { Node: not null } edge) + { + IType nodeType = NodeType; + + if (nodeType.Kind is TypeKind.NonNull) + { + nodeType = nodeType.InnerType(); + } - EntityType = context.GetType( - context.TypeInspector.GetTypeRef(typeof(T))); + if (nodeType.Kind is not TypeKind.Object) + { + throw new GraphQLException( + EdgeType_IsInstanceOfType_NonObject); + } + + return ((ObjectType)nodeType).IsInstanceOfType(context, edge.Node); + } + + return false; + } + + private static ObjectTypeDefinition CreateTypeDefinition(ITypeReference nodeType) + => new(default, EdgeType_Description, typeof(IEdge)) + { + Fields = + { + new(Names.Cursor, + EdgeType_Cursor_Description, + TypeReference.Parse($"{ScalarNames.String}!"), + pureResolver: GetCursor), + new(Names.Node, + EdgeType_Node_Description, + nodeType, + pureResolver: GetNode) + } + }; + + private static string GetCursor(IPureResolverContext context) + => context.Parent().Cursor; + + private static object? GetNode(IPureResolverContext context) + => context.Parent().Node; + + private static class Names + { + public const string Cursor = "cursor"; + public const string Node = "node"; } } } diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/Extensions/CursorPagingRequestExecutorBuilderExtension.cs b/src/HotChocolate/Core/src/Types.CursorPagination/Extensions/CursorPagingRequestExecutorBuilderExtension.cs new file mode 100644 index 00000000000..4db7c3f3a6d --- /dev/null +++ b/src/HotChocolate/Core/src/Types.CursorPagination/Extensions/CursorPagingRequestExecutorBuilderExtension.cs @@ -0,0 +1,137 @@ +using System; +using HotChocolate.Execution.Configuration; +using HotChocolate.Types.Pagination; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// GraphQL Configurations for cursor pagination. + /// + public static class CursorPagingRequestExecutorBuilderExtension + { + /// + /// Adds the queryable cursor paging provider to the DI. + /// + /// + /// The request executor builder. + /// + /// + /// The name the provider shall have. + /// + /// + /// Defines if the registered provider shall be registered as the default provider. + /// + /// + /// The request executor builder. + /// + /// + /// is null. + /// + public static IRequestExecutorBuilder AddQueryableCursorPagingProvider( + this IRequestExecutorBuilder builder, + string? providerName = null, + bool defaultProvider = false) + => AddCursorPagingProvider( + builder, + providerName, + defaultProvider); + + /// + /// Adds a cursor paging provider to the DI. + /// + /// + /// The request executor builder. + /// + /// + /// The name the provider shall have. + /// + /// + /// Defines if the registered provider shall be registered as the default provider. + /// + /// + /// The type of the provider. + /// + /// + /// The request executor builder. + /// + /// + /// is null. + /// + public static IRequestExecutorBuilder AddCursorPagingProvider( + this IRequestExecutorBuilder builder, + string? providerName = null, + bool defaultProvider = false) + where TProvider : CursorPagingProvider + { + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + builder.Services.TryAddSingleton(); + + AddCursorPagingProvider( + builder, + s => s.GetRequiredService(), + providerName, + defaultProvider); + + return builder; + } + + /// + /// Adds a cursor paging provider to the DI. + /// + /// + /// The request executor builder. + /// + /// + /// A factory to create the paging provider. + /// + /// + /// The name the provider shall have. + /// + /// + /// Defines if the registered provider shall be registered as the default provider. + /// + /// + /// The type of the provider. + /// + /// + /// The request executor builder. + /// + /// + /// is null. + /// + public static IRequestExecutorBuilder AddCursorPagingProvider( + this IRequestExecutorBuilder builder, + Func factory, + string? providerName = null, + bool defaultProvider = false) + where TProvider : CursorPagingProvider + { + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (defaultProvider) + { + var service = ServiceDescriptor.Singleton( + typeof(PagingProviderEntry), + CreateEntry); + builder.Services.Insert(0, service); + } + else + { + builder.Services.AddSingleton(CreateEntry); + } + + return builder; + + PagingProviderEntry CreateEntry(IServiceProvider services) + => new(providerName, factory(services)); + } + } +} diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/Extensions/GetCursorPagingProvider.cs b/src/HotChocolate/Core/src/Types.CursorPagination/Extensions/GetCursorPagingProvider.cs index d2b874627d4..4cbb126ee2f 100644 --- a/src/HotChocolate/Core/src/Types.CursorPagination/Extensions/GetCursorPagingProvider.cs +++ b/src/HotChocolate/Core/src/Types.CursorPagination/Extensions/GetCursorPagingProvider.cs @@ -14,10 +14,14 @@ namespace HotChocolate.Types /// /// The source type. /// + /// + /// The paging provider name that shall be selected. + /// /// /// Returns a paging provider for the specified . /// public delegate CursorPagingProvider GetCursorPagingProvider( IServiceProvider services, - IExtendedType sourceType); + IExtendedType sourceType, + string? providerName = null); } diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/Extensions/PagingObjectFieldDescriptorExtensions.cs b/src/HotChocolate/Core/src/Types.CursorPagination/Extensions/PagingObjectFieldDescriptorExtensions.cs index 962703cac0d..9992f9f6e34 100644 --- a/src/HotChocolate/Core/src/Types.CursorPagination/Extensions/PagingObjectFieldDescriptorExtensions.cs +++ b/src/HotChocolate/Core/src/Types.CursorPagination/Extensions/PagingObjectFieldDescriptorExtensions.cs @@ -7,7 +7,6 @@ using HotChocolate.Types.Descriptors; using HotChocolate.Types.Descriptors.Definitions; using HotChocolate.Types.Pagination; -using static HotChocolate.Utilities.ThrowHelper; using static HotChocolate.Types.Pagination.PagingDefaults; using static HotChocolate.Types.Pagination.CursorPagingArgumentNames; using static HotChocolate.Types.Properties.CursorResources; @@ -16,26 +15,40 @@ namespace HotChocolate.Types { public static class PagingObjectFieldDescriptorExtensions { - public static IObjectFieldDescriptor UsePaging( + public static IObjectFieldDescriptor UsePaging( this IObjectFieldDescriptor descriptor, GetCursorPagingProvider? resolvePagingProvider = null, + NameString? connectionName = null, PagingOptions options = default) - where TSchemaType : class, IOutputType => - UsePaging(descriptor, typeof(TEntity), resolvePagingProvider, options); + where TNodeType : class, IOutputType => + UsePaging( + descriptor, + typeof(TEntity), + resolvePagingProvider, + connectionName, + options); - public static IObjectFieldDescriptor UsePaging( + public static IObjectFieldDescriptor UsePaging( this IObjectFieldDescriptor descriptor, Type? entityType = null, GetCursorPagingProvider? resolvePagingProvider = null, + NameString? connectionName = null, PagingOptions options = default) - where TSchemaType : class, IOutputType => - UsePaging(descriptor, typeof(TSchemaType), entityType, resolvePagingProvider, options); + where TNodeType : class, IOutputType => + UsePaging( + descriptor, + typeof(TNodeType), + entityType, + resolvePagingProvider, + connectionName, + options); public static IObjectFieldDescriptor UsePaging( this IObjectFieldDescriptor descriptor, - Type? type = null, + Type? nodeType = null, Type? entityType = null, GetCursorPagingProvider? resolvePagingProvider = null, + NameString? connectionName = null, PagingOptions options = default) { if (descriptor is null) @@ -47,50 +60,51 @@ public static IObjectFieldDescriptor UsePaging( PagingHelper.UsePaging( descriptor, - type, entityType, - (services, source) => resolvePagingProvider(services, source), + (services, source, name) => resolvePagingProvider(services, source, name), options); descriptor .Extend() .OnBeforeCreate((c, d) => { - if (!(c.ContextData.TryGetValue(typeof(PagingOptions).FullName!, out var obj) && - obj is PagingOptions pagingOptions)) + PagingOptions pagingOptions = c.GetSettings(options); + var backward = pagingOptions.AllowBackwardPagination ?? AllowBackwardPagination; + + CreatePagingArguments(d.Arguments, backward); + + if (connectionName is null or { IsEmpty: true }) { - pagingOptions = default; + connectionName = + pagingOptions.InferConnectionNameFromField ?? + InferConnectionNameFromField + ? (NameString?)EnsureConnectionNameCasing(d.Name) + : null; } - var backward = pagingOptions.AllowBackwardPagination ?? AllowBackwardPagination; + ITypeReference? typeRef = nodeType is not null + ? c.TypeInspector.GetTypeRef(nodeType) + : null; - var field = ObjectFieldDescriptor.From(c, d); - field.AddPagingArguments(backward); - field.CreateDefinition(); + MemberInfo? resolverMember = d.ResolverMember ?? d.Member; + d.Type = CreateConnectionTypeRef(c, resolverMember, connectionName, typeRef, options); + d.CustomSettings.Add(typeof(Connection)); }); - descriptor - .Extend() - .OnBeforeCreate( - (c, d) => - { - MemberInfo? resolverMember = d.ResolverMember ?? d.Member; - d.Type = CreateConnectionTypeRef(c, resolverMember, type, options); - d.CustomSettings.Add(typeof(Connection)); - }); - return descriptor; } - public static IInterfaceFieldDescriptor UsePaging( + public static IInterfaceFieldDescriptor UsePaging( this IInterfaceFieldDescriptor descriptor, + NameString? connectionName = null, PagingOptions options = default) - where TSchemaType : class, IOutputType => - UsePaging(descriptor, typeof(TSchemaType), options); + where TNodeType : class, IOutputType => + UsePaging(descriptor, typeof(TNodeType), connectionName, options); public static IInterfaceFieldDescriptor UsePaging( this IInterfaceFieldDescriptor descriptor, - Type? type = null, + Type? nodeType = null, + NameString? connectionName = null, PagingOptions options = default) { if (descriptor is null) @@ -102,23 +116,27 @@ public static IInterfaceFieldDescriptor UsePaging( .Extend() .OnBeforeCreate((c, d) => { - if (!(c.ContextData.TryGetValue(typeof(PagingOptions).FullName!, out var obj) && - obj is PagingOptions pagingOptions)) + PagingOptions pagingOptions = c.GetSettings(options); + var backward = pagingOptions.AllowBackwardPagination ?? AllowBackwardPagination; + + CreatePagingArguments(d.Arguments, backward); + + if (connectionName is null or { IsEmpty: true }) { - pagingOptions = default; + connectionName = + pagingOptions.InferConnectionNameFromField ?? + InferConnectionNameFromField + ? (NameString?)EnsureConnectionNameCasing(d.Name) + : null; } - var backward = pagingOptions.AllowBackwardPagination ?? AllowBackwardPagination; + ITypeReference? typeRef = nodeType is not null + ? c.TypeInspector.GetTypeRef(nodeType) + : null; - var field = InterfaceFieldDescriptor.From(c, d); - field.AddPagingArguments(backward); - field.CreateDefinition(); + d.Type = CreateConnectionTypeRef(c, d.Member, connectionName, typeRef, options); }); - descriptor - .Extend() - .OnBeforeCreate( - (c, d) => d.Type = CreateConnectionTypeRef(c, d.Member, type, options)); return descriptor; } @@ -167,8 +185,8 @@ private static void CreatePagingArguments( IList arguments, bool allowBackwardPagination) { - SyntaxTypeReference intType = TypeReference.Parse("Int"); - SyntaxTypeReference stringType = TypeReference.Parse("String"); + SyntaxTypeReference intType = TypeReference.Parse(ScalarNames.Int); + SyntaxTypeReference stringType = TypeReference.Parse(ScalarNames.String); arguments.AddOrUpdate(First, PagingArguments_First_Description, intType); arguments.AddOrUpdate(After, PagingArguments_After_Description, stringType); @@ -201,48 +219,60 @@ private static void AddOrUpdate( private static ITypeReference CreateConnectionTypeRef( IDescriptorContext context, MemberInfo? resolverMember, - Type? type, + NameString? connectionName, + ITypeReference? nodeType, PagingOptions options) { - // first we will try and infer the schema type from the collection. - IExtendedType schemaType = PagingHelper.GetSchemaType( - context.TypeInspector, - resolverMember, - type); - - // we need to ensure that the schema type is a valid output type. For this we create a - // type info which decomposes the type into its logical type components and is able - // to check if the named type component is really an output type. - if (!context.TypeInspector.TryCreateTypeInfo(schemaType, out ITypeInfo? typeInfo) || - !typeInfo.IsOutputType()) + if (nodeType is null) { - throw PagingObjectFieldDescriptorExtensions_InvalidType(); + // if there is no explicit node type provided we will try and + // infer the schema type from the resolver member. + nodeType = TypeReference.Create( + PagingHelper.GetSchemaType( + context.TypeInspector, + resolverMember, + null), + TypeContext.Output); } options = context.GetSettings(options); - // once we have identified the correct type we will create the - // paging result type from it. - IExtendedType connectionType = context.TypeInspector.GetType( - options.IncludeTotalCount ?? false - ? typeof(ConnectionCountType<>).MakeGenericType(schemaType.Source) - : typeof(ConnectionType<>).MakeGenericType(schemaType.Source)); - // last but not leas we create a type reference that can be put on the field definition // to tell the type discovery that this field needs this result type. - return TypeReference.Create(connectionType, TypeContext.Output); + return CreateConnectionType( + connectionName, + nodeType, + options.IncludeTotalCount ?? false); } private static CursorPagingProvider ResolvePagingProvider( IServiceProvider services, - IExtendedType source) + IExtendedType source, + string? providerName) { try { - if (services.GetService>() is { } providers && - providers.FirstOrDefault(p => p.CanHandle(source)) is { } provider) + Func predicate = + providerName is null + ? entry => entry.Provider.CanHandle(source) + : entry => providerName.Equals(entry.Name, StringComparison.Ordinal); + PagingProviderEntry? defaultEntry = null; + + + foreach (var entry in services.GetServices()) + { + // the first provider is expected to be the default provider. + defaultEntry ??= entry; + + if (predicate(entry)) + { + return entry.Provider; + } + } + + if (defaultEntry is not null) { - return provider; + return defaultEntry.Provider; } } catch (InvalidOperationException) @@ -251,7 +281,38 @@ private static CursorPagingProvider ResolvePagingProvider( // in this case we will ignore the exception and return the default provider. } + // if no provider was added we will fallback to the queryable paging provider. return new QueryableCursorPagingProvider(); } + + private static ITypeReference CreateConnectionType( + NameString? connectionName, + ITypeReference nodeType, + bool withTotalCount) + { + return connectionName is null + ? TypeReference.Create( + "HotChocolate_Types_Connection", + nodeType, + _ => new ConnectionType(nodeType, withTotalCount), + TypeContext.Output) + : TypeReference.Create( + connectionName.Value + "Connection", + TypeContext.Output, + factory: _ => new ConnectionType( + connectionName.Value, + nodeType, + withTotalCount)); + } + + private static NameString EnsureConnectionNameCasing(string connectionName) + { + if (char.IsUpper(connectionName[0])) + { + return connectionName; + } + + return string.Concat(char.ToUpper(connectionName[0]), connectionName.Substring(1)); + } } } diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/Extensions/UsePagingAttribute.cs b/src/HotChocolate/Core/src/Types.CursorPagination/Extensions/UsePagingAttribute.cs index dde3e7f48e6..dda4bbe1209 100644 --- a/src/HotChocolate/Core/src/Types.CursorPagination/Extensions/UsePagingAttribute.cs +++ b/src/HotChocolate/Core/src/Types.CursorPagination/Extensions/UsePagingAttribute.cs @@ -10,10 +10,13 @@ namespace HotChocolate.Types /// public sealed class UsePagingAttribute : DescriptorAttribute { + private string? _connectionName; private int? _defaultPageSize; private int? _maxPageSize; private bool? _includeTotalCount; private bool? _allowBackwardPagination; + private bool? _requirePagingBoundaries; + private bool? _inferConnectionNameFromField; /// /// Applies the offset paging middleware to the annotated property. @@ -43,6 +46,15 @@ public Type? SchemaType /// public Type? Type { get; private set; } + /// + /// Specifies the connection name. + /// + public string? ConnectionName + { + get => _connectionName; + set => _connectionName = value; + } + /// /// Specifies the default page size for this field. /// @@ -79,7 +91,32 @@ public bool AllowBackwardPagination set => _allowBackwardPagination = value; } - protected override void TryConfigure( + /// + /// Defines if the paging middleware shall require the + /// API consumer to specify paging boundaries. + /// + public bool RequirePagingBoundaries + { + get => _requirePagingBoundaries ?? PagingDefaults.AllowBackwardPagination; + set => _requirePagingBoundaries = value; + } + + /// + /// Connection names are by default inferred from the field name to + /// which they are bound to as opposed to the node type name. + /// + public bool InferConnectionNameFromField + { + get => _inferConnectionNameFromField ?? PagingDefaults.AllowBackwardPagination; + set => _inferConnectionNameFromField = value; + } + + /// + /// Specifies the name of the paging provider that shall be used. + /// + public string? ProviderName { get; set; } + + protected internal override void TryConfigure( IDescriptorContext context, IDescriptor descriptor, ICustomAttributeProvider element) @@ -90,24 +127,36 @@ protected override void TryConfigure( { ofd.UsePaging( Type, + connectionName: string.IsNullOrEmpty(_connectionName) + ? default(NameString?) + : _connectionName, options: new PagingOptions { DefaultPageSize = _defaultPageSize, MaxPageSize = _maxPageSize, IncludeTotalCount = _includeTotalCount, - AllowBackwardPagination = AllowBackwardPagination + AllowBackwardPagination = _allowBackwardPagination, + RequirePagingBoundaries = _requirePagingBoundaries, + InferConnectionNameFromField = _inferConnectionNameFromField, + ProviderName = ProviderName }); } else if (descriptor is IInterfaceFieldDescriptor ifd) { ifd.UsePaging( Type, - new PagingOptions + connectionName: string.IsNullOrEmpty(_connectionName) + ? default(NameString?) + : _connectionName, + options: new() { DefaultPageSize = _defaultPageSize, MaxPageSize = _maxPageSize, IncludeTotalCount = _includeTotalCount, - AllowBackwardPagination = AllowBackwardPagination + AllowBackwardPagination = _allowBackwardPagination, + RequirePagingBoundaries = _requirePagingBoundaries, + InferConnectionNameFromField = _inferConnectionNameFromField, + ProviderName = ProviderName }); } } diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/IConnectionType.cs b/src/HotChocolate/Core/src/Types.CursorPagination/IConnectionType.cs new file mode 100644 index 00000000000..0d91265e68d --- /dev/null +++ b/src/HotChocolate/Core/src/Types.CursorPagination/IConnectionType.cs @@ -0,0 +1,18 @@ +namespace HotChocolate.Types.Pagination +{ + /// + /// The connection type. + /// + public interface IConnectionType : IObjectType + { + /// + /// Gets the connection name of this connection type. + /// + NameString ConnectionName { get; } + + /// + /// Gets the edge type of this connection. + /// + IEdgeType EdgeType { get; } + } +} diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/IEdgeType.cs b/src/HotChocolate/Core/src/Types.CursorPagination/IEdgeType.cs index 7257324a901..42b06d4030d 100644 --- a/src/HotChocolate/Core/src/Types.CursorPagination/IEdgeType.cs +++ b/src/HotChocolate/Core/src/Types.CursorPagination/IEdgeType.cs @@ -1,7 +1,26 @@ -namespace HotChocolate.Types.Pagination +using System; + +namespace HotChocolate.Types.Pagination { + /// + /// Represents an edge in a connection. + /// public interface IEdgeType : IObjectType { + /// + /// Gets the connection name of this connection type. + /// + NameString ConnectionName { get; } + + /// + /// Gets the item type of the node field on the edge type. + /// + IOutputType NodeType { get; } + + /// + /// Gets the item type of the node field on the edge type. + /// + [Obsolete("Use NodeType.")] IOutputType EntityType { get; } } } diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/NameHelper.cs b/src/HotChocolate/Core/src/Types.CursorPagination/NameHelper.cs new file mode 100644 index 00000000000..b59581b6cfc --- /dev/null +++ b/src/HotChocolate/Core/src/Types.CursorPagination/NameHelper.cs @@ -0,0 +1,11 @@ +namespace HotChocolate.Types.Pagination +{ + internal static class NameHelper + { + public static string CreateConnectionName(NameString connectionName) + => connectionName + "Connection"; + + public static string CreateEdgeName(NameString connectionName) + => connectionName + "Edge"; + } +} diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/PagingProviderEntry.cs b/src/HotChocolate/Core/src/Types.CursorPagination/PagingProviderEntry.cs new file mode 100644 index 00000000000..f29125af911 --- /dev/null +++ b/src/HotChocolate/Core/src/Types.CursorPagination/PagingProviderEntry.cs @@ -0,0 +1,15 @@ +namespace HotChocolate.Types.Pagination +{ + internal sealed class PagingProviderEntry + { + public PagingProviderEntry(string? name, CursorPagingProvider provider) + { + Name = name; + Provider = provider; + } + + public string? Name { get; } + + public CursorPagingProvider Provider { get; } + } +} diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/PublicAPI.Shipped.txt b/src/HotChocolate/Core/src/Types.CursorPagination/PublicAPI.Shipped.txt index c779cabf3cf..69821058b16 100644 --- a/src/HotChocolate/Core/src/Types.CursorPagination/PublicAPI.Shipped.txt +++ b/src/HotChocolate/Core/src/Types.CursorPagination/PublicAPI.Shipped.txt @@ -12,9 +12,6 @@ HotChocolate.Types.Pagination.Connection.Info.get -> HotChocolate.Types.Paginati HotChocolate.Types.Pagination.Connection HotChocolate.Types.Pagination.Connection.Connection(System.Collections.Generic.IReadOnlyCollection!>! edges, HotChocolate.Types.Pagination.ConnectionPageInfo! info, System.Func>! getTotalCount) -> void HotChocolate.Types.Pagination.Connection.Edges.get -> System.Collections.Generic.IReadOnlyCollection!>! -HotChocolate.Types.Pagination.ConnectionCountType -HotChocolate.Types.Pagination.ConnectionCountType.ConnectionCountType() -> void -HotChocolate.Types.Pagination.ConnectionCountType.ConnectionCountType(System.Action!>! configure) -> void HotChocolate.Types.Pagination.ConnectionPageInfo HotChocolate.Types.Pagination.ConnectionPageInfo.ConnectionPageInfo(bool hasNextPage, bool hasPreviousPage, string? startCursor, string? endCursor, int? totalCount = null) -> void HotChocolate.Types.Pagination.ConnectionPageInfo.EndCursor.get -> string? @@ -22,10 +19,6 @@ HotChocolate.Types.Pagination.ConnectionPageInfo.HasNextPage.get -> bool HotChocolate.Types.Pagination.ConnectionPageInfo.HasPreviousPage.get -> bool HotChocolate.Types.Pagination.ConnectionPageInfo.StartCursor.get -> string? HotChocolate.Types.Pagination.ConnectionPageInfo.TotalCount.get -> int? -HotChocolate.Types.Pagination.ConnectionType -HotChocolate.Types.Pagination.ConnectionType.ConnectionType() -> void -HotChocolate.Types.Pagination.ConnectionType.ConnectionType(System.Action!>! configure) -> void -HotChocolate.Types.Pagination.ConnectionType.EdgeType.get -> HotChocolate.Types.Pagination.IEdgeType! HotChocolate.Types.Pagination.CursorPagingArguments HotChocolate.Types.Pagination.CursorPagingArguments.After.get -> string? HotChocolate.Types.Pagination.CursorPagingArguments.Before.get -> string? @@ -49,9 +42,6 @@ HotChocolate.Types.Pagination.Edge HotChocolate.Types.Pagination.Edge.Cursor.get -> string! HotChocolate.Types.Pagination.Edge.Edge(T node, string! cursor) -> void HotChocolate.Types.Pagination.Edge.Node.get -> T -HotChocolate.Types.Pagination.EdgeType -HotChocolate.Types.Pagination.EdgeType.EdgeType() -> void -HotChocolate.Types.Pagination.EdgeType.EntityType.get -> HotChocolate.Types.IOutputType! HotChocolate.Types.Pagination.IEdge HotChocolate.Types.Pagination.IEdge.Cursor.get -> string! HotChocolate.Types.Pagination.IEdge.Node.get -> object? @@ -77,30 +67,17 @@ HotChocolate.Types.UsePagingAttribute.SchemaType.get -> System.Type? HotChocolate.Types.UsePagingAttribute.SchemaType.set -> void HotChocolate.Types.UsePagingAttribute.Type.get -> System.Type? HotChocolate.Types.UsePagingAttribute.UsePagingAttribute(System.Type? type = null) -> void -override HotChocolate.Types.Pagination.ConnectionCountType.Configure(HotChocolate.Types.IObjectTypeDescriptor! descriptor) -> void -override HotChocolate.Types.Pagination.ConnectionType.Configure(HotChocolate.Types.IObjectTypeDescriptor! descriptor) -> void -override HotChocolate.Types.Pagination.ConnectionType.OnCompleteType(HotChocolate.Configuration.ITypeCompletionContext! context, HotChocolate.Types.Descriptors.Definitions.ObjectTypeDefinition! definition) -> void -override HotChocolate.Types.Pagination.ConnectionType.OnRegisterDependencies(HotChocolate.Configuration.ITypeDiscoveryContext! context, HotChocolate.Types.Descriptors.Definitions.ObjectTypeDefinition! definition) -> void -override HotChocolate.Types.Pagination.EdgeType.Configure(HotChocolate.Types.IObjectTypeDescriptor! descriptor) -> void -override HotChocolate.Types.Pagination.EdgeType.OnCompleteType(HotChocolate.Configuration.ITypeCompletionContext! context, HotChocolate.Types.Descriptors.Definitions.ObjectTypeDefinition! definition) -> void override HotChocolate.Types.Pagination.PageInfoType.Configure(HotChocolate.Types.IObjectTypeDescriptor! descriptor) -> void override HotChocolate.Types.Pagination.QueryableCursorPagingHandler.SliceAsync(HotChocolate.Resolvers.IResolverContext! context, object! source, HotChocolate.Types.Pagination.CursorPagingArguments arguments) -> System.Threading.Tasks.ValueTask override HotChocolate.Types.Pagination.QueryableCursorPagingProvider.CanHandle(HotChocolate.Internal.IExtendedType! source) -> bool override HotChocolate.Types.Pagination.QueryableCursorPagingProvider.CreateHandler(HotChocolate.Internal.IExtendedType! source, HotChocolate.Types.Pagination.PagingOptions options) -> HotChocolate.Types.Pagination.CursorPagingHandler! static HotChocolate.Types.EnumerableCursorPagingExtensions.ApplyCursorPaginationAsync(this System.Collections.Generic.IEnumerable! source, HotChocolate.Types.Pagination.CursorPagingArguments arguments, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask static HotChocolate.Types.EnumerableCursorPagingExtensions.ApplyCursorPaginationAsync(this System.Collections.Generic.IEnumerable! source, int? first = null, int? last = null, string? after = null, string? before = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask -static HotChocolate.Types.Pagination.ConnectionCountType.ApplyConfig(HotChocolate.Types.IObjectTypeDescriptor! descriptor) -> void -static HotChocolate.Types.Pagination.ConnectionType.ApplyConfig(HotChocolate.Types.IObjectTypeDescriptor! descriptor) -> void static HotChocolate.Types.Pagination.CursorPagingHelper.ApplyPagination(TSource source, HotChocolate.Types.Pagination.CursorPagingArguments arguments, HotChocolate.Types.Pagination.CursorPagingHelper.ApplySkip! applySkip, HotChocolate.Types.Pagination.CursorPagingHelper.ApplyTake! applyTake, HotChocolate.Types.Pagination.CursorPagingHelper.ToIndexEdgesAsync! toIndexEdgesAsync, HotChocolate.Types.Pagination.CursorPagingHelper.CountAsync! countAsync, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.ValueTask static HotChocolate.Types.Pagination.IndexEdge.Create(T node, int index) -> HotChocolate.Types.Pagination.IndexEdge! static HotChocolate.Types.Pagination.IndexEdge.DeserializeCursor(string! cursor) -> int static HotChocolate.Types.PagingObjectFieldDescriptorExtensions.AddPagingArguments(this HotChocolate.Types.IInterfaceFieldDescriptor! descriptor) -> HotChocolate.Types.IInterfaceFieldDescriptor! static HotChocolate.Types.PagingObjectFieldDescriptorExtensions.AddPagingArguments(this HotChocolate.Types.IObjectFieldDescriptor! descriptor) -> HotChocolate.Types.IObjectFieldDescriptor! -static HotChocolate.Types.PagingObjectFieldDescriptorExtensions.UsePaging(this HotChocolate.Types.IInterfaceFieldDescriptor! descriptor, System.Type? type = null, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IInterfaceFieldDescriptor! -static HotChocolate.Types.PagingObjectFieldDescriptorExtensions.UsePaging(this HotChocolate.Types.IObjectFieldDescriptor! descriptor, System.Type? type = null, System.Type? entityType = null, HotChocolate.Types.GetCursorPagingProvider? resolvePagingProvider = null, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IObjectFieldDescriptor! -static HotChocolate.Types.PagingObjectFieldDescriptorExtensions.UsePaging(this HotChocolate.Types.IObjectFieldDescriptor! descriptor, HotChocolate.Types.GetCursorPagingProvider? resolvePagingProvider = null, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IObjectFieldDescriptor! -static HotChocolate.Types.PagingObjectFieldDescriptorExtensions.UsePaging(this HotChocolate.Types.IInterfaceFieldDescriptor! descriptor, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IInterfaceFieldDescriptor! -static HotChocolate.Types.PagingObjectFieldDescriptorExtensions.UsePaging(this HotChocolate.Types.IObjectFieldDescriptor! descriptor, System.Type? entityType = null, HotChocolate.Types.GetCursorPagingProvider? resolvePagingProvider = null, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IObjectFieldDescriptor! virtual HotChocolate.Types.Pagination.QueryableCursorPagingHandler.ApplyCursorToEdges(System.Linq.IQueryable! allEdges, int? after, int? before) -> System.Linq.IQueryable! virtual HotChocolate.Types.Pagination.QueryableCursorPagingHandler.ExecuteQueryableAsync(System.Linq.IQueryable! queryable, int offset, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask!>!> virtual HotChocolate.Types.Pagination.QueryableCursorPagingHandler.GetFirstEdges(System.Linq.IQueryable! edges, int first, ref int offset) -> System.Linq.IQueryable! diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/PublicAPI.Unshipped.txt b/src/HotChocolate/Core/src/Types.CursorPagination/PublicAPI.Unshipped.txt index c2eb6734205..04378457bad 100644 --- a/src/HotChocolate/Core/src/Types.CursorPagination/PublicAPI.Unshipped.txt +++ b/src/HotChocolate/Core/src/Types.CursorPagination/PublicAPI.Unshipped.txt @@ -3,9 +3,54 @@ *REMOVED*virtual HotChocolate.Types.Pagination.QueryableCursorPagingHandler.GetFirstEdges(System.Linq.IQueryable! edges, int first, ref int offset) -> System.Linq.IQueryable! *REMOVED*virtual HotChocolate.Types.Pagination.QueryableCursorPagingHandler.GetLastEdges(System.Linq.IQueryable! edges, int last, ref int offset) -> System.Linq.IQueryable! HotChocolate.Types.Pagination.CursorPagingHandler.RequirePagingBoundaries.get -> bool +HotChocolate.Types.Pagination.IConnectionType +HotChocolate.Types.Pagination.IConnectionType.ConnectionName.get -> HotChocolate.NameString +HotChocolate.Types.Pagination.IConnectionType.EdgeType.get -> HotChocolate.Types.Pagination.IEdgeType! +HotChocolate.Types.Pagination.IEdgeType.ConnectionName.get -> HotChocolate.NameString +HotChocolate.Types.Pagination.IEdgeType.NodeType.get -> HotChocolate.Types.IOutputType! HotChocolate.Types.UsePagingAttribute.AllowBackwardPagination.get -> bool HotChocolate.Types.UsePagingAttribute.AllowBackwardPagination.set -> void +HotChocolate.Types.UsePagingAttribute.ConnectionName.get -> string? +HotChocolate.Types.UsePagingAttribute.ConnectionName.set -> void +HotChocolate.Types.UsePagingAttribute.InferConnectionNameFromField.get -> bool +HotChocolate.Types.UsePagingAttribute.InferConnectionNameFromField.set -> void +HotChocolate.Types.UsePagingAttribute.ProviderName.get -> string? +HotChocolate.Types.UsePagingAttribute.ProviderName.set -> void +HotChocolate.Types.UsePagingAttribute.RequirePagingBoundaries.get -> bool +HotChocolate.Types.UsePagingAttribute.RequirePagingBoundaries.set -> void +Microsoft.Extensions.DependencyInjection.CursorPagingRequestExecutorBuilderExtension static HotChocolate.Types.PagingObjectFieldDescriptorExtensions.AddPagingArguments(this HotChocolate.Types.IInterfaceFieldDescriptor! descriptor, bool allowBackwardPagination) -> HotChocolate.Types.IInterfaceFieldDescriptor! *REMOVED*static HotChocolate.Types.PagingObjectFieldDescriptorExtensions.AddPagingArguments(this HotChocolate.Types.IInterfaceFieldDescriptor! descriptor) -> HotChocolate.Types.IInterfaceFieldDescriptor! *REMOVED*static HotChocolate.Types.PagingObjectFieldDescriptorExtensions.AddPagingArguments(this HotChocolate.Types.IObjectFieldDescriptor! descriptor) -> HotChocolate.Types.IObjectFieldDescriptor! -static HotChocolate.Types.PagingObjectFieldDescriptorExtensions.AddPagingArguments(this HotChocolate.Types.IObjectFieldDescriptor! descriptor, bool allowBackwardPagination) -> HotChocolate.Types.IObjectFieldDescriptor! \ No newline at end of file +static HotChocolate.Types.PagingObjectFieldDescriptorExtensions.AddPagingArguments(this HotChocolate.Types.IObjectFieldDescriptor! descriptor, bool allowBackwardPagination) -> HotChocolate.Types.IObjectFieldDescriptor! +static HotChocolate.Types.PagingObjectFieldDescriptorExtensions.UsePaging(this HotChocolate.Types.IInterfaceFieldDescriptor! descriptor, System.Type? nodeType = null, HotChocolate.NameString? connectionName = null, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IInterfaceFieldDescriptor! +static HotChocolate.Types.PagingObjectFieldDescriptorExtensions.UsePaging(this HotChocolate.Types.IObjectFieldDescriptor! descriptor, System.Type? nodeType = null, System.Type? entityType = null, HotChocolate.Types.GetCursorPagingProvider? resolvePagingProvider = null, HotChocolate.NameString? connectionName = null, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IObjectFieldDescriptor! +static HotChocolate.Types.PagingObjectFieldDescriptorExtensions.UsePaging(this HotChocolate.Types.IObjectFieldDescriptor! descriptor, HotChocolate.Types.GetCursorPagingProvider? resolvePagingProvider = null, HotChocolate.NameString? connectionName = null, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IObjectFieldDescriptor! +static HotChocolate.Types.PagingObjectFieldDescriptorExtensions.UsePaging(this HotChocolate.Types.IInterfaceFieldDescriptor! descriptor, HotChocolate.NameString? connectionName = null, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IInterfaceFieldDescriptor! +static HotChocolate.Types.PagingObjectFieldDescriptorExtensions.UsePaging(this HotChocolate.Types.IObjectFieldDescriptor! descriptor, System.Type? entityType = null, HotChocolate.Types.GetCursorPagingProvider? resolvePagingProvider = null, HotChocolate.NameString? connectionName = null, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IObjectFieldDescriptor! +static Microsoft.Extensions.DependencyInjection.CursorPagingRequestExecutorBuilderExtension.AddCursorPagingProvider(this HotChocolate.Execution.Configuration.IRequestExecutorBuilder! builder, string? providerName = null, bool defaultProvider = false) -> HotChocolate.Execution.Configuration.IRequestExecutorBuilder! +static Microsoft.Extensions.DependencyInjection.CursorPagingRequestExecutorBuilderExtension.AddCursorPagingProvider(this HotChocolate.Execution.Configuration.IRequestExecutorBuilder! builder, System.Func! factory, string? providerName = null, bool defaultProvider = false) -> HotChocolate.Execution.Configuration.IRequestExecutorBuilder! +static Microsoft.Extensions.DependencyInjection.CursorPagingRequestExecutorBuilderExtension.AddQueryableCursorPagingProvider(this HotChocolate.Execution.Configuration.IRequestExecutorBuilder! builder, string? providerName = null, bool defaultProvider = false) -> HotChocolate.Execution.Configuration.IRequestExecutorBuilder! +*REMOVED*HotChocolate.Types.Pagination.ConnectionCountType +*REMOVED*HotChocolate.Types.Pagination.ConnectionCountType.ConnectionCountType() -> void +*REMOVED*HotChocolate.Types.Pagination.ConnectionCountType.ConnectionCountType(System.Action!>! configure) -> void +*REMOVED*HotChocolate.Types.Pagination.ConnectionType +*REMOVED*HotChocolate.Types.Pagination.ConnectionType.ConnectionType() -> void +*REMOVED*HotChocolate.Types.Pagination.ConnectionType.ConnectionType(System.Action!>! configure) -> void +*REMOVED*HotChocolate.Types.Pagination.ConnectionType.EdgeType.get -> HotChocolate.Types.Pagination.IEdgeType! +*REMOVED*HotChocolate.Types.Pagination.EdgeType +*REMOVED*HotChocolate.Types.Pagination.EdgeType.EdgeType() -> void +*REMOVED*HotChocolate.Types.Pagination.EdgeType.EntityType.get -> HotChocolate.Types.IOutputType! +*REMOVED*override HotChocolate.Types.Pagination.ConnectionCountType.Configure(HotChocolate.Types.IObjectTypeDescriptor! descriptor) -> void +*REMOVED*override HotChocolate.Types.Pagination.ConnectionType.Configure(HotChocolate.Types.IObjectTypeDescriptor! descriptor) -> void +*REMOVED*override HotChocolate.Types.Pagination.ConnectionType.OnCompleteType(HotChocolate.Configuration.ITypeCompletionContext! context, HotChocolate.Types.Descriptors.Definitions.ObjectTypeDefinition! definition) -> void +*REMOVED*override HotChocolate.Types.Pagination.ConnectionType.OnRegisterDependencies(HotChocolate.Configuration.ITypeDiscoveryContext! context, HotChocolate.Types.Descriptors.Definitions.ObjectTypeDefinition! definition) -> void +*REMOVED*override HotChocolate.Types.Pagination.EdgeType.Configure(HotChocolate.Types.IObjectTypeDescriptor! descriptor) -> void +*REMOVED*override HotChocolate.Types.Pagination.EdgeType.OnCompleteType(HotChocolate.Configuration.ITypeCompletionContext! context, HotChocolate.Types.Descriptors.Definitions.ObjectTypeDefinition! definition) -> void +*REMOVED*static HotChocolate.Types.Pagination.ConnectionCountType.ApplyConfig(HotChocolate.Types.IObjectTypeDescriptor! descriptor) -> void +*REMOVED*static HotChocolate.Types.Pagination.ConnectionType.ApplyConfig(HotChocolate.Types.IObjectTypeDescriptor! descriptor) -> void +*REMOVED*static HotChocolate.Types.PagingObjectFieldDescriptorExtensions.UsePaging(this HotChocolate.Types.IInterfaceFieldDescriptor! descriptor, System.Type? type = null, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IInterfaceFieldDescriptor! +*REMOVED*static HotChocolate.Types.PagingObjectFieldDescriptorExtensions.UsePaging(this HotChocolate.Types.IObjectFieldDescriptor! descriptor, System.Type? type = null, System.Type? entityType = null, HotChocolate.Types.GetCursorPagingProvider? resolvePagingProvider = null, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IObjectFieldDescriptor! +*REMOVED*static HotChocolate.Types.PagingObjectFieldDescriptorExtensions.UsePaging(this HotChocolate.Types.IObjectFieldDescriptor! descriptor, HotChocolate.Types.GetCursorPagingProvider? resolvePagingProvider = null, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IObjectFieldDescriptor! +*REMOVED*static HotChocolate.Types.PagingObjectFieldDescriptorExtensions.UsePaging(this HotChocolate.Types.IInterfaceFieldDescriptor! descriptor, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IInterfaceFieldDescriptor! +*REMOVED*static HotChocolate.Types.PagingObjectFieldDescriptorExtensions.UsePaging(this HotChocolate.Types.IObjectFieldDescriptor! descriptor, System.Type? entityType = null, HotChocolate.Types.GetCursorPagingProvider? resolvePagingProvider = null, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IObjectFieldDescriptor! \ No newline at end of file diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/QueryableCursorPagingProvider.cs b/src/HotChocolate/Core/src/Types.CursorPagination/QueryableCursorPagingProvider.cs index f58142e77cf..6840fb1634a 100644 --- a/src/HotChocolate/Core/src/Types.CursorPagination/QueryableCursorPagingProvider.cs +++ b/src/HotChocolate/Core/src/Types.CursorPagination/QueryableCursorPagingProvider.cs @@ -4,8 +4,7 @@ namespace HotChocolate.Types.Pagination { - public class QueryableCursorPagingProvider - : CursorPagingProvider + public class QueryableCursorPagingProvider : CursorPagingProvider { private static readonly MethodInfo _createHandler = typeof(QueryableCursorPagingProvider).GetMethod( diff --git a/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/GetOffsetPagingProvider.cs b/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/GetOffsetPagingProvider.cs index 81208dea9b5..904793e74d0 100644 --- a/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/GetOffsetPagingProvider.cs +++ b/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/GetOffsetPagingProvider.cs @@ -14,10 +14,14 @@ namespace HotChocolate.Types /// /// The source type. /// + /// + /// The paging provider name that shall be selected. + /// /// /// Returns a paging provider for the specified . /// public delegate OffsetPagingProvider GetOffsetPagingProvider( IServiceProvider services, - IExtendedType sourceType); + IExtendedType sourceType, + string? providerName = null); } diff --git a/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/OffsetPagingObjectFieldDescriptorExtensions.cs b/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/OffsetPagingObjectFieldDescriptorExtensions.cs index 8ce927d063f..5d2d97e621a 100644 --- a/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/OffsetPagingObjectFieldDescriptorExtensions.cs +++ b/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/OffsetPagingObjectFieldDescriptorExtensions.cs @@ -89,16 +89,15 @@ public static IObjectFieldDescriptor UseOffsetPaging( PagingHelper.UsePaging( descriptor, - type, itemType, - (services, source) => resolvePagingProvider(services, source), + (services, source, name) => resolvePagingProvider(services, source, name), options); descriptor .Extend() .OnBeforeCreate((c, d) => { - MemberInfo resolverMember = d.ResolverMember ?? d.Member; + MemberInfo? resolverMember = d.ResolverMember ?? d.Member; d.Type = CreateTypeRef(c, resolverMember, type, options); d.CustomSettings.Add(typeof(CollectionSegment)); }); @@ -229,14 +228,32 @@ private static ITypeReference CreateTypeRef( private static OffsetPagingProvider ResolvePagingProvider( IServiceProvider services, - IExtendedType source) + IExtendedType source, + string? providerName) { try { - if (services.GetService>() is { } providers && - providers.FirstOrDefault(p => p.CanHandle(source)) is { } provider) + Func predicate = + providerName is null + ? entry => entry.Provider.CanHandle(source) + : entry => providerName.Equals(entry.Name, StringComparison.Ordinal); + PagingProviderEntry? defaultEntry = null; + + + foreach (var entry in services.GetServices()) + { + // the first provider is expected to be the default provider. + defaultEntry ??= entry; + + if (predicate(entry)) + { + return entry.Provider; + } + } + + if (defaultEntry is not null) { - return provider; + return defaultEntry.Provider; } } catch (InvalidOperationException) @@ -245,6 +262,7 @@ private static OffsetPagingProvider ResolvePagingProvider( // in this case we will ignore the exception and return the default provider. } + // if no provider was added we will fallback to the queryable paging provider. return new QueryableOffsetPagingProvider(); } } diff --git a/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/OffsetPagingRequestExecutorBuilderExtension.cs b/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/OffsetPagingRequestExecutorBuilderExtension.cs new file mode 100644 index 00000000000..ab4a0d01c92 --- /dev/null +++ b/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/OffsetPagingRequestExecutorBuilderExtension.cs @@ -0,0 +1,137 @@ +using System; +using HotChocolate.Execution.Configuration; +using HotChocolate.Types.Pagination; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// GraphQL Configurations for offset pagination. + /// + public static class OffsetPagingRequestExecutorBuilderExtension + { + /// + /// Adds the queryable offset paging provider to the DI. + /// + /// + /// The request executor builder. + /// + /// + /// The name the provider shall have. + /// + /// + /// Defines if the registered provider shall be registered as the default provider. + /// + /// + /// The request executor builder. + /// + /// + /// is null. + /// + public static IRequestExecutorBuilder AddQueryableOffsetPagingProvider( + this IRequestExecutorBuilder builder, + string? providerName = null, + bool defaultProvider = false) + => AddOffsetPagingProvider( + builder, + providerName, + defaultProvider); + + /// + /// Adds a offset paging provider to the DI. + /// + /// + /// The request executor builder. + /// + /// + /// The name the provider shall have. + /// + /// + /// Defines if the registered provider shall be registered as the default provider. + /// + /// + /// The type of the provider. + /// + /// + /// The request executor builder. + /// + /// + /// is null. + /// + public static IRequestExecutorBuilder AddOffsetPagingProvider( + this IRequestExecutorBuilder builder, + string? providerName = null, + bool defaultProvider = false) + where TProvider : OffsetPagingProvider + { + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + builder.Services.TryAddSingleton(); + + AddOffsetPagingProvider( + builder, + s => s.GetRequiredService(), + providerName, + defaultProvider); + + return builder; + } + + /// + /// Adds a offset paging provider to the DI. + /// + /// + /// The request executor builder. + /// + /// + /// A factory to create the paging provider. + /// + /// + /// The name the provider shall have. + /// + /// + /// Defines if the registered provider shall be registered as the default provider. + /// + /// + /// The type of the provider. + /// + /// + /// The request executor builder. + /// + /// + /// is null. + /// + public static IRequestExecutorBuilder AddOffsetPagingProvider( + this IRequestExecutorBuilder builder, + Func factory, + string? providerName = null, + bool defaultProvider = false) + where TProvider : OffsetPagingProvider + { + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (defaultProvider) + { + var service = ServiceDescriptor.Singleton( + typeof(PagingProviderEntry), + CreateEntry); + builder.Services.Insert(0, service); + } + else + { + builder.Services.AddSingleton(CreateEntry); + } + + return builder; + + PagingProviderEntry CreateEntry(IServiceProvider services) + => new(providerName, factory(services)); + } + } +} diff --git a/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/UseOffsetPagingAttribute.cs b/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/UseOffsetPagingAttribute.cs index 30b7de00eba..cf2549b16ad 100644 --- a/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/UseOffsetPagingAttribute.cs +++ b/src/HotChocolate/Core/src/Types.OffsetPagination/Extensions/UseOffsetPagingAttribute.cs @@ -14,6 +14,7 @@ public class UseOffsetPagingAttribute : DescriptorAttribute private int? _defaultPageSize; private int? _maxPageSize; private bool? _includeTotalCount; + private bool? _requirePagingBoundaries; /// /// Applies the offset paging middleware to the annotated property. @@ -58,6 +59,21 @@ public bool IncludeTotalCount set => _includeTotalCount = value; } + /// + /// Defines if the paging middleware shall require the + /// API consumer to specify paging boundaries. + /// + public bool RequirePagingBoundaries + { + get => _requirePagingBoundaries ?? PagingDefaults.AllowBackwardPagination; + set => _requirePagingBoundaries = value; + } + + /// + /// Specifies the name of the paging provider that shall be used. + /// + public string? ProviderName { get; set; } + protected override void TryConfigure( IDescriptorContext context, IDescriptor descriptor, @@ -71,7 +87,9 @@ protected override void TryConfigure( { DefaultPageSize = _defaultPageSize, MaxPageSize = _maxPageSize, - IncludeTotalCount = _includeTotalCount + IncludeTotalCount = _includeTotalCount, + RequirePagingBoundaries = _requirePagingBoundaries, + ProviderName = ProviderName }); } @@ -83,7 +101,9 @@ protected override void TryConfigure( { DefaultPageSize = _defaultPageSize, MaxPageSize = _maxPageSize, - IncludeTotalCount = _includeTotalCount + IncludeTotalCount = _includeTotalCount, + RequirePagingBoundaries = _requirePagingBoundaries, + ProviderName = ProviderName }); } } diff --git a/src/HotChocolate/Core/src/Types.OffsetPagination/HotChocolate.Types.OffsetPagination.csproj b/src/HotChocolate/Core/src/Types.OffsetPagination/HotChocolate.Types.OffsetPagination.csproj index 2d7394a3003..af080a575ed 100644 --- a/src/HotChocolate/Core/src/Types.OffsetPagination/HotChocolate.Types.OffsetPagination.csproj +++ b/src/HotChocolate/Core/src/Types.OffsetPagination/HotChocolate.Types.OffsetPagination.csproj @@ -14,13 +14,7 @@ - - - - + diff --git a/src/HotChocolate/Core/src/Types.OffsetPagination/PagingProviderEntry.cs b/src/HotChocolate/Core/src/Types.OffsetPagination/PagingProviderEntry.cs new file mode 100644 index 00000000000..4286bb2ec7e --- /dev/null +++ b/src/HotChocolate/Core/src/Types.OffsetPagination/PagingProviderEntry.cs @@ -0,0 +1,15 @@ +namespace HotChocolate.Types.Pagination +{ + internal sealed class PagingProviderEntry + { + public PagingProviderEntry(string? name, OffsetPagingProvider provider) + { + Name = name; + Provider = provider; + } + + public string? Name { get; } + + public OffsetPagingProvider Provider { get; } + } +} diff --git a/src/HotChocolate/Core/src/Types.OffsetPagination/PublicAPI.Unshipped.txt b/src/HotChocolate/Core/src/Types.OffsetPagination/PublicAPI.Unshipped.txt index 1b5a8e78cc4..962a6ca7e65 100644 --- a/src/HotChocolate/Core/src/Types.OffsetPagination/PublicAPI.Unshipped.txt +++ b/src/HotChocolate/Core/src/Types.OffsetPagination/PublicAPI.Unshipped.txt @@ -3,4 +3,12 @@ *REMOVED*virtual HotChocolate.Types.Pagination.QueryableOffsetPagingHandler.ExecuteQueryableAsync(System.Linq.IQueryable! queryable, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask!> HotChocolate.Types.Pagination.OffsetPagingArguments.OffsetPagingArguments(int? skip, int? take) -> void HotChocolate.Types.Pagination.OffsetPagingArguments.Take.get -> int? -HotChocolate.Types.Pagination.OffsetPagingHandler.RequirePagingBoundaries.get -> bool \ No newline at end of file +HotChocolate.Types.Pagination.OffsetPagingHandler.RequirePagingBoundaries.get -> bool +HotChocolate.Types.UseOffsetPagingAttribute.ProviderName.get -> string? +HotChocolate.Types.UseOffsetPagingAttribute.ProviderName.set -> void +HotChocolate.Types.UseOffsetPagingAttribute.RequirePagingBoundaries.get -> bool +HotChocolate.Types.UseOffsetPagingAttribute.RequirePagingBoundaries.set -> void +Microsoft.Extensions.DependencyInjection.OffsetPagingRequestExecutorBuilderExtension +static Microsoft.Extensions.DependencyInjection.OffsetPagingRequestExecutorBuilderExtension.AddOffsetPagingProvider(this HotChocolate.Execution.Configuration.IRequestExecutorBuilder! builder, string? providerName = null, bool defaultProvider = false) -> HotChocolate.Execution.Configuration.IRequestExecutorBuilder! +static Microsoft.Extensions.DependencyInjection.OffsetPagingRequestExecutorBuilderExtension.AddOffsetPagingProvider(this HotChocolate.Execution.Configuration.IRequestExecutorBuilder! builder, System.Func! factory, string? providerName = null, bool defaultProvider = false) -> HotChocolate.Execution.Configuration.IRequestExecutorBuilder! +static Microsoft.Extensions.DependencyInjection.OffsetPagingRequestExecutorBuilderExtension.AddQueryableOffsetPagingProvider(this HotChocolate.Execution.Configuration.IRequestExecutorBuilder! builder, string? providerName = null, bool defaultProvider = false) -> HotChocolate.Execution.Configuration.IRequestExecutorBuilder! \ No newline at end of file diff --git a/src/HotChocolate/Core/src/Types/Configuration/AggregateTypeInterceptor.cs b/src/HotChocolate/Core/src/Types/Configuration/AggregateTypeInterceptor.cs index f0ce5636aea..bdc6e5a68cb 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/AggregateTypeInterceptor.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/AggregateTypeInterceptor.cs @@ -11,6 +11,9 @@ namespace HotChocolate.Configuration { internal sealed class AggregateTypeInterceptor : TypeInterceptor { + private readonly List _discoveryContexts = new(); + private readonly List _completionContexts = new(); + private readonly List _typeReferences = new(); private IReadOnlyCollection _typeInterceptors; private IReadOnlyCollection _initInterceptors; private IReadOnlyCollection _agrInterceptors; @@ -32,6 +35,10 @@ public AggregateTypeInterceptor() public void SetInterceptors(IReadOnlyCollection interceptors) { + _discoveryContexts.Clear(); + _completionContexts.Clear(); + _typeReferences.Clear(); + _typeInterceptors = interceptors.OfType().ToList(); _initInterceptors = interceptors.OfType().ToList(); _agrInterceptors = _initInterceptors.Where(t => t.TriggerAggregations).ToList(); @@ -92,8 +99,7 @@ public override void OnAfterInitialize( { if (interceptor.CanHandle(discoveryContext)) { - interceptor.OnAfterInitialize( - discoveryContext, definition, contextData); + interceptor.OnAfterInitialize(discoveryContext, definition, contextData); } } } @@ -106,27 +112,55 @@ public override void OnTypeRegistered(ITypeDiscoveryContext discoveryContext) } } - public override void OnTypesInitialized( + public override IEnumerable RegisterMoreTypes( IReadOnlyCollection discoveryContexts) { + _typeReferences.Clear(); + if (_agrInterceptors.Count > 0) { - var list = new List(); + foreach (ITypeInitializationInterceptor interceptor in _agrInterceptors) + { + _discoveryContexts.Clear(); + + foreach (ITypeDiscoveryContext discoveryContext in discoveryContexts) + { + if (interceptor.CanHandle(discoveryContext)) + { + _discoveryContexts.Add(discoveryContext); + } + } + + _typeReferences.AddRange(interceptor.RegisterMoreTypes(_discoveryContexts)); + } + _discoveryContexts.Clear(); + } + + return _typeReferences; + } + + public override void OnTypesInitialized( + IReadOnlyCollection discoveryContexts) + { + if (_agrInterceptors.Count > 0) + { foreach (ITypeInitializationInterceptor interceptor in _agrInterceptors) { - list.Clear(); + _discoveryContexts.Clear(); foreach (ITypeDiscoveryContext discoveryContext in discoveryContexts) { if (interceptor.CanHandle(discoveryContext)) { - list.Add(discoveryContext); + _discoveryContexts.Add(discoveryContext); } } - interceptor.OnTypesInitialized(list); + interceptor.OnTypesInitialized(_discoveryContexts); } + + _discoveryContexts.Clear(); } } @@ -209,22 +243,22 @@ public override void OnTypesCompletedName( { if (_agrInterceptors.Count > 0) { - var list = new List(); - foreach (ITypeInitializationInterceptor interceptor in _agrInterceptors) { - list.Clear(); + _completionContexts.Clear(); foreach (ITypeCompletionContext completionContext in completionContexts) { if (interceptor.CanHandle(completionContext)) { - list.Add(completionContext); + _completionContexts.Add(completionContext); } } - interceptor.OnTypesCompletedName(list); + interceptor.OnTypesCompletedName(_completionContexts); } + + _completionContexts.Clear(); } } @@ -323,22 +357,22 @@ public override void OnTypesCompleted( { if (_agrInterceptors.Count > 0) { - var list = new List(); - foreach (ITypeInitializationInterceptor interceptor in _agrInterceptors) { - list.Clear(); + _completionContexts.Clear(); foreach (ITypeCompletionContext completionContext in completionContexts) { if (interceptor.CanHandle(completionContext)) { - list.Add(completionContext); + _completionContexts.Add(completionContext); } } - interceptor.OnTypesCompleted(list); + interceptor.OnTypesCompleted(_completionContexts); } + + _completionContexts.Clear(); } } } diff --git a/src/HotChocolate/Core/src/Types/Configuration/Contracts/ITypeCompletionContext.cs b/src/HotChocolate/Core/src/Types/Configuration/Contracts/ITypeCompletionContext.cs index fbbe4bff676..b3b2f267322 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/Contracts/ITypeCompletionContext.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/Contracts/ITypeCompletionContext.cs @@ -55,7 +55,7 @@ public interface ITypeCompletionContext : ITypeSystemObjectContext /// /// true if the type has been resolved; otherwise, false. /// - bool TryGetType(ITypeReference typeRef, [NotNullWhen(true)] out T type) where T : IType; + bool TryGetType(ITypeReference typeRef, [NotNullWhen(true)] out T? type) where T : IType; /// /// Gets a type by it's type reference. @@ -94,8 +94,6 @@ bool TryGetDirectiveType( IDirectiveReference directiveRef, [NotNullWhen(true)] out DirectiveType? directiveType); - DirectiveType GetDirectiveType(IDirectiveReference directiveRef); - Func GetSchemaResolver(); } } diff --git a/src/HotChocolate/Core/src/Types/Configuration/Contracts/ITypeDiscoveryContext.cs b/src/HotChocolate/Core/src/Types/Configuration/Contracts/ITypeDiscoveryContext.cs index 9152857357f..5ade3d751b5 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/Contracts/ITypeDiscoveryContext.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/Contracts/ITypeDiscoveryContext.cs @@ -16,7 +16,7 @@ public interface ITypeDiscoveryContext : ITypeSystemObjectContext /// /// The collected type dependencies. /// - IReadOnlyList TypeDependencies { get; } + IReadOnlyList Dependencies { get; } /// /// Register a reference to a type on which depends. diff --git a/src/HotChocolate/Core/src/Types/Configuration/Contracts/ITypeInitilizationInterceptor.cs b/src/HotChocolate/Core/src/Types/Configuration/Contracts/ITypeInitilizationInterceptor.cs index fb5b3f4756d..247e3328920 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/Contracts/ITypeInitilizationInterceptor.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/Contracts/ITypeInitilizationInterceptor.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using HotChocolate.Types.Descriptors; using HotChocolate.Types.Descriptors.Definitions; #nullable enable @@ -60,6 +61,21 @@ void OnAfterInitialize( DefinitionBase? definition, IDictionary contextData); + /// + /// If all types are registered you can analyze them and add more new types at this point. + /// This event could be hit multiple times. + /// + /// Ones is hit, no more types can be added. + /// + /// + /// The discovery contexts of types that are already initialized. + /// + /// + /// Returns types that shall be included into the schema. + /// + IEnumerable RegisterMoreTypes( + IReadOnlyCollection discoveryContexts); + /// /// This event is called after all type system members are initialized. /// diff --git a/src/HotChocolate/Core/src/Types/Configuration/DelegateTypeInitializationInterceptor.cs b/src/HotChocolate/Core/src/Types/Configuration/DelegateTypeInitializationInterceptor.cs index 6d1c1c9c90d..c3db66bdfad 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/DelegateTypeInitializationInterceptor.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/DelegateTypeInitializationInterceptor.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Linq; +using HotChocolate.Types.Descriptors; using HotChocolate.Types.Descriptors.Definitions; #nullable enable @@ -55,7 +57,10 @@ public void OnAfterInitialize( IDictionary contextData) => _onAfterInitialize?.Invoke(discoveryContext, definition, contextData); - + public IEnumerable RegisterMoreTypes( + IReadOnlyCollection discoveryContexts) => + Enumerable.Empty(); + public void OnBeforeRegisterDependencies( ITypeDiscoveryContext discoveryContext, DefinitionBase? definition, diff --git a/src/HotChocolate/Core/src/Types/Configuration/DelegateTypeInitializationInterceptor~1.cs b/src/HotChocolate/Core/src/Types/Configuration/DelegateTypeInitializationInterceptor~1.cs index 566f252f7a6..1f6de1a0584 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/DelegateTypeInitializationInterceptor~1.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/DelegateTypeInitializationInterceptor~1.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Linq; +using HotChocolate.Types.Descriptors; using HotChocolate.Types.Descriptors.Definitions; #nullable enable @@ -31,7 +33,7 @@ public DelegateTypeInitializationInterceptor( OnCompleteType? onBeforeCompleteType = null, OnCompleteType? onAfterCompleteType = null) { - _canHandle = canHandle ?? (c => true); + _canHandle = canHandle ?? (_ => true); _onBeforeInitialize = onBeforeInitialize; _onAfterInitialize = onAfterInitialize; _onBeforeRegisterDependencies = onBeforeRegisterDependencies; @@ -63,6 +65,10 @@ public void OnAfterInitialize( } } + public IEnumerable RegisterMoreTypes( + IReadOnlyCollection discoveryContexts) + => Enumerable.Empty(); + public void OnBeforeRegisterDependencies( ITypeDiscoveryContext discoveryContext, DefinitionBase? definition, diff --git a/src/HotChocolate/Core/src/Types/Configuration/DependantFactoryTypeReferenceHandler.cs b/src/HotChocolate/Core/src/Types/Configuration/DependantFactoryTypeReferenceHandler.cs new file mode 100644 index 00000000000..35eea2a8e96 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Configuration/DependantFactoryTypeReferenceHandler.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using HotChocolate.Types; +using HotChocolate.Types.Descriptors; + +namespace HotChocolate.Configuration +{ + internal sealed class DependantFactoryTypeReferenceHandler : ITypeRegistrarHandler + { + private readonly HashSet _handled = new(); + private readonly IDescriptorContext _context; + + public DependantFactoryTypeReferenceHandler(IDescriptorContext context) + { + _context = context ?? throw new ArgumentNullException(nameof(context)); + } + + public TypeReferenceKind Kind => TypeReferenceKind.DependantFactory; + + public void Handle(ITypeRegistrar typeRegistrar, ITypeReference typeReference) + { + var typeRef = (DependantFactoryTypeReference)typeReference; + + if (_handled.Add(typeRef)) + { + TypeSystemObjectBase obj = typeRef.Factory(_context); + typeRegistrar.Register(obj, typeRef.Scope, configure: AddTypeRef); + } + + void AddTypeRef(RegisteredType registeredType) + => registeredType.References.Add(typeRef); + } + } +} diff --git a/src/HotChocolate/Core/src/Types/Configuration/ExtendedTypeReferenceHandler.cs b/src/HotChocolate/Core/src/Types/Configuration/ExtendedTypeReferenceHandler.cs index ca1f58f397c..772545b4eea 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/ExtendedTypeReferenceHandler.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/ExtendedTypeReferenceHandler.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; using HotChocolate.Internal; using HotChocolate.Types; using HotChocolate.Types.Descriptors; @@ -11,8 +9,7 @@ namespace HotChocolate.Configuration { - internal sealed class ExtendedTypeReferenceHandler - : ITypeRegistrarHandler + internal sealed class ExtendedTypeReferenceHandler : ITypeRegistrarHandler { private readonly ITypeInspector _typeInspector; @@ -21,44 +18,42 @@ public ExtendedTypeReferenceHandler(ITypeInspector typeInspector) _typeInspector = typeInspector; } - public void Register( - ITypeRegistrar typeRegistrar, - IEnumerable typeReferences) + public TypeReferenceKind Kind => TypeReferenceKind.ExtendedType; + + public void Handle(ITypeRegistrar typeRegistrar, ITypeReference typeReference) { - foreach (ExtendedTypeReference typeReference in - typeReferences.OfType()) + var typeRef = (ExtendedTypeReference)typeReference; + + if (_typeInspector.TryCreateTypeInfo(typeRef.Type, out ITypeInfo? typeInfo) && + !ExtendedType.Tools.IsNonGenericBaseType(typeInfo.NamedType)) { - if (_typeInspector.TryCreateTypeInfo(typeReference.Type, out ITypeInfo? typeInfo) && - !ExtendedType.Tools.IsNonGenericBaseType(typeInfo.NamedType)) + if (typeInfo.NamedType == typeof(IExecutable)) { - if (typeInfo.NamedType == typeof(IExecutable)) - { - throw ThrowHelper.NonGenericExecutableNotAllowed(); - } + throw ThrowHelper.NonGenericExecutableNotAllowed(); + } - Type namedType = typeInfo.NamedType; - if (IsTypeSystemObject(namedType)) - { - IExtendedType extendedType = _typeInspector.GetType(namedType); - ExtendedTypeReference namedTypeReference = typeReference.With(extendedType); + Type namedType = typeInfo.NamedType; + if (IsTypeSystemObject(namedType)) + { + IExtendedType extendedType = _typeInspector.GetType(namedType); + ExtendedTypeReference namedTypeReference = typeRef.With(extendedType); - if (!typeRegistrar.IsResolved(namedTypeReference)) - { - typeRegistrar.Register( - typeRegistrar.CreateInstance(namedType), - typeReference.Scope, - ExtendedType.Tools.IsGenericBaseType(namedType)); - } - } - else + if (!typeRegistrar.IsResolved(namedTypeReference)) { - TryMapToExistingRegistration( - typeRegistrar, - typeInfo, - typeReference.Context, - typeReference.Scope); + typeRegistrar.Register( + typeRegistrar.CreateInstance(namedType), + typeReference.Scope, + ExtendedType.Tools.IsGenericBaseType(namedType)); } } + else + { + TryMapToExistingRegistration( + typeRegistrar, + typeInfo, + typeReference.Context, + typeReference.Scope); + } } } diff --git a/src/HotChocolate/Core/src/Types/Configuration/FactoryTypeReferenceHandler.cs b/src/HotChocolate/Core/src/Types/Configuration/FactoryTypeReferenceHandler.cs new file mode 100644 index 00000000000..8680098894f --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Configuration/FactoryTypeReferenceHandler.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using HotChocolate.Types; +using HotChocolate.Types.Descriptors; + +namespace HotChocolate.Configuration +{ + internal sealed class FactoryTypeReferenceHandler : ITypeRegistrarHandler + { + private readonly HashSet _handled = new(); + private readonly IDescriptorContext _context; + + public FactoryTypeReferenceHandler(IDescriptorContext context) + { + _context = context ?? throw new ArgumentNullException(nameof(context)); + } + + public TypeReferenceKind Kind => TypeReferenceKind.Factory; + + public void Handle(ITypeRegistrar typeRegistrar, ITypeReference typeReference) + { + var typeRef = (SyntaxTypeReference)typeReference; + + if (_handled.Add(typeRef.Name)) + { + TypeSystemObjectBase obj = typeRef.Factory!(_context); + typeRegistrar.Register(obj, typeRef.Scope, configure: AddTypeRef); + } + + void AddTypeRef(RegisteredType registeredType) + => registeredType.References.Add(typeRef); + } + } +} diff --git a/src/HotChocolate/Core/src/Types/Configuration/ITypeRegistrar.cs b/src/HotChocolate/Core/src/Types/Configuration/ITypeRegistrar.cs index 92b21a079a7..42fb0e70e05 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/ITypeRegistrar.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/ITypeRegistrar.cs @@ -10,9 +10,10 @@ namespace HotChocolate.Configuration internal interface ITypeRegistrar { void Register( - TypeSystemObjectBase typeSystemObject, + TypeSystemObjectBase obj, string? scope, - bool isInferred = false); + bool inferred = false, + Action? configure = null); void MarkUnresolved(ITypeReference typeReference); @@ -22,7 +23,7 @@ void Register( TypeSystemObjectBase CreateInstance(Type namedSchemaType); - IReadOnlyCollection GetUnresolved(); + IReadOnlyCollection Unresolved { get; } IReadOnlyCollection GetUnhandled(); } diff --git a/src/HotChocolate/Core/src/Types/Configuration/ITypeRegistrarHandler.cs b/src/HotChocolate/Core/src/Types/Configuration/ITypeRegistrarHandler.cs index e1f8fa1560a..c8c2c754211 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/ITypeRegistrarHandler.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/ITypeRegistrarHandler.cs @@ -1,14 +1,30 @@ -using System.Collections.Generic; using HotChocolate.Types.Descriptors; #nullable enable namespace HotChocolate.Configuration { + /// + /// The type registrar handler will process a type reference. + /// The handler may or may not create a type instance from the provided type reference + /// and register it with the . + /// internal interface ITypeRegistrarHandler { - void Register( - ITypeRegistrar typeRegistrar, - IEnumerable typeReferences); + /// + /// The type reference kind that can be handled. + /// + TypeReferenceKind Kind { get; } + + /// + /// Handles the type reference. + /// + /// + /// The type registrar that can be used to register types. + /// + /// + /// The type reference. + /// + void Handle(ITypeRegistrar typeRegistrar, ITypeReference typeReference); } } diff --git a/src/HotChocolate/Core/src/Types/Configuration/ListExtensions.cs b/src/HotChocolate/Core/src/Types/Configuration/ListExtensions.cs new file mode 100644 index 00000000000..ad2d4d36a93 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Configuration/ListExtensions.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace HotChocolate.Configuration +{ + internal static class ListExtensions + { + public static void TryAdd(this List list, T item) + { + if (!list.Contains(item)) + { + list.Add(item); + } + } + } +} diff --git a/src/HotChocolate/Core/src/Types/Configuration/RegisteredType.CompletionContext.cs b/src/HotChocolate/Core/src/Types/Configuration/RegisteredType.CompletionContext.cs new file mode 100644 index 00000000000..647055449ae --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Configuration/RegisteredType.CompletionContext.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using HotChocolate.Resolvers; +using HotChocolate.Types; +using HotChocolate.Types.Descriptors; +using static HotChocolate.Utilities.ThrowHelper; +using static HotChocolate.Properties.TypeResources; + +#nullable enable + +namespace HotChocolate.Configuration +{ + internal sealed partial class RegisteredType : ITypeCompletionContext + { + private TypeReferenceResolver? _typeReferenceResolver; + private Func? _schemaResolver; + + public TypeStatus Status { get; set; } = TypeStatus.Initialized; + + /// + public bool? IsQueryType { get; set; } + + /// + public bool? IsMutationType { get; set; } + + /// + public bool? IsSubscriptionType { get; set; } + + /// + /// Global middleware components. + /// + public List? GlobalComponents { get; private set; } + + /// + IReadOnlyList ITypeCompletionContext.GlobalComponents + => GlobalComponents ?? (IReadOnlyList)Array.Empty(); + + /// + public IsOfTypeFallback? IsOfType { get; private set; } + + public void PrepareForCompletion( + TypeReferenceResolver typeReferenceResolver, + Func schemaResolver, + List globalComponents, + IsOfTypeFallback? isOfType) + { + _typeReferenceResolver = typeReferenceResolver; + _schemaResolver = schemaResolver; + GlobalComponents = globalComponents; + IsOfType = isOfType; + } + + /// + public bool TryGetType( + ITypeReference typeRef, + [NotNullWhen(true)] out T? type) + where T : IType + { + if (_typeReferenceResolver is null) + { + throw new InvalidOperationException(RegisteredType_Completion_NotYetReady); + } + + if (_typeReferenceResolver.TryGetType(typeRef, out IType? t) && + t is T casted) + { + type = casted; + return true; + } + + type = default; + return false; + } + + /// + public T GetType(ITypeReference typeRef) where T : IType + { + if (typeRef is null) + { + throw new ArgumentNullException(nameof(typeRef)); + } + + if (!TryGetType(typeRef, out T? type)) + { + throw TypeCompletionContext_UnableToResolveType(Type, typeRef); + } + + return type; + } + + /// + public ITypeReference GetNamedTypeReference(ITypeReference typeRef) + { + if (_typeReferenceResolver is null) + { + throw new InvalidOperationException(RegisteredType_Completion_NotYetReady); + } + + return _typeReferenceResolver.GetNamedTypeReference(typeRef); + } + + /// + public IEnumerable GetTypes() where T : IType + { + if (_typeReferenceResolver is null) + { + throw new InvalidOperationException(RegisteredType_Completion_NotYetReady); + } + + if (Status == TypeStatus.Initialized) + { + throw new NotSupportedException(); + } + + return _typeReferenceResolver.GetTypes(); + } + + /// + public bool TryGetDirectiveType(IDirectiveReference directiveRef, + [NotNullWhen(true)] out DirectiveType? directiveType) + { + if (_typeReferenceResolver is null) + { + throw new InvalidOperationException(RegisteredType_Completion_NotYetReady); + } + + return _typeReferenceResolver.TryGetDirectiveType(directiveRef, out directiveType); + } + + /// + public Func GetSchemaResolver() + { + if (_schemaResolver is null) + { + throw new InvalidOperationException(RegisteredType_Completion_NotYetReady); + } + + if (Status == TypeStatus.Initialized) + { + throw new NotSupportedException(); + } + + return _schemaResolver; + } + } +} diff --git a/src/HotChocolate/Core/src/Types/Configuration/TypeDiscoveryContext.cs b/src/HotChocolate/Core/src/Types/Configuration/RegisteredType.DiscoveryContext.cs similarity index 64% rename from src/HotChocolate/Core/src/Types/Configuration/TypeDiscoveryContext.cs rename to src/HotChocolate/Core/src/Types/Configuration/RegisteredType.DiscoveryContext.cs index ff48919dbf8..14005feaaca 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/TypeDiscoveryContext.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/RegisteredType.DiscoveryContext.cs @@ -5,88 +5,99 @@ using HotChocolate.Types.Descriptors; using HotChocolate.Types.Descriptors.Definitions; +#nullable enable + namespace HotChocolate.Configuration { - internal sealed class TypeDiscoveryContext : ITypeDiscoveryContext + internal sealed partial class RegisteredType : ITypeDiscoveryContext { - private readonly TypeRegistry _typeRegistry; - private readonly TypeLookup _typeLookup; - private readonly List _typeDependencies = new(); private readonly List _directiveReferences = new(); + private List? _errors; - public TypeDiscoveryContext( - ITypeSystemObject type, - TypeRegistry typeRegistry, - TypeLookup typeLookup, - IDescriptorContext descriptorContext, - ITypeInterceptor typeInterceptor, - string scope) - { - Type = type ?? - throw new ArgumentNullException(nameof(type)); - _typeRegistry = typeRegistry ?? - throw new ArgumentNullException(nameof(typeRegistry)); - _typeLookup = typeLookup ?? - throw new ArgumentNullException(nameof(typeLookup)); - DescriptorContext = descriptorContext ?? - throw new ArgumentNullException(nameof(descriptorContext)); - TypeInterceptor = typeInterceptor ?? - throw new ArgumentNullException(nameof(typeInterceptor)); - Scope = scope; + public string? Scope { get; } - IsDirective = type is DirectiveType; - IsSchema = type is Schema; + public IDescriptorContext DescriptorContext { get; } - if (type is INamedType nt) - { - IsType = true; - IsIntrospectionType = nt.IsIntrospectionType(); - } + public IDictionary ContextData => DescriptorContext.ContextData; - InternalName = "Type_" + Guid.NewGuid().ToString("N"); - } + public IServiceProvider Services => DescriptorContext.Services; - public NameString InternalName { get; } + public ITypeInspector TypeInspector => DescriptorContext.TypeInspector; - public ITypeSystemObject Type { get; } + public ITypeInterceptor TypeInterceptor { get; } - public string Scope { get; } + IReadOnlyList ITypeDiscoveryContext.Dependencies => Dependencies; - public bool IsType { get; } + ITypeSystemObject ITypeSystemObjectContext.Type => Type; - public bool IsIntrospectionType { get; } + public void ReportError(ISchemaError error) + { + if (error is null) + { + throw new ArgumentNullException(nameof(error)); + } - public bool IsDirective { get; } + Errors.Add(error); + } - public bool IsSchema { get; } + public bool TryPredictTypeKind(ITypeReference typeRef, out TypeKind kind) + { + if (_typeLookup.TryNormalizeReference(typeRef, out ITypeReference? namedTypeRef) && + _typeRegistry.TryGetType(namedTypeRef, out RegisteredType? registeredType)) + { + switch (registeredType.Type) + { + case INamedType namedType: + kind = namedType.Kind; + return true; - public IServiceProvider Services => DescriptorContext.Services; + case DirectiveType: + kind = TypeKind.Directive; + return true; - public IReadOnlyList TypeDependencies => _typeDependencies; + default: + kind = default; + return false; + } + } - public ICollection DirectiveReferences => _directiveReferences; + namedTypeRef ??= typeRef; - public ICollection Errors { get; } = - new List(); + switch (namedTypeRef) + { + case ExtendedTypeReference r: + if (Scalars.TryGetScalar(r.Type.Type, out _)) + { + kind = TypeKind.Scalar; + return true; + } - public IDictionary ContextData => DescriptorContext.ContextData; + if (r.Type.IsSchemaType) + { + kind = GetTypeKindFromSchemaType(r.Type); + return true; + } - public IDescriptorContext DescriptorContext { get; } + return SchemaTypeResolver.TryInferSchemaTypeKind(r, out kind); - public ITypeInterceptor TypeInterceptor { get; } + case SchemaTypeReference r: + kind = GetTypeKindFromSchemaType(TypeInspector.GetType(r.Type.GetType())); + return true; - public ITypeInspector TypeInspector => DescriptorContext.TypeInspector; + default: + kind = default; + return false; + } + } - public void RegisterDependency( - ITypeReference reference, - TypeDependencyKind kind) + public void RegisterDependency(ITypeReference reference, TypeDependencyKind kind) { if (reference is null) { throw new ArgumentNullException(nameof(reference)); } - _typeDependencies.Add(new TypeDependency(reference, kind)); + Dependencies.Add(new TypeDependency(reference, kind)); } public void RegisterDependency(TypeDependency dependency) @@ -96,7 +107,7 @@ public void RegisterDependency(TypeDependency dependency) throw new ArgumentNullException(nameof(dependency)); } - _typeDependencies.Add(dependency); + Dependencies.Add(dependency); } public void RegisterDependencyRange( @@ -110,14 +121,18 @@ public void RegisterDependencyRange( foreach (ITypeReference reference in references) { - _typeDependencies.Add(new TypeDependency(reference, kind)); + Dependencies.Add(new TypeDependency(reference, kind)); } } - public void RegisterDependencyRange( - IEnumerable dependencies) + public void RegisterDependencyRange(IEnumerable dependencies) { - _typeDependencies.AddRange(dependencies); + if (dependencies is null) + { + throw new ArgumentNullException(nameof(dependencies)); + } + + Dependencies.AddRange(dependencies); } public void RegisterDependency(IDirectiveReference reference) @@ -130,8 +145,7 @@ public void RegisterDependency(IDirectiveReference reference) _directiveReferences.Add(reference); } - public void RegisterDependencyRange( - IEnumerable references) + public void RegisterDependencyRange(IEnumerable references) { if (references is null) { @@ -141,66 +155,6 @@ public void RegisterDependencyRange( _directiveReferences.AddRange(references); } - public void ReportError(ISchemaError error) - { - if (error is null) - { - throw new ArgumentNullException(nameof(error)); - } - - Errors.Add(error); - } - - public bool TryPredictTypeKind(ITypeReference typeRef, out TypeKind kind) - { - if (_typeLookup.TryNormalizeReference(typeRef, out ITypeReference namedTypeRef) && - _typeRegistry.TryGetType(namedTypeRef, out RegisteredType registeredType)) - { - switch (registeredType.Type) - { - case INamedType namedType: - kind = namedType.Kind; - return true; - - case DirectiveType: - kind = TypeKind.Directive; - return true; - - default: - kind = default; - return false; - } - } - - namedTypeRef ??= typeRef; - - switch (namedTypeRef) - { - case ExtendedTypeReference r: - if (Scalars.TryGetScalar(r.Type.Type, out _)) - { - kind = TypeKind.Scalar; - return true; - } - - if (r.Type.IsSchemaType) - { - kind = GetTypeKindFromSchemaType(r.Type); - return true; - } - - return SchemaTypeResolver.TryInferSchemaTypeKind(r, out kind); - - case SchemaTypeReference r: - kind = GetTypeKindFromSchemaType(TypeInspector.GetType(r.Type.GetType())); - return true; - - default: - kind = default; - return false; - } - } - private static TypeKind GetTypeKindFromSchemaType(IExtendedType type) { if (typeof(ScalarType).IsAssignableFrom(type.Type)) diff --git a/src/HotChocolate/Core/src/Types/Configuration/RegisteredType.cs b/src/HotChocolate/Core/src/Types/Configuration/RegisteredType.cs index 25dec437fae..0cfe97823e4 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/RegisteredType.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/RegisteredType.cs @@ -1,7 +1,5 @@ -using System.Linq; using System; using System.Collections.Generic; -using HotChocolate.Properties; using HotChocolate.Types; using HotChocolate.Types.Descriptors; using HotChocolate.Types.Descriptors.Definitions; @@ -10,26 +8,29 @@ namespace HotChocolate.Configuration { - internal sealed class RegisteredType - : IHasRuntimeType + internal sealed partial class RegisteredType : IHasRuntimeType { - private TypeCompletionContext? _completionContext; - private IReadOnlyList _references; - private IReadOnlyList _dependencies; + private readonly TypeRegistry _typeRegistry; + private readonly TypeLookup _typeLookup; public RegisteredType( TypeSystemObjectBase type, - IReadOnlyList references, - IReadOnlyList dependencies, - TypeDiscoveryContext discoveryContext, - bool isInferred) + bool isInferred, + TypeRegistry typeRegistry, + TypeLookup typeLookup, + IDescriptorContext descriptorContext, + ITypeInterceptor typeInterceptor, + string? scope) { Type = type; - _references = references; - _dependencies = dependencies; - DiscoveryContext = discoveryContext; + _typeRegistry = typeRegistry; + _typeLookup = typeLookup; IsInferred = isInferred; + DescriptorContext = descriptorContext; + TypeInterceptor = typeInterceptor; IsExtension = Type is INamedTypeExtensionMerger; + IsSchema = Type is ISchema; + Scope = scope; if (type is INamedType nt) { @@ -58,25 +59,9 @@ public Type RuntimeType } } - public IReadOnlyList References => _references; + public List References { get; } = new(); - public IReadOnlyList Dependencies => _dependencies; - - public TypeDiscoveryContext DiscoveryContext { get; } - - public TypeCompletionContext CompletionContext - { - get - { - if (_completionContext is null) - { - throw new InvalidOperationException( - TypeResources.RegisteredType_CompletionContext_Not_Initialized); - } - - return _completionContext; - } - } + public List Dependencies { get; } = new(); public bool IsInferred { get; } @@ -88,41 +73,26 @@ public TypeCompletionContext CompletionContext public bool IsIntrospectionType { get; } - public void AddReferences(IEnumerable references) - { - var merged = _references.ToList(); + public bool IsSchema { get; } - foreach (var reference in references) - { - if (!merged.Contains(reference)) - { - merged.Add(reference); - } - } + public bool IsType => IsNamedType; - _references = merged; - } + public bool IsDirective => IsDirectiveType; - public void AddDependencies(IEnumerable dependencies) - { - var merged = Dependencies.ToList(); - merged.AddRange(dependencies); - _dependencies = merged; - } + public List Errors => _errors ??= new(); - public void SetCompletionContext(TypeCompletionContext completionContext) + public override string? ToString() { - if (_completionContext is not null) + if (IsSchema) { - throw new InvalidOperationException( - TypeResources.RegisteredType_CompletionContext_Already_Set); + return "Schema"; } - _completionContext = completionContext; - } - - public override string? ToString() - { + if (Type is IHasName { Name: { IsEmpty: false } } hasName) + { + return IsDirective ? $"@{hasName.Name}" : hasName.Name; + } + return Type.ToString(); } } diff --git a/src/HotChocolate/Core/src/Types/Configuration/SchemaTypeReferenceHandler.cs b/src/HotChocolate/Core/src/Types/Configuration/SchemaTypeReferenceHandler.cs index d43a0bc27ba..f536fd65f8f 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/SchemaTypeReferenceHandler.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/SchemaTypeReferenceHandler.cs @@ -1,6 +1,4 @@ -using System.Linq; -using System.Collections.Generic; -using HotChocolate.Types; +using HotChocolate.Types; using HotChocolate.Types.Descriptors; #nullable enable @@ -9,27 +7,25 @@ namespace HotChocolate.Configuration { internal sealed class SchemaTypeReferenceHandler : ITypeRegistrarHandler { - public void Register( - ITypeRegistrar typeRegistrar, - IEnumerable typeReferences) + public TypeReferenceKind Kind => TypeReferenceKind.SchemaType; + + public void Handle(ITypeRegistrar typeRegistrar, ITypeReference typeReference) { - foreach (SchemaTypeReference typeReference in - typeReferences.OfType()) + var typeRef = (SchemaTypeReference)typeReference; + + if (!typeRegistrar.IsResolved(typeReference)) { - if (!typeRegistrar.IsResolved(typeReference)) - { - ITypeSystemMember tsm = typeReference.Type; + ITypeSystemMember tsm = typeRef.Type; - // if it is a type object we will make sure it is unwrapped. - if (typeReference.Type is IType type) - { - tsm = type.NamedType(); - } + // if it is a type object we will make sure it is unwrapped. + if (typeRef.Type is IType type) + { + tsm = type.NamedType(); + } - if (tsm is TypeSystemObjectBase tso) - { - typeRegistrar.Register(tso, typeReference.Scope); - } + if (tsm is TypeSystemObjectBase tso) + { + typeRegistrar.Register(tso, typeReference.Scope); } } } diff --git a/src/HotChocolate/Core/src/Types/Configuration/SyntaxTypeReferenceHandler.cs b/src/HotChocolate/Core/src/Types/Configuration/SyntaxTypeReferenceHandler.cs index be66663067b..aef93675651 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/SyntaxTypeReferenceHandler.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/SyntaxTypeReferenceHandler.cs @@ -1,46 +1,39 @@ -using System.Linq; +using System; using System.Collections.Generic; -using HotChocolate.Language; using HotChocolate.Types; using HotChocolate.Types.Descriptors; -using System; #nullable enable namespace HotChocolate.Configuration { - internal sealed class SyntaxTypeReferenceHandler - : ITypeRegistrarHandler + internal sealed class SyntaxTypeReferenceHandler : ITypeRegistrarHandler { - private readonly ITypeInspector _typeInspector; private readonly HashSet _handled = new(); + private readonly ITypeInspector _typeInspector; public SyntaxTypeReferenceHandler(ITypeInspector typeInspector) { - _typeInspector = typeInspector; + _typeInspector = typeInspector ?? + throw new ArgumentNullException(nameof(typeInspector)); } - public void Register( - ITypeRegistrar typeRegistrar, - IEnumerable typeReferences) + public TypeReferenceKind Kind => TypeReferenceKind.Syntax; + + public void Handle(ITypeRegistrar typeRegistrar, ITypeReference typeReference) { - foreach (SyntaxTypeReference typeReference in - typeReferences.OfType()) + var typeRef = (SyntaxTypeReference)typeReference; + + if (_handled.Add(typeRef.Name) && + Scalars.TryGetScalar(typeRef.Name, out Type? scalarType)) { - string name = typeReference.Type.NamedType().Name.Value; + ExtendedTypeReference namedTypeReference = _typeInspector.GetTypeRef(scalarType); - if (_handled.Add(name) && - Scalars.TryGetScalar(name, out Type? scalarType)) + if (!typeRegistrar.IsResolved(namedTypeReference)) { - ExtendedTypeReference namedTypeReference = - _typeInspector.GetTypeRef(scalarType); - - if (!typeRegistrar.IsResolved(namedTypeReference)) - { - typeRegistrar.Register( - typeRegistrar.CreateInstance(namedTypeReference.Type.Type), - typeReference.Scope); - } + typeRegistrar.Register( + typeRegistrar.CreateInstance(namedTypeReference.Type.Type), + typeRef.Scope); } } } diff --git a/src/HotChocolate/Core/src/Types/Configuration/TypeCompletionContext.cs b/src/HotChocolate/Core/src/Types/Configuration/TypeCompletionContext.cs deleted file mode 100644 index cfc8d855d09..00000000000 --- a/src/HotChocolate/Core/src/Types/Configuration/TypeCompletionContext.cs +++ /dev/null @@ -1,152 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics.CodeAnalysis; -using HotChocolate.Resolvers; -using HotChocolate.Types; -using HotChocolate.Types.Descriptors; -using static HotChocolate.Utilities.ThrowHelper; - -namespace HotChocolate.Configuration -{ - internal sealed class TypeCompletionContext : ITypeCompletionContext - { - private readonly TypeDiscoveryContext _initializationContext; - private readonly TypeReferenceResolver _typeReferenceResolver; - private readonly Func _schemaResolver; - - public TypeCompletionContext( - TypeDiscoveryContext initializationContext, - TypeReferenceResolver typeReferenceResolver, - IList globalComponents, - IsOfTypeFallback isOfType, - Func schemaResolver) - { - _initializationContext = initializationContext ?? - throw new ArgumentNullException(nameof(initializationContext)); - _typeReferenceResolver = typeReferenceResolver ?? - throw new ArgumentNullException(nameof(typeReferenceResolver)); - IsOfType = isOfType; - _schemaResolver = schemaResolver ?? - throw new ArgumentNullException(nameof(schemaResolver)); - GlobalComponents = new ReadOnlyCollection(globalComponents); - } - - public TypeStatus Status { get; set; } = TypeStatus.Initialized; - - public bool? IsQueryType { get; set; } - - public bool? IsMutationType { get; set; } - - public bool? IsSubscriptionType { get; set; } - - public IReadOnlyList GlobalComponents { get; } - - public IsOfTypeFallback IsOfType { get; } - - public ITypeSystemObject Type => _initializationContext.Type; - - public string Scope => _initializationContext.Scope; - - public bool IsType => _initializationContext.IsType; - - public bool IsIntrospectionType => _initializationContext.IsIntrospectionType; - - public bool IsDirective => _initializationContext.IsDirective; - - public bool IsSchema => _initializationContext.IsSchema; - - public IServiceProvider Services => _initializationContext.Services; - - public IDictionary ContextData => _initializationContext.ContextData; - - public IDescriptorContext DescriptorContext => _initializationContext.DescriptorContext; - - public ITypeInterceptor TypeInterceptor => _initializationContext.TypeInterceptor; - - public ITypeInspector TypeInspector => _initializationContext.TypeInspector; - - /// - public bool TryGetType(ITypeReference typeRef, out T type) - where T : IType - { - if (_typeReferenceResolver.TryGetType(typeRef, out IType t) && - t is T casted) - { - type = casted; - return true; - } - - type = default; - return false; - } - - /// - public T GetType(ITypeReference typeRef) - where T : IType - { - if (typeRef is null) - { - throw new ArgumentNullException(nameof(typeRef)); - } - - if (!TryGetType(typeRef, out T type)) - { - throw TypeCompletionContext_UnableToResolveType(Type, typeRef); - } - - return type; - } - - public ITypeReference GetNamedTypeReference(ITypeReference typeRef) - => _typeReferenceResolver.GetNamedTypeReference(typeRef); - - public bool TryGetDirectiveType( - IDirectiveReference directiveRef, - [NotNullWhen(true)] out DirectiveType directiveType) => - _typeReferenceResolver.TryGetDirectiveType(directiveRef, out directiveType); - - public DirectiveType GetDirectiveType(IDirectiveReference directiveRef) - { - return _typeReferenceResolver.TryGetDirectiveType( - directiveRef, - out DirectiveType directiveType) - ? directiveType - : null; - } - - public Func GetSchemaResolver() - { - if (Status == TypeStatus.Initialized) - { - throw new NotSupportedException(); - } - - return _schemaResolver; - } - - public IEnumerable GetTypes() - where T : IType - { - if (Status == TypeStatus.Initialized) - { - throw new NotSupportedException(); - } - - return _typeReferenceResolver.GetTypes(); - } - - public void ReportError(ISchemaError error) - { - if (error is null) - { - throw new ArgumentNullException(nameof(error)); - } - - _initializationContext.ReportError(error); - } - - public bool TryPredictTypeKind(ITypeReference typeRef, out TypeKind kind) => - _initializationContext.TryPredictTypeKind(typeRef, out kind); - } -} diff --git a/src/HotChocolate/Core/src/Types/Configuration/TypeDiscoverer.cs b/src/HotChocolate/Core/src/Types/Configuration/TypeDiscoverer.cs index 459cff22d1c..9b0fa6b281b 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/TypeDiscoverer.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/TypeDiscoverer.cs @@ -14,10 +14,12 @@ internal sealed class TypeDiscoverer { private readonly List _unregistered = new(); private readonly List _errors = new(); + private readonly List _resolved = new(); private readonly TypeRegistry _typeRegistry; private readonly TypeRegistrar _typeRegistrar; private readonly ITypeRegistrarHandler[] _handlers; private readonly ITypeInspector _typeInspector; + private readonly ITypeInterceptor _interceptor; public TypeDiscoverer( IDescriptorContext context, @@ -67,17 +69,23 @@ public TypeDiscoverer( _handlers = new ITypeRegistrarHandler[] { - new SchemaTypeReferenceHandler(), new ExtendedTypeReferenceHandler(context.TypeInspector), - new SyntaxTypeReferenceHandler(context.TypeInspector) + new SchemaTypeReferenceHandler(), + new SyntaxTypeReferenceHandler(context.TypeInspector), + new FactoryTypeReferenceHandler(context), + new DependantFactoryTypeReferenceHandler(context) }; _typeInspector = context.TypeInspector; + _interceptor = interceptor; } public IReadOnlyList DiscoverTypes() { const int max = 1000; + var processed = new HashSet(); + +DISCOVER: var tries = 0; var resolved = false; @@ -103,6 +111,23 @@ public IReadOnlyList DiscoverTypes() } while (resolved && tries < max && _errors.Count == 0); + if (_errors.Count == 0 && _unregistered.Count == 0) + { + foreach (ITypeReference typeReference in + _interceptor.RegisterMoreTypes(_typeRegistry.Types)) + { + if (processed.Add(typeReference)) + { + _unregistered.Add(typeReference); + } + } + + if (_unregistered.Count > 0) + { + goto DISCOVER; + } + } + CollectErrors(); if (_errors.Count == 0) @@ -117,9 +142,9 @@ private void RegisterTypes() { while (_unregistered.Count > 0) { - for (var i = 0; i < _handlers.Length; i++) + foreach (var typeRef in _unregistered) { - _handlers[i].Register(_typeRegistrar, _unregistered); + _handlers[(int)typeRef.Kind].Handle(_typeRegistrar, typeRef); } _unregistered.Clear(); @@ -131,25 +156,34 @@ private bool TryInferTypes() { var inferred = false; - foreach (ExtendedTypeReference unresolvedType in - _typeRegistrar.GetUnresolved().OfType()) + foreach (var typeRef in _typeRegistrar.Unresolved) { - if (Scalars.TryGetScalar(unresolvedType.Type.Type, out Type? scalarType)) + if (typeRef is ExtendedTypeReference unresolvedType) { - inferred = true; + if (Scalars.TryGetScalar(unresolvedType.Type.Type, out Type? scalarType)) + { + inferred = true; - ExtendedTypeReference typeReference = _typeInspector.GetTypeRef(scalarType); - _unregistered.Add(typeReference); - _typeRegistrar.MarkResolved(unresolvedType); - _typeRegistry.TryRegister(unresolvedType, typeReference); + ExtendedTypeReference typeReference = _typeInspector.GetTypeRef(scalarType); + _unregistered.Add(typeReference); + _resolved.Add(unresolvedType); + _typeRegistry.TryRegister(unresolvedType, typeReference); + } + else if (SchemaTypeResolver.TryInferSchemaType( + _typeInspector, unresolvedType, out ExtendedTypeReference? schemaType)) + { + inferred = true; + _unregistered.Add(schemaType); + _resolved.Add(unresolvedType); + } } - else if (SchemaTypeResolver.TryInferSchemaType( - _typeInspector, unresolvedType, out ExtendedTypeReference? schemaType)) - { - inferred = true; + } - _unregistered.Add(schemaType); - _typeRegistrar.MarkResolved(unresolvedType); + if (_resolved.Count > 0) + { + foreach (ITypeReference typeRef in _resolved) + { + _typeRegistrar.MarkResolved(typeRef); } } @@ -158,17 +192,19 @@ private bool TryInferTypes() private void CollectErrors() { - foreach (TypeDiscoveryContext context in - _typeRegistry.Types.Select(t => t.DiscoveryContext)) + foreach (RegisteredType type in _typeRegistry.Types) { - _errors.AddRange(context.Errors); - } + if (type.Errors.Count == 0) + { + continue; + } - IReadOnlyCollection unresolved = _typeRegistrar.GetUnresolved(); + _errors.AddRange(type.Errors); + } - if (_errors.Count == 0 && unresolved.Count > 0) + if (_errors.Count == 0 && _typeRegistrar.Unresolved.Count > 0) { - foreach (ITypeReference unresolvedReference in _typeRegistrar.GetUnresolved()) + foreach (ITypeReference unresolvedReference in _typeRegistrar.Unresolved) { var types = _typeRegistry.Types.Where( t => t.Dependencies.Select(d => d.TypeReference) diff --git a/src/HotChocolate/Core/src/Types/Configuration/TypeInitializationInterceptor.cs b/src/HotChocolate/Core/src/Types/Configuration/TypeInitializationInterceptor.cs index 4f90895e14d..ed436e01de9 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/TypeInitializationInterceptor.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/TypeInitializationInterceptor.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using HotChocolate.Types.Descriptors; using HotChocolate.Types.Descriptors.Definitions; @@ -42,6 +43,10 @@ public virtual void OnAfterInitialize( { } + public virtual IEnumerable RegisterMoreTypes( + IReadOnlyCollection discoveryContexts) + => Enumerable.Empty(); + public virtual void OnTypeRegistered( ITypeDiscoveryContext discoveryContext) { diff --git a/src/HotChocolate/Core/src/Types/Configuration/TypeInitializer.cs b/src/HotChocolate/Core/src/Types/Configuration/TypeInitializer.cs index ee7f3dc0e61..08c56e6b2fa 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/TypeInitializer.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/TypeInitializer.cs @@ -42,7 +42,7 @@ public TypeInitializer( throw new ArgumentNullException(nameof(initialTypes)); _isOfType = isOfType; _getTypeKind = getTypeKind ?? - throw new ArgumentNullException(nameof(_getTypeKind)); + throw new ArgumentNullException(nameof(getTypeKind)); _interceptor = descriptorContext.TypeInterceptor; ITypeInspector typeInspector = descriptorContext.TypeInspector; @@ -125,8 +125,7 @@ private void DiscoverTypes() // lets tell the type interceptors what types we have initialized. if (_interceptor.TriggerAggregations) { - _interceptor.OnTypesInitialized( - _typeRegistry.Types.Select(t => t.DiscoveryContext).ToList()); + _interceptor.OnTypesInitialized(_typeRegistry.Types); } _interceptor.OnAfterDiscoverTypes(); @@ -154,8 +153,6 @@ private void RegisterImplicitInterfaceDependencies() .Distinct() .ToList(); - var dependencies = new List(); - foreach (RegisteredType objectType in objectTypes) { foreach (RegisteredType interfaceType in interfaceTypes) @@ -164,16 +161,9 @@ private void RegisterImplicitInterfaceDependencies() { SchemaTypeReference typeReference = TypeReference.Create(interfaceType.Type); ((ObjectType)objectType.Type).Definition!.Interfaces.Add(typeReference); - dependencies.Add(new(typeReference, TypeDependencyKind.Completed)); + objectType.Dependencies.Add(new(typeReference, TypeDependencyKind.Completed)); } } - - if (dependencies.Count > 0) - { - objectType.AddDependencies(dependencies); - _typeRegistry.Register(objectType); - dependencies.Clear(); - } } foreach (RegisteredType implementing in interfaceTypes) @@ -185,16 +175,9 @@ private void RegisterImplicitInterfaceDependencies() { SchemaTypeReference typeReference = TypeReference.Create(interfaceType.Type); ((InterfaceType)implementing.Type).Definition!.Interfaces.Add(typeReference); - dependencies.Add(new(typeReference, TypeDependencyKind.Completed)); + implementing.Dependencies.Add(new(typeReference, TypeDependencyKind.Completed)); } } - - if (dependencies.Count > 0) - { - implementing.AddDependencies(dependencies); - _typeRegistry.Register(implementing); - dependencies.Clear(); - } } } @@ -202,26 +185,23 @@ private void CompleteNames(Func schemaResolver) { bool CompleteName(RegisteredType registeredType) { - registeredType.SetCompletionContext( - new TypeCompletionContext( - registeredType.DiscoveryContext, - _typeReferenceResolver, - GlobalComponents, - _isOfType, - schemaResolver)); + registeredType.PrepareForCompletion( + _typeReferenceResolver, + schemaResolver, + _globalComps, + _isOfType); - registeredType.Type.CompleteName(registeredType.CompletionContext); + registeredType.Type.CompleteName(registeredType); if (registeredType.IsNamedType || registeredType.IsDirectiveType) { _typeRegistry.Register(registeredType.Type.Name, registeredType); } - TypeCompletionContext context = registeredType.CompletionContext; RootTypeKind kind = _getTypeKind(registeredType.Type); - context.IsQueryType = kind == RootTypeKind.Query; - context.IsMutationType = kind == RootTypeKind.Mutation; - context.IsSubscriptionType = kind == RootTypeKind.Subscription; + registeredType.IsQueryType = kind == RootTypeKind.Query; + registeredType.IsMutationType = kind == RootTypeKind.Mutation; + registeredType.IsSubscriptionType = kind == RootTypeKind.Subscription; return true; } @@ -230,8 +210,7 @@ bool CompleteName(RegisteredType registeredType) if (ProcessTypes(TypeDependencyKind.Named, CompleteName) && _interceptor.TriggerAggregations) { - _interceptor.OnTypesCompletedName( - _typeRegistry.Types.Select(t => t.CompletionContext).ToList()); + _interceptor.OnTypesCompletedName(_typeRegistry.Types); } EnsureNoErrors(); @@ -340,12 +319,11 @@ private void MergeTypeExtension( } // merge - TypeCompletionContext context = extension.CompletionContext; - context.Status = TypeStatus.Named; - m.Merge(context, namedType); + extension.Status = TypeStatus.Named; + m.Merge(extension, namedType); // update dependencies - registeredType.AddDependencies(extension.Dependencies); + registeredType.Dependencies.AddRange(extension.Dependencies); _typeRegistry.Register(registeredType); } } @@ -357,13 +335,12 @@ bool CompleteType(RegisteredType registeredType) { if (!registeredType.IsExtension) { - TypeCompletionContext context = registeredType.CompletionContext; - context.Status = TypeStatus.Named; + registeredType.Status = TypeStatus.Named; RootTypeKind kind = _getTypeKind(registeredType.Type); - context.IsQueryType = kind == RootTypeKind.Query; - context.IsMutationType = kind == RootTypeKind.Mutation; - context.IsSubscriptionType = kind == RootTypeKind.Subscription; - registeredType.Type.CompleteType(context); + registeredType.IsQueryType = kind == RootTypeKind.Query; + registeredType.IsMutationType = kind == RootTypeKind.Mutation; + registeredType.IsSubscriptionType = kind == RootTypeKind.Subscription; + registeredType.Type.CompleteType(registeredType); } return true; } @@ -375,8 +352,7 @@ bool CompleteType(RegisteredType registeredType) if (_interceptor.TriggerAggregations) { - _interceptor.OnTypesCompleted( - _typeRegistry.Types.Select(t => t.CompletionContext).ToList()); + _interceptor.OnTypesCompleted(_typeRegistry.Types); } _interceptor.OnAfterCompleteTypes(); @@ -388,7 +364,7 @@ private void FinalizeTypes() { if (!registeredType.IsExtension) { - registeredType.Type.FinalizeType(registeredType.CompletionContext); + registeredType.Type.FinalizeType(registeredType); } } } @@ -518,10 +494,14 @@ private void EnsureNoErrors() { var errors = new List(_errors); - foreach (TypeDiscoveryContext context in - _typeRegistry.Types.Select(t => t.DiscoveryContext)) + foreach (RegisteredType type in _typeRegistry.Types) { - errors.AddRange(context.Errors); + if (type.Errors.Count == 0) + { + continue; + } + + errors.AddRange(type.Errors); } if (errors.Count > 0) diff --git a/src/HotChocolate/Core/src/Types/Configuration/TypeLookup.cs b/src/HotChocolate/Core/src/Types/Configuration/TypeLookup.cs index 241b702c6b2..a714a9cd67b 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/TypeLookup.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/TypeLookup.cs @@ -66,6 +66,11 @@ public bool TryNormalizeReference( return true; } break; + + case DependantFactoryTypeReference r: + _refs[typeRef] = r; + namedTypeRef = r; + return true; } namedTypeRef = null; diff --git a/src/HotChocolate/Core/src/Types/Configuration/TypeReferenceResolver.cs b/src/HotChocolate/Core/src/Types/Configuration/TypeReferenceResolver.cs index b7e0cdd0ea4..20c27927af2 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/TypeReferenceResolver.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/TypeReferenceResolver.cs @@ -98,6 +98,10 @@ public bool TryGetType(ITypeReference typeRef, [NotNullWhen(true)] out IType? ty type = CreateType(namedType, r.Type); return true; + case DependantFactoryTypeReference reference: + type = namedType; + return true; + default: throw new NotSupportedException(); } @@ -158,6 +162,7 @@ private TypeId CreateId(ITypeReference typeRef, ITypeReference namedTypeRef) return new TypeId(namedTypeRef, CreateFlags(r.Type)); case SchemaTypeReference: + case DependantFactoryTypeReference: return new TypeId(namedTypeRef, 1); default: @@ -175,7 +180,7 @@ private static int CreateFlags(ITypeInfo typeInfo) { case TypeComponentKind.List: flags <<= 1; - flags = flags | 1; + flags |= 1; break; case TypeComponentKind.NonNull: @@ -197,7 +202,7 @@ private static int CreateFlags(ITypeNode type) if (current is ListTypeNode listType) { flags <<= 1; - flags = flags | 1; + flags |= 1; current = listType.Type; } else if (current is NonNullTypeNode nonNullType) diff --git a/src/HotChocolate/Core/src/Types/Configuration/TypeRegistrar.cs b/src/HotChocolate/Core/src/Types/Configuration/TypeRegistrar.cs index b3b7dadf184..dbdb40ce6dd 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/TypeRegistrar.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/TypeRegistrar.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using HotChocolate.Internal; using HotChocolate.Types; using HotChocolate.Types.Descriptors; @@ -40,32 +39,34 @@ public TypeRegistrar( } public void Register( - TypeSystemObjectBase typeSystemObject, + TypeSystemObjectBase obj, string? scope, - bool isInferred = false) + bool inferred = false, + Action? configure = null) { - if (typeSystemObject is null) + if (obj is null) { - throw new ArgumentNullException(nameof(typeSystemObject)); + throw new ArgumentNullException(nameof(obj)); } - RegisteredType registeredType = InitializeType(typeSystemObject, scope, isInferred); + RegisteredType registeredType = InitializeType(obj, scope, inferred); + + configure?.Invoke(registeredType); if (registeredType.References.Count > 0) { RegisterTypeAndResolveReferences(registeredType); - if (typeSystemObject is IHasRuntimeType hasRuntimeType + if (obj is IHasRuntimeType hasRuntimeType && hasRuntimeType.RuntimeType != typeof(object)) { ExtendedTypeReference runtimeTypeRef = _context.TypeInspector.GetTypeRef( hasRuntimeType.RuntimeType, - SchemaTypeReference.InferTypeContext(typeSystemObject), - scope: scope); + SchemaTypeReference.InferTypeContext(obj), + scope); - var explicitBind = typeSystemObject is ScalarType scalar - && scalar.Bind == BindingBehavior.Explicit; + var explicitBind = obj is ScalarType { Bind: BindingBehavior.Explicit }; if (!explicitBind) { @@ -128,7 +129,7 @@ public TypeSystemObjectBase CreateInstance(Type namedSchemaType) } } - public IReadOnlyCollection GetUnresolved() => _unresolved.ToList(); + public IReadOnlyCollection Unresolved => _unresolved; public IReadOnlyCollection GetUnhandled() { @@ -140,12 +141,11 @@ public IReadOnlyCollection GetUnhandled() { if (_handled.Add(type)) { - foreach (ITypeReference typeReference in type.DiscoveryContext - .TypeDependencies.Select(t => t.TypeReference)) + foreach (TypeDependency typeDep in type.Dependencies) { - if (registered.Add(typeReference)) + if (registered.Add(typeDep.TypeReference)) { - unhandled.Add(typeReference); + unhandled.Add(typeDep.TypeReference); } } } @@ -172,8 +172,9 @@ private RegisteredType InitializeType( return registeredType; } - var discoveryContext = new TypeDiscoveryContext( + registeredType = new RegisteredType( typeSystemObject, + isInferred, _typeRegistry, _typeLookup, _context, @@ -184,23 +185,21 @@ private RegisteredType InitializeType( // standard initialization flow. if (!typeSystemObject.IsInitialized) { - typeSystemObject.Initialize(discoveryContext); + typeSystemObject.Initialize(registeredType); } - // if it is a yet unknown type we will go on with our - var references = new List(); - if (!isInferred) { - references.Add(instanceRef); + registeredType.References.TryAdd(instanceRef); } if (!ExtendedType.Tools.IsNonGenericBaseType(typeSystemObject.GetType())) { - references.Add(_context.TypeInspector.GetTypeRef( - typeSystemObject.GetType(), - SchemaTypeReference.InferTypeContext(typeSystemObject), - scope)); + registeredType.References.TryAdd( + _context.TypeInspector.GetTypeRef( + typeSystemObject.GetType(), + SchemaTypeReference.InferTypeContext(typeSystemObject), + scope)); } if (typeSystemObject is IHasTypeIdentity hasTypeIdentity && @@ -212,18 +211,16 @@ private RegisteredType InitializeType( SchemaTypeReference.InferTypeContext(typeSystemObject), scope); - if (!references.Contains(reference)) - { - references.Add(reference); - } + registeredType.References.TryAdd(reference); } - registeredType = new RegisteredType( - typeSystemObject, - references, - CollectDependencies(discoveryContext), - discoveryContext, - isInferred); + if (_interceptor.TryCreateScope( + registeredType, + out IReadOnlyList? dependencies)) + { + registeredType.Dependencies.Clear(); + registeredType.Dependencies.AddRange(dependencies); + } return registeredType; } @@ -237,18 +234,5 @@ private RegisteredType InitializeType( .Build()); } } - - private IReadOnlyList CollectDependencies( - ITypeDiscoveryContext discoveryContext) - { - if (discoveryContext.TypeInterceptor.TryCreateScope( - discoveryContext, - out IReadOnlyList? dependencies)) - { - return dependencies; - } - - return discoveryContext.TypeDependencies; - } } } diff --git a/src/HotChocolate/Core/src/Types/Configuration/TypeRegistry.cs b/src/HotChocolate/Core/src/Types/Configuration/TypeRegistry.cs index 92dc7646691..82eec2d6025 100644 --- a/src/HotChocolate/Core/src/Types/Configuration/TypeRegistry.cs +++ b/src/HotChocolate/Core/src/Types/Configuration/TypeRegistry.cs @@ -151,7 +151,7 @@ public void Register(RegisteredType registeredType) if (addToTypes) { _types.Add(registeredType); - _typeRegistryInterceptor.OnTypeRegistered(registeredType.DiscoveryContext); + _typeRegistryInterceptor.OnTypeRegistered(registeredType); } if (!registeredType.IsExtension) @@ -218,26 +218,20 @@ public void Register(NameString typeName, RegisteredType registeredType) public void CompleteDiscovery() { - var refs = new List(); - foreach (RegisteredType registeredType in _types) { - refs.Clear(); - ITypeReference reference = TypeReference.Create(registeredType.Type); - refs.Add(reference); + registeredType.References.TryAdd(reference); _typeRegister[reference] = registeredType; if (registeredType.Type.Scope is { } s) { reference = TypeReference.Create(registeredType.Type, s); - refs.Add(reference); + registeredType.References.TryAdd(reference); _typeRegister[reference] = registeredType; } - - registeredType.AddReferences(refs); } } } diff --git a/src/HotChocolate/Core/src/Types/Internal/TypeExtensionHelper.cs b/src/HotChocolate/Core/src/Types/Internal/TypeExtensionHelper.cs index 355de387ec2..3a180e9d4fe 100644 --- a/src/HotChocolate/Core/src/Types/Internal/TypeExtensionHelper.cs +++ b/src/HotChocolate/Core/src/Types/Internal/TypeExtensionHelper.cs @@ -18,7 +18,7 @@ public static void MergeInterfaceFields( IList typeFields) { MergeOutputFields(context, extensionFields, typeFields, - (fields, extensionField, typeField) => { }); + (_, _, _) => { }); } public static void MergeInputObjectFields( @@ -27,7 +27,7 @@ public static void MergeInputObjectFields( IList typeFields) { MergeFields(context, extensionFields, typeFields, - (fields, extensionField, typeField) => { }); + (_, _, _) => { }); } private static void MergeOutputFields( @@ -51,7 +51,7 @@ private static void MergeOutputFields( context, extensionField.Arguments, typeField.Arguments, - (args, extensionArg, typeArg) => { }); + (_, _, _) => { }); action(fields, extensionField, typeField); }, @@ -98,8 +98,12 @@ public static void MergeDirectives( foreach (DirectiveDefinition directive in type) { - DirectiveType directiveType = context.GetDirectiveType(directive.Reference); - directives.Add((directiveType, directive)); + if (context.TryGetDirectiveType( + directive.Reference, + out DirectiveType? directiveType)) + { + directives.Add((directiveType, directive)); + } } foreach (DirectiveDefinition directive in extension) @@ -135,7 +139,7 @@ private static void MergeDirective( } else { - int index = directives.IndexOf(entry); + var index = directives.IndexOf(entry); directives[index] = (directiveType, directive); } } @@ -187,10 +191,10 @@ public static void MergeTypes( } public static void MergeConfigurations( - ICollection extensionConfigurations, - ICollection typeConfigurations) + ICollection extensionConfigurations, + ICollection typeConfigurations) { - foreach (ILazyTypeConfiguration configuration in extensionConfigurations) + foreach (ITypeSystemMemberConfiguration configuration in extensionConfigurations) { typeConfigurations.Add(configuration); } diff --git a/src/HotChocolate/Core/src/Types/InternalsVisibleTo.cs b/src/HotChocolate/Core/src/Types/InternalsVisibleTo.cs index e38b646c70c..2082b3149c1 100644 --- a/src/HotChocolate/Core/src/Types/InternalsVisibleTo.cs +++ b/src/HotChocolate/Core/src/Types/InternalsVisibleTo.cs @@ -1,8 +1,8 @@ using System.Runtime.CompilerServices; -// todo : remove this internal dependency. [assembly: InternalsVisibleTo("HotChocolate.Validation")] +[assembly: InternalsVisibleTo("HotChocolate.Types.CursorPagination")] // Legacy [assembly: InternalsVisibleTo("HotChocolate.Types.Filters")] diff --git a/src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs b/src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs index 2285026e897..f6b9705fac8 100644 --- a/src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs +++ b/src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs @@ -1358,5 +1358,59 @@ internal static string FieldInitHelper_CompleteFields_MaxFieldCountToSmall { return ResourceManager.GetString("FieldInitHelper_CompleteFields_MaxFieldCountToSmall", resourceCulture); } } + + internal static string RegisteredType_Completion_NotYetReady { + get { + return ResourceManager.GetString("RegisteredType_Completion_NotYetReady", resourceCulture); + } + } + + internal static string EdgeType_IsInstanceOfType_NonObject { + get { + return ResourceManager.GetString("EdgeType_IsInstanceOfType_NonObject", resourceCulture); + } + } + + internal static string EdgeType_Description { + get { + return ResourceManager.GetString("EdgeType_Description", resourceCulture); + } + } + + internal static string EdgeType_Cursor_Description { + get { + return ResourceManager.GetString("EdgeType_Cursor_Description", resourceCulture); + } + } + + internal static string EdgeType_Node_Description { + get { + return ResourceManager.GetString("EdgeType_Node_Description", resourceCulture); + } + } + + internal static string ConnectionType_Description { + get { + return ResourceManager.GetString("ConnectionType_Description", resourceCulture); + } + } + + internal static string ConnectionType_PageInfo_Description { + get { + return ResourceManager.GetString("ConnectionType_PageInfo_Description", resourceCulture); + } + } + + internal static string ConnectionType_Edges_Description { + get { + return ResourceManager.GetString("ConnectionType_Edges_Description", resourceCulture); + } + } + + internal static string ConnectionType_Nodes_Description { + get { + return ResourceManager.GetString("ConnectionType_Nodes_Description", resourceCulture); + } + } } } diff --git a/src/HotChocolate/Core/src/Types/Properties/TypeResources.resx b/src/HotChocolate/Core/src/Types/Properties/TypeResources.resx index 762584fd7ee..9006c445983 100644 --- a/src/HotChocolate/Core/src/Types/Properties/TypeResources.resx +++ b/src/HotChocolate/Core/src/Types/Properties/TypeResources.resx @@ -780,4 +780,31 @@ Type: `{0}` The max expected field count cannot be smaller then 1. + + The object is not yet ready for this action. + + + Edge types that have a non object node are not supported. + + + An edge in a connection. + + + A cursor for use in pagination. + + + The item at the end of the edge. + + + A connection to a list of items. + + + Information to aid in pagination. + + + A list of edges. + + + A flattened list of the nodes. + diff --git a/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/CancellationTokenParameterExpressionBuilder.cs b/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/CancellationTokenParameterExpressionBuilder.cs index 6821959d004..1a3c021f1d1 100644 --- a/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/CancellationTokenParameterExpressionBuilder.cs +++ b/src/HotChocolate/Core/src/Types/Resolvers/Expressions/Parameters/CancellationTokenParameterExpressionBuilder.cs @@ -11,11 +11,11 @@ namespace HotChocolate.Resolvers.Expressions.Parameters { internal sealed class CancellationTokenParameterExpressionBuilder : IParameterExpressionBuilder { - private static readonly PropertyInfo _cancellationToken; + private static readonly PropertyInfo _cancellationToken = + ContextType.GetProperty(nameof(IResolverContext.RequestAborted))!; static CancellationTokenParameterExpressionBuilder() { - _cancellationToken = ContextType.GetProperty(nameof(IResolverContext.RequestAborted))!; Debug.Assert(_cancellationToken is not null!, "RequestAborted property is missing." ); } diff --git a/src/HotChocolate/Core/src/Types/Resolvers/IResolverContext.cs b/src/HotChocolate/Core/src/Types/Resolvers/IResolverContext.cs index 4d7dcd5ec22..738b3181622 100644 --- a/src/HotChocolate/Core/src/Types/Resolvers/IResolverContext.cs +++ b/src/HotChocolate/Core/src/Types/Resolvers/IResolverContext.cs @@ -95,8 +95,7 @@ public interface IResolverContext : IPureResolverContext [Obsolete("Use ArgumentValue(name) or " + "ArgumentLiteral(name) or " + "ArgumentOptional(name).")] - [return: MaybeNull] - T Argument(NameString name); + T? Argument(NameString name); /// /// Gets as required service from the dependency injection container. @@ -109,7 +108,7 @@ public interface IResolverContext : IPureResolverContext /// /// Report a non-terminating resolver error to the execution engine. - /// The error will be displayed in the errorsection with a reference to + /// The error will be displayed in the error section with a reference to /// the field selection that is associated with the current /// resolver context. /// @@ -120,7 +119,7 @@ public interface IResolverContext : IPureResolverContext /// /// Report a non-terminating resolver error to the execution engine. - /// The error will be displayed in the errorsection with a reference to + /// The error will be displayed in the error section with a reference to /// the field selection that is associated with the current /// resolver context. /// @@ -131,7 +130,7 @@ public interface IResolverContext : IPureResolverContext /// /// Report a non-terminating resolver error to the execution engine. - /// The error will be displayed in the errorsection with a reference to + /// The error will be displayed in the error section with a reference to /// the field selection that is associated with the current /// resolver context. /// diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/CompletedDependencyDescriptor~1.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/CompletedDependencyDescriptor~1.cs index ca658ead219..26cb2f1bb60 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/CompletedDependencyDescriptor~1.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/CompletedDependencyDescriptor~1.cs @@ -3,14 +3,13 @@ namespace HotChocolate.Types.Descriptors { - internal class CompletedDependencyDescriptor - : DependencyDescriptorBase + internal class CompletedDependencyDescriptor + : DependencyDescriptorBase , ICompletedDependencyDescriptor - where T : DefinitionBase { public CompletedDependencyDescriptor( ITypeInspector typeInspector, - TypeConfiguration configuration) + CompleteConfiguration configuration) : base(typeInspector, configuration) { } diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Contracts/ConfigurationKind.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Contracts/ConfigurationKind.cs index 01b0753fc1e..515d8629cce 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Contracts/ConfigurationKind.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Contracts/ConfigurationKind.cs @@ -2,6 +2,7 @@ { public enum ApplyConfigurationOn { + Create, Naming, Completion } diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/CompleteConfiguration.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/CompleteConfiguration.cs new file mode 100644 index 00000000000..fb7a6804642 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/CompleteConfiguration.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using HotChocolate.Configuration; + +#nullable enable + +namespace HotChocolate.Types.Descriptors.Definitions +{ + public sealed class CompleteConfiguration + : CompleteConfiguration + where TDefinition : IDefinition + { + public CompleteConfiguration( + Action configure, + TDefinition owner, + ApplyConfigurationOn on, + ITypeReference? typeReference = null, + TypeDependencyKind kind = TypeDependencyKind.Default) + : base((c, d) => configure(c, (TDefinition)d), owner, on, typeReference, kind) + { + } + + public CompleteConfiguration( + Action configure, + TDefinition owner, + ApplyConfigurationOn on, + IEnumerable dependencies) + : base((c, d) => configure(c, (TDefinition)d), owner, @on, dependencies) + { + } + } + + public class CompleteConfiguration : ITypeSystemMemberConfiguration + { + private readonly Action _configure; + private List? _dependencies; + + public CompleteConfiguration( + Action configure, + IDefinition owner, + ApplyConfigurationOn on, + ITypeReference? typeReference = null, + TypeDependencyKind kind = TypeDependencyKind.Default) + { + if (on is ApplyConfigurationOn.Create) + { + throw new ArgumentOutOfRangeException(nameof(on)); + } + + _configure = configure ?? throw new ArgumentNullException(nameof(configure)); + Owner = owner ?? throw new ArgumentNullException(nameof(owner)); + On = on; + + if (typeReference is not null) + { + _dependencies = new List(1) { new(typeReference, kind) }; + } + } + + public CompleteConfiguration( + Action configure, + IDefinition owner, + ApplyConfigurationOn on, + IEnumerable dependencies) + { + if (on is ApplyConfigurationOn.Create) + { + throw new ArgumentOutOfRangeException(nameof(on)); + } + + if (dependencies is null) + { + throw new ArgumentNullException(nameof(dependencies)); + } + + _configure = configure ?? throw new ArgumentNullException(nameof(configure)); + Owner = owner ?? throw new ArgumentNullException(nameof(owner)); + On = on; + _dependencies = new(dependencies); + } + + public IDefinition Owner { get; } + + public ApplyConfigurationOn On { get; } + + public IReadOnlyList Dependencies => + _dependencies ?? (IReadOnlyList)Array.Empty(); + + public void AddDependency(TypeDependency dependency) + { + if (dependency is null) + { + throw new ArgumentNullException(nameof(dependency)); + } + + _dependencies ??= new List(); + _dependencies.Add(dependency); + } + + public void Configure(ITypeCompletionContext context) + => _configure(context, Owner); + + public ITypeSystemMemberConfiguration Copy(DefinitionBase newOwner) + { + if (newOwner is null) + { + throw new ArgumentNullException(nameof(newOwner)); + } + + return new CompleteConfiguration(_configure, newOwner, On, Dependencies); + } + } +} diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/CreateConfiguration.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/CreateConfiguration.cs new file mode 100644 index 00000000000..a3ab532ee01 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/CreateConfiguration.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; + +#nullable enable + +namespace HotChocolate.Types.Descriptors.Definitions +{ + public sealed class CreateConfiguration : ITypeSystemMemberConfiguration + { + private readonly Action _configure; + + public CreateConfiguration( + Action configure, + IDefinition owner) + { + _configure = configure ?? throw new ArgumentNullException(nameof(configure)); + Owner = owner ?? throw new ArgumentNullException(nameof(owner)); + } + + public IDefinition Owner { get; } + + public ApplyConfigurationOn On => ApplyConfigurationOn.Create; + + public IReadOnlyList Dependencies { get; } = Array.Empty(); + + public void AddDependency(TypeDependency dependency) + => throw new NotSupportedException( + "Create configurations do not support dependencies."); + + public void Configure(IDescriptorContext context) + => _configure(context, Owner); + + public ITypeSystemMemberConfiguration Copy(DefinitionBase newOwner) + { + if (newOwner is null) + { + throw new ArgumentNullException(nameof(newOwner)); + } + + return new CreateConfiguration(_configure, newOwner); + } + } +} 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 348d80c584a..e7fad55ca6c 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/DefinitionBase.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/DefinitionBase.cs @@ -13,7 +13,7 @@ namespace HotChocolate.Types.Descriptors.Definitions public class DefinitionBase : IDefinition { private List? _dependencies; - private List? _configurations; + private List? _configurations; private ExtensionData? _contextData; protected DefinitionBase() { } @@ -45,25 +45,35 @@ protected DefinitionBase() { } public IList Dependencies => _dependencies ??= new List(); + /// + /// Defines if this type has dependencies. + /// + public bool HasDependencies => _dependencies is { Count: > 0 }; + /// /// Gets configurations that shall be applied at a later point. /// - public IList Configurations => - _configurations ??= new List(); + public IList Configurations => + _configurations ??= new List(); + + /// + /// Defines if this type has configurations. + /// + public bool HasConfigurations => _configurations is { Count: > 0 }; /// - /// Defines whether descriptor attributes are applied or not. + /// Defines whether descriptor attributes have been applied or not. /// public bool AttributesAreApplied { get; set; } /// /// Gets lazy configuration of this definition and all dependent definitions. /// - internal virtual IEnumerable GetConfigurations() + internal virtual IEnumerable GetConfigurations() { if (_configurations is null) { - return Array.Empty(); + return Array.Empty(); } return _configurations; @@ -105,9 +115,9 @@ protected void CopyTo(DefinitionBase target) if (_configurations is not null && _configurations.Count > 0) { - target._configurations = new List(); + target._configurations = new List(); - foreach (ILazyTypeConfiguration configuration in _configurations) + foreach (ITypeSystemMemberConfiguration configuration in _configurations) { target._configurations.Add(configuration.Copy(target)); } @@ -134,9 +144,9 @@ protected void MergeInto(DefinitionBase target) if (_configurations is not null && _configurations.Count > 0) { - target._configurations ??= new List(); + target._configurations ??= new List(); - foreach (ILazyTypeConfiguration configuration in _configurations) + foreach (ITypeSystemMemberConfiguration configuration in _configurations) { target._configurations.Add(configuration.Copy(target)); } @@ -167,9 +177,6 @@ protected void MergeInto(DefinitionBase target) } } - public override string ToString() - { - return GetType().Name + ": " + Name; - } + public override string ToString() => GetType().Name + ": " + Name; } } diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/DirectiveTypeDefinition.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/DirectiveTypeDefinition.cs index 3c5a363a5b0..0225d8c32fa 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/DirectiveTypeDefinition.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/DirectiveTypeDefinition.cs @@ -79,9 +79,9 @@ public Type RuntimeType public IBindableList Arguments => _arguments ??= new BindableList(); - internal override IEnumerable GetConfigurations() + internal override IEnumerable GetConfigurations() { - var configs = new List(); + var configs = new List(); configs.AddRange(Configurations); diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/EnumTypeDefinition.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/EnumTypeDefinition.cs index 3b03b09e90f..18e6ce061be 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/EnumTypeDefinition.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/EnumTypeDefinition.cs @@ -35,9 +35,9 @@ public EnumTypeDefinition( public IBindableList Values { get; } = new BindableList(); - internal override IEnumerable GetConfigurations() + internal override IEnumerable GetConfigurations() { - var configs = new List(); + var configs = new List(); configs.AddRange(Configurations); diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/IDefinition.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/IDefinition.cs index 45fdca14da7..acdccab4a72 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/IDefinition.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/IDefinition.cs @@ -41,9 +41,19 @@ public interface IDefinition /// IList Dependencies { get; } + /// + /// Defines if this type has dependencies. + /// + bool HasDependencies { get; } + /// /// Gets configurations that shall be applied at a later point. /// - IList Configurations { get; } + IList Configurations { get; } + + /// + /// Defines if this type has configurations. + /// + bool HasConfigurations { get; } } } diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/ITypeConfigration.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/ITypeSystemMemberConfiguration.cs similarity index 55% rename from src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/ITypeConfigration.cs rename to src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/ITypeSystemMemberConfiguration.cs index 260cad2c7fb..9a8a70518b9 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/ITypeConfigration.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/ITypeSystemMemberConfiguration.cs @@ -1,10 +1,21 @@ using System.Collections.Generic; -using HotChocolate.Configuration; + +#nullable enable namespace HotChocolate.Types.Descriptors.Definitions { - public interface ILazyTypeConfiguration + /// + /// A configuration object that is applied to a type system member at a certain event + /// during the type system initialization. + /// + public interface ITypeSystemMemberConfiguration { + /// + /// The definition of the type system member that shall be configured. + /// + /// + IDefinition Owner { get; } + /// /// Defines on which type initialization step this /// configurations is applied on. @@ -18,9 +29,12 @@ public interface ILazyTypeConfiguration IReadOnlyList Dependencies { get; } /// - /// Executes this configuration. + /// Adds an additional type dependency. /// - void Configure(ITypeCompletionContext context); + /// + /// The type dependency. + /// + void AddDependency(TypeDependency dependency); /// /// Creates a copy of this object with the new . @@ -31,6 +45,6 @@ public interface ILazyTypeConfiguration /// /// Returns the new configuration. /// - ILazyTypeConfiguration Copy(DefinitionBase newOwner); + ITypeSystemMemberConfiguration Copy(DefinitionBase newOwner); } } diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/InputObjectTypeDefinition.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/InputObjectTypeDefinition.cs index 8682d2ced46..55a48dc6403 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/InputObjectTypeDefinition.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/InputObjectTypeDefinition.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Reflection; using HotChocolate.Language; #nullable enable @@ -47,9 +46,9 @@ public InputObjectTypeDefinition( /// public Action? GetFieldData { get; set; } - internal override IEnumerable GetConfigurations() + internal override IEnumerable GetConfigurations() { - var configs = new List(); + var configs = new List(); configs.AddRange(Configurations); diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/InterfaceTypeDefinition.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/InterfaceTypeDefinition.cs index b1c13d52f5d..bfa18fd9fde 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/InterfaceTypeDefinition.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/InterfaceTypeDefinition.cs @@ -40,9 +40,9 @@ public InterfaceTypeDefinition( public IBindableList Fields { get; } = new BindableList(); - internal override IEnumerable GetConfigurations() + internal override IEnumerable GetConfigurations() { - var configs = new List(); + var configs = new List(); configs.AddRange(Configurations); diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/ObjectTypeDefinition.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/ObjectTypeDefinition.cs index ed48e84c78a..abc3f9f7144 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/ObjectTypeDefinition.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Definitions/ObjectTypeDefinition.cs @@ -90,23 +90,35 @@ public override Type RuntimeType public IBindableList Fields { get; } = new BindableList(); - internal override IEnumerable GetConfigurations() + internal override IEnumerable GetConfigurations() { - var configs = new List(); + List? configs = null; - configs.AddRange(Configurations); + if (HasConfigurations) + { + configs ??= new(); + configs.AddRange(Configurations); + } foreach (ObjectFieldDefinition field in Fields) { - configs.AddRange(field.Configurations); + if (field.HasConfigurations) + { + configs ??= new(); + configs.AddRange(field.Configurations); + } foreach (ArgumentDefinition argument in field.GetArguments()) { - configs.AddRange(argument.Configurations); + if (argument.HasConfigurations) + { + configs ??= new(); + configs.AddRange(argument.Configurations); + } } } - return configs; + return configs ?? Enumerable.Empty(); } internal IReadOnlyList GetKnownClrTypes() @@ -200,7 +212,7 @@ protected internal void MergeInto(ObjectTypeDefinition target) ObjectFieldDefinition? targetField = field switch { { BindToField: { Type: ObjectFieldBindingType.Property } bindTo } => - target.Fields.FirstOrDefault(t => bindTo.Name.Equals(t.Member?.Name)), + target.Fields.FirstOrDefault(t => bindTo.Name.Equals(t.Member?.Name!)), { BindToField: { Type: ObjectFieldBindingType.Field } bindTo } => target.Fields.FirstOrDefault(t => bindTo.Name.Equals(t.Name)), _ => target.Fields.FirstOrDefault(t => field.Name.Equals(t.Name)) diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/DependencyDescriptorBase~1.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/DependencyDescriptorBase~1.cs index 6c346fa2cdc..56fba0f64ab 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/DependencyDescriptorBase~1.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/DependencyDescriptorBase~1.cs @@ -8,17 +8,16 @@ namespace HotChocolate.Types.Descriptors { - internal abstract class DependencyDescriptorBase - where T : DefinitionBase + internal abstract class DependencyDescriptorBase { - private readonly TypeConfiguration _configuration; + private readonly ITypeSystemMemberConfiguration _configuration; protected DependencyDescriptorBase( ITypeInspector typeInspector, - TypeConfiguration configuration) + ITypeSystemMemberConfiguration configuration) { TypeInspector = typeInspector ?? - throw new ArgumentNullException(nameof(configuration)); + throw new ArgumentNullException(nameof(typeInspector)); _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); } @@ -52,7 +51,7 @@ protected void DependsOn(IExtendedType schemaType, bool mustBeNamedOrCompleted) ? DependencyKind : TypeDependencyKind.Default; - _configuration.Dependencies.Add( + _configuration.AddDependency( TypeDependency.FromSchemaType(schemaType, kind)); } @@ -66,7 +65,7 @@ protected void DependsOn( ? DependencyKind : TypeDependencyKind.Default; - _configuration.Dependencies.Add( + _configuration.AddDependency( new TypeDependency( TypeReference.Create(new NamedTypeNode(typeName), TypeContext.None), kind)); diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/DescriptorBase~1.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/DescriptorBase~1.cs index 46bca1d8e0f..c6e647c32fa 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/DescriptorBase~1.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/DescriptorBase~1.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.Collections.Generic; using HotChocolate.Configuration; using HotChocolate.Types.Descriptors.Definitions; @@ -14,8 +15,6 @@ public abstract class DescriptorBase , IDefinitionFactory where T : DefinitionBase { - private List>? _modifiers; - protected DescriptorBase(IDescriptorContext context) { Context = context ?? throw new ArgumentNullException(nameof(context)); @@ -29,53 +28,69 @@ protected DescriptorBase(IDescriptorContext context) T IDescriptorExtension.Definition => Definition; - public IDescriptorExtension Extend() - { - return this; - } + public IDescriptorExtension Extend() => this; public T CreateDefinition() { OnCreateDefinition(Definition); - if (_modifiers is not null) + if (Definition.HasConfigurations) { - foreach (Action modifier in _modifiers) + var i = 0; + var buffered = 0; + var length = Definition.Configurations.Count; + CreateConfiguration[] rented = ArrayPool.Shared.Rent(length); + IList configurations = Definition.Configurations; + + do { - modifier.Invoke(Context, Definition); + if (configurations[i] is { On: ApplyConfigurationOn.Create } config) + { + configurations.RemoveAt(i); + rented[buffered++] = (CreateConfiguration)config; + } + else + { + i++; + } + } while (i < configurations.Count); + + for (i = 0; i < buffered; i++) + { + rented[i].Configure(Context); } + + ArrayPool.Shared.Return(rented, true); } return Definition; } public void ConfigureContextData(Action configure) - { - configure(Definition.ContextData); - } + => configure(Definition.ContextData); protected virtual void OnCreateDefinition(T definition) { } - DefinitionBase IDefinitionFactory.CreateDefinition() => - CreateDefinition(); + DefinitionBase IDefinitionFactory.CreateDefinition() + => CreateDefinition(); void IDescriptorExtension.OnBeforeCreate( - Action configure) => - OnBeforeCreate((c, d) => configure(d)); + Action configure) + => OnBeforeCreate((_, d) => configure(d)); void IDescriptorExtension.OnBeforeCreate( - Action configure) => - OnBeforeCreate(configure); + Action configure) + => OnBeforeCreate(configure); void IDescriptorExtension.OnBeforeCreate( - Action configure) => - OnBeforeCreate((c, d) => configure(d)); + Action configure) + => OnBeforeCreate((_, d) => configure(d)); void IDescriptorExtension.OnBeforeCreate( - Action configure) => - OnBeforeCreate(configure); + Action configure) + => OnBeforeCreate(configure); private void OnBeforeCreate(Action configure) { @@ -84,18 +99,18 @@ private void OnBeforeCreate(Action configure) throw new ArgumentNullException(nameof(configure)); } - _modifiers ??= new List>(); - - _modifiers.Add(configure); + Definition.Configurations.Add(new CreateConfiguration( + (c, d) => configure(c, (T)d), + Definition)); } INamedDependencyDescriptor IDescriptorExtension.OnBeforeNaming( - Action configure) => - OnBeforeNaming(configure); + Action configure) + => OnBeforeNaming(configure); INamedDependencyDescriptor IDescriptorExtension.OnBeforeNaming( - Action configure) => - OnBeforeNaming(configure); + Action configure) + => OnBeforeNaming(configure); private INamedDependencyDescriptor OnBeforeNaming( Action configure) @@ -105,38 +120,35 @@ private INamedDependencyDescriptor OnBeforeNaming( throw new ArgumentNullException(nameof(configure)); } - var configuration = new TypeConfiguration - { - Definition = Definition, - On = ApplyConfigurationOn.Naming, - Configure = configure - }; + var configuration = new CompleteConfiguration( + (c, d) => configure(c, (T)d), + Definition, + ApplyConfigurationOn.Naming); Definition.Configurations.Add(configuration); - return new NamedDependencyDescriptor(Context.TypeInspector, configuration); + return new NamedDependencyDescriptor(Context.TypeInspector, configuration); } ICompletedDependencyDescriptor IDescriptorExtension.OnBeforeCompletion( - Action configure) => - OnBeforeCompletion(configure); + Action configure) + => OnBeforeCompletion(configure); ICompletedDependencyDescriptor IDescriptorExtension.OnBeforeCompletion( - Action configure) => - OnBeforeCompletion(configure); + Action configure) + => OnBeforeCompletion(configure); private ICompletedDependencyDescriptor OnBeforeCompletion( Action configure) { - var configuration = new TypeConfiguration - { - Definition = Definition, - On = ApplyConfigurationOn.Completion, - Configure = configure - }; + var configuration = new CompleteConfiguration( + (c, d) => configure(c, (T)d), + Definition, + ApplyConfigurationOn.Completion); + Definition.Configurations.Add(configuration); - return new CompletedDependencyDescriptor(Context.TypeInspector, configuration); + return new CompletedDependencyDescriptor(Context.TypeInspector, configuration); } } } diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/LazyTypeConfigurationBuilder.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/LazyTypeConfigurationBuilder.cs deleted file mode 100644 index 29b51a6e983..00000000000 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/LazyTypeConfigurationBuilder.cs +++ /dev/null @@ -1,11 +0,0 @@ -using HotChocolate.Types.Descriptors.Definitions; - -namespace HotChocolate.Types.Descriptors -{ - public static class LazyTypeConfigurationBuilder - { - public static LazyTypeConfigurationBuilder New() - where T : DefinitionBase => - new(); - } -} diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/LazyTypeConfigurationBuilder~1.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/LazyTypeConfigurationBuilder~1.cs deleted file mode 100644 index 8f9bccd4035..00000000000 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/LazyTypeConfigurationBuilder~1.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Collections.Generic; -using HotChocolate.Configuration; -using HotChocolate.Types.Descriptors.Definitions; - -namespace HotChocolate.Types.Descriptors -{ - public class LazyTypeConfigurationBuilder - where T : DefinitionBase - { - private ApplyConfigurationOn _on = ApplyConfigurationOn.Naming; - private TypeDependencyKind _completeKind = TypeDependencyKind.Named; - private Action _configure; - private T _definition; - private readonly List<(ITypeReference r, bool c)> _dependencies = new(); - - public LazyTypeConfigurationBuilder On(ApplyConfigurationOn kind) - { - _on = kind; - - _completeKind = kind == ApplyConfigurationOn.Naming - ? TypeDependencyKind.Named - : TypeDependencyKind.Completed; - - return this; - } - - public LazyTypeConfigurationBuilder Configure( - Action configure) - { - _configure = configure ?? throw new ArgumentNullException(nameof(configure)); - return this; - } - - public LazyTypeConfigurationBuilder Definition(T definition) - { - _definition = definition ?? throw new ArgumentNullException(nameof(definition)); - return this; - } - - public LazyTypeConfigurationBuilder DependsOn( - ITypeReference typeReference, bool mustBeCompleted) - { - if (typeReference is null) - { - throw new ArgumentNullException(nameof(typeReference)); - } - - _dependencies.Add((typeReference, mustBeCompleted)); - return this; - } - - public ILazyTypeConfiguration Build() - { - if (_configure is null) - { - // TODO : Resources - throw new InvalidOperationException( - "You have to set a configuration function " + - "before you can build."); - } - - if (_definition is null) - { - // TODO : Resources - throw new InvalidOperationException( - "You have to set the definition " + - "before you can build."); - } - - var configuration = new TypeConfiguration - { - On = _on, - Configure = _configure, - Definition = _definition - }; - - foreach ((ITypeReference r, bool c) dependency in _dependencies) - { - configuration.Dependencies.Add(new TypeDependency - ( - dependency.r, - dependency.c ? _completeKind : TypeDependencyKind.Default - )); - } - - return configuration; - } - } -} diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/NamedDependencyDescriptor~1.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/NamedDependencyDescriptor~1.cs index 5f9b0064dcf..7e110e87af7 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/NamedDependencyDescriptor~1.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/NamedDependencyDescriptor~1.cs @@ -3,14 +3,13 @@ namespace HotChocolate.Types.Descriptors { - internal class NamedDependencyDescriptor - : DependencyDescriptorBase + internal class NamedDependencyDescriptor + : DependencyDescriptorBase , INamedDependencyDescriptor - where T : DefinitionBase { public NamedDependencyDescriptor( ITypeInspector typeInspector, - TypeConfiguration configuration) + CompleteConfiguration configuration) : base(typeInspector, configuration) { } diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeConfiguration.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeConfiguration.cs deleted file mode 100644 index 5e924c91fa0..00000000000 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeConfiguration.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using System.Collections.Generic; -using HotChocolate.Configuration; -using HotChocolate.Properties; -using HotChocolate.Types.Descriptors.Definitions; - -#nullable enable - -namespace HotChocolate.Types.Descriptors -{ - internal class TypeConfiguration : ILazyTypeConfiguration where T : DefinitionBase - { - private List? _dependencies; - - public ApplyConfigurationOn On { get; set; } - - public Action? Configure { get; set; } - - public T? Definition { get; set; } - - public ICollection Dependencies => - _dependencies ??= new List(); - - IReadOnlyList ILazyTypeConfiguration.Dependencies => - _dependencies ??= new List(); - - ILazyTypeConfiguration ILazyTypeConfiguration.Copy(DefinitionBase newOwner) - { - return new TypeConfiguration - { - On = On, - Configure = Configure, - Definition = (T)newOwner, - _dependencies = _dependencies - }; - } - - void ILazyTypeConfiguration.Configure(ITypeCompletionContext context) - { - if (context is null) - { - throw new ArgumentNullException(nameof(context)); - } - - if (Definition is null) - { - throw new InvalidOperationException( - TypeResources.TypeConfiguration_DefinitionIsNull); - } - - if (Configure is null) - { - throw new InvalidOperationException( - TypeResources.TypeConfiguration_ConfigureIsNull); - } - - Configure(context, Definition); - } - } -} diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/DependantFactoryTypeReference.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/DependantFactoryTypeReference.cs new file mode 100644 index 00000000000..b7a801176a7 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/DependantFactoryTypeReference.cs @@ -0,0 +1,120 @@ +using System; + +#nullable enable + +namespace HotChocolate.Types.Descriptors +{ + public sealed class DependantFactoryTypeReference + : TypeReference + , IEquatable + { + public DependantFactoryTypeReference( + NameString name, + ITypeReference dependency, + Func factory, + TypeContext context, + string? scope = null) + : base( + TypeReferenceKind.DependantFactory, + context, + scope) + { + Name = name.EnsureNotEmpty(nameof(name)); + Dependency = dependency ?? throw new ArgumentNullException(nameof(dependency)); + Factory = factory ?? throw new ArgumentNullException(nameof(factory)); + } + + /// + /// Gets the name of this reference. + /// + public NameString Name { get; } + + /// + /// Gets the reference to the type this type is dependant on. + /// + public ITypeReference Dependency { get; } + + /// + /// Gets a factory to create this type. + /// + public Func Factory { get; } + + /// + public override bool Equals(ITypeReference? other) + { + if (other is null) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + if (other is DependantFactoryTypeReference c) + { + return Equals(c); + } + + return false; + } + + /// + public bool Equals(DependantFactoryTypeReference? other) + { + if (other is null) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + if (!IsEqual(other)) + { + return false; + } + + return Name.Equals(other.Name) && Dependency.Equals(other.Dependency); + } + + /// + public override bool Equals(object? obj) + { + if (obj is null) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj is DependantFactoryTypeReference c) + { + return Equals(c); + } + + return false; + } + + /// + public override int GetHashCode() + { + unchecked + { + return base.GetHashCode() ^ + Name.GetHashCode() * 397 ^ + Dependency.GetHashCode() * 397; + } + } + + /// + public override string ToString() + => $"{Context}: {Name}->{Dependency}"; + } +} diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/ExtendedTypeReference.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/ExtendedTypeReference.cs index 9ac87057e79..dc54c0dd382 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/ExtendedTypeReference.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/ExtendedTypeReference.cs @@ -13,7 +13,7 @@ public ExtendedTypeReference( IExtendedType type, TypeContext context, string? scope = null) - : base(context, scope) + : base(TypeReferenceKind.ExtendedType, context, scope) { Type = type ?? throw new ArgumentNullException(nameof(type)); } diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/ITypeReference.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/ITypeReference.cs index cc5157d49b7..eeb14618df7 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/ITypeReference.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/ITypeReference.cs @@ -4,11 +4,25 @@ namespace HotChocolate.Types.Descriptors { - public interface ITypeReference - : IEquatable + /// + /// A type reference is used to refer to a type in the type system. + /// This allows us to loosely couple types during schema creation. + /// + public interface ITypeReference : IEquatable { - string? Scope { get; } + /// + /// Gets the kind of type reference. + /// + TypeReferenceKind Kind { get; } + /// + /// Gets the context in which the type reference was created. + /// TypeContext Context { get; } + + /// + /// Gets the scope in which the type reference was created. + /// + string? Scope { get; } } } diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/SchemaTypeReference.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/SchemaTypeReference.cs index 180659ec3cc..ede1f932741 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/SchemaTypeReference.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/SchemaTypeReference.cs @@ -13,7 +13,7 @@ public SchemaTypeReference( ITypeSystemMember type, TypeContext? context = null, string? scope = null) - : base(context ?? InferTypeContext(type), scope) + : base(TypeReferenceKind.SchemaType, context ?? InferTypeContext(type), scope) { Type = type ?? throw new ArgumentNullException(nameof(type)); } diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/SyntaxTypeReference.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/SyntaxTypeReference.cs index 5a20720b0dd..ecbd01dab92 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/SyntaxTypeReference.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/SyntaxTypeReference.cs @@ -12,14 +12,34 @@ public sealed class SyntaxTypeReference public SyntaxTypeReference( ITypeNode type, TypeContext context, - string? scope = null) - : base(context, scope) + string? scope = null, + Func? factory = null) + : base( + factory is null ? TypeReferenceKind.Syntax : TypeReferenceKind.Factory, + context, + scope) { Type = type ?? throw new ArgumentNullException(nameof(type)); + Name = type.NamedType().Name.Value; + Factory = factory; } + /// + /// Gets the name of the named type. + /// + public string Name { get; } + + /// + /// Gets the internal syntax type reference. + /// public ITypeNode Type { get; } + /// + /// Gets a factory to create this type. Note, a factory is optional. + /// + public Func? Factory { get; } + + /// public bool Equals(SyntaxTypeReference? other) { if (other is null) @@ -40,6 +60,7 @@ public bool Equals(SyntaxTypeReference? other) return Type.IsEqualTo(other.Type); } + /// public override bool Equals(ITypeReference? other) { if (other is null) @@ -60,6 +81,7 @@ public override bool Equals(ITypeReference? other) return false; } + /// public override bool Equals(object? obj) { if (obj is null) @@ -80,6 +102,7 @@ public override bool Equals(object? obj) return false; } + /// public override int GetHashCode() { unchecked @@ -88,6 +111,7 @@ public override int GetHashCode() } } + /// public override string ToString() { return $"{Context}: {Type}"; @@ -104,19 +128,20 @@ public SyntaxTypeReference WithType(ITypeNode type) } public SyntaxTypeReference WithContext(TypeContext context = TypeContext.None) - { - return new SyntaxTypeReference(Type, context, Scope); - } + => new(Type, context, Scope); public SyntaxTypeReference WithScope(string? scope = null) - { - return new SyntaxTypeReference(Type, Context, scope); - } + => new(Type, Context, scope); + + public SyntaxTypeReference WithFactory( + Func? factory = null) + => new(Type, Context, Scope, Factory); public SyntaxTypeReference With( Optional type = default, Optional context = default, - Optional scope = default) + Optional scope = default, + Optional?> factory = default) { if (type.HasValue && type.Value is null) { @@ -126,7 +151,8 @@ public SyntaxTypeReference With( return new SyntaxTypeReference( type.HasValue ? type.Value! : Type, context.HasValue ? context.Value : Context, - scope.HasValue ? scope.Value : Scope); + scope.HasValue ? scope.Value : Scope, + factory.HasValue ? factory.Value : Factory); } } } diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/TypeReference.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/TypeReference.cs index 9b64ed84e80..ac3c0abd78d 100644 --- a/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/TypeReference.cs +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/TypeReference.cs @@ -1,3 +1,4 @@ +using System; using HotChocolate.Internal; using HotChocolate.Language; using HotChocolate.Utilities; @@ -9,27 +10,27 @@ namespace HotChocolate.Types.Descriptors { /// /// A type reference is used to refer to a type in the type system. - /// This allows us to loosly couple types. + /// This allows us to loosely couple types during schema creation. /// public abstract class TypeReference : ITypeReference { protected TypeReference( + TypeReferenceKind kind, TypeContext context, string? scope) { + Kind = kind; Context = context; Scope = scope; } - /// - /// The context in which the type reference was created. - /// + /// + public TypeReferenceKind Kind { get; } + + /// public TypeContext Context { get; } - /// - /// The scope in which the type reference was created. - /// - /// + /// public string? Scope { get; } protected bool IsEqual(ITypeReference other) @@ -81,11 +82,19 @@ public override int GetHashCode() } } + public static DependantFactoryTypeReference Create( + NameString name, + ITypeReference dependency, + Func factory, + TypeContext context = TypeContext.None, + string? scope = null) + => new(name, dependency, factory, context, scope); + public static SchemaTypeReference Create( ITypeSystemMember type, string? scope = null) { - if (scope is null && type is IHasScope withScope && withScope.Scope is { }) + if (scope is null && type is IHasScope { Scope: not null } withScope) { scope = withScope.Scope; } @@ -95,20 +104,23 @@ public static SchemaTypeReference Create( public static SyntaxTypeReference Create( ITypeNode type, TypeContext context = TypeContext.None, - string? scope = null) => - new(type, context, scope); + string? scope = null, + Func? factory = null) => + new(type, context, scope, factory); public static SyntaxTypeReference Create( NameString typeName, TypeContext context = TypeContext.None, - string? scope = null) => - new(new NamedTypeNode(typeName), context, scope); + string? scope = null, + Func? factory = null) => + new(new NamedTypeNode(typeName), context, scope, factory); public static SyntaxTypeReference Parse( string sourceText, TypeContext context = TypeContext.None, - string? scope = null) => - new(Utf8GraphQLParser.Syntax.ParseTypeReference(sourceText), context, scope); + string? scope = null, + Func? factory = null) => + new(Utf8GraphQLParser.Syntax.ParseTypeReference(sourceText), context, scope, factory); public static ExtendedTypeReference Create( IExtendedType type, diff --git a/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/TypeReferenceKind.cs b/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/TypeReferenceKind.cs new file mode 100644 index 00000000000..e44fda82ec2 --- /dev/null +++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/TypeReferences/TypeReferenceKind.cs @@ -0,0 +1,36 @@ +using HotChocolate.Language; + +namespace HotChocolate.Types.Descriptors +{ + /// + /// Specifies the kind of type reference. + /// + public enum TypeReferenceKind + { + /// + /// The type reference is represented by a .NET type. + /// + ExtendedType = 0, + + /// + /// The type reference is represented by a schema type reference. + /// + SchemaType = 1, + + /// + /// The type reference is represented by a . + /// + Syntax = 2, + + /// + /// The type reference is represented by a and + /// contains a factory to create the type. + /// + Factory = 3, + + /// + /// The type reference refers to a type that is dependant on another type. + /// + DependantFactory = 4 + } +} diff --git a/src/HotChocolate/Core/src/Types/Types/DirectiveType.cs b/src/HotChocolate/Core/src/Types/Types/DirectiveType.cs index a1de9afc29d..2a36b5d9369 100644 --- a/src/HotChocolate/Core/src/Types/Types/DirectiveType.cs +++ b/src/HotChocolate/Core/src/Types/Types/DirectiveType.cs @@ -161,7 +161,7 @@ public static DirectiveType CreateUnsafe(DirectiveTypeDefinition definition) /// Defines if instances of this directive type are publicly visible through introspection. /// internal bool IsPublic { get; private set; } - + protected override DirectiveTypeDefinition CreateDefinition(ITypeDiscoveryContext context) { try diff --git a/src/HotChocolate/Core/src/Types/Types/Directives/CostTypeInterceptor.cs b/src/HotChocolate/Core/src/Types/Types/Directives/CostTypeInterceptor.cs index f2330767d20..c7d16377d00 100644 --- a/src/HotChocolate/Core/src/Types/Types/Directives/CostTypeInterceptor.cs +++ b/src/HotChocolate/Core/src/Types/Types/Directives/CostTypeInterceptor.cs @@ -143,6 +143,11 @@ private static bool IsCostDirective(DirectiveDefinition directive) /// private static bool IsDataResolver(ObjectFieldDefinition field) { + if (field.PureResolver is not null && field.MiddlewareDefinitions.Count == 0) + { + return false; + } + if (field.Resolver is not null) { return true; diff --git a/src/HotChocolate/Core/src/Types/Types/ObjectType.cs b/src/HotChocolate/Core/src/Types/Types/ObjectType.cs index 2995c792ebf..6e9ae432f00 100644 --- a/src/HotChocolate/Core/src/Types/Types/ObjectType.cs +++ b/src/HotChocolate/Core/src/Types/Types/ObjectType.cs @@ -87,7 +87,7 @@ public static ObjectType CreateUnsafe(ObjectTypeDefinition definition) IFieldCollection IComplexOutputType.Fields => Fields; /// - public bool IsInstanceOfType(IResolverContext context, object resolverResult) + public virtual bool IsInstanceOfType(IResolverContext context, object resolverResult) { if (context is null) { diff --git a/src/HotChocolate/Core/src/Types/Types/Pagination/GetPagingProvider.cs b/src/HotChocolate/Core/src/Types/Types/Pagination/GetPagingProvider.cs index 51d5a336e12..93c901202f8 100644 --- a/src/HotChocolate/Core/src/Types/Types/Pagination/GetPagingProvider.cs +++ b/src/HotChocolate/Core/src/Types/Types/Pagination/GetPagingProvider.cs @@ -5,7 +5,14 @@ namespace HotChocolate.Types.Pagination { + /// + /// A delegate to resolve a paging provider. + /// + /// The service provider to resolver the paging providers from. + /// The type that is returned by the resolver. + /// The name of the provider that shall be selected. public delegate IPagingProvider GetPagingProvider( IServiceProvider services, - IExtendedType sourceType); + IExtendedType sourceType, + string? providerName); } diff --git a/src/HotChocolate/Core/src/Types/Types/Pagination/IPagingProvider.cs b/src/HotChocolate/Core/src/Types/Types/Pagination/IPagingProvider.cs index 4a56e7081fe..0fa82059ed8 100644 --- a/src/HotChocolate/Core/src/Types/Types/Pagination/IPagingProvider.cs +++ b/src/HotChocolate/Core/src/Types/Types/Pagination/IPagingProvider.cs @@ -5,10 +5,10 @@ namespace HotChocolate.Types.Pagination { /// - /// Represents an paging provider, which can be implemented to - /// create optimized pagination for data sources. - /// - /// The paging provider will be used by the configuration to choose + /// Represents an paging provider, which can be implemented to + /// create optimized pagination for data sources. + /// + /// The paging provider will be used by the configuration to choose /// the right paging handler for executing the field. /// public interface IPagingProvider @@ -17,17 +17,17 @@ public interface IPagingProvider /// Specifies if this paging provider can handle the specified . /// /// - /// The source type represents the result of the field resolver and could be a collection, + /// The source type represents the result of the field resolver and could be a collection, /// a query builder or some other object representing the data set. /// bool CanHandle(IExtendedType source); /// - /// Creates the paging handler that is able to interact with the specified source and + /// Creates the paging handler that is able to interact with the specified source and /// able to slice the data. /// /// - /// The source type represents the result of the field resolver and could be a collection, + /// The source type represents the result of the field resolver and could be a collection, /// a query builder or some other object representing the data set. /// /// diff --git a/src/HotChocolate/Core/src/Types/Types/Pagination/PagingDefaults.cs b/src/HotChocolate/Core/src/Types/Types/Pagination/PagingDefaults.cs index 12982a10be6..835313d8443 100644 --- a/src/HotChocolate/Core/src/Types/Types/Pagination/PagingDefaults.cs +++ b/src/HotChocolate/Core/src/Types/Types/Pagination/PagingDefaults.cs @@ -9,5 +9,7 @@ public static class PagingDefaults public const bool IncludeTotalCount = false; public const bool AllowBackwardPagination = true; + + public const bool InferConnectionNameFromField = true; } } diff --git a/src/HotChocolate/Core/src/Types/Types/Pagination/PagingHelper.cs b/src/HotChocolate/Core/src/Types/Types/Pagination/PagingHelper.cs index b3d87988914..0f9ef8ac270 100644 --- a/src/HotChocolate/Core/src/Types/Types/Pagination/PagingHelper.cs +++ b/src/HotChocolate/Core/src/Types/Types/Pagination/PagingHelper.cs @@ -19,7 +19,6 @@ public static class PagingHelper { public static IObjectFieldDescriptor UsePaging( IObjectFieldDescriptor descriptor, - Type? type, Type? entityType = null, GetPagingProvider? resolvePagingProvider = null, PagingOptions options = default) @@ -31,21 +30,20 @@ public static IObjectFieldDescriptor UsePaging( FieldMiddlewareDefinition placeholder = new(_ => _ => default, key: Paging); - descriptor.Extend().Definition.MiddlewareDefinitions.Add(placeholder); - - descriptor - .Extend() - .OnBeforeCreate(definition => - { - definition.Configurations.Add( - new TypeConfiguration - { - Definition = definition, - On = ApplyConfigurationOn.Completion, - Configure = (c, d) => ApplyConfiguration( - c, d, entityType, resolvePagingProvider, options, placeholder) - }); - }); + ObjectFieldDefinition definition = descriptor.Extend().Definition; + definition.MiddlewareDefinitions.Add(placeholder); + definition.Configurations.Add( + new CompleteConfiguration( + (c, d) => ApplyConfiguration( + c, + d, + entityType, + options.ProviderName, + resolvePagingProvider, + options, + placeholder), + definition, + ApplyConfigurationOn.Completion)); return descriptor; } @@ -54,17 +52,17 @@ private static void ApplyConfiguration( ITypeCompletionContext context, ObjectFieldDefinition definition, Type? entityType, - GetPagingProvider? resolvePagingProvider, + string? name, + GetPagingProvider resolvePagingProvider, PagingOptions options, FieldMiddlewareDefinition placeholder) { options = context.GetSettings(options); entityType ??= context.GetType(definition.Type!).ToRuntimeType(); - resolvePagingProvider ??= ResolvePagingProvider; - IExtendedType sourceType = GetSourceType(context.TypeInspector, definition, entityType); - IPagingProvider pagingProvider = resolvePagingProvider(context.Services, sourceType); - IPagingHandler pagingHandler = pagingProvider.CreateHandler(sourceType, options); + IExtendedType source = GetSourceType(context.TypeInspector, definition, entityType); + IPagingProvider pagingProvider = resolvePagingProvider(context.Services, source, name); + IPagingHandler pagingHandler = pagingProvider.CreateHandler(source, options); FieldMiddleware middleware = CreateMiddleware(pagingHandler); var index = definition.MiddlewareDefinitions.IndexOf(placeholder); @@ -180,10 +178,5 @@ public static PagingOptions GetSettings( return options; } - - private static IPagingProvider ResolvePagingProvider( - IServiceProvider services, - IExtendedType source) => - services.GetServices().First(p => p.CanHandle(source)); } } diff --git a/src/HotChocolate/Core/src/Types/Types/Pagination/PagingOptions.cs b/src/HotChocolate/Core/src/Types/Types/Pagination/PagingOptions.cs index 7a5bb443fe8..6b13e05e559 100644 --- a/src/HotChocolate/Core/src/Types/Types/Pagination/PagingOptions.cs +++ b/src/HotChocolate/Core/src/Types/Types/Pagination/PagingOptions.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace HotChocolate.Types.Pagination { /// @@ -32,6 +34,17 @@ public struct PagingOptions /// public bool? RequirePagingBoundaries { get; set; } + /// + /// Connection names are by default inferred from the field name to + /// which they are bound to as opposed to the node type name. + /// + public bool? InferConnectionNameFromField { get; set; } + + /// + /// The name of the paging provider that shall be used. + /// + public string? ProviderName { get; set; } + /// /// Merges the options into this options instance wherever /// a property is not set. @@ -46,6 +59,8 @@ internal void Merge(PagingOptions other) IncludeTotalCount ??= other.IncludeTotalCount; AllowBackwardPagination ??= other.AllowBackwardPagination; RequirePagingBoundaries ??= other.RequirePagingBoundaries; + InferConnectionNameFromField ??= other.InferConnectionNameFromField; + ProviderName ??= other.ProviderName; } /// @@ -58,7 +73,9 @@ internal PagingOptions Copy() MaxPageSize = MaxPageSize, IncludeTotalCount = IncludeTotalCount, AllowBackwardPagination = AllowBackwardPagination, - RequirePagingBoundaries = RequirePagingBoundaries + RequirePagingBoundaries = RequirePagingBoundaries, + InferConnectionNameFromField = InferConnectionNameFromField, + ProviderName = ProviderName }; } } diff --git a/src/HotChocolate/Core/src/Types/Types/TypeSystemObjectBase~1.cs b/src/HotChocolate/Core/src/Types/Types/TypeSystemObjectBase~1.cs index df595f56b81..661731c7524 100644 --- a/src/HotChocolate/Core/src/Types/Types/TypeSystemObjectBase~1.cs +++ b/src/HotChocolate/Core/src/Types/Types/TypeSystemObjectBase~1.cs @@ -17,8 +17,6 @@ public abstract class TypeSystemObjectBase private TDefinition? _definition; private ExtensionData? _contextData; - protected TypeSystemObjectBase() { } - public override IReadOnlyDictionary ContextData { get @@ -178,12 +176,14 @@ private void RegisterConfigurationDependencies( private static void ExecuteConfigurations( ITypeCompletionContext context, TDefinition definition, - ApplyConfigurationOn kind) + ApplyConfigurationOn on) { - foreach (ILazyTypeConfiguration configuration in - definition.GetConfigurations().Where(t => t.On == kind)) + foreach (ITypeSystemMemberConfiguration config in definition.GetConfigurations()) { - configuration.Configure(context); + if (config.On == on) + { + ((CompleteConfiguration)config).Configure(context); + } } } diff --git a/src/HotChocolate/Core/test/Execution.Tests/Integration/StarWarsCodeFirst/StarWarsCodeFirstTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Integration/StarWarsCodeFirst/StarWarsCodeFirstTests.cs index 4384a90bd7e..1e3ca365ab5 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Integration/StarWarsCodeFirst/StarWarsCodeFirstTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Integration/StarWarsCodeFirst/StarWarsCodeFirstTests.cs @@ -933,7 +933,7 @@ ... FriendEdge1 } } } - fragment FriendEdge1 on CharacterEdge { + fragment FriendEdge1 on FriendsEdge { node { __typename friends { diff --git a/src/HotChocolate/Core/test/Execution.Tests/Integration/StarWarsCodeFirst/__snapshots__/StarWarsCodeFirstTests.Ensure_Benchmark_Query_LargeQuery.snap b/src/HotChocolate/Core/test/Execution.Tests/Integration/StarWarsCodeFirst/__snapshots__/StarWarsCodeFirstTests.Ensure_Benchmark_Query_LargeQuery.snap index 526e926b408..49f020ced5e 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Integration/StarWarsCodeFirst/__snapshots__/StarWarsCodeFirstTests.Ensure_Benchmark_Query_LargeQuery.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/Integration/StarWarsCodeFirst/__snapshots__/StarWarsCodeFirstTests.Ensure_Benchmark_Query_LargeQuery.snap @@ -6187,7 +6187,7 @@ ], "type": { "kind": "OBJECT", - "name": "CharacterConnection", + "name": "FriendsConnection", "ofType": null }, "isDeprecated": false, @@ -6352,7 +6352,7 @@ ], "type": { "kind": "OBJECT", - "name": "CharacterConnection", + "name": "FriendsConnection", "ofType": null }, "isDeprecated": false, @@ -6436,8 +6436,8 @@ }, { "kind": "SCALAR", - "name": "Boolean", - "description": "The \u0060Boolean\u0060 scalar type represents \u0060true\u0060 or \u0060false\u0060.", + "name": "String", + "description": "The \u0060String\u0060 scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.", "fields": null, "inputFields": null, "interfaces": null, @@ -6446,8 +6446,8 @@ }, { "kind": "SCALAR", - "name": "String", - "description": "The \u0060String\u0060 scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.", + "name": "Boolean", + "description": "The \u0060Boolean\u0060 scalar type represents \u0060true\u0060 or \u0060false\u0060.", "fields": null, "inputFields": null, "interfaces": null, @@ -6548,7 +6548,7 @@ ], "type": { "kind": "OBJECT", - "name": "CharacterConnection", + "name": "FriendsConnection", "ofType": null }, "isDeprecated": false, @@ -6722,7 +6722,7 @@ }, { "kind": "OBJECT", - "name": "CharacterConnection", + "name": "FriendsConnection", "description": "A connection to a list of items.", "fields": [ { @@ -6753,7 +6753,7 @@ "name": null, "ofType": { "kind": "OBJECT", - "name": "CharacterEdge", + "name": "FriendsEdge", "ofType": null } } @@ -6955,7 +6955,7 @@ }, { "kind": "OBJECT", - "name": "CharacterEdge", + "name": "FriendsEdge", "description": "An edge in a connection.", "fields": [ { diff --git a/src/HotChocolate/Core/test/Execution.Tests/Integration/StarWarsCodeFirst/__snapshots__/StarWarsCodeFirstTests.Schema.snap b/src/HotChocolate/Core/test/Execution.Tests/Integration/StarWarsCodeFirst/__snapshots__/StarWarsCodeFirstTests.Schema.snap index c21dfee3057..b5284d1ce36 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Integration/StarWarsCodeFirst/__snapshots__/StarWarsCodeFirstTests.Schema.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/Integration/StarWarsCodeFirst/__snapshots__/StarWarsCodeFirstTests.Schema.snap @@ -7,43 +7,43 @@ 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): CharacterConnection + 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] 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 +} + "A connection to a list of items." -type CharacterConnection { +type FriendsConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [CharacterEdge!] + edges: [FriendsEdge!] "A flattened list of the nodes." nodes: [Character] } "An edge in a connection." -type CharacterEdge { +type FriendsEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." node: Character } -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): CharacterConnection - height(unit: Unit): Float - primaryFunction: String -} - 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): CharacterConnection + 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 diff --git a/src/HotChocolate/Core/test/Execution.Tests/Pipeline/ComplexityAnalyzerTests.cs b/src/HotChocolate/Core/test/Execution.Tests/Pipeline/ComplexityAnalyzerTests.cs index 42ef5a554b7..7a1fa48a7a6 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Pipeline/ComplexityAnalyzerTests.cs +++ b/src/HotChocolate/Core/test/Execution.Tests/Pipeline/ComplexityAnalyzerTests.cs @@ -232,7 +232,7 @@ await ExpectValid( }) .UseDefaultPipeline()); - Assert.Equal(110, complexity); + Assert.Equal(70, complexity); } [Fact] @@ -265,7 +265,7 @@ await ExpectValid( }) .UseDefaultPipeline()); - Assert.Equal(116, complexity); + Assert.Equal(76, complexity); } [Fact] @@ -299,7 +299,7 @@ await ExpectValid( }) .UseDefaultPipeline()); - Assert.Equal(117, complexity); + Assert.Equal(77, complexity); } [Fact] diff --git a/src/HotChocolate/Core/test/Execution.Tests/Pipeline/__snapshots__/ComplexityAnalyzerTests.Apply_Complexity_Defaults.snap b/src/HotChocolate/Core/test/Execution.Tests/Pipeline/__snapshots__/ComplexityAnalyzerTests.Apply_Complexity_Defaults.snap index dfbef56aa5c..5042ad97d19 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Pipeline/__snapshots__/ComplexityAnalyzerTests.Apply_Complexity_Defaults.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/Pipeline/__snapshots__/ComplexityAnalyzerTests.Apply_Complexity_Defaults.snap @@ -19,17 +19,17 @@ type Person { } "A connection to a list of items." -type PersonConnection { +type PersonsConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [PersonEdge!] + edges: [PersonsEdge!] "A flattened list of the nodes." - nodes: [Person] @cost(complexity: 5) + nodes: [Person] } "An edge in a connection." -type PersonEdge { +type PersonsEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." @@ -37,7 +37,7 @@ type PersonEdge { } type Query { - persons("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): PersonConnection @cost(complexity: 5, multipliers: [ "first", "last" ], defaultMultiplier: 10) + persons("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): PersonsConnection @cost(complexity: 5, multipliers: [ "first", "last" ], defaultMultiplier: 10) person: Person @cost(complexity: 5) sayHello: String } diff --git a/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Nested_Fragments.snap b/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Nested_Fragments.snap index 91e3f4ccf85..033c8d4a778 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Nested_Fragments.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Nested_Fragments.snap @@ -3,60 +3,22 @@ human(id: "1000") @__execute(id: 0, kind: PURE, type: COMPOSITE) { ... on Human { friends @include(if: $if) @skip(if: $if) @__execute(id: 1, kind: DEFAULT, type: COMPOSITE) { - ... on CharacterConnection { + ... on FriendsConnection { edges @include(if: $if) @skip(if: $if) @__execute(id: 2, kind: PURE, type: COMPOSITE_LIST) { - ... on CharacterEdge { - node @include(if: $if) @skip(if: $if) @__execute(id: 3, kind: PURE, type: COMPOSITE) { + ... on FriendsEdge { + node @skip(if: $if) @__execute(id: 3, kind: PURE, type: COMPOSITE) { ... on Human { - __typename @include(if: $if) @skip(if: $if) @__execute(id: 4, kind: DEFAULT, type: LEAF) - friends @include(if: $if) @__execute(id: 5, kind: DEFAULT, type: COMPOSITE) { - ... on CharacterConnection { - nodes @include(if: $if) @__execute(id: 10, kind: PURE, type: COMPOSITE_LIST) { - ... on Human { - __typename @include(if: $if) @__execute(id: 11, kind: DEFAULT, type: LEAF) - name @include(if: $if) @__execute(id: 12, kind: PURE, type: LEAF) - otherHuman @include(if: $if) @__execute(id: 13, kind: PURE, type: COMPOSITE) { - ... on Human { - __typename @include(if: $if) @__execute(id: 14, kind: DEFAULT, type: LEAF) - name @include(if: $if) @__execute(id: 15, kind: PURE, type: LEAF) - } - } - } - ... on Droid { - __typename @include(if: $if) @__execute(id: 16, kind: DEFAULT, type: LEAF) - } - } - } - } - name @skip(if: $if) @__execute(id: 6, kind: PURE, type: LEAF) - otherHuman @skip(if: $if) @__execute(id: 7, kind: PURE, type: COMPOSITE) { + __typename @skip(if: $if) @__execute(id: 4, kind: DEFAULT, type: LEAF) + name @skip(if: $if) @__execute(id: 5, kind: PURE, type: LEAF) + otherHuman @skip(if: $if) @__execute(id: 6, kind: PURE, type: COMPOSITE) { ... on Human { - __typename @skip(if: $if) @__execute(id: 8, kind: DEFAULT, type: LEAF) - name @skip(if: $if) @__execute(id: 9, kind: PURE, type: LEAF) + __typename @skip(if: $if) @__execute(id: 7, kind: DEFAULT, type: LEAF) + name @skip(if: $if) @__execute(id: 8, kind: PURE, type: LEAF) } } } ... on Droid { - __typename @include(if: $if) @skip(if: $if) @__execute(id: 17, kind: DEFAULT, type: LEAF) - friends @include(if: $if) @__execute(id: 18, kind: DEFAULT, type: COMPOSITE) { - ... on CharacterConnection { - nodes @include(if: $if) @__execute(id: 19, kind: PURE, type: COMPOSITE_LIST) { - ... on Human { - __typename @include(if: $if) @__execute(id: 20, kind: DEFAULT, type: LEAF) - name @include(if: $if) @__execute(id: 21, kind: PURE, type: LEAF) - otherHuman @include(if: $if) @__execute(id: 22, kind: PURE, type: COMPOSITE) { - ... on Human { - __typename @include(if: $if) @__execute(id: 23, kind: DEFAULT, type: LEAF) - name @include(if: $if) @__execute(id: 24, kind: PURE, type: LEAF) - } - } - } - ... on Droid { - __typename @include(if: $if) @__execute(id: 25, kind: DEFAULT, type: LEAF) - } - } - } - } + __typename @skip(if: $if) @__execute(id: 9, kind: DEFAULT, type: LEAF) } } } diff --git a/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Reuse_Selection.snap b/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Reuse_Selection.snap index 531bdbe1525..44310b09789 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Reuse_Selection.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/Processing/__snapshots__/OperationCompilerTests.Reuse_Selection.snap @@ -4,7 +4,7 @@ ... on Human { name @__execute(id: 1, kind: PURE, type: LEAF) friends @include(if: $withFriends) @__execute(id: 2, kind: DEFAULT, type: COMPOSITE) { - ... on CharacterConnection { + ... on FriendsConnection { nodes @include(if: $withFriends) @__execute(id: 3, kind: PURE, type: COMPOSITE_LIST) { ... on Human { id @include(if: $withFriends) @__execute(id: 4, kind: PURE, type: LEAF) @@ -19,7 +19,7 @@ ... on Droid { name @__execute(id: 6, kind: PURE, type: LEAF) friends @include(if: $withFriends) @__execute(id: 7, kind: DEFAULT, type: COMPOSITE) { - ... on CharacterConnection { + ... on FriendsConnection { nodes @include(if: $withFriends) @__execute(id: 8, kind: PURE, type: COMPOSITE_LIST) { ... on Human { id @include(if: $withFriends) @__execute(id: 9, kind: PURE, type: LEAF) diff --git a/src/HotChocolate/Core/test/Execution.Tests/__resources__/GetHeroWithFriendsQuery.graphql b/src/HotChocolate/Core/test/Execution.Tests/__resources__/GetHeroWithFriendsQuery.graphql index 6a86900b512..2a5bf32cdb8 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__resources__/GetHeroWithFriendsQuery.graphql +++ b/src/HotChocolate/Core/test/Execution.Tests/__resources__/GetHeroWithFriendsQuery.graphql @@ -21,7 +21,7 @@ fragment Droid on Droid { height } -fragment Friend on CharacterConnection { +fragment Friend on FriendsConnection { nodes { ...HasName friends { diff --git a/src/HotChocolate/Core/test/Execution.Tests/__resources__/GetTwoHerosWithFriendsQuery.graphql b/src/HotChocolate/Core/test/Execution.Tests/__resources__/GetTwoHerosWithFriendsQuery.graphql index 431f2acb614..de86ddd373d 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__resources__/GetTwoHerosWithFriendsQuery.graphql +++ b/src/HotChocolate/Core/test/Execution.Tests/__resources__/GetTwoHerosWithFriendsQuery.graphql @@ -24,7 +24,7 @@ fragment Droid on Droid { height } -fragment Friend on CharacterConnection { +fragment Friend on FriendsConnection { nodes { ...HasName friends { diff --git a/src/HotChocolate/Core/test/Execution.Tests/__resources__/LargeQuery.graphql b/src/HotChocolate/Core/test/Execution.Tests/__resources__/LargeQuery.graphql index 9783d51fe1a..912119a5aa1 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__resources__/LargeQuery.graphql +++ b/src/HotChocolate/Core/test/Execution.Tests/__resources__/LargeQuery.graphql @@ -57,7 +57,7 @@ fragment Droid on Droid { height } -fragment Friend on CharacterConnection { +fragment Friend on FriendsConnection { nodes { ...HasName friends { diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.DefaultValueIsInputObject.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.DefaultValueIsInputObject.snap index a05a7096e28..6a66960e3f3 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.DefaultValueIsInputObject.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.DefaultValueIsInputObject.snap @@ -922,8 +922,8 @@ }, { "kind": "SCALAR", - "name": "Boolean", - "description": "The \u0060Boolean\u0060 scalar type represents \u0060true\u0060 or \u0060false\u0060.", + "name": "String", + "description": "The \u0060String\u0060 scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.", "fields": null, "inputFields": null, "interfaces": null, @@ -932,8 +932,8 @@ }, { "kind": "SCALAR", - "name": "String", - "description": "The \u0060String\u0060 scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.", + "name": "Boolean", + "description": "The \u0060Boolean\u0060 scalar type represents \u0060true\u0060 or \u0060false\u0060.", "fields": null, "inputFields": null, "interfaces": null, diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.DirectiveIntrospection_AllDirectives_Internal.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.DirectiveIntrospection_AllDirectives_Internal.snap index cc62e838c8b..5210a74e129 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.DirectiveIntrospection_AllDirectives_Internal.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.DirectiveIntrospection_AllDirectives_Internal.snap @@ -2,16 +2,6 @@ "data": { "__schema": { "types": [ - { - "fields": [ - { - "appliedDirectives": [] - } - ] - }, - { - "fields": null - }, { "fields": [ { @@ -182,6 +172,16 @@ } ] }, + { + "fields": [ + { + "appliedDirectives": [] + } + ] + }, + { + "fields": null + }, { "fields": null }, diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.DirectiveIntrospection_AllDirectives_Public.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.DirectiveIntrospection_AllDirectives_Public.snap index 1480402a220..3c8cbddf88e 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.DirectiveIntrospection_AllDirectives_Public.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.DirectiveIntrospection_AllDirectives_Public.snap @@ -2,61 +2,6 @@ "data": { "__schema": { "types": [ - { - "fields": [ - { - "appliedDirectives": [ - { - "name": "foo", - "args": [] - }, - { - "name": "bar", - "args": [ - { - "name": "baz", - "value": "\u0022ABC\u0022" - } - ] - }, - { - "name": "bar", - "args": [ - { - "name": "baz", - "value": "null" - } - ] - }, - { - "name": "bar", - "args": [ - { - "name": "quox", - "value": "{ a: \u0022ABC\u0022 }" - } - ] - }, - { - "name": "bar", - "args": [ - { - "name": "quox", - "value": "{ }" - } - ] - }, - { - "name": "bar", - "args": [] - } - ] - } - ] - }, - { - "fields": null - }, { "fields": [ { @@ -227,6 +172,61 @@ } ] }, + { + "fields": [ + { + "appliedDirectives": [ + { + "name": "foo", + "args": [] + }, + { + "name": "bar", + "args": [ + { + "name": "baz", + "value": "\u0022ABC\u0022" + } + ] + }, + { + "name": "bar", + "args": [ + { + "name": "baz", + "value": "null" + } + ] + }, + { + "name": "bar", + "args": [ + { + "name": "quox", + "value": "{ a: \u0022ABC\u0022 }" + } + ] + }, + { + "name": "bar", + "args": [ + { + "name": "quox", + "value": "{ }" + } + ] + }, + { + "name": "bar", + "args": [] + } + ] + } + ] + }, + { + "fields": null + }, { "fields": null }, diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.DirectiveIntrospection_SomeDirectives_Internal.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.DirectiveIntrospection_SomeDirectives_Internal.snap index d1442e8243f..8e69f9efd0f 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.DirectiveIntrospection_SomeDirectives_Internal.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.DirectiveIntrospection_SomeDirectives_Internal.snap @@ -2,57 +2,6 @@ "data": { "__schema": { "types": [ - { - "fields": [ - { - "appliedDirectives": [ - { - "name": "bar", - "args": [ - { - "name": "baz", - "value": "\u0022ABC\u0022" - } - ] - }, - { - "name": "bar", - "args": [ - { - "name": "baz", - "value": "null" - } - ] - }, - { - "name": "bar", - "args": [ - { - "name": "quox", - "value": "{ a: \u0022ABC\u0022 }" - } - ] - }, - { - "name": "bar", - "args": [ - { - "name": "quox", - "value": "{ }" - } - ] - }, - { - "name": "bar", - "args": [] - } - ] - } - ] - }, - { - "fields": null - }, { "fields": [ { @@ -223,6 +172,57 @@ } ] }, + { + "fields": [ + { + "appliedDirectives": [ + { + "name": "bar", + "args": [ + { + "name": "baz", + "value": "\u0022ABC\u0022" + } + ] + }, + { + "name": "bar", + "args": [ + { + "name": "baz", + "value": "null" + } + ] + }, + { + "name": "bar", + "args": [ + { + "name": "quox", + "value": "{ a: \u0022ABC\u0022 }" + } + ] + }, + { + "name": "bar", + "args": [ + { + "name": "quox", + "value": "{ }" + } + ] + }, + { + "name": "bar", + "args": [] + } + ] + } + ] + }, + { + "fields": null + }, { "fields": null }, diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.ExecuteGraphiQLIntrospectionQuery.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.ExecuteGraphiQLIntrospectionQuery.snap index 1198afc1539..09ef518dd3a 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.ExecuteGraphiQLIntrospectionQuery.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.ExecuteGraphiQLIntrospectionQuery.snap @@ -923,8 +923,8 @@ }, { "kind": "SCALAR", - "name": "Boolean", - "description": "The \u0060Boolean\u0060 scalar type represents \u0060true\u0060 or \u0060false\u0060.", + "name": "String", + "description": "The \u0060String\u0060 scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.", "fields": null, "inputFields": null, "interfaces": null, @@ -933,8 +933,8 @@ }, { "kind": "SCALAR", - "name": "String", - "description": "The \u0060String\u0060 scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.", + "name": "Boolean", + "description": "The \u0060Boolean\u0060 scalar type represents \u0060true\u0060 or \u0060false\u0060.", "fields": null, "inputFields": null, "interfaces": null, diff --git a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.ExecuteGraphiQLIntrospectionQuery_ToJson.snap b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.ExecuteGraphiQLIntrospectionQuery_ToJson.snap index 1198afc1539..09ef518dd3a 100644 --- a/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.ExecuteGraphiQLIntrospectionQuery_ToJson.snap +++ b/src/HotChocolate/Core/test/Execution.Tests/__snapshots__/IntrospectionTests.ExecuteGraphiQLIntrospectionQuery_ToJson.snap @@ -923,8 +923,8 @@ }, { "kind": "SCALAR", - "name": "Boolean", - "description": "The \u0060Boolean\u0060 scalar type represents \u0060true\u0060 or \u0060false\u0060.", + "name": "String", + "description": "The \u0060String\u0060 scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.", "fields": null, "inputFields": null, "interfaces": null, @@ -933,8 +933,8 @@ }, { "kind": "SCALAR", - "name": "String", - "description": "The \u0060String\u0060 scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.", + "name": "Boolean", + "description": "The \u0060Boolean\u0060 scalar type represents \u0060true\u0060 or \u0060false\u0060.", "fields": null, "inputFields": null, "interfaces": null, diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/IntegrationTests.cs b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/IntegrationTests.cs index 1eadba167f5..249700e3038 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/IntegrationTests.cs +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/IntegrationTests.cs @@ -1,10 +1,11 @@ -using System.Collections; +using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using HotChocolate.Execution; +using HotChocolate.Internal; +using HotChocolate.Resolvers; using HotChocolate.Tests; using Snapshooter.Xunit; using Xunit; @@ -85,7 +86,7 @@ public async Task No_Boundaries_Set() await new ServiceCollection() .AddGraphQL() .AddQueryType() - .SetPagingOptions(new PagingOptions { RequirePagingBoundaries = true}) + .SetPagingOptions(new PagingOptions { RequirePagingBoundaries = true }) .Services .BuildServiceProvider() .GetRequestExecutorAsync(); @@ -678,6 +679,87 @@ public async Task Deactivate_BackwardPagination_Interface() schema.Print().MatchSnapshot(); } + [Fact] + public async Task Infer_ConnectionName_From_Field() + { + Snapshot.FullName(); + + ISchema schema = + await new ServiceCollection() + .AddGraphQL() + .AddQueryType() + .Services + .BuildServiceProvider() + .GetSchemaAsync(); + + schema.Print().MatchSnapshot(); + } + + [Fact] + public async Task Explicit_ConnectionName() + { + Snapshot.FullName(); + + ISchema schema = + await new ServiceCollection() + .AddGraphQL() + .AddQueryType() + .Services + .BuildServiceProvider() + .GetSchemaAsync(); + + schema.Print().MatchSnapshot(); + } + + [Fact] + public async Task SelectProviderByName() + { + Snapshot.FullName(); + + IRequestExecutor executor = + await new ServiceCollection() + .AddGraphQL() + .AddQueryType() + .AddCursorPagingProvider(providerName: "Abc") + .SetPagingOptions(new PagingOptions{ InferConnectionNameFromField = false }) + .Services + .BuildServiceProvider() + .GetRequestExecutorAsync(); + + await executor + .ExecuteAsync(@" + { + abc { + nodes + } + }") + .MatchSnapshotAsync(); + } + + [Fact] + public async Task SelectDefaultProvider() + { + Snapshot.FullName(); + + IRequestExecutor executor = + await new ServiceCollection() + .AddGraphQL() + .AddQueryType() + .AddCursorPagingProvider() + .AddCursorPagingProvider(defaultProvider: true) + .Services + .BuildServiceProvider() + .GetRequestExecutorAsync(); + + await executor + .ExecuteAsync(@" + { + abc { + nodes + } + }") + .MatchSnapshotAsync(); + } public class QueryType : ObjectType { protected override void Configure(IObjectTypeDescriptor descriptor) @@ -812,37 +894,92 @@ public interface ISome2 [UsePaging(typeof(NonNullType))] public string[] ExplicitType(); } - } - public class MockExecutable : IExecutable - { - private readonly IQueryable _source; + public class InferConnectionNameFromFieldType : ObjectType + { + protected override void Configure( + IObjectTypeDescriptor descriptor) + { + descriptor + .Field(t => t.Names()) + .UsePaging(options: new() { InferConnectionNameFromField = true }); + + } + } - public MockExecutable(IQueryable source) + public class InferConnectionNameFromField { - _source = source; + public string[] Names() => new[] { "a", "b" }; + } + + public class ExplicitConnectionName + { + [UsePaging(ConnectionName = "Connection1")] + public string[] Abc => throw new NotImplementedException(); + + [UsePaging(ConnectionName = "Connection2")] + public string[] Def => throw new NotImplementedException(); + + [UsePaging] + public string[] Ghi => throw new NotImplementedException(); } - public object Source => _source; + public class ProviderByName + { + [UsePaging(ProviderName = "Abc")] + public string[] Abc => Array.Empty(); + } - public ValueTask ToListAsync(CancellationToken cancellationToken) + public class DummyProvider : CursorPagingProvider { - return new(_source.ToList()); + public override bool CanHandle(IExtendedType source) => false; + + protected override CursorPagingHandler CreateHandler( + IExtendedType source, + PagingOptions options) + => new DummyHandler(options); } - public ValueTask FirstOrDefaultAsync(CancellationToken cancellationToken) + public class DummyHandler : CursorPagingHandler { - return new(_source.FirstOrDefault()); + public DummyHandler(PagingOptions options) : base(options) + { + } + + protected override ValueTask SliceAsync( + IResolverContext context, + object source, + CursorPagingArguments arguments) + => new(new Connection( + new[] { new Edge("a", "b") }, + new ConnectionPageInfo(false, false, null, null), + _ => new(1))); } - public ValueTask SingleOrDefaultAsync(CancellationToken cancellationToken) + public class Dummy2Provider : CursorPagingProvider { - return new(_source.SingleOrDefault()); + public override bool CanHandle(IExtendedType source) => false; + + protected override CursorPagingHandler CreateHandler( + IExtendedType source, + PagingOptions options) + => new Dummy2Handler(options); } - public string Print() + public class Dummy2Handler : CursorPagingHandler { - return _source.ToString()!; + public Dummy2Handler(PagingOptions options) : base(options) + { + } + + protected override ValueTask SliceAsync( + IResolverContext context, + object source, + CursorPagingArguments arguments) + => new(new Connection( + new[] { new Edge("d", "e") }, + new ConnectionPageInfo(false, false, null, null), + _ => new(1))); } } } diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/MockExecutable.cs b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/MockExecutable.cs new file mode 100644 index 00000000000..bc01c34e7dc --- /dev/null +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/MockExecutable.cs @@ -0,0 +1,39 @@ +using System.Collections; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace HotChocolate.Types.Pagination +{ + public class MockExecutable : IExecutable + { + private readonly IQueryable _source; + + public MockExecutable(IQueryable source) + { + _source = source; + } + + public object Source => _source; + + public ValueTask ToListAsync(CancellationToken cancellationToken) + { + return new(_source.ToList()); + } + + public ValueTask FirstOrDefaultAsync(CancellationToken cancellationToken) + { + return new(_source.FirstOrDefault()); + } + + public ValueTask SingleOrDefaultAsync(CancellationToken cancellationToken) + { + return new(_source.SingleOrDefault()); + } + + public string Print() + { + return _source.ToString()!; + } + } +} diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/CustomCursorHandlerTests.Infer_Schema_Correctly_When_Connection_IsUsed.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/CustomCursorHandlerTests.Infer_Schema_Correctly_When_Connection_IsUsed.snap index a4626e278b8..7a79ab7cac6 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/CustomCursorHandlerTests.Infer_Schema_Correctly_When_Connection_IsUsed.snap +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/CustomCursorHandlerTests.Infer_Schema_Correctly_When_Connection_IsUsed.snap @@ -2,40 +2,40 @@ query: Query } -"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 { - items("Returns the first _n_ elements from the list." first: Int = 10 "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): StringConnection -} - "A connection to a list of items." -type StringConnection { +type ItemsConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [StringEdge!] + edges: [ItemsEdge!] "A flattened list of the nodes." nodes: [String!] } "An edge in a connection." -type StringEdge { +type ItemsEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." node: String! } +"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 { + items("Returns the first _n_ elements from the list." first: Int = 10 "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): ItemsConnection +} + "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 diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Attribute_Interface_With_Paging_Field.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Attribute_Interface_With_Paging_Field.snap index eaef71f9e29..1c28a1f916b 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Attribute_Interface_With_Paging_Field.snap +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Attribute_Interface_With_Paging_Field.snap @@ -3,7 +3,25 @@ } interface ISome2 { - explicitType("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): StringConnection + explicitType("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): ExplicitTypeConnection +} + +"A connection to a list of items." +type ExplicitTypeConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [ExplicitTypeEdge!] + "A flattened list of the nodes." + nodes: [String!] +} + +"An edge in a connection." +type ExplicitTypeEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: String! } type Foo { @@ -11,18 +29,36 @@ type Foo { } "A connection to a list of items." -type FooConnection { +type LettersConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [FooEdge!] + edges: [LettersEdge!] + "A flattened list of the nodes." + nodes: [String!] +} + +"An edge in a connection." +type LettersEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: String! +} + +"A connection to a list of items." +type NestedObjectListConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [NestedObjectListEdge!] "A flattened list of the nodes." nodes: [[Foo!]!] totalCount: Int! } "An edge in a connection." -type FooEdge { +type NestedObjectListEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." @@ -42,27 +78,9 @@ type PageInfo { } type QueryAttr { - nestedObjectList("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): FooConnection - letters("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): StringConnection - explicitType("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): StringConnection -} - -"A connection to a list of items." -type StringConnection { - "Information to aid in pagination." - pageInfo: PageInfo! - "A list of edges." - edges: [StringEdge!] - "A flattened list of the nodes." - nodes: [String!] -} - -"An edge in a connection." -type StringEdge { - "A cursor for use in pagination." - cursor: String! - "The item at the end of the edge." - node: String! + nestedObjectList("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): NestedObjectListConnection + letters("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): LettersConnection + explicitType("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): ExplicitTypeConnection } "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`." diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Attribute_Simple_StringList_Schema.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Attribute_Simple_StringList_Schema.snap index 5a2fa0f6e00..7a4f2bb4b12 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Attribute_Simple_StringList_Schema.snap +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Attribute_Simple_StringList_Schema.snap @@ -2,23 +2,59 @@ query: QueryAttr } +"A connection to a list of items." +type ExplicitTypeConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [ExplicitTypeEdge!] + "A flattened list of the nodes." + nodes: [String!] +} + +"An edge in a connection." +type ExplicitTypeEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: String! +} + type Foo { bar: String! } "A connection to a list of items." -type FooConnection { +type LettersConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [FooEdge!] + edges: [LettersEdge!] + "A flattened list of the nodes." + nodes: [String!] +} + +"An edge in a connection." +type LettersEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: String! +} + +"A connection to a list of items." +type NestedObjectListConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [NestedObjectListEdge!] "A flattened list of the nodes." nodes: [[Foo!]!] totalCount: Int! } "An edge in a connection." -type FooEdge { +type NestedObjectListEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." @@ -38,27 +74,9 @@ type PageInfo { } type QueryAttr { - nestedObjectList("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): FooConnection - letters("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): StringConnection - explicitType("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): StringConnection -} - -"A connection to a list of items." -type StringConnection { - "Information to aid in pagination." - pageInfo: PageInfo! - "A list of edges." - edges: [StringEdge!] - "A flattened list of the nodes." - nodes: [String!] -} - -"An edge in a connection." -type StringEdge { - "A cursor for use in pagination." - cursor: String! - "The item at the end of the edge." - node: String! + nestedObjectList("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): NestedObjectListConnection + letters("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): LettersConnection + explicitType("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): ExplicitTypeConnection } "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`." diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Deactivate_BackwardPagination.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Deactivate_BackwardPagination.snap index 5600aa7fa05..188deae6dba 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Deactivate_BackwardPagination.snap +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Deactivate_BackwardPagination.snap @@ -2,23 +2,59 @@ query: Query } +"A connection to a list of items." +type ExplicitTypeConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [ExplicitTypeEdge!] + "A flattened list of the nodes." + nodes: [String!] +} + +"An edge in a connection." +type ExplicitTypeEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: String! +} + type Foo { bar: String! } "A connection to a list of items." -type FooConnection { +type LettersConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [FooEdge!] + edges: [LettersEdge!] + "A flattened list of the nodes." + nodes: [String!] +} + +"An edge in a connection." +type LettersEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: String! +} + +"A connection to a list of items." +type NestedObjectListConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [NestedObjectListEdge!] "A flattened list of the nodes." nodes: [[Foo!]!] totalCount: Int! } "An edge in a connection." -type FooEdge { +type NestedObjectListEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." @@ -38,27 +74,9 @@ type PageInfo { } type Query { - letters("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): StringConnection - explicitType("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): StringConnection - nestedObjectList("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): FooConnection -} - -"A connection to a list of items." -type StringConnection { - "Information to aid in pagination." - pageInfo: PageInfo! - "A list of edges." - edges: [StringEdge!] - "A flattened list of the nodes." - nodes: [String!] -} - -"An edge in a connection." -type StringEdge { - "A cursor for use in pagination." - cursor: String! - "The item at the end of the edge." - node: String! + letters("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): LettersConnection + explicitType("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): ExplicitTypeConnection + nestedObjectList("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): NestedObjectListConnection } "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`." diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Deactivate_BackwardPagination_Interface.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Deactivate_BackwardPagination_Interface.snap index 00951d29980..f6f8e00a027 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Deactivate_BackwardPagination_Interface.snap +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Deactivate_BackwardPagination_Interface.snap @@ -3,7 +3,25 @@ } interface ISome { - explicitType("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): StringConnection + explicitType("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): ExplicitTypeConnection +} + +"A connection to a list of items." +type ExplicitTypeConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [ExplicitTypeEdge!] + "A flattened list of the nodes." + nodes: [String!] +} + +"An edge in a connection." +type ExplicitTypeEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: String! } type Foo { @@ -11,18 +29,36 @@ type Foo { } "A connection to a list of items." -type FooConnection { +type LettersConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [FooEdge!] + edges: [LettersEdge!] + "A flattened list of the nodes." + nodes: [String!] +} + +"An edge in a connection." +type LettersEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: String! +} + +"A connection to a list of items." +type NestedObjectListConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [NestedObjectListEdge!] "A flattened list of the nodes." nodes: [[Foo!]!] totalCount: Int! } "An edge in a connection." -type FooEdge { +type NestedObjectListEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." @@ -42,27 +78,9 @@ type PageInfo { } type QueryAttr { - nestedObjectList("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): FooConnection - letters("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): StringConnection - explicitType("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): StringConnection -} - -"A connection to a list of items." -type StringConnection { - "Information to aid in pagination." - pageInfo: PageInfo! - "A list of edges." - edges: [StringEdge!] - "A flattened list of the nodes." - nodes: [String!] -} - -"An edge in a connection." -type StringEdge { - "A cursor for use in pagination." - cursor: String! - "The item at the end of the edge." - node: String! + nestedObjectList("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): NestedObjectListConnection + letters("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): LettersConnection + explicitType("Returns the first _n_ elements from the list." first: Int "Returns the elements in the list that come after the specified cursor." after: String): ExplicitTypeConnection } "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`." diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Explicit_ConnectionName.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Explicit_ConnectionName.snap new file mode 100644 index 00000000000..6a2a46d4b3b --- /dev/null +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Explicit_ConnectionName.snap @@ -0,0 +1,81 @@ +schema { + query: ExplicitConnectionName +} + +"A connection to a list of items." +type Connection1Connection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [Connection1Edge!] + "A flattened list of the nodes." + nodes: [String!] +} + +"An edge in a connection." +type Connection1Edge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: String! +} + +"A connection to a list of items." +type Connection2Connection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [Connection2Edge!] + "A flattened list of the nodes." + nodes: [String!] +} + +"An edge in a connection." +type Connection2Edge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: String! +} + +type ExplicitConnectionName { + abc("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): Connection1Connection + def("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): Connection2Connection + ghi("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): GhiConnection +} + +"A connection to a list of items." +type GhiConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [GhiEdge!] + "A flattened list of the nodes." + nodes: [String!] +} + +"An edge in a connection." +type GhiEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: String! +} + +"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 +} + +"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! "Streamed when true." if: Boolean!) on FIELD diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Infer_ConnectionName_From_Field.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Infer_ConnectionName_From_Field.snap new file mode 100644 index 00000000000..dd76628e165 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Infer_ConnectionName_From_Field.snap @@ -0,0 +1,43 @@ +schema { + query: InferConnectionNameFromField +} + +type InferConnectionNameFromField { + names("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): NamesConnection +} + +"A connection to a list of items." +type NamesConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [NamesEdge!] + "A flattened list of the nodes." + nodes: [String!] +} + +"An edge in a connection." +type NamesEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: String! +} + +"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 +} + +"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! "Streamed when true." if: Boolean!) on FIELD diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Interface_With_Paging_Field.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Interface_With_Paging_Field.snap index 3cc18a3acac..27b7fe41330 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Interface_With_Paging_Field.snap +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Interface_With_Paging_Field.snap @@ -3,7 +3,25 @@ } interface ISome { - explicitType("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): StringConnection + explicitType("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): ExplicitTypeConnection +} + +"A connection to a list of items." +type ExplicitTypeConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [ExplicitTypeEdge!] + "A flattened list of the nodes." + nodes: [String!] +} + +"An edge in a connection." +type ExplicitTypeEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: String! } type Foo { @@ -11,18 +29,36 @@ type Foo { } "A connection to a list of items." -type FooConnection { +type LettersConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [FooEdge!] + edges: [LettersEdge!] + "A flattened list of the nodes." + nodes: [String!] +} + +"An edge in a connection." +type LettersEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: String! +} + +"A connection to a list of items." +type NestedObjectListConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [NestedObjectListEdge!] "A flattened list of the nodes." nodes: [[Foo!]!] totalCount: Int! } "An edge in a connection." -type FooEdge { +type NestedObjectListEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." @@ -42,27 +78,9 @@ type PageInfo { } type QueryAttr { - nestedObjectList("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): FooConnection - letters("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): StringConnection - explicitType("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): StringConnection -} - -"A connection to a list of items." -type StringConnection { - "Information to aid in pagination." - pageInfo: PageInfo! - "A list of edges." - edges: [StringEdge!] - "A flattened list of the nodes." - nodes: [String!] -} - -"An edge in a connection." -type StringEdge { - "A cursor for use in pagination." - cursor: String! - "The item at the end of the edge." - node: String! + nestedObjectList("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): NestedObjectListConnection + letters("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): LettersConnection + explicitType("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): ExplicitTypeConnection } "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`." diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.No_Boundaries_Set.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.No_Boundaries_Set.snap index 34208c9a28f..24c23556e1f 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.No_Boundaries_Set.snap +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.No_Boundaries_Set.snap @@ -1,7 +1,7 @@ { "errors": [ { - "message": "You must provide a \u0060first\u0060 or \u0060last\u0060 value to properly paginate the \u0060StringConnection\u0060.", + "message": "You must provide a \u0060first\u0060 or \u0060last\u0060 value to properly paginate the \u0060LettersConnection\u0060.", "path": [ "letters" ], diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.SelectDefaultProvider.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.SelectDefaultProvider.snap new file mode 100644 index 00000000000..11a80533e01 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.SelectDefaultProvider.snap @@ -0,0 +1,9 @@ +{ + "data": { + "abc": { + "nodes": [ + "d" + ] + } + } +} diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.SelectProviderByName.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.SelectProviderByName.snap new file mode 100644 index 00000000000..9903f5f02b4 --- /dev/null +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.SelectProviderByName.snap @@ -0,0 +1,9 @@ +{ + "data": { + "abc": { + "nodes": [ + "a" + ] + } + } +} diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Simple_StringList_Schema.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Simple_StringList_Schema.snap index fd0dadf283b..acd52652400 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Simple_StringList_Schema.snap +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/IntegrationTests.Simple_StringList_Schema.snap @@ -2,23 +2,59 @@ query: Query } +"A connection to a list of items." +type ExplicitTypeConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [ExplicitTypeEdge!] + "A flattened list of the nodes." + nodes: [String!] +} + +"An edge in a connection." +type ExplicitTypeEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: String! +} + type Foo { bar: String! } "A connection to a list of items." -type FooConnection { +type LettersConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [FooEdge!] + edges: [LettersEdge!] + "A flattened list of the nodes." + nodes: [String!] +} + +"An edge in a connection." +type LettersEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: String! +} + +"A connection to a list of items." +type NestedObjectListConnection { + "Information to aid in pagination." + pageInfo: PageInfo! + "A list of edges." + edges: [NestedObjectListEdge!] "A flattened list of the nodes." nodes: [[Foo!]!] totalCount: Int! } "An edge in a connection." -type FooEdge { +type NestedObjectListEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." @@ -38,27 +74,9 @@ type PageInfo { } type Query { - letters("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): StringConnection - explicitType("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): StringConnection - nestedObjectList("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): FooConnection -} - -"A connection to a list of items." -type StringConnection { - "Information to aid in pagination." - pageInfo: PageInfo! - "A list of edges." - edges: [StringEdge!] - "A flattened list of the nodes." - nodes: [String!] -} - -"An edge in a connection." -type StringEdge { - "A cursor for use in pagination." - cursor: String! - "The item at the end of the edge." - node: String! + letters("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): LettersConnection + explicitType("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): ExplicitTypeConnection + nestedObjectList("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): NestedObjectListConnection } "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`." diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/UsePagingAttributeTests.Ensure_Attributes_Are_Applied_Once.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/UsePagingAttributeTests.Ensure_Attributes_Are_Applied_Once.snap index d38661dfd3a..e3e97b74264 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/UsePagingAttributeTests.Ensure_Attributes_Are_Applied_Once.snap +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/UsePagingAttributeTests.Ensure_Attributes_Are_Applied_Once.snap @@ -12,17 +12,17 @@ type Foo { } "A connection to a list of items." -type FooConnection { +type FoosConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [FooEdge!] + edges: [FoosEdge!] "A flattened list of the nodes." nodes: [Foo] } "An edge in a connection." -type FooEdge { +type FoosEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." @@ -42,7 +42,7 @@ type PageInfo { } type Query1 implements Node { - foos("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): FooConnection + foos("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): FoosConnection id: ID! } diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/UsePagingAttributeTests.UsePagingAttribute_Infer_Types.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/UsePagingAttributeTests.UsePagingAttribute_Infer_Types.snap index f64dc0f01e6..1f6b2a9e627 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/UsePagingAttributeTests.UsePagingAttribute_Infer_Types.snap +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/UsePagingAttributeTests.UsePagingAttribute_Infer_Types.snap @@ -7,17 +7,17 @@ type Foo { } "A connection to a list of items." -type FooConnection { +type FoosConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [FooEdge!] + edges: [FoosEdge!] "A flattened list of the nodes." nodes: [Foo] } "An edge in a connection." -type FooEdge { +type FoosEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." @@ -37,7 +37,7 @@ type PageInfo { } type Query { - foos("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): FooConnection + foos("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): FoosConnection } "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`." diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/UsePagingAttributeTests.UsePagingAttribute_Infer_Types_On_Interface.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/UsePagingAttributeTests.UsePagingAttribute_Infer_Types_On_Interface.snap index cd0ff47b88e..90f3892902c 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/UsePagingAttributeTests.UsePagingAttribute_Infer_Types_On_Interface.snap +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/UsePagingAttributeTests.UsePagingAttribute_Infer_Types_On_Interface.snap @@ -1,5 +1,5 @@ interface IHasFoos { - foos("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): FooConnection + foos("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): FoosConnection } type Foo { @@ -7,17 +7,17 @@ type Foo { } "A connection to a list of items." -type FooConnection { +type FoosConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [FooEdge!] + edges: [FoosEdge!] "A flattened list of the nodes." nodes: [Foo] } "An edge in a connection." -type FooEdge { +type FoosEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." diff --git a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/UsePagingAttributeTests.UsePagingAttribute_On_Extension_Infer_Types.snap b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/UsePagingAttributeTests.UsePagingAttribute_On_Extension_Infer_Types.snap index f64dc0f01e6..1f6b2a9e627 100644 --- a/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/UsePagingAttributeTests.UsePagingAttribute_On_Extension_Infer_Types.snap +++ b/src/HotChocolate/Core/test/Types.CursorPagination.Tests/__snapshots__/UsePagingAttributeTests.UsePagingAttribute_On_Extension_Infer_Types.snap @@ -7,17 +7,17 @@ type Foo { } "A connection to a list of items." -type FooConnection { +type FoosConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [FooEdge!] + edges: [FoosEdge!] "A flattened list of the nodes." nodes: [Foo] } "An edge in a connection." -type FooEdge { +type FoosEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." @@ -37,7 +37,7 @@ type PageInfo { } type Query { - foos("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): FooConnection + foos("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): FoosConnection } "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`." diff --git a/src/HotChocolate/Core/test/Types.Tests/Configuration/SchemaTypeDiscoveryTests.cs b/src/HotChocolate/Core/test/Types.Tests/Configuration/SchemaTypeDiscoveryTests.cs index fbd46011d52..0cb2544efdd 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Configuration/SchemaTypeDiscoveryTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Configuration/SchemaTypeDiscoveryTests.cs @@ -193,7 +193,7 @@ public override bool IsInstanceOfType(IValueNode literal) throw new NotSupportedException(); } - public override object? ParseLiteral(IValueNode literal) + public override object ParseLiteral(IValueNode literal) { throw new NotSupportedException(); } diff --git a/src/HotChocolate/Core/test/Types.Tests/Configuration/TypeInitializationOrderTests.cs b/src/HotChocolate/Core/test/Types.Tests/Configuration/TypeInitializationOrderTests.cs index ec17da3873e..7f057cc0f53 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Configuration/TypeInitializationOrderTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Configuration/TypeInitializationOrderTests.cs @@ -50,23 +50,21 @@ protected override void Configure(IObjectTypeDescriptor descriptor) ExtendedTypeReference reference = c.TypeInspector.GetTypeRef(typeof(Word), TypeContext.Output); - ILazyTypeConfiguration lazyConfiguration = - LazyTypeConfigurationBuilder - .New() - .Definition(d) - .Configure((context, definition) => + d.Configurations.Add( + new CompleteConfiguration( + (context, _) => { ObjectType type = context.GetType(reference); + if (!type.IsCompleted) { throw new Exception("Order should not matter"); } - }) - .On(ApplyConfigurationOn.Completion) - .DependsOn(reference, true) - .Build(); - - d.Configurations.Add(lazyConfiguration); + }, + d, + ApplyConfigurationOn.Completion, + reference, + TypeDependencyKind.Completed)); }); } } diff --git a/src/HotChocolate/Core/test/Types.Tests/Configuration/TypeInitializerTests.cs b/src/HotChocolate/Core/test/Types.Tests/Configuration/TypeInitializerTests.cs index c6721bb87e9..1b1ac3378fb 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Configuration/TypeInitializerTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Configuration/TypeInitializerTests.cs @@ -201,18 +201,61 @@ public void Detect_Duplicate_Types() Assert.Equal(1, interceptor.Count); } - public class FooType - : ObjectType + [Fact] + public void InitializeFactoryTypeRefOnce() { - protected override void Configure( - IObjectTypeDescriptor descriptor) - { - descriptor.Field(t => t.Bar).Type>(); - } + // arrange + SyntaxTypeReference typeRef1 = TypeReference.Parse( + "Abc", + factory: _ => new ObjectType(d => d.Name("Abc").Field("def").Resolve("ghi"))); + + SyntaxTypeReference typeRef2 = TypeReference.Parse( + "Abc", + factory: _ => new ObjectType(d => d.Name("Abc").Field("def").Resolve("ghi"))); + + var interceptor = new InjectTypes(new[] { typeRef1, typeRef2 }); + + // act + ISchema schema = + SchemaBuilder.New() + .TryAddTypeInterceptor(interceptor) + .ModifyOptions(o => o.StrictValidation = false) + .Create(); + + // assert + schema.Print().MatchSnapshot(); + } + + [Fact] + public void FactoryAndNameRefsAreRecognizedAsTheSameType() + { + // arrange + SyntaxTypeReference typeRef1 = TypeReference.Parse( + "Abc", + factory: _ => new ObjectType(d => d.Name("Abc").Field("def").Resolve("ghi"))); + + SyntaxTypeReference typeRef2 = TypeReference.Parse("Abc"); + + var interceptor = new InjectTypes(new[] { typeRef1, typeRef2 }); + + // act + ISchema schema = + SchemaBuilder.New() + .TryAddTypeInterceptor(interceptor) + .ModifyOptions(o => o.StrictValidation = false) + .Create(); + + // assert + schema.Print().MatchSnapshot(); } - public class BarType - : ObjectType + public class FooType : ObjectType + { + protected override void Configure(IObjectTypeDescriptor descriptor) + => descriptor.Field(t => t.Bar).Type>(); + } + + public class BarType : ObjectType { } @@ -257,5 +300,19 @@ public override void OnTypeRegistered(ITypeDiscoveryContext discoveryContext) } } } + + private class InjectTypes : TypeInterceptor + { + private readonly List _typeReferences; + + public InjectTypes(IEnumerable typeReferences) + => _typeReferences = typeReferences.ToList(); + + public override bool TriggerAggregations => true; + + public override IEnumerable RegisterMoreTypes( + IReadOnlyCollection discoveryContexts) + => _typeReferences; + } } } diff --git a/src/HotChocolate/Core/test/Types.Tests/Configuration/TypeScopeInterceptorTests.cs b/src/HotChocolate/Core/test/Types.Tests/Configuration/TypeScopeInterceptorTests.cs index 34c4830c985..6456b6ee993 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Configuration/TypeScopeInterceptorTests.cs +++ b/src/HotChocolate/Core/test/Types.Tests/Configuration/TypeScopeInterceptorTests.cs @@ -97,7 +97,7 @@ public override void OnBeforeRegisterDependencies( { foreach (ObjectFieldDefinition field in def.Fields) { - if (field.Type.Scope is null) + if (field.Type is not null && field.Type.Scope is null) { field.Type = field.Type.With(scope: discoveryContext.Scope); } @@ -121,46 +121,6 @@ public override void OnTypesInitialized( _types.Add(context.Type); } } - - public bool TryCreateScope( - ITypeDiscoveryContext discoveryContext, - out IReadOnlyList typeDependencies) - { - if (discoveryContext is { Scope: not null }) - { - var list = new List(); - - foreach (TypeDependency typeDependency in discoveryContext.TypeDependencies) - { - if (!discoveryContext.TryPredictTypeKind( - typeDependency.TypeReference, - out TypeKind kind) || - kind == TypeKind.Scalar) - { - list.Add(typeDependency); - continue; - } - - var typeReference = (ExtendedTypeReference)typeDependency.TypeReference; - - if (typeDependency.TypeReference.Scope is null) - { - typeReference = typeReference.WithScope(discoveryContext.Scope); - list.Add(typeDependency.With(typeReference)); - } - else - { - list.Add(typeDependency); - } - - typeDependencies = list; - return true; - } - } - - typeDependencies = null; - return false; - } } } } diff --git a/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscovererTests.Register_ClrType_InferSchemaTypes.snap b/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscovererTests.Register_ClrType_InferSchemaTypes.snap index c0c21fd9f15..e5f86bbdf15 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscovererTests.Register_ClrType_InferSchemaTypes.snap +++ b/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscovererTests.Register_ClrType_InferSchemaTypes.snap @@ -112,14 +112,6 @@ "None: DeprecatedDirectiveType" ] }, - { - "type": "HotChocolate.Types.BooleanType", - "runtimeType": "System.Boolean", - "references": [ - "None: HotChocolate.Types.BooleanType", - "None: BooleanType" - ] - }, { "type": "HotChocolate.Types.StringType", "runtimeType": "System.String", @@ -128,6 +120,14 @@ "None: StringType" ] }, + { + "type": "HotChocolate.Types.BooleanType", + "runtimeType": "System.Boolean", + "references": [ + "None: HotChocolate.Types.BooleanType", + "None: BooleanType" + ] + }, { "type": "HotChocolate.Types.IntType", "runtimeType": "System.Int32", @@ -164,8 +164,8 @@ "None: TypeKind!": "None: HotChocolate.Types.Introspection.__TypeKind", "None: DeferDirective": "None: HotChocolate.Types.DeferDirectiveType", "None: DeprecatedDirective": "None: HotChocolate.Types.DeprecatedDirectiveType", - "None: Boolean!": "None: HotChocolate.Types.BooleanType", "None: String": "None: HotChocolate.Types.StringType", + "None: Boolean!": "None: HotChocolate.Types.BooleanType", "None: Int32!": "None: HotChocolate.Types.IntType", "Output: Foo": "Output: ObjectType", "Output: Bar": "Output: ObjectType" diff --git a/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscovererTests.Register_SchemaType_ClrTypeExists.snap b/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscovererTests.Register_SchemaType_ClrTypeExists.snap index 57258a28287..0101ec302b3 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscovererTests.Register_SchemaType_ClrTypeExists.snap +++ b/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscovererTests.Register_SchemaType_ClrTypeExists.snap @@ -121,14 +121,6 @@ "Output: ObjectType" ] }, - { - "type": "HotChocolate.Types.BooleanType", - "runtimeType": "System.Boolean", - "references": [ - "None: HotChocolate.Types.BooleanType", - "None: BooleanType" - ] - }, { "type": "HotChocolate.Types.StringType", "runtimeType": "System.String", @@ -137,6 +129,14 @@ "None: StringType" ] }, + { + "type": "HotChocolate.Types.BooleanType", + "runtimeType": "System.Boolean", + "references": [ + "None: HotChocolate.Types.BooleanType", + "None: BooleanType" + ] + }, { "type": "HotChocolate.Types.IntType", "runtimeType": "System.Int32", @@ -167,8 +167,8 @@ "None: DeferDirective": "None: HotChocolate.Types.DeferDirectiveType", "None: DeprecatedDirective": "None: HotChocolate.Types.DeprecatedDirectiveType", "Output: Foo": "Output: HotChocolate.Configuration.TypeDiscovererTests+FooType", - "None: Boolean!": "None: HotChocolate.Types.BooleanType", "None: String": "None: HotChocolate.Types.StringType", + "None: Boolean!": "None: HotChocolate.Types.BooleanType", "None: Int32!": "None: HotChocolate.Types.IntType", "Output: Bar": "Output: HotChocolate.Configuration.TypeDiscovererTests+BarType" } diff --git a/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscovererTests.Upgrade_Type_From_GenericType.snap b/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscovererTests.Upgrade_Type_From_GenericType.snap index 6fdf8f8d6c8..98403ca361e 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscovererTests.Upgrade_Type_From_GenericType.snap +++ b/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscovererTests.Upgrade_Type_From_GenericType.snap @@ -121,14 +121,6 @@ "Output: ObjectType" ] }, - { - "type": "HotChocolate.Types.BooleanType", - "runtimeType": "System.Boolean", - "references": [ - "None: HotChocolate.Types.BooleanType", - "None: BooleanType" - ] - }, { "type": "HotChocolate.Types.StringType", "runtimeType": "System.String", @@ -137,6 +129,14 @@ "None: StringType" ] }, + { + "type": "HotChocolate.Types.BooleanType", + "runtimeType": "System.Boolean", + "references": [ + "None: HotChocolate.Types.BooleanType", + "None: BooleanType" + ] + }, { "type": "HotChocolate.Types.IntType", "runtimeType": "System.Int32", @@ -167,8 +167,8 @@ "None: DeferDirective": "None: HotChocolate.Types.DeferDirectiveType", "None: DeprecatedDirective": "None: HotChocolate.Types.DeprecatedDirectiveType", "Output: Foo": "Output: ObjectType", - "None: Boolean!": "None: HotChocolate.Types.BooleanType", "None: String": "None: HotChocolate.Types.StringType", + "None: Boolean!": "None: HotChocolate.Types.BooleanType", "None: Int32!": "None: HotChocolate.Types.IntType", "Output: Bar": "Output: HotChocolate.Configuration.TypeDiscovererTests+BarType" } diff --git a/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscoveryTests.InferDateTimeFromModel.snap b/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscoveryTests.InferDateTimeFromModel.snap index 5e53d45cf5e..1bb4dad3a65 100644 --- a/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscoveryTests.InferDateTimeFromModel.snap +++ b/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeDiscoveryTests.InferDateTimeFromModel.snap @@ -10,39 +10,39 @@ type Model { baz: 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 +} + "A connection to a list of items." -type ModelConnection { +type PagingConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [ModelEdge!] + edges: [PagingEdge!] "A flattened list of the nodes." nodes: [Model] } "An edge in a connection." -type ModelEdge { +type PagingEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." node: Model } -"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 { items: [Model] - paging("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): ModelConnection + paging("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): PagingConnection } "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`." diff --git a/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeInitializerTests.FactoryAndNameRefsAreRecognizedAsTheSameType.snap b/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeInitializerTests.FactoryAndNameRefsAreRecognizedAsTheSameType.snap new file mode 100644 index 00000000000..fffb30639ff --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeInitializerTests.FactoryAndNameRefsAreRecognizedAsTheSameType.snap @@ -0,0 +1,9 @@ +type Abc { + def: String +} + +"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! "Streamed when true." if: Boolean!) on FIELD diff --git a/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeInitializerTests.InitializeFactoryTypeRefOnce.snap b/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeInitializerTests.InitializeFactoryTypeRefOnce.snap new file mode 100644 index 00000000000..fffb30639ff --- /dev/null +++ b/src/HotChocolate/Core/test/Types.Tests/Configuration/__snapshots__/TypeInitializerTests.InitializeFactoryTypeRefOnce.snap @@ -0,0 +1,9 @@ +type Abc { + def: String +} + +"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! "Streamed when true." if: Boolean!) on FIELD diff --git a/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SchemaFirstTests.DescriptionsAreCorrectlyRead.snap b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SchemaFirstTests.DescriptionsAreCorrectlyRead.snap index 164286cfb9d..c791c8655a0 100644 --- a/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SchemaFirstTests.DescriptionsAreCorrectlyRead.snap +++ b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SchemaFirstTests.DescriptionsAreCorrectlyRead.snap @@ -7,231 +7,6 @@ "mutationType": null, "subscriptionType": null, "types": [ - { - "kind": "OBJECT", - "name": "Query", - "description": "Query\nLine 1\nLine 2", - "fields": [ - { - "name": "translate", - "description": "translate\nLine 1\nLine 2", - "args": [ - { - "name": "fromLanguage", - "description": "fromLanguage\nLine 1\nLine 2", - "type": { - "kind": "ENUM", - "name": "Language", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "text", - "description": "text\nLine 1\nLine 2", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "toLanguage", - "description": "toLanguage\nLine 1\nLine 2", - "type": { - "kind": "ENUM", - "name": "Language", - "ofType": null - }, - "defaultValue": null - } - ], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "Language", - "description": "Language\nLine 1\nLine 2", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { - "name": "EN", - "description": "EN\nLine 1\nLine 2", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "FR", - "description": "FR\nLine 1\nLine 2", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "CH", - "description": "CH\nLine 1\nLine 2", - "isDeprecated": false, - "deprecationReason": null - } - ], - "possibleTypes": null - }, - { - "kind": "UNION", - "name": "AandB", - "description": "AandB\nLine 1\nLine 2", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": [ - { - "kind": "OBJECT", - "name": "A", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "B", - "ofType": null - } - ] - }, - { - "kind": "INTERFACE", - "name": "C", - "description": "interface\nC\nLine 1\nLine 2", - "fields": [ - { - "name": "translate", - "description": "interface\ntranslate\nLine 1\nLine 2", - "args": [ - { - "name": "fromLanguage", - "description": "interface\nfromLanguage\nLine 1\nLine 2", - "type": { - "kind": "ENUM", - "name": "Language", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "text", - "description": "interface\ntext\nLine 1\nLine 2", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "toLanguage", - "description": "interface\ntoLanguage\nLine 1\nLine 2", - "type": { - "kind": "ENUM", - "name": "Language", - "ofType": null - }, - "defaultValue": null - } - ], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": [] - }, - { - "kind": "INPUT_OBJECT", - "name": "Foo", - "description": "foo\nLine 1\nLine 2", - "fields": null, - "inputFields": [ - { - "name": "bar", - "description": "bar\nLine 1\nLine 2", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "A", - "description": null, - "fields": [ - { - "name": "a", - "description": null, - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "B", - "description": null, - "fields": [ - { - "name": "b", - "description": null, - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, { "kind": "OBJECT", "name": "__Directive", @@ -1112,12 +887,227 @@ "possibleTypes": null }, { - "kind": "SCALAR", - "name": "Boolean", - "description": "The \u0060Boolean\u0060 scalar type represents \u0060true\u0060 or \u0060false\u0060.", + "kind": "OBJECT", + "name": "Query", + "description": "Query\nLine 1\nLine 2", + "fields": [ + { + "name": "translate", + "description": "translate\nLine 1\nLine 2", + "args": [ + { + "name": "fromLanguage", + "description": "fromLanguage\nLine 1\nLine 2", + "type": { + "kind": "ENUM", + "name": "Language", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "text", + "description": "text\nLine 1\nLine 2", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "toLanguage", + "description": "toLanguage\nLine 1\nLine 2", + "type": { + "kind": "ENUM", + "name": "Language", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "ENUM", + "name": "Language", + "description": "Language\nLine 1\nLine 2", "fields": null, "inputFields": null, "interfaces": null, + "enumValues": [ + { + "name": "EN", + "description": "EN\nLine 1\nLine 2", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "FR", + "description": "FR\nLine 1\nLine 2", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "CH", + "description": "CH\nLine 1\nLine 2", + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, + { + "kind": "UNION", + "name": "AandB", + "description": "AandB\nLine 1\nLine 2", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": [ + { + "kind": "OBJECT", + "name": "A", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "B", + "ofType": null + } + ] + }, + { + "kind": "INTERFACE", + "name": "C", + "description": "interface\nC\nLine 1\nLine 2", + "fields": [ + { + "name": "translate", + "description": "interface\ntranslate\nLine 1\nLine 2", + "args": [ + { + "name": "fromLanguage", + "description": "interface\nfromLanguage\nLine 1\nLine 2", + "type": { + "kind": "ENUM", + "name": "Language", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "text", + "description": "interface\ntext\nLine 1\nLine 2", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "toLanguage", + "description": "interface\ntoLanguage\nLine 1\nLine 2", + "type": { + "kind": "ENUM", + "name": "Language", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": [] + }, + { + "kind": "INPUT_OBJECT", + "name": "Foo", + "description": "foo\nLine 1\nLine 2", + "fields": null, + "inputFields": [ + { + "name": "bar", + "description": "bar\nLine 1\nLine 2", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "A", + "description": null, + "fields": [ + { + "name": "a", + "description": null, + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "B", + "description": null, + "fields": [ + { + "name": "b", + "description": null, + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], "enumValues": null, "possibleTypes": null }, @@ -1131,6 +1121,16 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "SCALAR", + "name": "Boolean", + "description": "The \u0060Boolean\u0060 scalar type represents \u0060true\u0060 or \u0060false\u0060.", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, { "kind": "SCALAR", "name": "Int", diff --git a/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SchemaFirstTests.Interfaces_Impl_Interfaces_Are_Correctly_Exposed_Through_Introspection.snap b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SchemaFirstTests.Interfaces_Impl_Interfaces_Are_Correctly_Exposed_Through_Introspection.snap index 89d92906e43..99edb8a2c6d 100644 --- a/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SchemaFirstTests.Interfaces_Impl_Interfaces_Are_Correctly_Exposed_Through_Introspection.snap +++ b/src/HotChocolate/Core/test/Types.Tests/__snapshots__/SchemaFirstTests.Interfaces_Impl_Interfaces_Are_Correctly_Exposed_Through_Introspection.snap @@ -7,127 +7,6 @@ "mutationType": null, "subscriptionType": null, "types": [ - { - "kind": "OBJECT", - "name": "Query", - "description": null, - "fields": [ - { - "name": "c", - "description": null, - "args": [], - "type": { - "kind": "OBJECT", - "name": "C", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INTERFACE", - "name": "A", - "description": null, - "fields": [ - { - "name": "a", - "description": null, - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": [ - { - "kind": "OBJECT", - "name": "C", - "ofType": null - } - ] - }, - { - "kind": "INTERFACE", - "name": "B", - "description": null, - "fields": [ - { - "name": "a", - "description": null, - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "A", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": [ - { - "kind": "OBJECT", - "name": "C", - "ofType": null - } - ] - }, - { - "kind": "OBJECT", - "name": "C", - "description": null, - "fields": [ - { - "name": "a", - "description": null, - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "A", - "ofType": null - }, - { - "kind": "INTERFACE", - "name": "B", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, { "kind": "OBJECT", "name": "__Directive", @@ -1007,10 +886,131 @@ ], "possibleTypes": null }, + { + "kind": "OBJECT", + "name": "Query", + "description": null, + "fields": [ + { + "name": "c", + "description": null, + "args": [], + "type": { + "kind": "OBJECT", + "name": "C", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INTERFACE", + "name": "A", + "description": null, + "fields": [ + { + "name": "a", + "description": null, + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": [ + { + "kind": "OBJECT", + "name": "C", + "ofType": null + } + ] + }, + { + "kind": "INTERFACE", + "name": "B", + "description": null, + "fields": [ + { + "name": "a", + "description": null, + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "A", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": [ + { + "kind": "OBJECT", + "name": "C", + "ofType": null + } + ] + }, + { + "kind": "OBJECT", + "name": "C", + "description": null, + "fields": [ + { + "name": "a", + "description": null, + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "A", + "ofType": null + }, + { + "kind": "INTERFACE", + "name": "B", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, { "kind": "SCALAR", - "name": "Boolean", - "description": "The \u0060Boolean\u0060 scalar type represents \u0060true\u0060 or \u0060false\u0060.", + "name": "String", + "description": "The \u0060String\u0060 scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.", "fields": null, "inputFields": null, "interfaces": null, @@ -1019,8 +1019,8 @@ }, { "kind": "SCALAR", - "name": "String", - "description": "The \u0060String\u0060 scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.", + "name": "Boolean", + "description": "The \u0060Boolean\u0060 scalar type represents \u0060true\u0060 or \u0060false\u0060.", "fields": null, "inputFields": null, "interfaces": null, diff --git a/src/HotChocolate/Core/test/Validation.Tests/__resources__/StarWars_Request.graphql b/src/HotChocolate/Core/test/Validation.Tests/__resources__/StarWars_Request.graphql index 1d06357f9bb..963b7c24539 100644 --- a/src/HotChocolate/Core/test/Validation.Tests/__resources__/StarWars_Request.graphql +++ b/src/HotChocolate/Core/test/Validation.Tests/__resources__/StarWars_Request.graphql @@ -51,7 +51,7 @@ fragment Starship on Starship { name } -fragment Friend on CharacterConnection { +fragment Friend on FriendsConnection { nodes { ...HasName friends { diff --git a/src/HotChocolate/Data/src/Data/Filters/Extensions/FilterObjectFieldDescriptorExtensions.cs b/src/HotChocolate/Data/src/Data/Filters/Extensions/FilterObjectFieldDescriptorExtensions.cs index 1c23c4bb1a5..cc55acaa15a 100644 --- a/src/HotChocolate/Data/src/Data/Filters/Extensions/FilterObjectFieldDescriptorExtensions.cs +++ b/src/HotChocolate/Data/src/Data/Filters/Extensions/FilterObjectFieldDescriptorExtensions.cs @@ -177,31 +177,26 @@ private static IObjectFieldDescriptor UseFiltering( definition.Arguments.Add(argumentDefinition); definition.Configurations.Add( - LazyTypeConfigurationBuilder - .New() - .Definition(definition) - .Configure( - (ctx, d) => - CompileMiddleware( - ctx, - d, - argumentTypeReference, - placeholder, - scope)) - .On(ApplyConfigurationOn.Completion) - .DependsOn(argumentTypeReference, true) - .Build()); + new CompleteConfiguration( + (ctx, d) => + CompileMiddleware( + ctx, + d, + argumentTypeReference, + placeholder, + scope), + definition, + ApplyConfigurationOn.Completion, + argumentTypeReference, + TypeDependencyKind.Completed)); argumentDefinition.Configurations.Add( - LazyTypeConfigurationBuilder - .New() - .Definition(argumentDefinition) - .Configure( - (context, argDef) => - argDef.Name = - context.GetFilterConvention(scope).GetArgumentName()) - .On(ApplyConfigurationOn.Naming) - .Build()); + new CompleteConfiguration( + (context, argDef) => + argDef.Name = + context.GetFilterConvention(scope).GetArgumentName(), + argumentDefinition, + ApplyConfigurationOn.Naming)); }); return descriptor; diff --git a/src/HotChocolate/Data/src/Data/Projections/Extensions/SelectionObjectFieldDescriptorExtensions.cs b/src/HotChocolate/Data/src/Data/Projections/Extensions/SelectionObjectFieldDescriptorExtensions.cs index eab435b1569..56edb417783 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Extensions/SelectionObjectFieldDescriptorExtensions.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Extensions/SelectionObjectFieldDescriptorExtensions.cs @@ -4,7 +4,6 @@ using HotChocolate.Data; using HotChocolate.Internal; using HotChocolate.Resolvers; -using HotChocolate.Types.Descriptors; using HotChocolate.Types.Descriptors.Definitions; using HotChocolate.Data.Projections; using static HotChocolate.Data.Projections.ProjectionProvider; @@ -150,21 +149,17 @@ public static IObjectFieldDescriptor UseProjection( selectionType = typeInfo.NamedType; } - ILazyTypeConfiguration lazyConfiguration = - LazyTypeConfigurationBuilder - .New() - .Definition(definition) - .Configure( - (c, d) => - CompileMiddleware( - selectionType, - d, - placeholder, - c, - scope)) - .On(ApplyConfigurationOn.Completion) - .Build(); - definition.Configurations.Add(lazyConfiguration); + definition.Configurations.Add( + new CompleteConfiguration( + (c, d) => + CompileMiddleware( + selectionType, + d, + placeholder, + c, + scope), + definition, + ApplyConfigurationOn.Completion)); }); return descriptor; diff --git a/src/HotChocolate/Data/src/Data/Projections/Extensions/SingleOrDefaultObjectFieldDescriptorExtensions.cs b/src/HotChocolate/Data/src/Data/Projections/Extensions/SingleOrDefaultObjectFieldDescriptorExtensions.cs index 8719aa603d9..9baa2a5ee44 100644 --- a/src/HotChocolate/Data/src/Data/Projections/Extensions/SingleOrDefaultObjectFieldDescriptorExtensions.cs +++ b/src/HotChocolate/Data/src/Data/Projections/Extensions/SingleOrDefaultObjectFieldDescriptorExtensions.cs @@ -1,11 +1,8 @@ using System; -using System.Threading.Tasks; using HotChocolate.Internal; using HotChocolate.Resolvers; -using HotChocolate.Types.Descriptors; using HotChocolate.Types.Descriptors.Definitions; using HotChocolate.Data.Projections; -using HotChocolate.Language; namespace HotChocolate.Types { @@ -59,23 +56,18 @@ private static IObjectFieldDescriptor ApplyMiddleware( definition.ResultType = selectionType; definition.Type = context.TypeInspector.GetTypeRef(selectionType); - ILazyTypeConfiguration lazyConfiguration = - LazyTypeConfigurationBuilder - .New() - .Definition(definition) - .Configure( - (_, definition) => - { - CompileMiddleware( - selectionType, - definition, - placeholder, - middlewareDefinition); - }) - .On(ApplyConfigurationOn.Completion) - .Build(); - - definition.Configurations.Add(lazyConfiguration); + definition.Configurations.Add( + new CompleteConfiguration( + (_, d) => + { + CompileMiddleware( + selectionType, + d, + placeholder, + middlewareDefinition); + }, + definition, + ApplyConfigurationOn.Completion)); }); return descriptor; diff --git a/src/HotChocolate/Data/src/Data/Sorting/Extensions/SortObjectFieldDescriptorExtensions.cs b/src/HotChocolate/Data/src/Data/Sorting/Extensions/SortObjectFieldDescriptorExtensions.cs index bc9c0563efd..dd75b3f3ee7 100644 --- a/src/HotChocolate/Data/src/Data/Sorting/Extensions/SortObjectFieldDescriptorExtensions.cs +++ b/src/HotChocolate/Data/src/Data/Sorting/Extensions/SortObjectFieldDescriptorExtensions.cs @@ -152,31 +152,26 @@ private static IObjectFieldDescriptor UseSortingInternal( definition.Arguments.Add(argumentDefinition); definition.Configurations.Add( - LazyTypeConfigurationBuilder - .New() - .Definition(definition) - .Configure( - (context, def) => - CompileMiddleware( - context, - def, - argumentTypeReference, - placeholder, - scope)) - .On(ApplyConfigurationOn.Completion) - .DependsOn(argumentTypeReference, true) - .Build()); + new CompleteConfiguration( + (context, def) => + CompileMiddleware( + context, + def, + argumentTypeReference, + placeholder, + scope), + definition, + ApplyConfigurationOn.Completion, + argumentTypeReference, + TypeDependencyKind.Completed)); argumentDefinition.Configurations.Add( - LazyTypeConfigurationBuilder - .New() - .Definition(argumentDefinition) - .Configure( - (context, argDef) => - argDef.Name = - context.GetSortConvention(scope).GetArgumentName()) - .On(ApplyConfigurationOn.Naming) - .Build()); + new CompleteConfiguration( + (context, argDef) => + argDef.Name = + context.GetSortConvention(scope).GetArgumentName(), + argumentDefinition, + ApplyConfigurationOn.Naming)); }); return descriptor; diff --git a/src/HotChocolate/Data/test/Data.EntityFramework.Tests/__snapshots__/UseDbContextTests.Infer_Schema_From_IQueryable_Fields.snap b/src/HotChocolate/Data/test/Data.EntityFramework.Tests/__snapshots__/UseDbContextTests.Infer_Schema_From_IQueryable_Fields.snap index dbba9242a99..064f9e7ee23 100644 --- a/src/HotChocolate/Data/test/Data.EntityFramework.Tests/__snapshots__/UseDbContextTests.Infer_Schema_From_IQueryable_Fields.snap +++ b/src/HotChocolate/Data/test/Data.EntityFramework.Tests/__snapshots__/UseDbContextTests.Infer_Schema_From_IQueryable_Fields.snap @@ -16,18 +16,18 @@ type AuthorCollectionSegment { } "A connection to a list of items." -type AuthorConnection { +type AuthorCursorPagingExecutableConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [AuthorEdge!] + edges: [AuthorCursorPagingExecutableEdge!] "A flattened list of the nodes." nodes: [Author!] totalCount: Int! } "An edge in a connection." -type AuthorEdge { +type AuthorCursorPagingExecutableEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." @@ -67,7 +67,7 @@ type Query { authorSync: Author authorOffsetPaging(skip: Int take: Int where: AuthorFilterInput order: [AuthorSortInput!]): AuthorCollectionSegment authorOffsetPagingExecutable(skip: Int take: Int where: AuthorFilterInput order: [AuthorSortInput!]): AuthorCollectionSegment - authorCursorPagingExecutable("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: AuthorFilterInput order: [AuthorSortInput!]): AuthorConnection + authorCursorPagingExecutable("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: AuthorFilterInput order: [AuthorSortInput!]): AuthorCursorPagingExecutableConnection } input AuthorFilterInput { diff --git a/src/HotChocolate/Data/test/Data.EntityFramework.Tests/__snapshots__/UseDbContextTests.Infer_Schema_From_IQueryable_Task_Fields.snap b/src/HotChocolate/Data/test/Data.EntityFramework.Tests/__snapshots__/UseDbContextTests.Infer_Schema_From_IQueryable_Task_Fields.snap index 30a62c57e26..bf23f67e674 100644 --- a/src/HotChocolate/Data/test/Data.EntityFramework.Tests/__snapshots__/UseDbContextTests.Infer_Schema_From_IQueryable_Task_Fields.snap +++ b/src/HotChocolate/Data/test/Data.EntityFramework.Tests/__snapshots__/UseDbContextTests.Infer_Schema_From_IQueryable_Task_Fields.snap @@ -16,18 +16,18 @@ type AuthorCollectionSegment { } "A connection to a list of items." -type AuthorConnection { +type AuthorCursorPagingExecutableConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [AuthorEdge!] + edges: [AuthorCursorPagingExecutableEdge!] "A flattened list of the nodes." nodes: [Author!] totalCount: Int! } "An edge in a connection." -type AuthorEdge { +type AuthorCursorPagingExecutableEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." @@ -65,7 +65,7 @@ type QueryTask { authors: [Author!]! authorOffsetPaging(skip: Int take: Int where: AuthorFilterInput order: [AuthorSortInput!]): AuthorCollectionSegment authorOffsetPagingExecutable(skip: Int take: Int where: AuthorFilterInput order: [AuthorSortInput!]): AuthorCollectionSegment - authorCursorPagingExecutable("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: AuthorFilterInput order: [AuthorSortInput!]): AuthorConnection + authorCursorPagingExecutable("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: AuthorFilterInput order: [AuthorSortInput!]): AuthorCursorPagingExecutableConnection } input AuthorFilterInput { diff --git a/src/HotChocolate/Data/test/Data.EntityFramework.Tests/__snapshots__/UseDbContextTests.Infer_Schema_From_IQueryable_ValueTask_Fields.snap b/src/HotChocolate/Data/test/Data.EntityFramework.Tests/__snapshots__/UseDbContextTests.Infer_Schema_From_IQueryable_ValueTask_Fields.snap index 88e0e74aa1e..38c25884a67 100644 --- a/src/HotChocolate/Data/test/Data.EntityFramework.Tests/__snapshots__/UseDbContextTests.Infer_Schema_From_IQueryable_ValueTask_Fields.snap +++ b/src/HotChocolate/Data/test/Data.EntityFramework.Tests/__snapshots__/UseDbContextTests.Infer_Schema_From_IQueryable_ValueTask_Fields.snap @@ -16,18 +16,18 @@ type AuthorCollectionSegment { } "A connection to a list of items." -type AuthorConnection { +type AuthorCursorPagingExecutableConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [AuthorEdge!] + edges: [AuthorCursorPagingExecutableEdge!] "A flattened list of the nodes." nodes: [Author!] totalCount: Int! } "An edge in a connection." -type AuthorEdge { +type AuthorCursorPagingExecutableEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." @@ -65,7 +65,7 @@ type QueryValueTask { authors: [Author!]! authorOffsetPaging(skip: Int take: Int where: AuthorFilterInput order: [AuthorSortInput!]): AuthorCollectionSegment authorOffsetPagingExecutable(skip: Int take: Int where: AuthorFilterInput order: [AuthorSortInput!]): AuthorCollectionSegment - authorCursorPagingExecutable("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: AuthorFilterInput order: [AuthorSortInput!]): AuthorConnection + authorCursorPagingExecutable("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: AuthorFilterInput order: [AuthorSortInput!]): AuthorCursorPagingExecutableConnection } input AuthorFilterInput { diff --git a/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.CreateSchema_OnDifferentScope_Schema.snap b/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.CreateSchema_OnDifferentScope_Schema.snap index 1d61593827a..6c1a0d8ac8a 100644 --- a/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.CreateSchema_OnDifferentScope_Schema.snap +++ b/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.CreateSchema_OnDifferentScope_Schema.snap @@ -16,17 +16,17 @@ type Book { } "A connection to a list of items." -type BookConnection { +type BooksConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [BookEdge!] + edges: [BooksEdge!] "A flattened list of the nodes." nodes: [Book!] } "An edge in a connection." -type BookEdge { +type BooksEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." @@ -34,7 +34,7 @@ type BookEdge { } type DifferentScope { - 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: Foo_BookFilterInput order: [Foo_BookSortInput!]): BookConnection + 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: Foo_BookFilterInput order: [Foo_BookSortInput!]): BooksConnection } "Information about pagination in a connection." diff --git a/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.ExecuteAsync_Should_ProjectAndPage_When_AliasIsSameAsAlwaysProjectedField_Schema.snap b/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.ExecuteAsync_Should_ProjectAndPage_When_AliasIsSameAsAlwaysProjectedField_Schema.snap index 8af21405a5d..dc16f831820 100644 --- a/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.ExecuteAsync_Should_ProjectAndPage_When_AliasIsSameAsAlwaysProjectedField_Schema.snap +++ b/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.ExecuteAsync_Should_ProjectAndPage_When_AliasIsSameAsAlwaysProjectedField_Schema.snap @@ -21,17 +21,17 @@ type Book implements Node { } "A connection to a list of items." -type BookConnection { +type BooksConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [BookEdge!] + edges: [BooksEdge!] "A flattened list of the nodes." nodes: [Book!] } "An edge in a connection." -type BookEdge { +type BooksEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." @@ -55,7 +55,7 @@ type Query { node("ID of the object." id: ID!): Node "Lookup nodes by a list of IDs." nodes("The list of node IDs." ids: [ID!]!): [Node]! - 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!]): BookConnection + 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 } input AuthorFilterInput { diff --git a/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.ExecuteAsync_Should_ProjectAndPage_When_NodesFragmentContainsProjectedField_With_Extensions_Schema.snap b/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.ExecuteAsync_Should_ProjectAndPage_When_NodesFragmentContainsProjectedField_With_Extensions_Schema.snap index 8af21405a5d..dc16f831820 100644 --- a/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.ExecuteAsync_Should_ProjectAndPage_When_NodesFragmentContainsProjectedField_With_Extensions_Schema.snap +++ b/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.ExecuteAsync_Should_ProjectAndPage_When_NodesFragmentContainsProjectedField_With_Extensions_Schema.snap @@ -21,17 +21,17 @@ type Book implements Node { } "A connection to a list of items." -type BookConnection { +type BooksConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [BookEdge!] + edges: [BooksEdge!] "A flattened list of the nodes." nodes: [Book!] } "An edge in a connection." -type BookEdge { +type BooksEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." @@ -55,7 +55,7 @@ type Query { node("ID of the object." id: ID!): Node "Lookup nodes by a list of IDs." nodes("The list of node IDs." ids: [ID!]!): [Node]! - 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!]): BookConnection + 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 } input AuthorFilterInput { diff --git a/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.Execute_And_OnRoot_Reverse_Schema.snap b/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.Execute_And_OnRoot_Reverse_Schema.snap index 1d61593827a..6c1a0d8ac8a 100644 --- a/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.Execute_And_OnRoot_Reverse_Schema.snap +++ b/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.Execute_And_OnRoot_Reverse_Schema.snap @@ -16,17 +16,17 @@ type Book { } "A connection to a list of items." -type BookConnection { +type BooksConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [BookEdge!] + edges: [BooksEdge!] "A flattened list of the nodes." nodes: [Book!] } "An edge in a connection." -type BookEdge { +type BooksEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." @@ -34,7 +34,7 @@ type BookEdge { } type DifferentScope { - 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: Foo_BookFilterInput order: [Foo_BookSortInput!]): BookConnection + 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: Foo_BookFilterInput order: [Foo_BookSortInput!]): BooksConnection } "Information about pagination in a connection." diff --git a/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.Execute_And_OnRoot_Schema.snap b/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.Execute_And_OnRoot_Schema.snap index 1d61593827a..6c1a0d8ac8a 100644 --- a/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.Execute_And_OnRoot_Schema.snap +++ b/src/HotChocolate/Data/test/Data.Tests/__snapshots__/IntegrationTests.Execute_And_OnRoot_Schema.snap @@ -16,17 +16,17 @@ type Book { } "A connection to a list of items." -type BookConnection { +type BooksConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [BookEdge!] + edges: [BooksEdge!] "A flattened list of the nodes." nodes: [Book!] } "An edge in a connection." -type BookEdge { +type BooksEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." @@ -34,7 +34,7 @@ type BookEdge { } type DifferentScope { - 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: Foo_BookFilterInput order: [Foo_BookSortInput!]): BookConnection + 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: Foo_BookFilterInput order: [Foo_BookSortInput!]): BooksConnection } "Information about pagination in a connection." diff --git a/src/HotChocolate/Data/test/Data.Tests/__snapshots__/TypeValidationTests.EnsureCorrectlyOrderedMiddlewarePassValidation.snap b/src/HotChocolate/Data/test/Data.Tests/__snapshots__/TypeValidationTests.EnsureCorrectlyOrderedMiddlewarePassValidation.snap index 0ecccdb84b5..323c6822c40 100644 --- a/src/HotChocolate/Data/test/Data.Tests/__snapshots__/TypeValidationTests.EnsureCorrectlyOrderedMiddlewarePassValidation.snap +++ b/src/HotChocolate/Data/test/Data.Tests/__snapshots__/TypeValidationTests.EnsureCorrectlyOrderedMiddlewarePassValidation.snap @@ -2,32 +2,32 @@ query: CorrectMiddlewarePipeline } -type CorrectMiddlewarePipeline { - bars("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: FooFilterInput order: [FooSortInput!]): FooConnection -} - -type Foo { - bar: String -} - "A connection to a list of items." -type FooConnection { +type BarsConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [FooEdge!] + edges: [BarsEdge!] "A flattened list of the nodes." nodes: [Foo!] } "An edge in a connection." -type FooEdge { +type BarsEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." node: Foo! } +type CorrectMiddlewarePipeline { + bars("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: FooFilterInput order: [FooSortInput!]): BarsConnection +} + +type Foo { + bar: String +} + "Information about pagination in a connection." type PageInfo { "Indicates whether more edges exist following the set defined by the clients arguments." diff --git a/src/HotChocolate/Filters/src/Types.Filters/Extensions/FilterObjectFieldDescriptorExtensions.cs b/src/HotChocolate/Filters/src/Types.Filters/Extensions/FilterObjectFieldDescriptorExtensions.cs index 10c7759e9cb..a0956ff48f3 100644 --- a/src/HotChocolate/Filters/src/Types.Filters/Extensions/FilterObjectFieldDescriptorExtensions.cs +++ b/src/HotChocolate/Filters/src/Types.Filters/Extensions/FilterObjectFieldDescriptorExtensions.cs @@ -125,20 +125,19 @@ private static IObjectFieldDescriptor UseFiltering( argumentDefinition.ConfigureArgumentName(); definition.Arguments.Add(argumentDefinition); - ILazyTypeConfiguration lazyConfiguration = - LazyTypeConfigurationBuilder - .New() - .Definition(definition) - .Configure((context, d) => - CompileMiddleware( - context, - d, - argumentTypeReference, - placeholder)) - .On(ApplyConfigurationOn.Completion) - .DependsOn(argumentTypeReference, true) - .Build(); - definition.Configurations.Add(lazyConfiguration); + var fieldConfig = new CompleteConfiguration( + (context, d) => + CompileMiddleware( + context, + d, + argumentTypeReference, + placeholder), + definition, + ApplyConfigurationOn.Completion, + argumentTypeReference, + TypeDependencyKind.Completed); + + definition.Configurations.Add(fieldConfig); }); return descriptor; @@ -205,20 +204,17 @@ private static IDescriptorExtension ConfigureArgumentName( private static ArgumentDefinition ConfigureArgumentName( this ArgumentDefinition definition) { - ILazyTypeConfiguration lazyArgumentConfiguration = - LazyTypeConfigurationBuilder - .New() - .Definition(definition) - .Configure((context, d) => - { - IFilterNamingConvention convention = - context.DescriptorContext.GetFilterNamingConvention(); - d.Name = convention.ArgumentName; - }) - .On(ApplyConfigurationOn.Completion) - .Build(); - - definition.Configurations.Add(lazyArgumentConfiguration); + var argumentConfig = new CompleteConfiguration( + (context, d) => + { + IFilterNamingConvention convention = + context.DescriptorContext.GetFilterNamingConvention(); + d.Name = convention.ArgumentName; + }, + definition, + ApplyConfigurationOn.Completion); + + definition.Configurations.Add(argumentConfig); return definition; } } diff --git a/src/HotChocolate/Filters/src/Types.Sorting/Extensions/SortObjectFieldDescriptorExtensions.cs b/src/HotChocolate/Filters/src/Types.Sorting/Extensions/SortObjectFieldDescriptorExtensions.cs index 062e22bf507..b5a0bd90fea 100644 --- a/src/HotChocolate/Filters/src/Types.Sorting/Extensions/SortObjectFieldDescriptorExtensions.cs +++ b/src/HotChocolate/Filters/src/Types.Sorting/Extensions/SortObjectFieldDescriptorExtensions.cs @@ -95,36 +95,30 @@ public static IObjectFieldDescriptor UseSorting( Type = c.TypeInspector.GetTypeRef(argumentType, TypeContext.Input) }; - ILazyTypeConfiguration lazyArgumentConfiguration = - LazyTypeConfigurationBuilder - .New() - .Definition(argumentDefinition) - .Configure((context, d) => - { - ISortingNamingConvention convention = - context.DescriptorContext.GetSortingNamingConvention(); - d.Name = convention.ArgumentName; - }) - .On(ApplyConfigurationOn.Completion) - .Build(); - - argumentDefinition.Configurations.Add(lazyArgumentConfiguration); - definition.Arguments.Add(argumentDefinition); + argumentDefinition.Configurations.Add( + new CompleteConfiguration( + (context, d) => + { + ISortingNamingConvention convention = + context.DescriptorContext.GetSortingNamingConvention(); + d.Name = convention.ArgumentName; + }, + argumentDefinition, + ApplyConfigurationOn.Completion)); - ILazyTypeConfiguration lazyConfiguration = - LazyTypeConfigurationBuilder - .New() - .Definition(definition) - .Configure((context, d) => + definition.Arguments.Add(argumentDefinition); + definition.Configurations.Add( + new CompleteConfiguration( + (context, d) => CompileMiddleware( context, d, argumentTypeReference, - placeholder)) - .On(ApplyConfigurationOn.Completion) - .DependsOn(argumentTypeReference, true) - .Build(); - definition.Configurations.Add(lazyConfiguration); + placeholder), + definition, + ApplyConfigurationOn.Completion, + argumentTypeReference, + TypeDependencyKind.Completed)); }); return descriptor; diff --git a/src/HotChocolate/MongoDb/src/Data/Extensions/MongoDbDataRequestBuilderExtensions.cs b/src/HotChocolate/MongoDb/src/Data/Extensions/MongoDbDataRequestBuilderExtensions.cs index e2e62e71d98..11f4080aee7 100644 --- a/src/HotChocolate/MongoDb/src/Data/Extensions/MongoDbDataRequestBuilderExtensions.cs +++ b/src/HotChocolate/MongoDb/src/Data/Extensions/MongoDbDataRequestBuilderExtensions.cs @@ -1,5 +1,8 @@ +using System; using HotChocolate.Execution.Configuration; using HotChocolate.Data.MongoDb; +using HotChocolate.Data.MongoDb.Paging; +using HotChocolate.Types.Pagination; namespace Microsoft.Extensions.DependencyInjection { @@ -14,6 +17,7 @@ public static class MongoDbDataRequestBuilderExtensions /// /// The . /// + /// /// /// Returns the . /// @@ -28,6 +32,7 @@ public static IRequestExecutorBuilder AddMongoDbFiltering( /// /// The . /// + /// /// /// Returns the . /// @@ -42,6 +47,7 @@ public static IRequestExecutorBuilder AddMongoDbSorting( /// /// The . /// + /// /// /// Returns the . /// @@ -49,5 +55,41 @@ public static IRequestExecutorBuilder AddMongoDbProjections( this IRequestExecutorBuilder builder, string? name = null) => builder.ConfigureSchema(s => s.AddMongoDbProjections(name)); + + /// + /// Adds the MongoDB cursor and offset paging providers. + /// + /// + /// The GraphQL configuration builder. + /// + /// + /// The name which shall be used to refer to this registration. + /// + /// + /// Defines if these providers shall be registered as default providers. + /// + /// + /// Returns the GraphQL configuration builder for further configuration chaining. + /// + public static IRequestExecutorBuilder AddMongoDbPagingProviders( + this IRequestExecutorBuilder builder, + string? providerName = null, + bool defaultProvider = false) + { + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + builder.AddCursorPagingProvider( + providerName, + defaultProvider); + + builder.AddOffsetPagingProvider( + providerName, + defaultProvider); + + return builder; + } } } diff --git a/src/HotChocolate/MongoDb/src/Data/Extensions/SchemaBuilderExtensions.cs b/src/HotChocolate/MongoDb/src/Data/Extensions/SchemaBuilderExtensions.cs index a404a4fb8cc..a2b4b8c8249 100644 --- a/src/HotChocolate/MongoDb/src/Data/Extensions/SchemaBuilderExtensions.cs +++ b/src/HotChocolate/MongoDb/src/Data/Extensions/SchemaBuilderExtensions.cs @@ -14,6 +14,7 @@ public static class MongoDbSchemaBuilderExtensions /// /// The . /// + /// /// /// Returns the . /// @@ -28,6 +29,7 @@ public static ISchemaBuilder AddMongoDbFiltering( /// /// The . /// + /// /// /// Returns the . /// @@ -42,6 +44,7 @@ public static ISchemaBuilder AddMongoDbSorting( /// /// The . /// + /// /// /// Returns the . /// diff --git a/src/HotChocolate/MongoDb/src/Data/Paging/AggregateFluentPagingContainer.cs b/src/HotChocolate/MongoDb/src/Data/Paging/AggregateFluentPagingContainer.cs index f8608614cf4..ec11bb06212 100644 --- a/src/HotChocolate/MongoDb/src/Data/Paging/AggregateFluentPagingContainer.cs +++ b/src/HotChocolate/MongoDb/src/Data/Paging/AggregateFluentPagingContainer.cs @@ -8,7 +8,7 @@ namespace HotChocolate.Data.MongoDb.Paging { internal class AggregateFluentPagingContainer : IMongoPagingContainer { - public readonly IAggregateFluent _source; + private readonly IAggregateFluent _source; private readonly IAggregateFluent _countSource; public AggregateFluentPagingContainer(IAggregateFluent source) diff --git a/src/HotChocolate/MongoDb/src/Data/Paging/MongoPagingObjectFieldDescriptorExtensions.cs b/src/HotChocolate/MongoDb/src/Data/Paging/MongoPagingObjectFieldDescriptorExtensions.cs deleted file mode 100644 index 4be92177ff7..00000000000 --- a/src/HotChocolate/MongoDb/src/Data/Paging/MongoPagingObjectFieldDescriptorExtensions.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System; -using HotChocolate.Data.MongoDb.Paging; -using HotChocolate.Types.Pagination; -using Microsoft.Extensions.DependencyInjection; - -namespace HotChocolate.Types -{ - public static class MongoPagingObjectFieldDescriptorExtensions - { - /// - /// Adds cursor pagination support to the field. Rewrites the type to a connection type and - /// registers the mongo pagination handler - /// - /// The descriptor of the field - /// The options for pagination - /// - /// The schema type of the entity. Not a connection type - /// - /// The type of the entity - /// The - public static IObjectFieldDescriptor UseMongoDbPaging( - this IObjectFieldDescriptor descriptor, - PagingOptions options = default) - where TSchemaType : class, IOutputType => - UseMongoDbPaging(descriptor, typeof(TEntity), options); - - /// - /// Adds cursor pagination support to the field. Rewrites the type to a connection type and - /// registers the mongo pagination handler - /// - /// The descriptor of the field - /// The options for pagination - /// - /// The schema type of the entity. Not a connection type - /// - /// The - public static IObjectFieldDescriptor UseMongoDbPaging( - this IObjectFieldDescriptor descriptor, - Type? entityType = null, - PagingOptions options = default) - where TSchemaType : class, IOutputType => - UseMongoDbPaging(descriptor, typeof(TSchemaType), entityType, options); - - /// - /// Adds cursor pagination support to the field. Rewrites the type to a connection type and - /// registers the mongo pagination handler - /// - /// The descriptor of the field - /// - /// The schema type of the entity. Not a connection type - /// - /// The type of the entity - /// The options for pagination - /// The - public static IObjectFieldDescriptor UseMongoDbPaging( - this IObjectFieldDescriptor descriptor, - Type? type = null, - Type? entityType = null, - PagingOptions options = default) => - descriptor.UsePaging( - type, - entityType, - (services, sourceType) => services.GetService() ?? - new MongoDbCursorPagingProvider(), - options); - - /// - /// Adds offset pagination support to the field. Rewrites the type to a connection type and - /// registers the mongo pagination handler - /// - /// The descriptor of the field - /// - /// The schema type of the entity. Not a connection type - /// - /// The type of the entity - /// The options for pagination - /// The - public static IObjectFieldDescriptor UseMongoDbOffsetPaging( - this IObjectFieldDescriptor descriptor, - Type? type = null, - Type? entityType = null, - PagingOptions options = default) => - descriptor.UseOffsetPaging( - type, - entityType, - (services, sourceType) => services.GetService() ?? - new MongoDbOffsetPagingProvider(), - options); - - /// - /// Adds offset pagination support to the field. Rewrites the type to a connection type and - /// registers the mongo pagination handler - /// - /// The descriptor of the field - /// The type of the entity - /// The options for pagination - /// - /// The schema type of the entity. Not a connection type - /// - /// The - public static IObjectFieldDescriptor UseMongoDbOffsetPaging( - this IObjectFieldDescriptor descriptor, - Type? entityType = null, - PagingOptions options = default) - where TSchemaType : IOutputType => - UseMongoDbOffsetPaging( - descriptor, - typeof(TSchemaType), - entityType, - options); - - /// - /// Adds offset pagination support to the field. Rewrites the type to a connection type and - /// registers the mongo pagination handler - /// - /// The descriptor of the field - /// The options for pagination - /// - /// The schema type of the entity. Not a connection type - /// - /// The type of the entity - /// The - public static IObjectFieldDescriptor UseMongoDbOffsetPaging( - this IObjectFieldDescriptor descriptor, - PagingOptions options = default) - where TSchemaType : class, IOutputType => - UseMongoDbOffsetPaging(descriptor, typeof(TEntity), options); - } -} diff --git a/src/HotChocolate/MongoDb/src/Data/Paging/UseMongoDbOffsetPagingAttribute.cs b/src/HotChocolate/MongoDb/src/Data/Paging/UseMongoDbOffsetPagingAttribute.cs deleted file mode 100644 index 8ae2f8b4356..00000000000 --- a/src/HotChocolate/MongoDb/src/Data/Paging/UseMongoDbOffsetPagingAttribute.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Reflection; -using HotChocolate.Types.Descriptors; -using HotChocolate.Types.Pagination; - -namespace HotChocolate.Types -{ - /// - /// This attribute adds the offset paging middleware for mongodb to the annotated method or - /// property. - /// - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] - public class UseMongoDbOffsetPagingAttribute : DescriptorAttribute - { - private int? _defaultPageSize; - private int? _maxPageSize; - private bool? _includeTotalCount; - - /// - /// Applies the offset paging middleware to the annotated property. - /// - /// - /// The schema type representing the item type. - /// - public UseMongoDbOffsetPagingAttribute(Type? type = null) - { - Type = type; - } - - /// - /// The schema type representation of the item type. - /// - public Type? Type { get; } - - /// - /// Specifies the default page size for this field. - /// - public int DefaultPageSize - { - get => _defaultPageSize ?? PagingDefaults.DefaultPageSize; - set => _defaultPageSize = value; - } - - /// - /// Specifies the maximum allowed page size. - /// - public int MaxPageSize - { - get => _maxPageSize ?? PagingDefaults.MaxPageSize; - set => _maxPageSize = value; - } - - /// - /// Include the total count field to the result type. - /// - public bool IncludeTotalCount - { - get => _includeTotalCount ?? PagingDefaults.IncludeTotalCount; - set => _includeTotalCount = value; - } - - /// - protected override void TryConfigure( - IDescriptorContext context, - IDescriptor descriptor, - ICustomAttributeProvider element) - { - if (descriptor is IObjectFieldDescriptor odf) - { - odf.UseMongoDbOffsetPaging( - Type, - options: new PagingOptions - { - DefaultPageSize = _defaultPageSize, - MaxPageSize = _maxPageSize, - IncludeTotalCount = _includeTotalCount - }); - } - } - } -} diff --git a/src/HotChocolate/MongoDb/src/Data/Paging/UseMongoDbPagingAttribute.cs b/src/HotChocolate/MongoDb/src/Data/Paging/UseMongoDbPagingAttribute.cs deleted file mode 100644 index 9fe405c90e5..00000000000 --- a/src/HotChocolate/MongoDb/src/Data/Paging/UseMongoDbPagingAttribute.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Reflection; -using HotChocolate.Types.Descriptors; -using HotChocolate.Types.Pagination; - -namespace HotChocolate.Types -{ - /// - /// This attribute adds the cursor paging middleware for mongodb to the annotated method or - /// property - /// - public sealed class UseMongoDbPagingAttribute : DescriptorAttribute - { - private int? _defaultPageSize; - private int? _maxPageSize; - private bool? _includeTotalCount; - - /// - /// Applies the offset paging middleware to the annotated property. - /// - /// - /// The schema type representing the item type. - /// - public UseMongoDbPagingAttribute(Type? type = null) - { - Type = type; - } - - /// - /// The schema type representation of the item type. - /// - public Type? Type { get; } - - /// - /// Specifies the default page size for this field. - /// - public int DefaultPageSize - { - get => _defaultPageSize ?? PagingDefaults.DefaultPageSize; - set => _defaultPageSize = value; - } - - /// - /// Specifies the maximum allowed page size. - /// - public int MaxPageSize - { - get => _maxPageSize ?? PagingDefaults.MaxPageSize; - set => _maxPageSize = value; - } - - /// - /// Include the total count field to the result type. - /// - public bool IncludeTotalCount - { - get => _includeTotalCount ?? PagingDefaults.IncludeTotalCount; - set => _includeTotalCount = value; - } - - /// - protected override void TryConfigure( - IDescriptorContext context, - IDescriptor descriptor, - ICustomAttributeProvider element) - { - if (element is MemberInfo) - { - if (descriptor is IObjectFieldDescriptor ofd) - { - ofd.UseMongoDbPaging( - Type, - options: new PagingOptions - { - DefaultPageSize = _defaultPageSize, - MaxPageSize = _maxPageSize, - IncludeTotalCount = _includeTotalCount - }); - } - } - } - } -} diff --git a/src/HotChocolate/MongoDb/src/Data/Projections/Convention/Extensions/MongoDbProjectionProviderDescriptorExtensions.cs b/src/HotChocolate/MongoDb/src/Data/Projections/Convention/Extensions/MongoDbProjectionProviderDescriptorExtensions.cs index bdb02ef5487..2d5e768d3b9 100644 --- a/src/HotChocolate/MongoDb/src/Data/Projections/Convention/Extensions/MongoDbProjectionProviderDescriptorExtensions.cs +++ b/src/HotChocolate/MongoDb/src/Data/Projections/Convention/Extensions/MongoDbProjectionProviderDescriptorExtensions.cs @@ -1,5 +1,4 @@ using System; -using HotChocolate.Data.MongoDb; using HotChocolate.Data.MongoDb.Projections; using HotChocolate.Data.Projections; using HotChocolate.Data.Projections.Handlers; diff --git a/src/HotChocolate/MongoDb/src/Data/PublicAPI.Shipped.txt b/src/HotChocolate/MongoDb/src/Data/PublicAPI.Shipped.txt index c8c7da372cf..98963758c96 100644 --- a/src/HotChocolate/MongoDb/src/Data/PublicAPI.Shipped.txt +++ b/src/HotChocolate/MongoDb/src/Data/PublicAPI.Shipped.txt @@ -140,25 +140,6 @@ HotChocolate.Data.MongoDbProjectionConventionDescriptorExtensions HotChocolate.Data.MongoDbProjectionProviderDescriptorExtensions HotChocolate.Data.MongoSortingConventionDescriptorExtensions HotChocolate.Data.SortConventionDescriptorMongoExtensions -HotChocolate.Types.MongoPagingObjectFieldDescriptorExtensions -HotChocolate.Types.UseMongoDbOffsetPagingAttribute -HotChocolate.Types.UseMongoDbOffsetPagingAttribute.DefaultPageSize.get -> int -HotChocolate.Types.UseMongoDbOffsetPagingAttribute.DefaultPageSize.set -> void -HotChocolate.Types.UseMongoDbOffsetPagingAttribute.IncludeTotalCount.get -> bool -HotChocolate.Types.UseMongoDbOffsetPagingAttribute.IncludeTotalCount.set -> void -HotChocolate.Types.UseMongoDbOffsetPagingAttribute.MaxPageSize.get -> int -HotChocolate.Types.UseMongoDbOffsetPagingAttribute.MaxPageSize.set -> void -HotChocolate.Types.UseMongoDbOffsetPagingAttribute.Type.get -> System.Type? -HotChocolate.Types.UseMongoDbOffsetPagingAttribute.UseMongoDbOffsetPagingAttribute(System.Type? type = null) -> void -HotChocolate.Types.UseMongoDbPagingAttribute -HotChocolate.Types.UseMongoDbPagingAttribute.DefaultPageSize.get -> int -HotChocolate.Types.UseMongoDbPagingAttribute.DefaultPageSize.set -> void -HotChocolate.Types.UseMongoDbPagingAttribute.IncludeTotalCount.get -> bool -HotChocolate.Types.UseMongoDbPagingAttribute.IncludeTotalCount.set -> void -HotChocolate.Types.UseMongoDbPagingAttribute.MaxPageSize.get -> int -HotChocolate.Types.UseMongoDbPagingAttribute.MaxPageSize.set -> void -HotChocolate.Types.UseMongoDbPagingAttribute.Type.get -> System.Type? -HotChocolate.Types.UseMongoDbPagingAttribute.UseMongoDbPagingAttribute(System.Type? type = null) -> void Microsoft.Extensions.DependencyInjection.MongoDbDataRequestBuilderExtensions override HotChocolate.Data.MongoDb.Filters.MongoDbComparableGreaterThanHandler.HandleOperation(HotChocolate.Data.MongoDb.Filters.MongoDbFilterVisitorContext! context, HotChocolate.Data.Filters.IFilterOperationField! field, HotChocolate.Language.IValueNode! value, object? parsedValue) -> HotChocolate.Data.MongoDb.MongoDbFilterDefinition! override HotChocolate.Data.MongoDb.Filters.MongoDbComparableGreaterThanHandler.Operation.get -> int @@ -252,7 +233,6 @@ override HotChocolate.Data.MongoDb.Sorting.MongoDbDefaultSortFieldHandler.TryHan override HotChocolate.Data.MongoDb.Sorting.MongoDbSortOperationHandlerBase.CanHandle(HotChocolate.Configuration.ITypeCompletionContext! context, HotChocolate.Types.Descriptors.Definitions.EnumTypeDefinition! typeDefinition, HotChocolate.Data.Sorting.SortEnumValueDefinition! valueDefinition) -> bool override HotChocolate.Data.MongoDb.Sorting.MongoDbSortOperationHandlerBase.TryHandleEnter(HotChocolate.Data.MongoDb.Sorting.MongoDbSortVisitorContext! context, HotChocolate.Data.Sorting.ISortField! field, HotChocolate.Data.Sorting.ISortEnumValue? sortValue, HotChocolate.Language.EnumValueNode! node, out HotChocolate.Language.Visitors.ISyntaxVisitorAction? action) -> bool override HotChocolate.Data.MongoDb.Sorting.MongoDbSortProvider.CreateExecutor(HotChocolate.NameString argumentName) -> HotChocolate.Resolvers.FieldMiddleware! -override HotChocolate.Types.UseMongoDbOffsetPagingAttribute.TryConfigure(HotChocolate.Types.Descriptors.IDescriptorContext! context, HotChocolate.Types.IDescriptor! descriptor, System.Reflection.ICustomAttributeProvider! element) -> void static HotChocolate.Data.MongoDb.Filters.FilterConventionDescriptorMongoDbExtensions.AddDefaultMongoDbFieldHandlers(this HotChocolate.Data.Filters.IFilterProviderDescriptor! descriptor) -> HotChocolate.Data.Filters.IFilterProviderDescriptor! static HotChocolate.Data.MongoDb.Filters.FilterConventionDescriptorMongoDbExtensions.UseMongoDbProvider(this HotChocolate.Data.Filters.IFilterConventionDescriptor! descriptor) -> HotChocolate.Data.Filters.IFilterConventionDescriptor! static HotChocolate.Data.MongoDb.Filters.MongoDbFilterConventionDescriptorExtensions.AddDefaultMongoDbOperations(this HotChocolate.Data.Filters.IFilterConventionDescriptor! descriptor) -> HotChocolate.Data.Filters.IFilterConventionDescriptor! @@ -275,12 +255,6 @@ static HotChocolate.Data.MongoSortingConventionDescriptorExtensions.AddMongoDbDe static HotChocolate.Data.MongoSortingConventionDescriptorExtensions.BindDefaultMongoDbTypes(this HotChocolate.Data.Sorting.ISortConventionDescriptor! descriptor) -> HotChocolate.Data.Sorting.ISortConventionDescriptor! static HotChocolate.Data.SortConventionDescriptorMongoExtensions.AddDefaultFieldHandlers(this HotChocolate.Data.Sorting.ISortProviderDescriptor! descriptor) -> HotChocolate.Data.Sorting.ISortProviderDescriptor! static HotChocolate.Data.SortConventionDescriptorMongoExtensions.UseMongoDbProvider(this HotChocolate.Data.Sorting.ISortConventionDescriptor! descriptor) -> HotChocolate.Data.Sorting.ISortConventionDescriptor! -static HotChocolate.Types.MongoPagingObjectFieldDescriptorExtensions.UseMongoDbOffsetPaging(this HotChocolate.Types.IObjectFieldDescriptor! descriptor, System.Type? type = null, System.Type? entityType = null, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IObjectFieldDescriptor! -static HotChocolate.Types.MongoPagingObjectFieldDescriptorExtensions.UseMongoDbOffsetPaging(this HotChocolate.Types.IObjectFieldDescriptor! descriptor, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IObjectFieldDescriptor! -static HotChocolate.Types.MongoPagingObjectFieldDescriptorExtensions.UseMongoDbOffsetPaging(this HotChocolate.Types.IObjectFieldDescriptor! descriptor, System.Type? entityType = null, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IObjectFieldDescriptor! -static HotChocolate.Types.MongoPagingObjectFieldDescriptorExtensions.UseMongoDbPaging(this HotChocolate.Types.IObjectFieldDescriptor! descriptor, System.Type? type = null, System.Type? entityType = null, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IObjectFieldDescriptor! -static HotChocolate.Types.MongoPagingObjectFieldDescriptorExtensions.UseMongoDbPaging(this HotChocolate.Types.IObjectFieldDescriptor! descriptor, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IObjectFieldDescriptor! -static HotChocolate.Types.MongoPagingObjectFieldDescriptorExtensions.UseMongoDbPaging(this HotChocolate.Types.IObjectFieldDescriptor! descriptor, System.Type? entityType = null, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IObjectFieldDescriptor! static Microsoft.Extensions.DependencyInjection.MongoDbDataRequestBuilderExtensions.AddMongoDbFiltering(this HotChocolate.Execution.Configuration.IRequestExecutorBuilder! builder, string? name = null) -> HotChocolate.Execution.Configuration.IRequestExecutorBuilder! static Microsoft.Extensions.DependencyInjection.MongoDbDataRequestBuilderExtensions.AddMongoDbProjections(this HotChocolate.Execution.Configuration.IRequestExecutorBuilder! builder, string? name = null) -> HotChocolate.Execution.Configuration.IRequestExecutorBuilder! static Microsoft.Extensions.DependencyInjection.MongoDbDataRequestBuilderExtensions.AddMongoDbSorting(this HotChocolate.Execution.Configuration.IRequestExecutorBuilder! builder, string? name = null) -> HotChocolate.Execution.Configuration.IRequestExecutorBuilder! diff --git a/src/HotChocolate/MongoDb/src/Data/PublicAPI.Unshipped.txt b/src/HotChocolate/MongoDb/src/Data/PublicAPI.Unshipped.txt index 3968d404a0d..4b2c8ec0adb 100644 --- a/src/HotChocolate/MongoDb/src/Data/PublicAPI.Unshipped.txt +++ b/src/HotChocolate/MongoDb/src/Data/PublicAPI.Unshipped.txt @@ -43,3 +43,30 @@ HotChocolate.Data.MongoDb.Filters.MongoDbStringStartsWithHandler.MongoDbStringSt *REMOVED*HotChocolate.Data.MongoDb.Filters.MongoDbStringNotStartsWithHandler.MongoDbStringNotStartsWithHandler() -> void *REMOVED*HotChocolate.Data.MongoDb.Filters.MongoDbStringOperationHandler.MongoDbStringOperationHandler() -> void *REMOVED*HotChocolate.Data.MongoDb.Filters.MongoDbStringStartsWithHandler.MongoDbStringStartsWithHandler() -> void +static Microsoft.Extensions.DependencyInjection.MongoDbDataRequestBuilderExtensions.AddMongoDbPagingProviders(this HotChocolate.Execution.Configuration.IRequestExecutorBuilder! builder, string? providerName = null, bool defaultProvider = false) -> HotChocolate.Execution.Configuration.IRequestExecutorBuilder! +*REMOVED*HotChocolate.Types.MongoPagingObjectFieldDescriptorExtensions +*REMOVED*HotChocolate.Types.UseMongoDbOffsetPagingAttribute +*REMOVED*HotChocolate.Types.UseMongoDbOffsetPagingAttribute.DefaultPageSize.get -> int +*REMOVED*HotChocolate.Types.UseMongoDbOffsetPagingAttribute.DefaultPageSize.set -> void +*REMOVED*HotChocolate.Types.UseMongoDbOffsetPagingAttribute.IncludeTotalCount.get -> bool +*REMOVED*HotChocolate.Types.UseMongoDbOffsetPagingAttribute.IncludeTotalCount.set -> void +*REMOVED*HotChocolate.Types.UseMongoDbOffsetPagingAttribute.MaxPageSize.get -> int +*REMOVED*HotChocolate.Types.UseMongoDbOffsetPagingAttribute.MaxPageSize.set -> void +*REMOVED*HotChocolate.Types.UseMongoDbOffsetPagingAttribute.Type.get -> System.Type? +*REMOVED*HotChocolate.Types.UseMongoDbOffsetPagingAttribute.UseMongoDbOffsetPagingAttribute(System.Type? type = null) -> void +*REMOVED*HotChocolate.Types.UseMongoDbPagingAttribute +*REMOVED*HotChocolate.Types.UseMongoDbPagingAttribute.DefaultPageSize.get -> int +*REMOVED*HotChocolate.Types.UseMongoDbPagingAttribute.DefaultPageSize.set -> void +*REMOVED*HotChocolate.Types.UseMongoDbPagingAttribute.IncludeTotalCount.get -> bool +*REMOVED*HotChocolate.Types.UseMongoDbPagingAttribute.IncludeTotalCount.set -> void +*REMOVED*HotChocolate.Types.UseMongoDbPagingAttribute.MaxPageSize.get -> int +*REMOVED*HotChocolate.Types.UseMongoDbPagingAttribute.MaxPageSize.set -> void +*REMOVED*HotChocolate.Types.UseMongoDbPagingAttribute.Type.get -> System.Type? +*REMOVED*HotChocolate.Types.UseMongoDbPagingAttribute.UseMongoDbPagingAttribute(System.Type? type = null) -> void +*REMOVED*override HotChocolate.Types.UseMongoDbOffsetPagingAttribute.TryConfigure(HotChocolate.Types.Descriptors.IDescriptorContext! context, HotChocolate.Types.IDescriptor! descriptor, System.Reflection.ICustomAttributeProvider! element) -> void +*REMOVED*static HotChocolate.Types.MongoPagingObjectFieldDescriptorExtensions.UseMongoDbOffsetPaging(this HotChocolate.Types.IObjectFieldDescriptor! descriptor, System.Type? type = null, System.Type? entityType = null, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IObjectFieldDescriptor! +*REMOVED*static HotChocolate.Types.MongoPagingObjectFieldDescriptorExtensions.UseMongoDbOffsetPaging(this HotChocolate.Types.IObjectFieldDescriptor! descriptor, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IObjectFieldDescriptor! +*REMOVED*static HotChocolate.Types.MongoPagingObjectFieldDescriptorExtensions.UseMongoDbOffsetPaging(this HotChocolate.Types.IObjectFieldDescriptor! descriptor, System.Type? entityType = null, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IObjectFieldDescriptor! +*REMOVED*static HotChocolate.Types.MongoPagingObjectFieldDescriptorExtensions.UseMongoDbPaging(this HotChocolate.Types.IObjectFieldDescriptor! descriptor, System.Type? type = null, System.Type? entityType = null, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IObjectFieldDescriptor! +*REMOVED*static HotChocolate.Types.MongoPagingObjectFieldDescriptorExtensions.UseMongoDbPaging(this HotChocolate.Types.IObjectFieldDescriptor! descriptor, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IObjectFieldDescriptor! +*REMOVED*static HotChocolate.Types.MongoPagingObjectFieldDescriptorExtensions.UseMongoDbPaging(this HotChocolate.Types.IObjectFieldDescriptor! descriptor, System.Type? entityType = null, HotChocolate.Types.Pagination.PagingOptions options = default(HotChocolate.Types.Pagination.PagingOptions)) -> HotChocolate.Types.IObjectFieldDescriptor! \ No newline at end of file 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 eac25781a77..8e06bd8c3e1 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingAggregateFluentTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingAggregateFluentTests.cs @@ -218,8 +218,8 @@ private Func> private ValueTask CreateSchemaAsync() { return new ServiceCollection() - .AddTransient() .AddGraphQL() + .AddMongoDbPagingProviders() .AddFiltering(x => x.AddMongoDbDefaults()) .AddQueryType( descriptor => 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 8f2f4feb745..f76a00d2614 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingFindFluentTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbCursorPagingFindFluentTests.cs @@ -248,8 +248,8 @@ private Func> BuildResolv private ValueTask CreateSchemaAsync() { return new ServiceCollection() - .AddTransient() .AddGraphQL() + .AddMongoDbPagingProviders() .AddFiltering(x => x.AddMongoDbDefaults()) .AddQueryType( descriptor => 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 61ee293f6c1..13329ae6ae3 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbOffsetPagingAggregateTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbOffsetPagingAggregateTests.cs @@ -17,7 +17,7 @@ namespace HotChocolate.Data.MongoDb.Paging { public class MongoDbOffsetPagingAggregateTests : IClassFixture { - private readonly List foos = new List + private readonly List _foos = new() { new Foo { Bar = "a" }, new Foo { Bar = "b" }, @@ -42,19 +42,18 @@ public async Task Simple_StringList_Default_Items() IExecutionResult result = await executor .ExecuteAsync( - @" - { - foos { - items { - bar - } - pageInfo { - hasNextPage - hasPreviousPage + @"{ + foos { + items { + bar + } + pageInfo { + hasNextPage + hasPreviousPage + } + totalCount } - totalCount - } - }"); + }"); result.MatchDocumentSnapshot(); } @@ -68,17 +67,17 @@ public async Task Simple_StringList_Take_2() IExecutionResult result = await executor .ExecuteAsync( @" - { - foos(take: 2) { - items { - bar - } - pageInfo { - hasNextPage - hasPreviousPage + { + foos(take: 2) { + items { + bar + } + pageInfo { + hasNextPage + hasPreviousPage + } } - } - }"); + }"); result.MatchDocumentSnapshot(); } @@ -92,17 +91,17 @@ public async Task Simple_StringList_Take_2_After() IExecutionResult result = await executor .ExecuteAsync( @" - { - foos(take: 2 skip: 2) { - items { - bar - } - pageInfo { - hasNextPage - hasPreviousPage + { + foos(take: 2 skip: 2) { + items { + bar + } + pageInfo { + hasNextPage + hasPreviousPage + } } - } - }"); + }"); result.MatchDocumentSnapshot(); } @@ -117,17 +116,17 @@ public async Task Simple_StringList_Global_DefaultItem_2() IExecutionResult result = await executor .ExecuteAsync( @" - { - foos { - items { - bar - } - pageInfo { - hasNextPage - hasPreviousPage + { + foos { + items { + bar + } + pageInfo { + hasNextPage + hasPreviousPage + } } - } - }"); + }"); result.MatchDocumentSnapshot(); } @@ -138,7 +137,6 @@ public async Task JustTotalCount() IRequestExecutor executor = await CreateSchemaAsync(); - IExecutionResult result = await executor .ExecuteAsync( @" @@ -147,6 +145,7 @@ public async Task JustTotalCount() totalCount } }"); + result.MatchDocumentSnapshot(); } @@ -169,21 +168,21 @@ private Func> collection.InsertMany(results); - return ctx => collection.Aggregate().AsExecutable(); + return _ => collection.Aggregate().AsExecutable(); } private ValueTask CreateSchemaAsync() { return new ServiceCollection() - .AddTransient() .AddGraphQL() + .AddMongoDbPagingProviders() .AddFiltering(x => x.AddMongoDbDefaults()) .AddQueryType( descriptor => { descriptor .Field("foos") - .Resolve(BuildResolver(_resource, foos)) + .Resolve(BuildResolver(_resource, _foos)) .Type>>() .Use( next => async context => @@ -194,7 +193,7 @@ private ValueTask CreateSchemaAsync() context.ContextData["query"] = executable.Print(); } }) - .UseMongoDbOffsetPaging>( + .UseOffsetPaging>( options: new PagingOptions { IncludeTotalCount = true }); }) .UseRequest( @@ -202,7 +201,7 @@ private ValueTask CreateSchemaAsync() { await next(context); if (context.Result is IReadOnlyQueryResult result && - context.ContextData.TryGetValue("query", out object? queryString)) + context.ContextData.TryGetValue("query", out var queryString)) { context.Result = QueryResultBuilder 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 30b024a4e3a..7e7d83b05cf 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbOffsetPagingFindFluentTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Paging.Tests/MongoDbOffsetPagingFindFluentTests.cs @@ -138,7 +138,6 @@ public async Task JustTotalCount() IRequestExecutor executor = await CreateSchemaAsync(); - IExecutionResult result = await executor .ExecuteAsync( @" @@ -147,6 +146,7 @@ public async Task JustTotalCount() totalCount } }"); + result.MatchDocumentSnapshot(); } @@ -174,8 +174,8 @@ private Func> BuildResolv private ValueTask CreateSchemaAsync() { return new ServiceCollection() - .AddTransient() .AddGraphQL() + .AddMongoDbPagingProviders() .AddFiltering(x => x.AddMongoDbDefaults()) .AddQueryType( descriptor => @@ -193,7 +193,7 @@ private ValueTask CreateSchemaAsync() context.ContextData["query"] = executable.Print(); } }) - .UseMongoDbOffsetPaging>( + .UseOffsetPaging>( options: new PagingOptions { IncludeTotalCount = true }); }) .UseRequest( 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 9c6c8ab079c..10d4536be7f 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorPagingTests.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/MongoDbProjectionVisitorPagingTests.cs @@ -250,7 +250,7 @@ public async Task CreateOffsetPaging_ProjectsTwoProperties_Items_WithArgs() // assert IExecutionResult res1 = await tester.ExecuteAsync( QueryRequestBuilder.New() - .SetQuery("{ root(take:10, skip:1){ items { bar baz } }}") + .SetQuery("{ root(take:10, skip:1) { items { bar baz } } }") .Create()); res1.MatchDocumentSnapshot(); diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/ProjectionVisitorTestBase.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/ProjectionVisitorTestBase.cs index 6142195b705..6225b40316e 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/ProjectionVisitorTestBase.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/ProjectionVisitorTestBase.cs @@ -48,6 +48,7 @@ public IRequestExecutor CreateSchema( .AddMongoDbProjections() .AddMongoDbFiltering() .AddMongoDbSorting() + .AddMongoDbPagingProviders() .AddQueryType( new ObjectType>( c => @@ -88,12 +89,12 @@ private static void ApplyConfigurationToFieldDescriptor( { if (usePaging) { - descriptor.UseMongoDbPaging>(); + descriptor.UsePaging>(); } if (useOffsetPaging) { - descriptor.UseMongoDbOffsetPaging>(); + descriptor.UseOffsetPaging>(); } descriptor diff --git a/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/SchemaCache.cs b/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/SchemaCache.cs index 40aeafc99d9..a468d725675 100644 --- a/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/SchemaCache.cs +++ b/src/HotChocolate/MongoDb/test/Data.MongoDb.Projections.Tests/SchemaCache.cs @@ -10,8 +10,7 @@ public class SchemaCache : ProjectionVisitorTestBase , IDisposable { - private readonly ConcurrentDictionary<(Type, object), IRequestExecutor> _cache = - new ConcurrentDictionary<(Type, object), IRequestExecutor>(); + private readonly ConcurrentDictionary<(Type, object), IRequestExecutor> _cache = new(); private readonly MongoResource _resource; @@ -30,7 +29,7 @@ public IRequestExecutor CreateSchema( (Type, T[] entites) key = (typeof(T), entities); return _cache.GetOrAdd( key, - k => base.CreateSchema( + _ => base.CreateSchema( entities, usePaging: usePaging, useOffsetPaging: useOffsetPaging, diff --git a/src/HotChocolate/Neo4J/src/Data/Extensions/Neo4JDataRequestBuilderExtensions.cs b/src/HotChocolate/Neo4J/src/Data/Extensions/Neo4JDataRequestBuilderExtensions.cs index 3daad088bf7..7ff21e61875 100644 --- a/src/HotChocolate/Neo4J/src/Data/Extensions/Neo4JDataRequestBuilderExtensions.cs +++ b/src/HotChocolate/Neo4J/src/Data/Extensions/Neo4JDataRequestBuilderExtensions.cs @@ -1,3 +1,5 @@ +using System; +using HotChocolate.Data.Neo4J.Paging; using HotChocolate.Execution.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -58,5 +60,37 @@ public static IRequestExecutorBuilder AddNeo4JProjections( this IRequestExecutorBuilder builder, string? name = null) => builder.ConfigureSchema(s => s.AddNeo4JProjections(name)); + + /// + /// Adds the MongoDB cursor and offset paging providers. + /// + /// + /// The GraphQL configuration builder. + /// + /// + /// The name which shall be used to refer to this registration. + /// + /// + /// Defines if these providers shall be registered as default providers. + /// + /// + /// Returns the GraphQL configuration builder for further configuration chaining. + /// + public static IRequestExecutorBuilder AddNeo4JPagingProviders( + this IRequestExecutorBuilder builder, + string? providerName = null, + bool defaultProvider = false) + { + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + builder.AddOffsetPagingProvider( + providerName, + defaultProvider); + + return builder; + } } } diff --git a/src/HotChocolate/Neo4J/src/Data/Paging/Neo4JPagingObjectFieldDescriptorExtensions.cs b/src/HotChocolate/Neo4J/src/Data/Paging/Neo4JPagingObjectFieldDescriptorExtensions.cs deleted file mode 100644 index ccf1b1d74bf..00000000000 --- a/src/HotChocolate/Neo4J/src/Data/Paging/Neo4JPagingObjectFieldDescriptorExtensions.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using HotChocolate.Types; -using HotChocolate.Types.Pagination; -using Microsoft.Extensions.DependencyInjection; - -namespace HotChocolate.Data.Neo4J.Paging -{ - public static class Neo4JPagingObjectFieldDescriptorExtensions - { - /// - /// Adds offset pagination support to the field. Rewrites the type to a connection type and - /// registers the neo4j pagination handler - /// - /// The descriptor of the field - /// - /// The schema type of the entity. Not a connection type - /// - /// The type of the entity - /// The options for pagination - /// The - public static IObjectFieldDescriptor UseNeo4JOffsetPaging( - this IObjectFieldDescriptor descriptor, - Type? type = null, - Type? entityType = null, - PagingOptions options = default) => - descriptor.UseOffsetPaging( - type, - entityType, - (services, _) => services.GetService() ?? - new Neo4JOffsetPagingProvider(), - options); - - /// - /// Adds offset pagination support to the field. Rewrites the type to a connection type and - /// registers the neo4j pagination handler - /// - /// The descriptor of the field - /// The type of the entity - /// The options for pagination - /// - /// The schema type of the entity. Not a connection type - /// - /// The - public static IObjectFieldDescriptor UseNeo4JOffsetPaging( - this IObjectFieldDescriptor descriptor, - Type? entityType = null, - PagingOptions options = default) - where TSchemaType : IOutputType => - UseNeo4JOffsetPaging( - descriptor, - typeof(TSchemaType), - entityType, - options); - - /// - /// Adds offset pagination support to the field. Rewrites the type to a connection type and - /// registers the neo4j pagination handler - /// - /// The descriptor of the field - /// The options for pagination - /// - /// The schema type of the entity. Not a connection type - /// - /// The type of the entity - /// The - public static IObjectFieldDescriptor UseNeo4JOffsetPaging( - this IObjectFieldDescriptor descriptor, - PagingOptions options = default) - where TSchemaType : class, IOutputType => - UseNeo4JOffsetPaging(descriptor, typeof(TEntity), options); - } -} diff --git a/src/HotChocolate/Neo4J/src/Data/Paging/UseNeo4JOffsetPagingAttribute.cs b/src/HotChocolate/Neo4J/src/Data/Paging/UseNeo4JOffsetPagingAttribute.cs deleted file mode 100644 index 455f65c163e..00000000000 --- a/src/HotChocolate/Neo4J/src/Data/Paging/UseNeo4JOffsetPagingAttribute.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using System.Reflection; -using HotChocolate.Types; -using HotChocolate.Types.Descriptors; -using HotChocolate.Types.Pagination; - -namespace HotChocolate.Data.Neo4J.Paging -{ - /// - /// This attribute adds the offset paging middleware for Neo4j to the annotated method or - /// property. - /// - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] - public class UseNeo4JOffsetPagingAttribute : DescriptorAttribute - { - private int? _defaultPageSize; - private int? _maxPageSize; - private bool? _includeTotalCount; - - /// - /// Applies the offset paging middleware to the annotated property. - /// - /// - /// The schema type representing the item type. - /// - public UseNeo4JOffsetPagingAttribute(Type? type = null) - { - Type = type; - } - - /// - /// The schema type representation of the item type. - /// - public Type? Type { get; } - - /// - /// Specifies the default page size for this field. - /// - public int DefaultPageSize - { - get => _defaultPageSize ?? PagingDefaults.DefaultPageSize; - set => _defaultPageSize = value; - } - - /// - /// Specifies the maximum allowed page size. - /// - public int MaxPageSize - { - get => _maxPageSize ?? PagingDefaults.MaxPageSize; - set => _maxPageSize = value; - } - - /// - /// Include the total count field to the result type. - /// - public bool IncludeTotalCount - { - get => _includeTotalCount ?? PagingDefaults.IncludeTotalCount; - set => _includeTotalCount = value; - } - - /// - protected override void TryConfigure( - IDescriptorContext context, - IDescriptor descriptor, - ICustomAttributeProvider element) - { - if (descriptor is IObjectFieldDescriptor odf) - { - var options = new PagingOptions - { - DefaultPageSize = _defaultPageSize, - MaxPageSize = _maxPageSize, - IncludeTotalCount = _includeTotalCount - }; - - odf.UseNeo4JOffsetPaging(Type, options: options); - } - } - } -} diff --git a/src/HotChocolate/Neo4J/test/HotChocolate.Data.Neo4J.Paging.Tests/Neo4JFixture.cs b/src/HotChocolate/Neo4J/test/HotChocolate.Data.Neo4J.Paging.Tests/Neo4JFixture.cs index a52fec17700..c32a8f45f56 100644 --- a/src/HotChocolate/Neo4J/test/HotChocolate.Data.Neo4J.Paging.Tests/Neo4JFixture.cs +++ b/src/HotChocolate/Neo4J/test/HotChocolate.Data.Neo4J.Paging.Tests/Neo4JFixture.cs @@ -51,7 +51,8 @@ private async Task CreateSchema(string cypher) context.ContextData["query"] = executable.Print(); } }) - .UseNeo4JOffsetPaging>()) + .UseOffsetPaging>()) + .AddNeo4JPagingProviders() .UseRequest( next => async context => { diff --git a/src/HotChocolate/Stitching/test/Stitching.Tests/Integration/__snapshots__/BaseTests.AutoMerge_Schema.snap b/src/HotChocolate/Stitching/test/Stitching.Tests/Integration/__snapshots__/BaseTests.AutoMerge_Schema.snap index 8b51de974b4..60122866d29 100644 --- a/src/HotChocolate/Stitching/test/Stitching.Tests/Integration/__snapshots__/BaseTests.AutoMerge_Schema.snap +++ b/src/HotChocolate/Stitching/test/Stitching.Tests/Integration/__snapshots__/BaseTests.AutoMerge_Schema.snap @@ -16,7 +16,7 @@ interface Node @source(name: "Node", schema: "contract") @source(name: "Node", s type Consultant implements Node @source(name: "Consultant", schema: "customer") { id: ID! name: String! - customers("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): CustomerConnection + customers("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): CustomersConnection } type CreateCustomerPayload @source(name: "CreateCustomerPayload", schema: "customer") { @@ -36,17 +36,17 @@ type Customer implements Node @source(name: "Customer", schema: "customer") { } "A connection to a list of items." -type CustomerConnection @source(name: "CustomerConnection", schema: "customer") { +type CustomersConnection @source(name: "CustomersConnection", schema: "customer") { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [CustomerEdge!] + edges: [CustomersEdge!] "A flattened list of the nodes." nodes: [Customer] } "An edge in a connection." -type CustomerEdge @source(name: "CustomerEdge", schema: "customer") { +type CustomersEdge @source(name: "CustomersEdge", schema: "customer") { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." diff --git a/src/HotChocolate/Stitching/test/Stitching.Tests/Schemas/__snapshots__/SchemaTests.CustomerSchemaSnapshot.snap b/src/HotChocolate/Stitching/test/Stitching.Tests/Schemas/__snapshots__/SchemaTests.CustomerSchemaSnapshot.snap index e40d8225064..9bab1f201b4 100644 --- a/src/HotChocolate/Stitching/test/Stitching.Tests/Schemas/__snapshots__/SchemaTests.CustomerSchemaSnapshot.snap +++ b/src/HotChocolate/Stitching/test/Stitching.Tests/Schemas/__snapshots__/SchemaTests.CustomerSchemaSnapshot.snap @@ -11,7 +11,7 @@ interface Node { type Consultant implements Node { id: ID! name: String! - customers("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): CustomerConnection + customers("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): CustomersConnection } type CreateCustomerPayload { @@ -31,17 +31,17 @@ type Customer implements Node { } "A connection to a list of items." -type CustomerConnection { +type CustomersConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [CustomerEdge!] + edges: [CustomersEdge!] "A flattened list of the nodes." nodes: [Customer] } "An edge in a connection." -type CustomerEdge { +type CustomersEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." diff --git a/src/HotChocolate/Utilities/test/Utilities.Introspection.Tests/__snapshots__/IntrospectionClientTests.Download_Schema_AST.snap b/src/HotChocolate/Utilities/test/Utilities.Introspection.Tests/__snapshots__/IntrospectionClientTests.Download_Schema_AST.snap index ce3b6608766..20b175018e3 100644 --- a/src/HotChocolate/Utilities/test/Utilities.Introspection.Tests/__snapshots__/IntrospectionClientTests.Download_Schema_AST.snap +++ b/src/HotChocolate/Utilities/test/Utilities.Introspection.Tests/__snapshots__/IntrospectionClientTests.Download_Schema_AST.snap @@ -29,7 +29,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): CharacterConnection + 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 @@ -39,7 +39,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): CharacterConnection + 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 } @@ -53,7 +53,7 @@ enum Episode { 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): CharacterConnection + 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] height(unit: Unit): Float } @@ -71,11 +71,11 @@ input ReviewInput { } "A connection to a list of items." -type CharacterConnection { +type FriendsConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [CharacterEdge!] + edges: [FriendsEdge!] "A flattened list of the nodes." nodes: [Character] } @@ -104,7 +104,7 @@ type PageInfo { } "An edge in a connection." -type CharacterEdge { +type FriendsEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." diff --git a/src/HotChocolate/Utilities/test/Utilities.Introspection.Tests/__snapshots__/IntrospectionClientTests.Download_Schema_SDL.snap b/src/HotChocolate/Utilities/test/Utilities.Introspection.Tests/__snapshots__/IntrospectionClientTests.Download_Schema_SDL.snap index 3e24b9fe656..3a0453c350f 100644 --- a/src/HotChocolate/Utilities/test/Utilities.Introspection.Tests/__snapshots__/IntrospectionClientTests.Download_Schema_SDL.snap +++ b/src/HotChocolate/Utilities/test/Utilities.Introspection.Tests/__snapshots__/IntrospectionClientTests.Download_Schema_SDL.snap @@ -29,7 +29,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): CharacterConnection + 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 @@ -39,7 +39,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): CharacterConnection + 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 } @@ -53,7 +53,7 @@ enum Episode { 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): CharacterConnection + 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] height(unit: Unit): Float } @@ -71,11 +71,11 @@ input ReviewInput { } "A connection to a list of items." -type CharacterConnection { +type FriendsConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [CharacterEdge!] + edges: [FriendsEdge!] "A flattened list of the nodes." nodes: [Character] } @@ -104,7 +104,7 @@ type PageInfo { } "An edge in a connection." -type CharacterEdge { +type FriendsEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." 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 3792700b022..8fec488f887 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 @@ -420,9 +420,9 @@ public GetHero_Hero_Human(global::System.String name, global::StrawberryShake.Co /// A connection to a list of items. /// [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] - public partial class GetHero_Hero_Friends_CharacterConnection : global::System.IEquatable, IGetHero_Hero_Friends_CharacterConnection + public partial class GetHero_Hero_Friends_FriendsConnection : global::System.IEquatable, IGetHero_Hero_Friends_FriendsConnection { - public GetHero_Hero_Friends_CharacterConnection(global::System.Collections.Generic.IReadOnlyList? nodes) + public GetHero_Hero_Friends_FriendsConnection(global::System.Collections.Generic.IReadOnlyList? nodes) { Nodes = nodes; } @@ -435,7 +435,7 @@ public GetHero_Hero_Friends_CharacterConnection(global::System.Collections.Gener get; } - public virtual global::System.Boolean Equals(GetHero_Hero_Friends_CharacterConnection? other) + public virtual global::System.Boolean Equals(GetHero_Hero_Friends_FriendsConnection? other) { if (ReferenceEquals(null, other)) { @@ -472,7 +472,7 @@ public GetHero_Hero_Friends_CharacterConnection(global::System.Collections.Gener return false; } - return Equals((GetHero_Hero_Friends_CharacterConnection)obj); + return Equals((GetHero_Hero_Friends_FriendsConnection)obj); } public override global::System.Int32 GetHashCode() @@ -684,7 +684,7 @@ public interface IGetHero_Hero_Friends /// A connection to a list of items. /// [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] - public interface IGetHero_Hero_Friends_CharacterConnection : IGetHero_Hero_Friends + public interface IGetHero_Hero_Friends_FriendsConnection : IGetHero_Hero_Friends { } @@ -1726,7 +1726,7 @@ namespace StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "0.0.0.0")] public partial class DroidEntity { - public DroidEntity(global::System.String name = default !, global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.CharacterConnectionData? friends = default !) + public DroidEntity(global::System.String name = default !, global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.FriendsConnectionData? friends = default !) { Name = name; Friends = friends; @@ -1737,7 +1737,7 @@ public partial class DroidEntity get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.CharacterConnectionData? Friends + public global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.FriendsConnectionData? Friends { get; } @@ -1747,7 +1747,7 @@ public partial class DroidEntity [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "0.0.0.0")] public partial class HumanEntity { - public HumanEntity(global::System.String name = default !, global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.CharacterConnectionData? friends = default !) + public HumanEntity(global::System.String name = default !, global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.FriendsConnectionData? friends = default !) { Name = name; Friends = friends; @@ -1758,7 +1758,7 @@ public partial class HumanEntity get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.CharacterConnectionData? Friends + public global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.FriendsConnectionData? Friends { get; } @@ -1870,7 +1870,7 @@ public GetHero_Hero_Droid Map(global::StrawberryShake.CodeGeneration.CSharp.Inte return new GetHero_Hero_Droid(entity.Name, MapIGetHero_Hero_Friends(entity.Friends, snapshot)); } - private global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.IGetHero_Hero_Friends? MapIGetHero_Hero_Friends(global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.CharacterConnectionData? data, global::StrawberryShake.IEntityStoreSnapshot snapshot) + private global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.IGetHero_Hero_Friends? MapIGetHero_Hero_Friends(global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.FriendsConnectionData? data, global::StrawberryShake.IEntityStoreSnapshot snapshot) { if (data is null) { @@ -1878,9 +1878,9 @@ public GetHero_Hero_Droid Map(global::StrawberryShake.CodeGeneration.CSharp.Inte } IGetHero_Hero_Friends returnValue = default !; - if (data?.__typename.Equals("CharacterConnection", global::System.StringComparison.Ordinal) ?? false) + if (data?.__typename.Equals("FriendsConnection", global::System.StringComparison.Ordinal) ?? false) { - returnValue = new GetHero_Hero_Friends_CharacterConnection(MapIGetHero_Hero_Friends_NodesArray(data.Nodes, snapshot)); + returnValue = new GetHero_Hero_Friends_FriendsConnection(MapIGetHero_Hero_Friends_NodesArray(data.Nodes, snapshot)); } else { @@ -1951,7 +1951,7 @@ public GetHero_Hero_Human Map(global::StrawberryShake.CodeGeneration.CSharp.Inte return new GetHero_Hero_Human(entity.Name, MapIGetHero_Hero_Friends(entity.Friends, snapshot)); } - private global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.IGetHero_Hero_Friends? MapIGetHero_Hero_Friends(global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.CharacterConnectionData? data, global::StrawberryShake.IEntityStoreSnapshot snapshot) + private global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.IGetHero_Hero_Friends? MapIGetHero_Hero_Friends(global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.FriendsConnectionData? data, global::StrawberryShake.IEntityStoreSnapshot snapshot) { if (data is null) { @@ -1959,9 +1959,9 @@ public GetHero_Hero_Human Map(global::StrawberryShake.CodeGeneration.CSharp.Inte } IGetHero_Hero_Friends returnValue = default !; - if (data?.__typename.Equals("CharacterConnection", global::System.StringComparison.Ordinal) ?? false) + if (data?.__typename.Equals("FriendsConnection", global::System.StringComparison.Ordinal) ?? false) { - returnValue = new GetHero_Hero_Friends_CharacterConnection(MapIGetHero_Hero_Friends_NodesArray(data.Nodes, snapshot)); + returnValue = new GetHero_Hero_Friends_FriendsConnection(MapIGetHero_Hero_Friends_NodesArray(data.Nodes, snapshot)); } else { @@ -2325,7 +2325,7 @@ public GetHeroBuilder(global::StrawberryShake.IEntityStore entityStore, global:: return _stringParser.Parse(obj.Value.GetString()!); } - private global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.CharacterConnectionData? DeserializeIGetHero_Hero_Friends(global::StrawberryShake.IEntityStoreUpdateSession session, global::System.Text.Json.JsonElement? obj, global::System.Collections.Generic.ISet entityIds) + private global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.FriendsConnectionData? DeserializeIGetHero_Hero_Friends(global::StrawberryShake.IEntityStoreUpdateSession session, global::System.Text.Json.JsonElement? obj, global::System.Collections.Generic.ISet entityIds) { if (!obj.HasValue) { @@ -2333,9 +2333,9 @@ public GetHeroBuilder(global::StrawberryShake.IEntityStore entityStore, global:: } var typename = obj.Value.GetProperty("__typename").GetString(); - if (typename?.Equals("CharacterConnection", global::System.StringComparison.Ordinal) ?? false) + if (typename?.Equals("FriendsConnection", global::System.StringComparison.Ordinal) ?? false) { - return new global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.CharacterConnectionData(typename, nodes: UpdateIGetHero_Hero_Friends_NodesEntityArray(session, global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "nodes"), entityIds)); + return new global::StrawberryShake.CodeGeneration.CSharp.Integration.MultiProfile.State.FriendsConnectionData(typename, nodes: UpdateIGetHero_Hero_Friends_NodesEntityArray(session, global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "nodes"), entityIds)); } throw new global::System.NotSupportedException(); @@ -2615,9 +2615,9 @@ public CreateReviewMutBuilder(global::StrawberryShake.IEntityStore entityStore, // 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 CharacterConnectionData + public partial class FriendsConnectionData { - public CharacterConnectionData(global::System.String __typename, global::System.Collections.Generic.IReadOnlyList? nodes = default !) + public FriendsConnectionData(global::System.String __typename, global::System.Collections.Generic.IReadOnlyList? nodes = default !) { this.__typename = __typename ?? throw new global::System.ArgumentNullException(nameof(__typename)); Nodes = nodes; 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 f323cb94581..d0bfcfe0f8f 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 @@ -323,9 +323,9 @@ public GetHero_Hero_Human(global::System.String name, global::StrawberryShake.Co /// A connection to a list of items. /// [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] - public partial class GetHero_Hero_Friends_CharacterConnection : global::System.IEquatable, IGetHero_Hero_Friends_CharacterConnection + public partial class GetHero_Hero_Friends_FriendsConnection : global::System.IEquatable, IGetHero_Hero_Friends_FriendsConnection { - public GetHero_Hero_Friends_CharacterConnection(global::System.Collections.Generic.IReadOnlyList? nodes) + public GetHero_Hero_Friends_FriendsConnection(global::System.Collections.Generic.IReadOnlyList? nodes) { Nodes = nodes; } @@ -338,7 +338,7 @@ public GetHero_Hero_Friends_CharacterConnection(global::System.Collections.Gener get; } - public virtual global::System.Boolean Equals(GetHero_Hero_Friends_CharacterConnection? other) + public virtual global::System.Boolean Equals(GetHero_Hero_Friends_FriendsConnection? other) { if (ReferenceEquals(null, other)) { @@ -375,7 +375,7 @@ public GetHero_Hero_Friends_CharacterConnection(global::System.Collections.Gener return false; } - return Equals((GetHero_Hero_Friends_CharacterConnection)obj); + return Equals((GetHero_Hero_Friends_FriendsConnection)obj); } public override global::System.Int32 GetHashCode() @@ -587,7 +587,7 @@ public interface IGetHero_Hero_Friends /// A connection to a list of items. /// [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] - public interface IGetHero_Hero_Friends_CharacterConnection : IGetHero_Hero_Friends + public interface IGetHero_Hero_Friends_FriendsConnection : IGetHero_Hero_Friends { } @@ -822,7 +822,7 @@ public GetHeroResult Create(global::StrawberryShake.IOperationResultDataInfo dat return returnValue; } - private global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriendsNoStore.IGetHero_Hero_Friends? MapIGetHero_Hero_Friends(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriendsNoStore.State.CharacterConnectionData? data) + private global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriendsNoStore.IGetHero_Hero_Friends? MapIGetHero_Hero_Friends(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriendsNoStore.State.FriendsConnectionData? data) { if (data is null) { @@ -830,9 +830,9 @@ public GetHeroResult Create(global::StrawberryShake.IOperationResultDataInfo dat } IGetHero_Hero_Friends returnValue = default !; - if (data?.__typename.Equals("CharacterConnection", global::System.StringComparison.Ordinal) ?? false) + if (data?.__typename.Equals("FriendsConnection", global::System.StringComparison.Ordinal) ?? false) { - returnValue = new GetHero_Hero_Friends_CharacterConnection(MapIGetHero_Hero_Friends_NodesArray(data.Nodes)); + returnValue = new GetHero_Hero_Friends_FriendsConnection(MapIGetHero_Hero_Friends_NodesArray(data.Nodes)); } else { @@ -993,7 +993,7 @@ public GetHeroBuilder(global::StrawberryShake.IOperationResultDataFactoryA connection to a list of items. [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "0.0.0.0")] - public partial class CharacterConnectionData + public partial class FriendsConnectionData { - public CharacterConnectionData(global::System.String __typename, global::System.Collections.Generic.IReadOnlyList? nodes = default !) + public FriendsConnectionData(global::System.String __typename, global::System.Collections.Generic.IReadOnlyList? nodes = default !) { this.__typename = __typename ?? throw new global::System.ArgumentNullException(nameof(__typename)); Nodes = nodes; 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 6dfeed99990..b799e2bd5dc 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 @@ -330,9 +330,9 @@ public GetHero_Hero_Human(global::System.String name, global::StrawberryShake.Co /// A connection to a list of items. /// [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] - public partial class GetHero_Hero_Friends_CharacterConnection : global::System.IEquatable, IGetHero_Hero_Friends_CharacterConnection + public partial class GetHero_Hero_Friends_FriendsConnection : global::System.IEquatable, IGetHero_Hero_Friends_FriendsConnection { - public GetHero_Hero_Friends_CharacterConnection(global::System.Collections.Generic.IReadOnlyList? nodes) + public GetHero_Hero_Friends_FriendsConnection(global::System.Collections.Generic.IReadOnlyList? nodes) { Nodes = nodes; } @@ -345,7 +345,7 @@ public GetHero_Hero_Friends_CharacterConnection(global::System.Collections.Gener get; } - public virtual global::System.Boolean Equals(GetHero_Hero_Friends_CharacterConnection? other) + public virtual global::System.Boolean Equals(GetHero_Hero_Friends_FriendsConnection? other) { if (ReferenceEquals(null, other)) { @@ -382,7 +382,7 @@ public GetHero_Hero_Friends_CharacterConnection(global::System.Collections.Gener return false; } - return Equals((GetHero_Hero_Friends_CharacterConnection)obj); + return Equals((GetHero_Hero_Friends_FriendsConnection)obj); } public override global::System.Int32 GetHashCode() @@ -594,7 +594,7 @@ public interface IGetHero_Hero_Friends /// A connection to a list of items. /// [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] - public interface IGetHero_Hero_Friends_CharacterConnection : IGetHero_Hero_Friends + public interface IGetHero_Hero_Friends_FriendsConnection : IGetHero_Hero_Friends { } @@ -826,7 +826,7 @@ namespace StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.S [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "0.0.0.0")] public partial class DroidEntity { - public DroidEntity(global::System.String name = default !, global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.State.CharacterConnectionData? friends = default !) + public DroidEntity(global::System.String name = default !, global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.State.FriendsConnectionData? friends = default !) { Name = name; Friends = friends; @@ -837,7 +837,7 @@ public partial class DroidEntity get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.State.CharacterConnectionData? Friends + public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.State.FriendsConnectionData? Friends { get; } @@ -847,7 +847,7 @@ public partial class DroidEntity [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "0.0.0.0")] public partial class HumanEntity { - public HumanEntity(global::System.String name = default !, global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.State.CharacterConnectionData? friends = default !) + public HumanEntity(global::System.String name = default !, global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.State.FriendsConnectionData? friends = default !) { Name = name; Friends = friends; @@ -858,7 +858,7 @@ public partial class HumanEntity get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.State.CharacterConnectionData? Friends + public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.State.FriendsConnectionData? Friends { get; } @@ -970,7 +970,7 @@ public GetHero_Hero_Droid Map(global::StrawberryShake.CodeGeneration.CSharp.Inte return new GetHero_Hero_Droid(entity.Name, MapIGetHero_Hero_Friends(entity.Friends, snapshot)); } - private global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.IGetHero_Hero_Friends? MapIGetHero_Hero_Friends(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.State.CharacterConnectionData? data, global::StrawberryShake.IEntityStoreSnapshot snapshot) + private global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.IGetHero_Hero_Friends? MapIGetHero_Hero_Friends(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.State.FriendsConnectionData? data, global::StrawberryShake.IEntityStoreSnapshot snapshot) { if (data is null) { @@ -978,9 +978,9 @@ public GetHero_Hero_Droid Map(global::StrawberryShake.CodeGeneration.CSharp.Inte } IGetHero_Hero_Friends returnValue = default !; - if (data?.__typename.Equals("CharacterConnection", global::System.StringComparison.Ordinal) ?? false) + if (data?.__typename.Equals("FriendsConnection", global::System.StringComparison.Ordinal) ?? false) { - returnValue = new GetHero_Hero_Friends_CharacterConnection(MapIGetHero_Hero_Friends_NodesArray(data.Nodes, snapshot)); + returnValue = new GetHero_Hero_Friends_FriendsConnection(MapIGetHero_Hero_Friends_NodesArray(data.Nodes, snapshot)); } else { @@ -1051,7 +1051,7 @@ public GetHero_Hero_Human Map(global::StrawberryShake.CodeGeneration.CSharp.Inte return new GetHero_Hero_Human(entity.Name, MapIGetHero_Hero_Friends(entity.Friends, snapshot)); } - private global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.IGetHero_Hero_Friends? MapIGetHero_Hero_Friends(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.State.CharacterConnectionData? data, global::StrawberryShake.IEntityStoreSnapshot snapshot) + private global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.IGetHero_Hero_Friends? MapIGetHero_Hero_Friends(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.State.FriendsConnectionData? data, global::StrawberryShake.IEntityStoreSnapshot snapshot) { if (data is null) { @@ -1059,9 +1059,9 @@ public GetHero_Hero_Human Map(global::StrawberryShake.CodeGeneration.CSharp.Inte } IGetHero_Hero_Friends returnValue = default !; - if (data?.__typename.Equals("CharacterConnection", global::System.StringComparison.Ordinal) ?? false) + if (data?.__typename.Equals("FriendsConnection", global::System.StringComparison.Ordinal) ?? false) { - returnValue = new GetHero_Hero_Friends_CharacterConnection(MapIGetHero_Hero_Friends_NodesArray(data.Nodes, snapshot)); + returnValue = new GetHero_Hero_Friends_FriendsConnection(MapIGetHero_Hero_Friends_NodesArray(data.Nodes, snapshot)); } else { @@ -1264,7 +1264,7 @@ public GetHeroBuilder(global::StrawberryShake.IEntityStore entityStore, global:: return _stringParser.Parse(obj.Value.GetString()!); } - private global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.State.CharacterConnectionData? DeserializeIGetHero_Hero_Friends(global::StrawberryShake.IEntityStoreUpdateSession session, global::System.Text.Json.JsonElement? obj, global::System.Collections.Generic.ISet entityIds) + private global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.State.FriendsConnectionData? DeserializeIGetHero_Hero_Friends(global::StrawberryShake.IEntityStoreUpdateSession session, global::System.Text.Json.JsonElement? obj, global::System.Collections.Generic.ISet entityIds) { if (!obj.HasValue) { @@ -1272,9 +1272,9 @@ public GetHeroBuilder(global::StrawberryShake.IEntityStore entityStore, global:: } var typename = obj.Value.GetProperty("__typename").GetString(); - if (typename?.Equals("CharacterConnection", global::System.StringComparison.Ordinal) ?? false) + if (typename?.Equals("FriendsConnection", global::System.StringComparison.Ordinal) ?? false) { - return new global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.State.CharacterConnectionData(typename, nodes: UpdateIGetHero_Hero_Friends_NodesEntityArray(session, global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "nodes"), entityIds)); + return new global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsGetFriends.State.FriendsConnectionData(typename, nodes: UpdateIGetHero_Hero_Friends_NodesEntityArray(session, global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "nodes"), entityIds)); } throw new global::System.NotSupportedException(); @@ -1340,9 +1340,9 @@ public GetHeroBuilder(global::StrawberryShake.IEntityStore entityStore, global:: // 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 CharacterConnectionData + public partial class FriendsConnectionData { - public CharacterConnectionData(global::System.String __typename, global::System.Collections.Generic.IReadOnlyList? nodes = default !) + public FriendsConnectionData(global::System.String __typename, global::System.Collections.Generic.IReadOnlyList? nodes = default !) { this.__typename = __typename ?? throw new global::System.ArgumentNullException(nameof(__typename)); Nodes = nodes; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsTest.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsTest.cs index f3895dcbfc7..99931a6e508 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsTest.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/Integration/StarWarsGetFriendsTest.cs @@ -1,10 +1,9 @@ using System; -using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using HotChocolate.AspNetCore.Utilities; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; +using HotChocolate.AspNetCore.Utilities; using StrawberryShake.Transport.WebSockets; using Xunit; 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 371a434cfe5..775fab1754e 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 @@ -384,9 +384,9 @@ public SearchHero_Search_Droid(global::System.String name) /// A connection to a list of items. /// [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] - public partial class SearchHero_Search_Friends_CharacterConnection : global::System.IEquatable, ISearchHero_Search_Friends_CharacterConnection + public partial class SearchHero_Search_Friends_FriendsConnection : global::System.IEquatable, ISearchHero_Search_Friends_FriendsConnection { - public SearchHero_Search_Friends_CharacterConnection(global::System.Collections.Generic.IReadOnlyList? nodes) + public SearchHero_Search_Friends_FriendsConnection(global::System.Collections.Generic.IReadOnlyList? nodes) { Nodes = nodes; } @@ -399,7 +399,7 @@ public SearchHero_Search_Friends_CharacterConnection(global::System.Collections. get; } - public virtual global::System.Boolean Equals(SearchHero_Search_Friends_CharacterConnection? other) + public virtual global::System.Boolean Equals(SearchHero_Search_Friends_FriendsConnection? other) { if (ReferenceEquals(null, other)) { @@ -436,7 +436,7 @@ public SearchHero_Search_Friends_CharacterConnection(global::System.Collections. return false; } - return Equals((SearchHero_Search_Friends_CharacterConnection)obj); + return Equals((SearchHero_Search_Friends_FriendsConnection)obj); } public override global::System.Int32 GetHashCode() @@ -658,7 +658,7 @@ public interface ISearchHero_Search_Friends /// A connection to a list of items. /// [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] - public interface ISearchHero_Search_Friends_CharacterConnection : ISearchHero_Search_Friends + public interface ISearchHero_Search_Friends_FriendsConnection : ISearchHero_Search_Friends { } @@ -920,7 +920,7 @@ public partial class StarshipEntity [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "0.0.0.0")] public partial class HumanEntity { - public HumanEntity(global::System.String name = default !, global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsUnionList.State.CharacterConnectionData? friends = default !) + public HumanEntity(global::System.String name = default !, global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsUnionList.State.FriendsConnectionData? friends = default !) { Name = name; Friends = friends; @@ -931,7 +931,7 @@ public partial class HumanEntity get; } - public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsUnionList.State.CharacterConnectionData? Friends + public global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsUnionList.State.FriendsConnectionData? Friends { get; } @@ -1102,7 +1102,7 @@ public SearchHero_Search_Human Map(global::StrawberryShake.CodeGeneration.CSharp return new SearchHero_Search_Human(entity.Name, MapISearchHero_Search_Friends(entity.Friends, snapshot)); } - private global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsUnionList.ISearchHero_Search_Friends? MapISearchHero_Search_Friends(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsUnionList.State.CharacterConnectionData? data, global::StrawberryShake.IEntityStoreSnapshot snapshot) + private global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsUnionList.ISearchHero_Search_Friends? MapISearchHero_Search_Friends(global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsUnionList.State.FriendsConnectionData? data, global::StrawberryShake.IEntityStoreSnapshot snapshot) { if (data is null) { @@ -1110,9 +1110,9 @@ public SearchHero_Search_Human Map(global::StrawberryShake.CodeGeneration.CSharp } ISearchHero_Search_Friends returnValue = default !; - if (data?.__typename.Equals("CharacterConnection", global::System.StringComparison.Ordinal) ?? false) + if (data?.__typename.Equals("FriendsConnection", global::System.StringComparison.Ordinal) ?? false) { - returnValue = new SearchHero_Search_Friends_CharacterConnection(MapISearchHero_Search_Friends_NodesArray(data.Nodes, snapshot)); + returnValue = new SearchHero_Search_Friends_FriendsConnection(MapISearchHero_Search_Friends_NodesArray(data.Nodes, snapshot)); } else { @@ -1366,7 +1366,7 @@ public SearchHeroBuilder(global::StrawberryShake.IEntityStore entityStore, globa return _stringParser.Parse(obj.Value.GetString()!); } - private global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsUnionList.State.CharacterConnectionData? DeserializeISearchHero_Search_Friends(global::StrawberryShake.IEntityStoreUpdateSession session, global::System.Text.Json.JsonElement? obj, global::System.Collections.Generic.ISet entityIds) + private global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsUnionList.State.FriendsConnectionData? DeserializeISearchHero_Search_Friends(global::StrawberryShake.IEntityStoreUpdateSession session, global::System.Text.Json.JsonElement? obj, global::System.Collections.Generic.ISet entityIds) { if (!obj.HasValue) { @@ -1374,9 +1374,9 @@ public SearchHeroBuilder(global::StrawberryShake.IEntityStore entityStore, globa } var typename = obj.Value.GetProperty("__typename").GetString(); - if (typename?.Equals("CharacterConnection", global::System.StringComparison.Ordinal) ?? false) + if (typename?.Equals("FriendsConnection", global::System.StringComparison.Ordinal) ?? false) { - return new global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsUnionList.State.CharacterConnectionData(typename, nodes: UpdateISearchHero_Search_Friends_NodesEntityArray(session, global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "nodes"), entityIds)); + return new global::StrawberryShake.CodeGeneration.CSharp.Integration.StarWarsUnionList.State.FriendsConnectionData(typename, nodes: UpdateISearchHero_Search_Friends_NodesEntityArray(session, global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "nodes"), entityIds)); } throw new global::System.NotSupportedException(); @@ -1442,9 +1442,9 @@ public SearchHeroBuilder(global::StrawberryShake.IEntityStore entityStore, globa // 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 CharacterConnectionData + public partial class FriendsConnectionData { - public CharacterConnectionData(global::System.String __typename, global::System.Collections.Generic.IReadOnlyList? nodes = default !) + public FriendsConnectionData(global::System.String __typename, global::System.Collections.Generic.IReadOnlyList? nodes = default !) { this.__typename = __typename ?? throw new global::System.ArgumentNullException(nameof(__typename)); Nodes = nodes; 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 bd541bccca8..9fc4fa58f8d 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 @@ -1276,7 +1276,7 @@ ], "Type": { "Kind": "Object", - "Name": "CharacterConnection", + "Name": "FriendsConnection", "OfType": null }, "IsDeprecated": false, @@ -1441,7 +1441,7 @@ ], "Type": { "Kind": "Object", - "Name": "CharacterConnection", + "Name": "FriendsConnection", "OfType": null }, "IsDeprecated": false, @@ -1525,8 +1525,8 @@ }, { "Kind": "Scalar", - "Name": "Boolean", - "Description": "The `Boolean` scalar type represents `true` or `false`.", + "Name": "String", + "Description": "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.", "Fields": null, "InputFields": null, "Interfaces": null, @@ -1535,8 +1535,8 @@ }, { "Kind": "Scalar", - "Name": "String", - "Description": "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.", + "Name": "Boolean", + "Description": "The `Boolean` scalar type represents `true` or `false`.", "Fields": null, "InputFields": null, "Interfaces": null, @@ -1637,7 +1637,7 @@ ], "Type": { "Kind": "Object", - "Name": "CharacterConnection", + "Name": "FriendsConnection", "OfType": null }, "IsDeprecated": false, @@ -1811,7 +1811,7 @@ }, { "Kind": "Object", - "Name": "CharacterConnection", + "Name": "FriendsConnection", "Description": "A connection to a list of items.", "Fields": [ { @@ -1842,7 +1842,7 @@ "Name": null, "OfType": { "Kind": "Object", - "Name": "CharacterEdge", + "Name": "FriendsEdge", "OfType": null } } @@ -2044,7 +2044,7 @@ }, { "Kind": "Object", - "Name": "CharacterEdge", + "Name": "FriendsEdge", "Description": "An edge in a connection.", "Fields": [ { @@ -4519,7 +4519,7 @@ ], "Type": { "__typename": "__Type", - "Name": "CharacterConnection", + "Name": "FriendsConnection", "Kind": "Object", "Description": null, "Fields": null, @@ -4810,7 +4810,7 @@ ], "Type": { "__typename": "__Type", - "Name": "CharacterConnection", + "Name": "FriendsConnection", "Kind": "Object", "Description": null, "Fields": null, @@ -4938,9 +4938,9 @@ }, { "__typename": "__Type", - "Name": "Boolean", + "Name": "String", "Kind": "Scalar", - "Description": "The `Boolean` scalar type represents `true` or `false`.", + "Description": "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.", "Fields": null, "InputFields": null, "Interfaces": null, @@ -4950,9 +4950,9 @@ }, { "__typename": "__Type", - "Name": "String", + "Name": "Boolean", "Kind": "Scalar", - "Description": "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.", + "Description": "The `Boolean` scalar type represents `true` or `false`.", "Fields": null, "InputFields": null, "Interfaces": null, @@ -5120,7 +5120,7 @@ ], "Type": { "__typename": "__Type", - "Name": "CharacterConnection", + "Name": "FriendsConnection", "Kind": "Object", "Description": null, "Fields": null, @@ -5422,7 +5422,7 @@ }, { "__typename": "__Type", - "Name": "CharacterConnection", + "Name": "FriendsConnection", "Kind": "Object", "Description": "A connection to a list of items.", "Fields": [ @@ -5484,7 +5484,7 @@ "PossibleTypes": null, "OfType": { "__typename": "__Type", - "Name": "CharacterEdge", + "Name": "FriendsEdge", "Kind": "Object", "Description": null, "Fields": null, @@ -5818,7 +5818,7 @@ }, { "__typename": "__Type", - "Name": "CharacterEdge", + "Name": "FriendsEdge", "Kind": "Object", "Description": "An edge in a connection.", "Fields": [ diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__resources__/Schema.graphql b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__resources__/Schema.graphql index f14098ac568..06d4a8a502d 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__resources__/Schema.graphql +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.CSharp.Tests/__resources__/Schema.graphql @@ -7,43 +7,43 @@ schema { interface Character { id: ID! name: String! - friends(first: Int after: String last: Int before: String): CharacterConnection + 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] 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 +} + "A connection to a list of items." -type CharacterConnection { +type FriendsConnection { "Information to aid in pagination." pageInfo: PageInfo! "A list of edges." - edges: [CharacterEdge!] + edges: [FriendsEdge!] "A flattened list of the nodes." nodes: [Character] } "An edge in a connection." -type CharacterEdge { +type FriendsEdge { "A cursor for use in pagination." cursor: String! "The item at the end of the edge." node: Character } -type Droid implements Character { - id: ID! - name: String! - appearsIn: [Episode] - friends(first: Int after: String last: Int before: String): CharacterConnection - height(unit: Unit): Float - primaryFunction: String -} - type Human implements Character { id: ID! name: String! appearsIn: [Episode] - friends(first: Int after: String last: Int before: String): CharacterConnection + 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 @@ -75,14 +75,14 @@ type Query { } type Review { - stars: Int! commentary: String + stars: Int! } type Starship { - id: String - name: String - length: Float! + id: ID! + name: String! + length(unit: Unit): Float! } type Subscription { @@ -110,14 +110,5 @@ 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 -"The @deprecated directive is used within the type system definition language to indicate deprecated portions of a GraphQL service’s schema,such as deprecated fields on a type or deprecated enum values." -directive @deprecated("Deprecations include a reason for why it is deprecated, which is formatted using Markdown syntax (as specified by CommonMark)." reason: String = "No longer supported") on FIELD_DEFINITION | ENUM_VALUE - -"Directs the executor to include this field or fragment only when the `if` argument is true." -directive @include("Included when true." if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT - -"Directs the executor to skip this field or fragment when the `if` argument is true." -directive @skip("Skipped when true." if: Boolean!) on FIELD | 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! "Streamed when true." if: Boolean!) on FIELD 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 0f7eb04e75a..11bf5b494f1 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 @@ -264,9 +264,9 @@ namespace Foo.Bar /// A connection to a list of items. /// [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] - public partial class GetHero_Hero_Friends_CharacterConnection : global::System.IEquatable, IGetHero_Hero_Friends_CharacterConnection + public partial class GetHero_Hero_Friends_FriendsConnection : global::System.IEquatable, IGetHero_Hero_Friends_FriendsConnection { - public GetHero_Hero_Friends_CharacterConnection(global::System.Collections.Generic.IReadOnlyList? nodes) + public GetHero_Hero_Friends_FriendsConnection(global::System.Collections.Generic.IReadOnlyList? nodes) { Nodes = nodes; } @@ -279,7 +279,7 @@ namespace Foo.Bar get; } - public virtual global::System.Boolean Equals(GetHero_Hero_Friends_CharacterConnection? other) + public virtual global::System.Boolean Equals(GetHero_Hero_Friends_FriendsConnection? other) { if (ReferenceEquals(null, other)) { @@ -316,7 +316,7 @@ namespace Foo.Bar return false; } - return Equals((GetHero_Hero_Friends_CharacterConnection)obj); + return Equals((GetHero_Hero_Friends_FriendsConnection)obj); } public override global::System.Int32 GetHashCode() @@ -584,7 +584,7 @@ namespace Foo.Bar /// A connection to a list of items. /// [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] - public interface IGetHero_Hero_Friends_CharacterConnection : IGetHero_Hero_Friends + public interface IGetHero_Hero_Friends_FriendsConnection : IGetHero_Hero_Friends { } @@ -861,7 +861,7 @@ namespace Foo.Bar.State return returnValue; } - private global::Foo.Bar.IGetHero_Hero_Friends? MapIGetHero_Hero_Friends(global::Foo.Bar.State.CharacterConnectionData? data) + private global::Foo.Bar.IGetHero_Hero_Friends? MapIGetHero_Hero_Friends(global::Foo.Bar.State.FriendsConnectionData? data) { if (data is null) { @@ -869,9 +869,9 @@ namespace Foo.Bar.State } IGetHero_Hero_Friends returnValue = default !; - if (data?.__typename.Equals("CharacterConnection", global::System.StringComparison.Ordinal) ?? false) + if (data?.__typename.Equals("FriendsConnection", global::System.StringComparison.Ordinal) ?? false) { - returnValue = new GetHero_Hero_Friends_CharacterConnection(MapIGetHero_Hero_Friends_NodesArray(data.Nodes)); + returnValue = new GetHero_Hero_Friends_FriendsConnection(MapIGetHero_Hero_Friends_NodesArray(data.Nodes)); } else { @@ -1042,7 +1042,7 @@ namespace Foo.Bar.State return _stringParser.Parse(obj.Value.GetString()!); } - private global::Foo.Bar.State.CharacterConnectionData? DeserializeIGetHero_Hero_Friends(global::System.Text.Json.JsonElement? obj) + private global::Foo.Bar.State.FriendsConnectionData? DeserializeIGetHero_Hero_Friends(global::System.Text.Json.JsonElement? obj) { if (!obj.HasValue) { @@ -1050,9 +1050,9 @@ namespace Foo.Bar.State } var typename = obj.Value.GetProperty("__typename").GetString(); - if (typename?.Equals("CharacterConnection", global::System.StringComparison.Ordinal) ?? false) + if (typename?.Equals("FriendsConnection", global::System.StringComparison.Ordinal) ?? false) { - return new global::Foo.Bar.State.CharacterConnectionData(typename, nodes: DeserializeICharacterDataArray(global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "nodes"))); + return new global::Foo.Bar.State.FriendsConnectionData(typename, nodes: DeserializeICharacterDataArray(global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "nodes"))); } throw new global::System.NotSupportedException(); @@ -1099,7 +1099,7 @@ namespace Foo.Bar.State [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "0.0.0.0")] public partial class DroidData : ISearchResultData, ICharacterData { - public DroidData(global::System.String __typename, global::System.String? name = default !, global::System.String? primaryFunction = default !, global::Foo.Bar.State.CharacterConnectionData? friends = default !) + public DroidData(global::System.String __typename, global::System.String? name = default !, global::System.String? primaryFunction = default !, global::Foo.Bar.State.FriendsConnectionData? friends = default !) { this.__typename = __typename ?? throw new global::System.ArgumentNullException(nameof(__typename)); Name = name; @@ -1122,7 +1122,7 @@ namespace Foo.Bar.State get; } - public global::Foo.Bar.State.CharacterConnectionData? Friends + public global::Foo.Bar.State.FriendsConnectionData? Friends { get; } @@ -1132,7 +1132,7 @@ namespace Foo.Bar.State [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "0.0.0.0")] public partial class HumanData : ISearchResultData, ICharacterData { - public HumanData(global::System.String __typename, global::System.String? name = default !, global::System.String? homePlanet = default !, global::Foo.Bar.State.CharacterConnectionData? friends = default !) + public HumanData(global::System.String __typename, global::System.String? name = default !, global::System.String? homePlanet = default !, global::Foo.Bar.State.FriendsConnectionData? friends = default !) { this.__typename = __typename ?? throw new global::System.ArgumentNullException(nameof(__typename)); Name = name; @@ -1155,7 +1155,7 @@ namespace Foo.Bar.State get; } - public global::Foo.Bar.State.CharacterConnectionData? Friends + public global::Foo.Bar.State.FriendsConnectionData? Friends { get; } @@ -1164,9 +1164,9 @@ namespace Foo.Bar.State // 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 CharacterConnectionData + public partial class FriendsConnectionData { - public CharacterConnectionData(global::System.String __typename, global::System.Collections.Generic.IReadOnlyList? nodes = default !) + public FriendsConnectionData(global::System.String __typename, global::System.Collections.Generic.IReadOnlyList? nodes = default !) { this.__typename = __typename ?? throw new global::System.ArgumentNullException(nameof(__typename)); Nodes = nodes; 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 ab3b3c2a09d..9eb03ee1b11 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 @@ -264,9 +264,9 @@ namespace Foo.Bar /// A connection to a list of items. /// [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] - public partial class GetHero_Hero_Friends_CharacterConnection : global::System.IEquatable, IGetHero_Hero_Friends_CharacterConnection + public partial class GetHero_Hero_Friends_FriendsConnection : global::System.IEquatable, IGetHero_Hero_Friends_FriendsConnection { - public GetHero_Hero_Friends_CharacterConnection(global::System.Collections.Generic.IReadOnlyList? nodes) + public GetHero_Hero_Friends_FriendsConnection(global::System.Collections.Generic.IReadOnlyList? nodes) { Nodes = nodes; } @@ -279,7 +279,7 @@ namespace Foo.Bar get; } - public virtual global::System.Boolean Equals(GetHero_Hero_Friends_CharacterConnection? other) + public virtual global::System.Boolean Equals(GetHero_Hero_Friends_FriendsConnection? other) { if (ReferenceEquals(null, other)) { @@ -316,7 +316,7 @@ namespace Foo.Bar return false; } - return Equals((GetHero_Hero_Friends_CharacterConnection)obj); + return Equals((GetHero_Hero_Friends_FriendsConnection)obj); } public override global::System.Int32 GetHashCode() @@ -584,7 +584,7 @@ namespace Foo.Bar /// A connection to a list of items. /// [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "11.0.0")] - public interface IGetHero_Hero_Friends_CharacterConnection : IGetHero_Hero_Friends + public interface IGetHero_Hero_Friends_FriendsConnection : IGetHero_Hero_Friends { } @@ -858,7 +858,7 @@ namespace Foo.Bar.State [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "0.0.0.0")] public partial class DroidEntity { - public DroidEntity(global::System.String name = default !, global::System.String? primaryFunction = default !, global::Foo.Bar.State.CharacterConnectionData? friends = default !) + public DroidEntity(global::System.String name = default !, global::System.String? primaryFunction = default !, global::Foo.Bar.State.FriendsConnectionData? friends = default !) { Name = name; PrimaryFunction = primaryFunction; @@ -875,7 +875,7 @@ namespace Foo.Bar.State get; } - public global::Foo.Bar.State.CharacterConnectionData? Friends + public global::Foo.Bar.State.FriendsConnectionData? Friends { get; } @@ -885,7 +885,7 @@ namespace Foo.Bar.State [global::System.CodeDom.Compiler.GeneratedCode("StrawberryShake", "0.0.0.0")] public partial class HumanEntity { - public HumanEntity(global::System.String name = default !, global::System.String? homePlanet = default !, global::Foo.Bar.State.CharacterConnectionData? friends = default !) + public HumanEntity(global::System.String name = default !, global::System.String? homePlanet = default !, global::Foo.Bar.State.FriendsConnectionData? friends = default !) { Name = name; HomePlanet = homePlanet; @@ -902,7 +902,7 @@ namespace Foo.Bar.State get; } - public global::Foo.Bar.State.CharacterConnectionData? Friends + public global::Foo.Bar.State.FriendsConnectionData? Friends { get; } @@ -1014,7 +1014,7 @@ namespace Foo.Bar.State return new GetHero_Hero_Droid(entity.Name, entity.PrimaryFunction, MapIGetHero_Hero_Friends(entity.Friends, snapshot)); } - private global::Foo.Bar.IGetHero_Hero_Friends? MapIGetHero_Hero_Friends(global::Foo.Bar.State.CharacterConnectionData? data, global::StrawberryShake.IEntityStoreSnapshot snapshot) + private global::Foo.Bar.IGetHero_Hero_Friends? MapIGetHero_Hero_Friends(global::Foo.Bar.State.FriendsConnectionData? data, global::StrawberryShake.IEntityStoreSnapshot snapshot) { if (data is null) { @@ -1022,9 +1022,9 @@ namespace Foo.Bar.State } IGetHero_Hero_Friends returnValue = default !; - if (data?.__typename.Equals("CharacterConnection", global::System.StringComparison.Ordinal) ?? false) + if (data?.__typename.Equals("FriendsConnection", global::System.StringComparison.Ordinal) ?? false) { - returnValue = new GetHero_Hero_Friends_CharacterConnection(MapIGetHero_Hero_Friends_NodesArray(data.Nodes, snapshot)); + returnValue = new GetHero_Hero_Friends_FriendsConnection(MapIGetHero_Hero_Friends_NodesArray(data.Nodes, snapshot)); } else { @@ -1095,7 +1095,7 @@ namespace Foo.Bar.State return new GetHero_Hero_Human(entity.Name, entity.HomePlanet, MapIGetHero_Hero_Friends(entity.Friends, snapshot)); } - private global::Foo.Bar.IGetHero_Hero_Friends? MapIGetHero_Hero_Friends(global::Foo.Bar.State.CharacterConnectionData? data, global::StrawberryShake.IEntityStoreSnapshot snapshot) + private global::Foo.Bar.IGetHero_Hero_Friends? MapIGetHero_Hero_Friends(global::Foo.Bar.State.FriendsConnectionData? data, global::StrawberryShake.IEntityStoreSnapshot snapshot) { if (data is null) { @@ -1103,9 +1103,9 @@ namespace Foo.Bar.State } IGetHero_Hero_Friends returnValue = default !; - if (data?.__typename.Equals("CharacterConnection", global::System.StringComparison.Ordinal) ?? false) + if (data?.__typename.Equals("FriendsConnection", global::System.StringComparison.Ordinal) ?? false) { - returnValue = new GetHero_Hero_Friends_CharacterConnection(MapIGetHero_Hero_Friends_NodesArray(data.Nodes, snapshot)); + returnValue = new GetHero_Hero_Friends_FriendsConnection(MapIGetHero_Hero_Friends_NodesArray(data.Nodes, snapshot)); } else { @@ -1318,7 +1318,7 @@ namespace Foo.Bar.State return _stringParser.Parse(obj.Value.GetString()!); } - private global::Foo.Bar.State.CharacterConnectionData? DeserializeIGetHero_Hero_Friends(global::StrawberryShake.IEntityStoreUpdateSession session, global::System.Text.Json.JsonElement? obj, global::System.Collections.Generic.ISet entityIds) + private global::Foo.Bar.State.FriendsConnectionData? DeserializeIGetHero_Hero_Friends(global::StrawberryShake.IEntityStoreUpdateSession session, global::System.Text.Json.JsonElement? obj, global::System.Collections.Generic.ISet entityIds) { if (!obj.HasValue) { @@ -1326,9 +1326,9 @@ namespace Foo.Bar.State } var typename = obj.Value.GetProperty("__typename").GetString(); - if (typename?.Equals("CharacterConnection", global::System.StringComparison.Ordinal) ?? false) + if (typename?.Equals("FriendsConnection", global::System.StringComparison.Ordinal) ?? false) { - return new global::Foo.Bar.State.CharacterConnectionData(typename, nodes: UpdateIGetHero_Hero_Friends_NodesEntityArray(session, global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "nodes"), entityIds)); + return new global::Foo.Bar.State.FriendsConnectionData(typename, nodes: UpdateIGetHero_Hero_Friends_NodesEntityArray(session, global::StrawberryShake.Json.JsonElementExtensions.GetPropertyOrNull(obj, "nodes"), entityIds)); } throw new global::System.NotSupportedException(); @@ -1394,9 +1394,9 @@ namespace Foo.Bar.State // 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 CharacterConnectionData + public partial class FriendsConnectionData { - public CharacterConnectionData(global::System.String __typename, global::System.Collections.Generic.IReadOnlyList? nodes = default !) + public FriendsConnectionData(global::System.String __typename, global::System.Collections.Generic.IReadOnlyList? nodes = default !) { this.__typename = __typename ?? throw new global::System.ArgumentNullException(nameof(__typename)); Nodes = nodes; diff --git a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.Tests/Mappers/DataTypeMapperTests.cs b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.Tests/Mappers/DataTypeMapperTests.cs index c8e73712ee0..2c1ebfc2d33 100644 --- a/src/StrawberryShake/CodeGeneration/test/CodeGeneration.Tests/Mappers/DataTypeMapperTests.cs +++ b/src/StrawberryShake/CodeGeneration/test/CodeGeneration.Tests/Mappers/DataTypeMapperTests.cs @@ -61,7 +61,7 @@ query GetHeroEdges { type => { Assert.Equal( - "CharacterConnectionData", + "FriendsConnectionData", type.RuntimeType.Name); Assert.Equal( "Foo.Bar.State", @@ -91,7 +91,7 @@ query GetHeroEdges { type => { Assert.Equal( - "CharacterEdgeData", + "FriendsEdgeData", type.RuntimeType.Name); Assert.Equal( "Foo.Bar.State", @@ -115,7 +115,7 @@ query GetHeroEdges { public void MapDataTypeDescriptors_DataUnionType() { // arrange - var clientModel = + ClientModel clientModel = CreateClientModelAsync("union.query3.graphql", "union.schema.graphql"); // act diff --git a/website/src/docs/hotchocolate/api-reference/migrate-from-11-to-12.md b/website/src/docs/hotchocolate/api-reference/migrate-from-11-to-12.md index a0308e6eb0b..6cc7a9495c9 100644 --- a/website/src/docs/hotchocolate/api-reference/migrate-from-11-to-12.md +++ b/website/src/docs/hotchocolate/api-reference/migrate-from-11-to-12.md @@ -45,3 +45,63 @@ services .AddGraphQLServer() .AddType(() => new UrlType("Url")); ``` + +## Pagination + +### ConnectionType + +We have changed the way we infer the name for the connection type when using cursor-based pagination. By default, the connection name is now inferred from the field name instead of the type name. + +```SDL +type Person { + friends: [Person] +} +``` + +In version 11, we would have created a connection named `PersonConnection`. + +```SDL +type Person { + friends(first: Int, last: Int, after: String, before: String): PersonConnection +} +``` + +In version 12, we now will infer the connection name as `FriendsConnection`. + +```SDL +type Person { + friends(first: Int, last: Int, after: String, before: String): FriendsConnection +} +``` + +To keep your schema stable when you migrate, you can switch the behavior back to how you did in version 11. + +```csharp +services + .AddGraphQLServer() + .SetPagingOptions(new PagingOptions{ InferConnectionNameFromField = false }) + ... +``` + +Moreover, you now can explicitly define the connection name per field. + +```csharp +public class Person +{ + [UsePaging(ConnectionName = "Persons")] + public IQueryable GetFriends() => ... +} +``` + +### MongoDB Paging + +In version 11 we had the `UseMongoDbPagingAttribute` and the `UseMongoDbOffsetPagingAttribute`, which we removed with version 11. In version 12 you now can use the standard attributes `UsePagingAttribute` and `UseOffsetPagingAttribute`. + +To use these attributes with mongo, you need to register the mongo paging provider with your GraphQL configuration: + +```csharp +services + .AddGraphQLServer() + .AddMongoDbPagingProviders() + ... +```