diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 711794a8c4f..a76f831520f 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -23,7 +23,6 @@
"args": [
"build",
"src/All.sln",
- "-c Debug",
// Ask dotnet build to generate full paths for file names.
"/property:GenerateFullPaths=true",
// Do not generate summary otherwise it leads to duplicate errors in Problems panel
diff --git a/global.json b/global.json
index 5c393dc1ff6..56187be55a3 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,5 @@
{
"sdk": {
- "version": "7.0.100-preview.7.22377.5",
- "rollForward": "latestMinor"
+ "version": "7.0.100-preview.7.22377.5"
}
-}
\ No newline at end of file
+}
diff --git a/src/HotChocolate/Core/src/Abstractions/WellKnownContextData.cs b/src/HotChocolate/Core/src/Abstractions/WellKnownContextData.cs
index 02af101380d..b5819e47746 100644
--- a/src/HotChocolate/Core/src/Abstractions/WellKnownContextData.cs
+++ b/src/HotChocolate/Core/src/Abstractions/WellKnownContextData.cs
@@ -183,6 +183,11 @@ public static class WellKnownContextData
///
public const string InternalId = "HotChocolate.Relay.Node.Id.InternalId";
+ ///
+ /// The key to get the id type name from the context data.
+ ///
+ public const string InternalTypeName = "HotChocolate.Relay.Node.Id.InternalTypeName";
+
///
/// The key to get the id type from the context data.
///
diff --git a/src/HotChocolate/Core/src/Execution/HotChocolate.Execution.csproj b/src/HotChocolate/Core/src/Execution/HotChocolate.Execution.csproj
index 9c9efc2c24f..011949fc289 100644
--- a/src/HotChocolate/Core/src/Execution/HotChocolate.Execution.csproj
+++ b/src/HotChocolate/Core/src/Execution/HotChocolate.Execution.csproj
@@ -14,6 +14,7 @@
+
diff --git a/src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.cs b/src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.cs
index f52ffe79189..af4419e70f1 100644
--- a/src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.cs
+++ b/src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.cs
@@ -466,7 +466,7 @@ private void CollectFields(CompilerContext context)
responseName: responseName,
isParallelExecutable: field.IsParallelExecutable,
arguments: CoerceArgumentValues(field, selection, responseName),
- includeCondition: includeCondition);
+ includeConditions: includeCondition == 0 ? null : new[] { includeCondition });
context.Fields.Add(responseName, preparedSelection);
diff --git a/src/HotChocolate/Core/src/Execution/Processing/Selection.cs b/src/HotChocolate/Core/src/Execution/Processing/Selection.cs
index ae9b373441b..300c8bd57de 100644
--- a/src/HotChocolate/Core/src/Execution/Processing/Selection.cs
+++ b/src/HotChocolate/Core/src/Execution/Processing/Selection.cs
@@ -29,7 +29,7 @@ public class Selection : ISelection
FieldNode syntaxNode,
string responseName,
IArgumentMap? arguments = null,
- long includeCondition = 0,
+ long[]? includeConditions = null,
bool isInternal = false,
bool isParallelExecutable = true,
FieldDelegate? resolverPipeline = null,
@@ -46,9 +46,7 @@ public class Selection : ISelection
PureResolver = pureResolver;
Strategy = InferStrategy(!isParallelExecutable, pureResolver is not null);
- _includeConditions = includeCondition is 0
- ? Array.Empty()
- : new[] { includeCondition };
+ _includeConditions = includeConditions ?? Array.Empty();
_flags = isInternal ? Flags.Internal : Flags.None;
@@ -152,6 +150,8 @@ public bool HasStreamDirective(long includeFlags)
public bool IsConditional
=> _includeConditions.Length > 0 || (_flags & Flags.Internal) == Flags.Internal;
+ internal ReadOnlySpan IncludeConditions => _includeConditions;
+
public bool IsIncluded(long includeFlags, bool allowInternals = false)
{
// in most case we do not have any include condition,
diff --git a/src/HotChocolate/Core/src/Fetching/Extensions/ObjectFieldDataLoaderExtensions.cs b/src/HotChocolate/Core/src/Fetching/Extensions/ObjectFieldDataLoaderExtensions.cs
index 0247e4b4a9f..038c59a28ec 100644
--- a/src/HotChocolate/Core/src/Fetching/Extensions/ObjectFieldDataLoaderExtensions.cs
+++ b/src/HotChocolate/Core/src/Fetching/Extensions/ObjectFieldDataLoaderExtensions.cs
@@ -67,7 +67,7 @@ public static class DataLoaderObjectFieldExtensions
dataLoaderType);
},
definition,
- ApplyConfigurationOn.Completion));
+ ApplyConfigurationOn.BeforeCompletion));
});
return descriptor;
diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/ConnectionType.cs b/src/HotChocolate/Core/src/Types.CursorPagination/ConnectionType.cs
index 1e99cee9c40..d6c150c43d5 100644
--- a/src/HotChocolate/Core/src/Types.CursorPagination/ConnectionType.cs
+++ b/src/HotChocolate/Core/src/Types.CursorPagination/ConnectionType.cs
@@ -58,14 +58,14 @@ internal class ConnectionType
TypeContext.Output);
},
Definition,
- ApplyConfigurationOn.Naming,
+ ApplyConfigurationOn.BeforeNaming,
nodeType,
TypeDependencyKind.Named));
Definition.Configurations.Add(
new CompleteConfiguration(
(c, _) => EdgeType = c.GetType(TypeReference.Create(edgeTypeName)),
Definition,
- ApplyConfigurationOn.Completion));
+ ApplyConfigurationOn.BeforeCompletion));
}
internal ConnectionType(ITypeReference nodeType, bool withTotalCount)
@@ -110,7 +110,7 @@ internal ConnectionType(ITypeReference nodeType, bool withTotalCount)
TypeContext.Output);
},
Definition,
- ApplyConfigurationOn.Naming,
+ ApplyConfigurationOn.BeforeNaming,
nodeType,
TypeDependencyKind.Named));
Definition.Configurations.Add(
@@ -120,7 +120,7 @@ internal ConnectionType(ITypeReference nodeType, bool withTotalCount)
EdgeType = c.GetType(edgeType);
},
Definition,
- ApplyConfigurationOn.Completion));
+ ApplyConfigurationOn.BeforeCompletion));
}
///
diff --git a/src/HotChocolate/Core/src/Types.CursorPagination/EdgeType.cs b/src/HotChocolate/Core/src/Types.CursorPagination/EdgeType.cs
index a1b73424299..25f4f847487 100644
--- a/src/HotChocolate/Core/src/Types.CursorPagination/EdgeType.cs
+++ b/src/HotChocolate/Core/src/Types.CursorPagination/EdgeType.cs
@@ -32,7 +32,7 @@ internal sealed class EdgeType : ObjectType, IEdgeType
new CompleteConfiguration(
(c, _) => NodeType = c.GetType(nodeType),
Definition,
- ApplyConfigurationOn.Completion));
+ ApplyConfigurationOn.BeforeCompletion));
}
internal EdgeType(ITypeReference nodeType)
@@ -54,14 +54,14 @@ internal EdgeType(ITypeReference nodeType)
((ObjectTypeDefinition)d).Name = NameHelper.CreateEdgeName(ConnectionName);
},
Definition,
- ApplyConfigurationOn.Naming,
+ ApplyConfigurationOn.BeforeNaming,
nodeType,
TypeDependencyKind.Named));
Definition.Configurations.Add(
new CompleteConfiguration(
(c, _) => NodeType = c.GetType(nodeType),
Definition,
- ApplyConfigurationOn.Completion));
+ ApplyConfigurationOn.BeforeCompletion));
}
///
diff --git a/src/HotChocolate/Core/src/Types.Json/FromJsonSchemaDirective.cs b/src/HotChocolate/Core/src/Types.Json/FromJsonSchemaDirective.cs
index 9802b09b398..7a30661e2fd 100644
--- a/src/HotChocolate/Core/src/Types.Json/FromJsonSchemaDirective.cs
+++ b/src/HotChocolate/Core/src/Types.Json/FromJsonSchemaDirective.cs
@@ -42,7 +42,7 @@ internal sealed class FromJsonSchemaDirective : ISchemaDirective
throw ThrowHelper.CannotInferTypeFromJsonObj(ctx.Type.Name);
},
fieldDef,
- ApplyConfigurationOn.Completion));
+ ApplyConfigurationOn.BeforeCompletion));
}
}
diff --git a/src/HotChocolate/Core/src/Types.OffsetPagination/CollectionSegmentType~1.cs b/src/HotChocolate/Core/src/Types.OffsetPagination/CollectionSegmentType~1.cs
index 774a6f9a479..5c8c211cab4 100644
--- a/src/HotChocolate/Core/src/Types.OffsetPagination/CollectionSegmentType~1.cs
+++ b/src/HotChocolate/Core/src/Types.OffsetPagination/CollectionSegmentType~1.cs
@@ -41,7 +41,7 @@ internal class CollectionSegmentType
definition.Name = type.NamedType().Name + "CollectionSegment";
},
Definition,
- ApplyConfigurationOn.Naming,
+ ApplyConfigurationOn.BeforeNaming,
nodeType,
TypeDependencyKind.Named));
}
@@ -57,7 +57,7 @@ internal class CollectionSegmentType
nodes.Type = TypeReference.Parse($"[{ItemType.Print()}]", TypeContext.Output);
},
Definition,
- ApplyConfigurationOn.Naming,
+ ApplyConfigurationOn.BeforeNaming,
nodeType,
TypeDependencyKind.Named));
diff --git a/src/HotChocolate/Core/src/Types/SchemaErrorBuilder.cs b/src/HotChocolate/Core/src/Types/SchemaErrorBuilder.cs
index d84f2d2a4f0..1a95a64166f 100644
--- a/src/HotChocolate/Core/src/Types/SchemaErrorBuilder.cs
+++ b/src/HotChocolate/Core/src/Types/SchemaErrorBuilder.cs
@@ -6,10 +6,9 @@
namespace HotChocolate;
-public partial class SchemaErrorBuilder
- : ISchemaErrorBuilder
+public partial class SchemaErrorBuilder : ISchemaErrorBuilder
{
- private Error _error = new Error();
+ private readonly Error _error = new();
public ISchemaErrorBuilder SetMessage(string message)
{
@@ -84,5 +83,5 @@ public ISchemaError Build()
return _error.Clone();
}
- public static SchemaErrorBuilder New() => new SchemaErrorBuilder();
+ public static SchemaErrorBuilder New() => new();
}
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 2d4bb20a401..e29b32501fe 100644
--- a/src/HotChocolate/Core/src/Types/Types/Descriptors/Contracts/ConfigurationKind.cs
+++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/Contracts/ConfigurationKind.cs
@@ -1,8 +1,33 @@
namespace HotChocolate.Types;
+///
+/// This enum defines the events on which configurations can be applied.
+///
public enum ApplyConfigurationOn
{
+
+ ///
+ /// Before the type is created.
+ ///
Create,
- Naming,
- Completion
+
+ ///
+ /// Before the types name is completed.
+ ///
+ BeforeNaming,
+
+ ///
+ /// After the types name is completed.
+ ///
+ AfterNaming,
+
+ ///
+ /// Before the type is completed.
+ ///
+ BeforeCompletion,
+
+ ///
+ /// After the type is completed.
+ ///
+ AfterCompletion
}
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 eb61edc3a9b..1e0774cb60c 100644
--- a/src/HotChocolate/Core/src/Types/Types/Descriptors/DescriptorBase~1.cs
+++ b/src/HotChocolate/Core/src/Types/Types/Descriptors/DescriptorBase~1.cs
@@ -109,7 +109,7 @@ private void OnBeforeCreate(Action configure)
var configuration = new CompleteConfiguration(
(c, d) => configure(c, (T)d),
Definition,
- ApplyConfigurationOn.Naming);
+ ApplyConfigurationOn.BeforeNaming);
Definition.Configurations.Add(configuration);
@@ -130,7 +130,7 @@ private void OnBeforeCreate(Action configure)
var configuration = new CompleteConfiguration(
(c, d) => configure(c, (T)d),
Definition,
- ApplyConfigurationOn.Completion);
+ ApplyConfigurationOn.BeforeCompletion);
Definition.Configurations.Add(configuration);
diff --git a/src/HotChocolate/Core/src/Types/Types/Extensions/DirectiveCollectionExtensions.cs b/src/HotChocolate/Core/src/Types/Types/Extensions/DirectiveCollectionExtensions.cs
index c410201677c..bbbd28b8c41 100644
--- a/src/HotChocolate/Core/src/Types/Types/Extensions/DirectiveCollectionExtensions.cs
+++ b/src/HotChocolate/Core/src/Types/Types/Extensions/DirectiveCollectionExtensions.cs
@@ -24,7 +24,7 @@ public static T SingleOrDefault(this IDirectiveCollection directives)
}
}
- return default;
+ return default!;
}
internal static IValueNode? SkipValue(this IReadOnlyList directives)
diff --git a/src/HotChocolate/Core/src/Types/Types/Pagination/PagingHelper.cs b/src/HotChocolate/Core/src/Types/Types/Pagination/PagingHelper.cs
index ca03c374c32..53150486d56 100644
--- a/src/HotChocolate/Core/src/Types/Types/Pagination/PagingHelper.cs
+++ b/src/HotChocolate/Core/src/Types/Types/Pagination/PagingHelper.cs
@@ -43,7 +43,7 @@ public static class PagingHelper
options,
placeholder),
definition,
- ApplyConfigurationOn.Completion));
+ ApplyConfigurationOn.BeforeCompletion));
return descriptor;
}
diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/Descriptors/NodeDescriptor~1.cs b/src/HotChocolate/Core/src/Types/Types/Relay/Descriptors/NodeDescriptor~1.cs
index 57b6e9e6e53..5585fcb0fd1 100644
--- a/src/HotChocolate/Core/src/Types/Types/Relay/Descriptors/NodeDescriptor~1.cs
+++ b/src/HotChocolate/Core/src/Types/Types/Relay/Descriptors/NodeDescriptor~1.cs
@@ -33,10 +33,16 @@ public NodeDescriptor(IObjectTypeDescriptor descriptor)
{
_typeDescriptor = descriptor;
- _typeDescriptor
- .Implements()
- .Extend()
- .OnBeforeCompletion(OnCompleteDefinition);
+ // we use the CompleteConfiguration instead of the higher level api since
+ // we want to target a specific event.
+ var ownerDef = _typeDescriptor.Implements().Extend().Definition;
+
+ var configuration = new CompleteConfiguration(
+ (c, d) => OnCompleteDefinition(c, (ObjectTypeDefinition)d),
+ ownerDef,
+ ApplyConfigurationOn.AfterNaming);
+
+ ownerDef.Configurations.Add(configuration);
}
private void OnCompleteDefinition(
@@ -45,7 +51,21 @@ public NodeDescriptor(IObjectTypeDescriptor descriptor)
{
if (Definition.ResolverField is null)
{
- ResolveNodeWith();
+ var resolverMethod =
+ Context.TypeInspector.GetNodeResolverMethod(typeof(TNode), typeof(TNode));
+
+ // we allow a node to not have a node resolver.
+ // this opens up type interceptors bringing these in later.
+ // we also introduced a validation option that makes sure that node resolvers are
+ // available after the schema is completed.
+ if (resolverMethod is not null)
+ {
+ ResolveNodeWith(resolverMethod);
+ }
+ else
+ {
+ ConfigureNodeField();
+ }
}
CompleteResolver(context, definition);
@@ -150,11 +170,13 @@ public INodeDescriptor IdField(MemberInfo propertyOrMethod)
}
///
- public IObjectFieldDescriptor ResolveNodeWith() =>
- ResolveNodeWith(
+ public IObjectFieldDescriptor ResolveNodeWith()
+ {
+ return ResolveNodeWith(
Context.TypeInspector.GetNodeResolverMethod(
typeof(TNode),
typeof(TResolver))!);
+ }
///
public IObjectFieldDescriptor ResolveNodeWith(Type type) =>
diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/Extensions/RelayIdFieldHelpers.cs b/src/HotChocolate/Core/src/Types/Types/Relay/Extensions/RelayIdFieldHelpers.cs
index 28ce042ab58..96319aa1f81 100644
--- a/src/HotChocolate/Core/src/Types/Types/Relay/Extensions/RelayIdFieldHelpers.cs
+++ b/src/HotChocolate/Core/src/Types/Types/Relay/Extensions/RelayIdFieldHelpers.cs
@@ -105,7 +105,7 @@ internal static class RelayIdFieldHelpers
placeholder,
typeName),
definition,
- ApplyConfigurationOn.Completion);
+ ApplyConfigurationOn.BeforeCompletion);
definition.Configurations.Add(configuration);
}
diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/NodeFieldResolvers.cs b/src/HotChocolate/Core/src/Types/Types/Relay/NodeFieldResolvers.cs
index ded5e092658..684a0de14b3 100644
--- a/src/HotChocolate/Core/src/Types/Types/Relay/NodeFieldResolvers.cs
+++ b/src/HotChocolate/Core/src/Types/Types/Relay/NodeFieldResolvers.cs
@@ -2,6 +2,7 @@
using System;
using System.Buffers;
+using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using HotChocolate.Execution;
@@ -36,7 +37,7 @@ internal static class NodeFieldResolvers
type.ContextData.TryGetValue(NodeResolver, out var o) &&
o is NodeResolverInfo nodeResolverInfo)
{
- SetLocalContext(context, nodeId, deserializedId, typeName);
+ SetLocalContext(context, nodeId, deserializedId, type);
TryReplaceArguments(context, nodeResolverInfo, Id, nodeId);
await nodeResolverInfo.Pipeline.Invoke(context);
@@ -86,7 +87,7 @@ internal static class NodeFieldResolvers
{
var nodeContext = context.Clone();
- SetLocalContext(nodeContext, nodeId, deserializedId, typeName);
+ SetLocalContext(nodeContext, nodeId, deserializedId, type);
TryReplaceArguments(nodeContext, nodeResolverInfo, Ids, nodeId);
tasks[i] = ExecutePipelineAsync(nodeContext, nodeResolverInfo);
@@ -155,7 +156,7 @@ internal static class NodeFieldResolvers
{
var nodeContext = context.Clone();
- SetLocalContext(nodeContext, nodeId, deserializedId, typeName);
+ SetLocalContext(nodeContext, nodeId, deserializedId, type);
TryReplaceArguments(nodeContext, nodeResolverInfo, Ids, nodeId);
result[0] = await ExecutePipelineAsync(nodeContext, nodeResolverInfo);
@@ -188,11 +189,12 @@ internal static class NodeFieldResolvers
IMiddlewareContext context,
StringValueNode nodeId,
IdValue deserializedId,
- string typeName)
+ ObjectType type)
{
context.SetLocalState(NodeId, nodeId.Value);
context.SetLocalState(InternalId, deserializedId.Value);
- context.SetLocalState(InternalType, typeName);
+ context.SetLocalState(InternalType, type);
+ context.SetLocalState(InternalTypeName, type.Name);
context.SetLocalState(WellKnownContextData.IdValue, deserializedId);
}
diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/NodeFieldTypeInterceptor.cs b/src/HotChocolate/Core/src/Types/Types/Relay/NodeFieldTypeInterceptor.cs
index 6ff878f4f35..15d541268e9 100644
--- a/src/HotChocolate/Core/src/Types/Types/Relay/NodeFieldTypeInterceptor.cs
+++ b/src/HotChocolate/Core/src/Types/Types/Relay/NodeFieldTypeInterceptor.cs
@@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Linq;
using HotChocolate.Configuration;
-using HotChocolate.Language;
using HotChocolate.Types.Descriptors;
using HotChocolate.Types.Descriptors.Definitions;
using HotChocolate.Types.Introspection;
diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/NodeResolverInfo.cs b/src/HotChocolate/Core/src/Types/Types/Relay/NodeResolverInfo.cs
index a1a092f82ba..beedb08878c 100644
--- a/src/HotChocolate/Core/src/Types/Types/Relay/NodeResolverInfo.cs
+++ b/src/HotChocolate/Core/src/Types/Types/Relay/NodeResolverInfo.cs
@@ -1,19 +1,22 @@
#nullable enable
+using System.Diagnostics.CodeAnalysis;
using HotChocolate.Resolvers;
namespace HotChocolate.Types.Relay;
///
-/// The node resolver info is used by the fields node and nodes to execute
+/// The node resolver info is used by the fields node and nodes to execute
/// the node resolver pipeline for a specific node.
///
internal sealed class NodeResolverInfo
{
- public NodeResolverInfo(Argument? id, FieldDelegate pipeline)
+ public NodeResolverInfo(ObjectField? resolverField, FieldDelegate pipeline)
{
- Id = id;
+ Id = resolverField?.Arguments[0];
Pipeline = pipeline;
+ QueryField = resolverField;
+ IsQueryFieldResolver = resolverField is not null;
}
///
@@ -26,4 +29,18 @@ public NodeResolverInfo(Argument? id, FieldDelegate pipeline)
/// Gets the node resolver pipeline.
///
public FieldDelegate Pipeline { get; }
+
+ ///
+ /// Gets the query field from which we inferred the node resolver.
+ ///
+ public ObjectField? QueryField { get; }
+
+ ///
+ /// Defines if the node resolver was inferred from a query field.
+ ///
+ ///
+#if NET5_0_OR_GREATER
+ [MemberNotNullWhen(true, nameof(QueryField), nameof(Id))]
+#endif
+ public bool IsQueryFieldResolver { get; }
}
diff --git a/src/HotChocolate/Core/src/Types/Types/Relay/NodeResolverTypeInterceptor.cs b/src/HotChocolate/Core/src/Types/Types/Relay/NodeResolverTypeInterceptor.cs
index 860ca3b8e6c..2eedf10d5bd 100644
--- a/src/HotChocolate/Core/src/Types/Types/Relay/NodeResolverTypeInterceptor.cs
+++ b/src/HotChocolate/Core/src/Types/Types/Relay/NodeResolverTypeInterceptor.cs
@@ -191,7 +191,8 @@ public override void OnAfterCompleteTypes()
{
var fieldName = (string)node[NodeResolver]!;
var field = QueryType.Fields[fieldName];
- node[NodeResolver] = new NodeResolverInfo(field.Arguments[0], field.Middleware);
+
+ node[NodeResolver] = new NodeResolverInfo(field, field.Middleware);
}
}
}
diff --git a/src/HotChocolate/Core/src/Types/Types/TypeSystemObjectBase~1.cs b/src/HotChocolate/Core/src/Types/Types/TypeSystemObjectBase~1.cs
index 19408246d8f..bf49d0ee093 100644
--- a/src/HotChocolate/Core/src/Types/Types/TypeSystemObjectBase~1.cs
+++ b/src/HotChocolate/Core/src/Types/Types/TypeSystemObjectBase~1.cs
@@ -80,7 +80,7 @@ internal sealed override void CompleteName(ITypeCompletionContext context)
OnBeforeCompleteName(context, definition, definition.ContextData);
- ExecuteConfigurations(context, definition, ApplyConfigurationOn.Naming);
+ ExecuteConfigurations(context, definition, ApplyConfigurationOn.BeforeNaming);
OnCompleteName(context, definition);
Debug.Assert(
@@ -99,6 +99,7 @@ internal sealed override void CompleteName(ITypeCompletionContext context)
}
OnAfterCompleteName(context, definition, definition.ContextData);
+ ExecuteConfigurations(context, definition, ApplyConfigurationOn.AfterNaming);
MarkNamed();
}
@@ -121,7 +122,7 @@ internal sealed override void CompleteType(ITypeCompletionContext context)
OnBeforeCompleteType(context, definition, definition.ContextData);
- ExecuteConfigurations(context, definition, ApplyConfigurationOn.Completion);
+ ExecuteConfigurations(context, definition, ApplyConfigurationOn.BeforeCompletion);
Description = definition.Description;
OnCompleteType(context, definition);
@@ -129,6 +130,8 @@ internal sealed override void CompleteType(ITypeCompletionContext context)
_definition = null;
OnAfterCompleteType(context, definition, _contextData);
+ ExecuteConfigurations(context, definition, ApplyConfigurationOn.AfterCompletion);
+
OnValidateType(context, definition, _contextData);
MarkCompleted();
diff --git a/src/HotChocolate/Core/test/Types.Tests/Configuration/TypeInitializationOrderTests.cs b/src/HotChocolate/Core/test/Types.Tests/Configuration/TypeInitializationOrderTests.cs
index fa9732d6d11..0d247685cec 100644
--- a/src/HotChocolate/Core/test/Types.Tests/Configuration/TypeInitializationOrderTests.cs
+++ b/src/HotChocolate/Core/test/Types.Tests/Configuration/TypeInitializationOrderTests.cs
@@ -61,7 +61,7 @@ protected override void Configure(IObjectTypeDescriptor descriptor)
}
},
d,
- ApplyConfigurationOn.Completion,
+ ApplyConfigurationOn.BeforeCompletion,
reference,
TypeDependencyKind.Completed));
});
diff --git a/src/HotChocolate/Data/src/Data/Filters/Extensions/FilterObjectFieldDescriptorExtensions.cs b/src/HotChocolate/Data/src/Data/Filters/Extensions/FilterObjectFieldDescriptorExtensions.cs
index c5f14971825..6e42b7e9415 100644
--- a/src/HotChocolate/Data/src/Data/Filters/Extensions/FilterObjectFieldDescriptorExtensions.cs
+++ b/src/HotChocolate/Data/src/Data/Filters/Extensions/FilterObjectFieldDescriptorExtensions.cs
@@ -187,7 +187,7 @@ public static class FilterObjectFieldDescriptorExtensions
placeholder,
scope),
definition,
- ApplyConfigurationOn.Completion,
+ ApplyConfigurationOn.BeforeCompletion,
argumentTypeReference,
TypeDependencyKind.Completed));
@@ -197,7 +197,7 @@ public static class FilterObjectFieldDescriptorExtensions
argDef.Name =
context.GetFilterConvention(scope).GetArgumentName(),
argumentDefinition,
- ApplyConfigurationOn.Naming));
+ ApplyConfigurationOn.BeforeNaming));
});
return descriptor;
diff --git a/src/HotChocolate/Data/src/Data/Filters/FilterInputType.cs b/src/HotChocolate/Data/src/Data/Filters/FilterInputType.cs
index 4ba17a0b026..ad274a49ceb 100644
--- a/src/HotChocolate/Data/src/Data/Filters/FilterInputType.cs
+++ b/src/HotChocolate/Data/src/Data/Filters/FilterInputType.cs
@@ -188,7 +188,7 @@ protected virtual void Configure(IFilterInputTypeDescriptor descriptor)
new CompleteConfiguration(
CreateNamingConfiguration,
filterTypeDefinition,
- ApplyConfigurationOn.Naming,
+ ApplyConfigurationOn.BeforeNaming,
new TypeDependency[] {
new(filterOperationType, TypeDependencyKind.Named) ,
new(filterType, TypeDependencyKind.Named)
@@ -197,7 +197,7 @@ protected virtual void Configure(IFilterInputTypeDescriptor descriptor)
new CompleteConfiguration(
CreateOperationFieldConfiguration,
filterTypeDefinition,
- ApplyConfigurationOn.Naming,
+ ApplyConfigurationOn.BeforeNaming,
new TypeDependency[] {
new(filterOperationType, TypeDependencyKind.Named),
new(filterType, TypeDependencyKind.Named)
diff --git a/src/HotChocolate/Data/src/Data/Filters/Visitor/IFilterVisitorContext.cs b/src/HotChocolate/Data/src/Data/Filters/Visitor/IFilterVisitorContext.cs
index 57b3f7f5a13..6ecf2ad5dbe 100644
--- a/src/HotChocolate/Data/src/Data/Filters/Visitor/IFilterVisitorContext.cs
+++ b/src/HotChocolate/Data/src/Data/Filters/Visitor/IFilterVisitorContext.cs
@@ -8,8 +8,7 @@ namespace HotChocolate.Data.Filters;
///
/// A context object that is passed along the visitation cycle
///
-public interface IFilterVisitorContext
- : ISyntaxVisitorContext
+public interface IFilterVisitorContext : ISyntaxVisitorContext
{
///
/// The already visited types
diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionFieldHandler.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionFieldHandler.cs
index 3149867832d..de680208abf 100644
--- a/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionFieldHandler.cs
+++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/Handlers/QueryableProjectionFieldHandler.cs
@@ -3,7 +3,6 @@
using System.Linq.Expressions;
using System.Reflection;
using HotChocolate.Execution.Processing;
-using HotChocolate.Types;
using static HotChocolate.Data.Projections.Expressions.ProjectionExpressionBuilder;
namespace HotChocolate.Data.Projections.Expressions.Handlers;
@@ -59,7 +58,7 @@ public class QueryableProjectionFieldHandler
return false;
}
- // Deque last
+ // Dequeue last
var scope = context.PopScope();
if (scope is not QueryableProjectionScope queryableScope)
diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/Interceptor/QueryableFilterInterceptor.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/Interceptor/QueryableFilterInterceptor.cs
index b10fde0ec6a..86279cd8ca2 100644
--- a/src/HotChocolate/Data/src/Data/Projections/Expressions/Interceptor/QueryableFilterInterceptor.cs
+++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/Interceptor/QueryableFilterInterceptor.cs
@@ -36,7 +36,7 @@ public class QueryableFilterInterceptor : IProjectionFieldInterceptor 0 &&
context.Selection.Peek().Arguments
- .TryCoerceArguments(context.Context, out var coercedArgs) &&
+ .TryCoerceArguments(context.ResolverContext, out var coercedArgs) &&
coercedArgs.TryGetValue(argumentName, out var argumentValue) &&
argumentValue.Type is IFilterInputType filterInputType &&
argumentValue.ValueLiteral is { } valueNode and not NullValueNode)
diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/Interceptor/QueryableSortInterceptor.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/Interceptor/QueryableSortInterceptor.cs
index 4a3dd368f2d..5fbd71ba9cf 100644
--- a/src/HotChocolate/Data/src/Data/Projections/Expressions/Interceptor/QueryableSortInterceptor.cs
+++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/Interceptor/QueryableSortInterceptor.cs
@@ -36,7 +36,7 @@ public class QueryableSortInterceptor : IProjectionFieldInterceptor 0 &&
context.Selection.Peek().Arguments
- .TryCoerceArguments(context.Context, out var coercedArgs) &&
+ .TryCoerceArguments(context.ResolverContext, out var coercedArgs) &&
coercedArgs.TryGetValue(argumentName, out var argumentValue) &&
argumentValue.Type is ListType lt &&
lt.ElementType is NonNullType nn &&
diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/QueryableProjectionProvider.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/QueryableProjectionProvider.cs
index 7315b891514..afcbae5dd34 100644
--- a/src/HotChocolate/Data/src/Data/Projections/Expressions/QueryableProjectionProvider.cs
+++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/QueryableProjectionProvider.cs
@@ -1,10 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Linq.Expressions;
+using System.Reflection;
using System.Threading.Tasks;
+using HotChocolate.Execution.Processing;
+using HotChocolate.Language;
using HotChocolate.Resolvers;
using HotChocolate.Types;
+using static HotChocolate.WellKnownContextData;
namespace HotChocolate.Data.Projections.Expressions;
@@ -26,7 +29,7 @@ public QueryableProjectionProvider(Action configu
public override FieldMiddleware CreateExecutor()
{
- var applyProjection = CreateApplicatorAsync();
+ var applyProjection = CreateApplicator();
return next => context => ExecuteAsync(next, context);
@@ -44,9 +47,8 @@ public override FieldMiddleware CreateExecutor()
}
}
- private static ApplyProjection CreateApplicatorAsync()
- {
- return (context, input) =>
+ private static ApplyProjection CreateApplicator()
+ => (context, input) =>
{
if (input is null)
{
@@ -67,27 +69,125 @@ private static ApplyProjection CreateApplicatorAsync()
return input;
}
+ // in case we are being called from the node/nodes field we need to enrich
+ // the projections context with the type that shall be resolved.
+ Type? selectionRuntimeType = null;
+ ISelection? selection = null;
+
+ if (context.LocalContextData.TryGetValue(InternalType, out var value) &&
+ value is ObjectType objectType &&
+ objectType.RuntimeType != typeof(object))
+ {
+ selectionRuntimeType = objectType.RuntimeType;
+ var fieldProxy = new NodeFieldProxy(context.Selection.Field, objectType);
+ selection = CreateProxySelection(context.Selection, fieldProxy);
+ }
+
var visitorContext =
new QueryableProjectionContext(
context,
context.ObjectType,
- context.Selection.Type.UnwrapRuntimeType());
+ selectionRuntimeType ?? context.Selection.Type.UnwrapRuntimeType());
var visitor = new QueryableProjectionVisitor();
- visitor.Visit(visitorContext);
- var projection =
- visitorContext.Project();
+ // if we do not have a node selection proxy than this is a standard field and we
+ // just traverse
+ if (selection is null)
+ {
+ visitor.Visit(visitorContext);
+ }
+
+ // but if we have a node selection proxy we will push that into the visitor to use
+ // it instead of the selection on the context.
+ else
+ {
+ visitor.Visit(visitorContext, selection);
+ }
+
+ var projection = visitorContext.Project();
- input = input switch
+ return input switch
{
IQueryable q => q.Select(projection),
IEnumerable e => e.AsQueryable().Select(projection),
- QueryableExecutable ex =>
- ex.WithSource(ex.Source.Select(projection)),
+ QueryableExecutable ex => ex.WithSource(ex.Source.Select(projection)),
_ => input
};
-
- return input;
};
+
+ private static Selection CreateProxySelection(ISelection selection, NodeFieldProxy field)
+ {
+ var includeConditionsSource = ((Selection)selection).IncludeConditions;
+ var includeConditions = new long[includeConditionsSource.Length];
+ includeConditionsSource.CopyTo(includeConditions);
+
+ var proxy = new Selection(selection.Id, selection.DeclaringType, field, field.Type, selection.SyntaxNode, selection.ResponseName, selection.Arguments, includeConditions, selection.IsInternal, selection.Strategy != SelectionExecutionStrategy.Serial, selection.ResolverPipeline, selection.PureResolver);
+ proxy.SetSelectionSetId(((Selection)selection).SelectionSetId);
+ proxy.Seal(selection.DeclaringSelectionSet);
+ return proxy;
+ }
+
+ private sealed class NodeFieldProxy : IObjectField
+ {
+ private readonly IObjectField _nodeField;
+ private readonly ObjectType _type;
+ private readonly Type _runtimeType;
+
+ public NodeFieldProxy(IObjectField nodeField, ObjectType type)
+ {
+ _nodeField = nodeField;
+ _type = type;
+ _runtimeType = type.RuntimeType;
+ }
+
+ public IObjectType DeclaringType => _nodeField.DeclaringType;
+
+ public bool IsParallelExecutable => _nodeField.IsParallelExecutable;
+
+ public bool HasStreamResult => _nodeField.HasStreamResult;
+
+ public FieldDelegate Middleware => _nodeField.Middleware;
+
+ public FieldResolverDelegate? Resolver => _nodeField.Resolver;
+
+ public PureFieldDelegate? PureResolver => _nodeField.PureResolver;
+
+ public SubscribeResolverDelegate? SubscribeResolver => _nodeField.SubscribeResolver;
+
+ public IReadOnlyList ExecutableDirectives => _nodeField.ExecutableDirectives;
+
+ public MemberInfo? Member => _nodeField.Member;
+
+ public MemberInfo? ResolverMember => _nodeField.ResolverMember;
+
+ public bool IsIntrospectionField => _nodeField.IsIntrospectionField;
+
+ public bool IsDeprecated => _nodeField.IsDeprecated;
+
+ public string? DeprecationReason => _nodeField.DeprecationReason;
+
+ public int Index => _nodeField.Index;
+
+ public string? Description => _nodeField.Description;
+
+ public IDirectiveCollection Directives => _nodeField.Directives;
+
+ public ISyntaxNode? SyntaxNode => _nodeField.SyntaxNode;
+
+ public IReadOnlyDictionary ContextData => _nodeField.ContextData;
+
+ public IOutputType Type => _type;
+
+ public IFieldCollection Arguments => _nodeField.Arguments;
+
+ public string Name => _nodeField.Name;
+
+ public FieldCoordinate Coordinate => _nodeField.Coordinate;
+
+ public Type RuntimeType => _runtimeType;
+
+ IComplexOutputType IOutputField.DeclaringType => _nodeField.DeclaringType;
+
+ ITypeSystemObject IField.DeclaringType => ((IField)_nodeField).DeclaringType;
}
}
diff --git a/src/HotChocolate/Data/src/Data/Projections/Expressions/QueryableProjectionVisitor.cs b/src/HotChocolate/Data/src/Data/Projections/Expressions/QueryableProjectionVisitor.cs
index ca0e6b93646..7f5d740c2a4 100644
--- a/src/HotChocolate/Data/src/Data/Projections/Expressions/QueryableProjectionVisitor.cs
+++ b/src/HotChocolate/Data/src/Data/Projections/Expressions/QueryableProjectionVisitor.cs
@@ -17,7 +17,7 @@ public class QueryableProjectionVisitor : ProjectionVisitor field.Type.Kind != TypeKind.NonNull && field.Type.InnerType() is IPageType ||
+ => (field.Type.Kind != TypeKind.NonNull && field.Type.InnerType() is IPageType) ||
field.Type is IPageType ||
field.ContextData.ContainsKey(ProjectionContextIdentifier);
}
diff --git a/src/HotChocolate/Data/src/Data/Projections/Extensions/SelectionObjectFieldDescriptorExtensions.cs b/src/HotChocolate/Data/src/Data/Projections/Extensions/SelectionObjectFieldDescriptorExtensions.cs
index 9d358fba5e0..e81f40185ea 100644
--- a/src/HotChocolate/Data/src/Data/Projections/Extensions/SelectionObjectFieldDescriptorExtensions.cs
+++ b/src/HotChocolate/Data/src/Data/Projections/Extensions/SelectionObjectFieldDescriptorExtensions.cs
@@ -3,7 +3,6 @@
using HotChocolate.Configuration;
using HotChocolate.Data;
using HotChocolate.Data.Projections;
-using HotChocolate.Internal;
using HotChocolate.Resolvers;
using HotChocolate.Types.Descriptors.Definitions;
using static HotChocolate.Data.Projections.ProjectionProvider;
@@ -18,13 +17,16 @@ public static class ProjectionObjectFieldDescriptorExtensions
.GetMethod(nameof(CreateMiddleware), BindingFlags.Static | BindingFlags.NonPublic)!;
///
+ ///
/// Configure if this field should be projected by or if it
/// should be skipped
- ///
+ ///
+ ///
/// if is false, this field will never be projected even if
/// it is in the selection set
/// if is true, this field will always be projected even it
/// it is not in the selection set
+ ///
///
/// The descriptor
///
@@ -101,13 +103,13 @@ public static class ProjectionObjectFieldDescriptorExtensions
/// uses the registered to apply the projections
///
/// The descriptor
+ ///
+ /// The of the resolved field
+ ///
///
/// Specify which is used, based on the value passed in
///
///
- ///
- /// The of the resolved field
- ///
/// The descriptor passed in by
///
/// In case the descriptor is null
@@ -159,7 +161,7 @@ public static class ProjectionObjectFieldDescriptorExtensions
c,
scope),
definition,
- ApplyConfigurationOn.Completion));
+ ApplyConfigurationOn.BeforeCompletion));
});
return descriptor;
@@ -172,8 +174,7 @@ public static class ProjectionObjectFieldDescriptorExtensions
ITypeCompletionContext context,
string? scope)
{
- var convention =
- context.DescriptorContext.GetProjectionConvention(scope);
+ var convention = context.DescriptorContext.GetProjectionConvention(scope);
RegisterOptimizer(definition.ContextData, convention.CreateOptimizer());
definition.ContextData[ProjectionContextIdentifier] = true;
diff --git a/src/HotChocolate/Data/src/Data/Projections/Extensions/SingleOrDefaultObjectFieldDescriptorExtensions.cs b/src/HotChocolate/Data/src/Data/Projections/Extensions/SingleOrDefaultObjectFieldDescriptorExtensions.cs
index bcd17dcdeae..0ae08b4952a 100644
--- a/src/HotChocolate/Data/src/Data/Projections/Extensions/SingleOrDefaultObjectFieldDescriptorExtensions.cs
+++ b/src/HotChocolate/Data/src/Data/Projections/Extensions/SingleOrDefaultObjectFieldDescriptorExtensions.cs
@@ -68,7 +68,7 @@ public static class SingleOrDefaultObjectFieldDescriptorExtensions
middlewareDefinition);
},
definition,
- ApplyConfigurationOn.Completion));
+ ApplyConfigurationOn.BeforeCompletion));
});
return descriptor;
diff --git a/src/HotChocolate/Data/src/Data/Projections/ISelectionVisitorContext.cs b/src/HotChocolate/Data/src/Data/Projections/ISelectionVisitorContext.cs
index b3c92d7ce62..e70bc11e4f7 100644
--- a/src/HotChocolate/Data/src/Data/Projections/ISelectionVisitorContext.cs
+++ b/src/HotChocolate/Data/src/Data/Projections/ISelectionVisitorContext.cs
@@ -1,6 +1,6 @@
+using System;
using System.Collections.Generic;
using HotChocolate.Execution.Processing;
-using HotChocolate.Language;
using HotChocolate.Resolvers;
using HotChocolate.Types;
@@ -12,5 +12,8 @@ public interface ISelectionVisitorContext
Stack ResolvedType { get; }
- IResolverContext Context { get; }
+ [Obsolete("Use ResolverContext")]
+ IResolverContext Context => ResolverContext;
+
+ IResolverContext ResolverContext { get; }
}
diff --git a/src/HotChocolate/Data/src/Data/Projections/ProjectionOptimizer.cs b/src/HotChocolate/Data/src/Data/Projections/ProjectionOptimizer.cs
index 8a95bcc06de..11a3aa491cd 100644
--- a/src/HotChocolate/Data/src/Data/Projections/ProjectionOptimizer.cs
+++ b/src/HotChocolate/Data/src/Data/Projections/ProjectionOptimizer.cs
@@ -4,7 +4,7 @@
namespace HotChocolate.Data.Projections;
-public class ProjectionOptimizer : ISelectionSetOptimizer
+internal sealed class ProjectionOptimizer : ISelectionSetOptimizer
{
private readonly IProjectionProvider _provider;
diff --git a/src/HotChocolate/Data/src/Data/Projections/ProjectionVisitor.cs b/src/HotChocolate/Data/src/Data/Projections/ProjectionVisitor.cs
index ff42000ec46..5577278ad83 100644
--- a/src/HotChocolate/Data/src/Data/Projections/ProjectionVisitor.cs
+++ b/src/HotChocolate/Data/src/Data/Projections/ProjectionVisitor.cs
@@ -11,8 +11,13 @@ public class ProjectionVisitor
{
public virtual void Visit(TContext context)
{
- context.Selection.Push((ISelection)context.Context.Selection);
- Visit(context.Context.Selection.Field, context);
+ Visit(context, context.ResolverContext.Selection);
+ }
+
+ public virtual void Visit(TContext context, ISelection selection)
+ {
+ context.Selection.Push(selection);
+ Visit(selection.Field, context);
}
protected override TContext OnBeforeLeave(ISelection selection, TContext localContext)
@@ -123,7 +128,7 @@ protected override ISelectionVisitorAction Visit(IOutputField field, TContext co
if (field.Type is IPageType and ObjectType pageType &&
context.Selection.Peek() is { } pagingFieldSelection)
{
- var selections = context.Context.GetSelections(pageType, pagingFieldSelection, true);
+ var selections = context.ResolverContext.GetSelections(pageType, pagingFieldSelection, true);
foreach (var selection in selections)
{
diff --git a/src/HotChocolate/Data/src/Data/Projections/SelectionVisitorContext.cs b/src/HotChocolate/Data/src/Data/Projections/SelectionVisitorContext.cs
index d98ed1f5072..e03ac7eabce 100644
--- a/src/HotChocolate/Data/src/Data/Projections/SelectionVisitorContext.cs
+++ b/src/HotChocolate/Data/src/Data/Projections/SelectionVisitorContext.cs
@@ -13,7 +13,7 @@ public SelectionVisitorContext(IResolverContext context)
Selection = new Stack();
SelectionSetNodes = new Stack();
ResolvedType = new Stack();
- Context = context;
+ ResolverContext = context;
}
public Stack Selection { get; }
@@ -22,5 +22,5 @@ public SelectionVisitorContext(IResolverContext context)
public Stack ResolvedType { get; }
- public IResolverContext Context { get; }
+ public IResolverContext ResolverContext { get; }
}
diff --git a/src/HotChocolate/Data/src/Data/Projections/SelectionVisitor`1.cs b/src/HotChocolate/Data/src/Data/Projections/SelectionVisitor`1.cs
index f2c0010fd1f..00ff8f81d38 100644
--- a/src/HotChocolate/Data/src/Data/Projections/SelectionVisitor`1.cs
+++ b/src/HotChocolate/Data/src/Data/Projections/SelectionVisitor`1.cs
@@ -64,12 +64,10 @@ public class SelectionVisitor
var result = Enter(selection, localContext);
localContext = OnAfterEnter(selection, localContext, result);
- if (result.Kind == SelectionVisitorActionKind.Continue)
+ if (result.Kind == SelectionVisitorActionKind.Continue &&
+ VisitChildren(selection, context).Kind == SelectionVisitorActionKind.Break)
{
- if (VisitChildren(selection, context).Kind == SelectionVisitorActionKind.Break)
- {
- return Break;
- }
+ return Break;
}
if (result.Kind == SelectionVisitorActionKind.Continue ||
@@ -113,9 +111,8 @@ protected virtual ISelectionVisitorAction VisitChildren(IOutputField field, TCon
var namedType = type.NamedType();
if (namedType.IsAbstractType())
{
- var possibleTypes = context.Context.Schema.GetPossibleTypes(field.Type.NamedType());
-
- foreach (var possibleType in possibleTypes)
+ foreach (var possibleType in
+ context.ResolverContext.Schema.GetPossibleTypes(field.Type.NamedType()))
{
var result = VisitObjectType(field, possibleType, selection, context);
@@ -143,11 +140,11 @@ protected virtual ISelectionVisitorAction VisitChildren(IOutputField field, TCon
try
{
- var selections = context.Context.GetSelections(objectType, selection, true);
+ var selections = context.ResolverContext.GetSelections(objectType, selection, true);
for (var i = 0; i < selections.Count; i++)
{
- var result = Visit((ISelection)selections[i], context);
+ var result = Visit(selections[i], context);
if (result.Kind is SelectionVisitorActionKind.Break)
{
return Break;
diff --git a/src/HotChocolate/Data/src/Data/Sorting/Extensions/SortObjectFieldDescriptorExtensions.cs b/src/HotChocolate/Data/src/Data/Sorting/Extensions/SortObjectFieldDescriptorExtensions.cs
index 4217d671437..8e1fc08e84e 100644
--- a/src/HotChocolate/Data/src/Data/Sorting/Extensions/SortObjectFieldDescriptorExtensions.cs
+++ b/src/HotChocolate/Data/src/Data/Sorting/Extensions/SortObjectFieldDescriptorExtensions.cs
@@ -184,7 +184,7 @@ public static class SortObjectFieldDescriptorExtensions
def.Type = TypeReference.Parse($"[{namedType.Name}!]");
},
argumentDefinition,
- ApplyConfigurationOn.Naming,
+ ApplyConfigurationOn.BeforeNaming,
argumentTypeReference,
TypeDependencyKind.Named));
@@ -200,7 +200,7 @@ public static class SortObjectFieldDescriptorExtensions
placeholder,
scope),
definition,
- ApplyConfigurationOn.Completion,
+ ApplyConfigurationOn.BeforeCompletion,
argumentTypeReference,
TypeDependencyKind.Completed));
@@ -210,7 +210,7 @@ public static class SortObjectFieldDescriptorExtensions
argDef.Name =
context.GetSortConvention(scope).GetArgumentName(),
argumentDefinition,
- ApplyConfigurationOn.Naming));
+ ApplyConfigurationOn.BeforeNaming));
});
return descriptor;
diff --git a/src/HotChocolate/Data/src/Data/Sorting/SortInputType.cs b/src/HotChocolate/Data/src/Data/Sorting/SortInputType.cs
index 26256a6d5eb..f1ae5e2ec35 100644
--- a/src/HotChocolate/Data/src/Data/Sorting/SortInputType.cs
+++ b/src/HotChocolate/Data/src/Data/Sorting/SortInputType.cs
@@ -147,7 +147,7 @@ protected sealed override void Configure(IInputObjectTypeDescriptor descriptor)
new CompleteConfiguration(
CreateNamingConfiguration,
sortTypeDefinition,
- ApplyConfigurationOn.Naming,
+ ApplyConfigurationOn.BeforeNaming,
new TypeDependency[]
{
new(sortOperationType, TypeDependencyKind.Named),
diff --git a/src/HotChocolate/Data/src/Data/Sorting/Visitor/ISortVisitorContext.cs b/src/HotChocolate/Data/src/Data/Sorting/Visitor/ISortVisitorContext.cs
index b62e7ec0e1a..1f759a10ebf 100644
--- a/src/HotChocolate/Data/src/Data/Sorting/Visitor/ISortVisitorContext.cs
+++ b/src/HotChocolate/Data/src/Data/Sorting/Visitor/ISortVisitorContext.cs
@@ -4,8 +4,7 @@
namespace HotChocolate.Data.Sorting;
-public interface ISortVisitorContext
- : ISyntaxVisitorContext
+public interface ISortVisitorContext : ISyntaxVisitorContext
{
Stack Types { get; }
diff --git a/src/HotChocolate/Data/test/Data.Projections.Tests/IntegrationTests.cs b/src/HotChocolate/Data/test/Data.Projections.Tests/IntegrationTests.cs
index 4f952405d4b..647b001cccf 100644
--- a/src/HotChocolate/Data/test/Data.Projections.Tests/IntegrationTests.cs
+++ b/src/HotChocolate/Data/test/Data.Projections.Tests/IntegrationTests.cs
@@ -4,6 +4,7 @@
using CookieCrumble;
using HotChocolate.Execution;
using HotChocolate.Types;
+using HotChocolate.Types.Relay;
using Microsoft.Extensions.DependencyInjection;
namespace HotChocolate.Data;
@@ -113,6 +114,36 @@ public async Task Projection_Should_NotBreakProjections_When_ExtensionsObjectReq
result.MatchSnapshot();
}
+
+ [Fact]
+ public async Task Node_Resolver_With_SingleOrDefault_Schema()
+ {
+ var schema = await new ServiceCollection()
+ .AddGraphQL()
+ .AddQueryType()
+ .AddObjectType(d => d.ImplementsNode().IdField(t => t.Bar))
+ .AddGlobalObjectIdentification()
+ .AddProjections()
+ .BuildSchemaAsync();
+
+ schema.MatchSnapshot();
+ }
+
+ [Fact]
+ public async Task Node_Resolver_With_SingleOrDefault()
+ {
+ var executor = await new ServiceCollection()
+ .AddGraphQL()
+ .AddQueryType()
+ .AddObjectType(d => d.ImplementsNode().IdField(t => t.Bar))
+ .AddGlobalObjectIdentification()
+ .AddProjections()
+ .BuildRequestExecutorAsync();
+
+ var result = await executor.ExecuteAsync(@"{ node(id: ""Rm9vCmRB"") { id __typename } }");
+
+ result.MatchSnapshot();
+ }
}
public class Query
@@ -120,8 +151,8 @@ public class Query
[UseProjection]
public IQueryable Foos => new Foo[]
{
- new() { Bar = "A" },
- new() { Bar = "B" }
+ new() { Bar = "A" },
+ new() { Bar = "B" }
}.AsQueryable();
}
@@ -147,3 +178,22 @@ public class Foo
{
public string? Bar { get; set; }
}
+
+public class QueryWithNodeResolvers
+{
+ [UseProjection]
+ public IQueryable All()
+ => new Foo[]
+ {
+ new() { Bar = "A" },
+ }.AsQueryable();
+
+ [NodeResolver]
+ [UseSingleOrDefault]
+ [UseProjection]
+ public IQueryable GetById(string id)
+ => new Foo[]
+ {
+ new() { Bar = "A" },
+ }.AsQueryable();
+}
diff --git a/src/HotChocolate/Data/test/Data.Projections.Tests/__snapshots__/IntegrationTests.Node_Resolver_With_SingleOrDefault.snap b/src/HotChocolate/Data/test/Data.Projections.Tests/__snapshots__/IntegrationTests.Node_Resolver_With_SingleOrDefault.snap
new file mode 100644
index 00000000000..5409d1bcbc9
--- /dev/null
+++ b/src/HotChocolate/Data/test/Data.Projections.Tests/__snapshots__/IntegrationTests.Node_Resolver_With_SingleOrDefault.snap
@@ -0,0 +1,9 @@
+{
+ "data": {
+ "all": [
+ {
+ "id": "Rm9vCmRB"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/src/HotChocolate/Data/test/Data.Projections.Tests/__snapshots__/IntegrationTests.Node_Resolver_With_SingleOrDefault_Schema.graphql b/src/HotChocolate/Data/test/Data.Projections.Tests/__snapshots__/IntegrationTests.Node_Resolver_With_SingleOrDefault_Schema.graphql
new file mode 100644
index 00000000000..9fc08d4284b
--- /dev/null
+++ b/src/HotChocolate/Data/test/Data.Projections.Tests/__snapshots__/IntegrationTests.Node_Resolver_With_SingleOrDefault_Schema.graphql
@@ -0,0 +1,27 @@
+schema {
+ query: QueryWithNodeResolvers
+}
+
+"The node interface is implemented by entities that have a global unique identifier."
+interface Node {
+ id: ID!
+}
+
+type Foo implements Node {
+ id: ID!
+}
+
+type QueryWithNodeResolvers {
+ "Fetches an object given its ID."
+ node("ID of the object." id: ID!): Node
+ "Lookup nodes by a list of IDs."
+ nodes("The list of node IDs." ids: [ID!]!): [Node]!
+ all: [Foo!]!
+ byId(id: ID!): Foo
+}
+
+"The `@defer` directive may be provided for fragment spreads and inline fragments to inform the executor to delay the execution of the current fragment to indicate deprioritization of the current fragment. A query with `@defer` directive will cause the request to potentially return multiple responses, where non-deferred data is delivered in the initial response and data deferred is delivered in a subsequent response. `@include` and `@skip` take precedence over `@defer`."
+directive @defer("If this argument label has a value other than null, it will be passed on to the result of this defer directive. This label is intended to give client applications a way to identify to which fragment a deferred result belongs to." label: String "Deferred when true." if: Boolean) on FRAGMENT_SPREAD | INLINE_FRAGMENT
+
+"The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`."
+directive @stream("If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String "The initial elements that shall be send down to the consumer." initialCount: Int! = 0 "Streamed when true." if: Boolean) on FIELD
\ No newline at end of file
diff --git a/src/HotChocolate/Filters/src/Types.Filters/Extensions/FilterObjectFieldDescriptorExtensions.cs b/src/HotChocolate/Filters/src/Types.Filters/Extensions/FilterObjectFieldDescriptorExtensions.cs
index 2e8d8bcc286..0d6d119b445 100644
--- a/src/HotChocolate/Filters/src/Types.Filters/Extensions/FilterObjectFieldDescriptorExtensions.cs
+++ b/src/HotChocolate/Filters/src/Types.Filters/Extensions/FilterObjectFieldDescriptorExtensions.cs
@@ -133,7 +133,7 @@ public static class FilterObjectFieldDescriptorExtensions
argumentTypeReference,
placeholder),
definition,
- ApplyConfigurationOn.Completion,
+ ApplyConfigurationOn.BeforeCompletion,
argumentTypeReference,
TypeDependencyKind.Completed);
@@ -212,9 +212,9 @@ public static class FilterObjectFieldDescriptorExtensions
d.Name = convention.ArgumentName;
},
definition,
- ApplyConfigurationOn.Completion);
+ ApplyConfigurationOn.BeforeCompletion);
definition.Configurations.Add(argumentConfig);
return definition;
}
-}
\ No newline at end of file
+}
diff --git a/src/HotChocolate/Filters/src/Types.Sorting/Extensions/SortObjectFieldDescriptorExtensions.cs b/src/HotChocolate/Filters/src/Types.Sorting/Extensions/SortObjectFieldDescriptorExtensions.cs
index 27d7c03e0cb..03dc0fa3c09 100644
--- a/src/HotChocolate/Filters/src/Types.Sorting/Extensions/SortObjectFieldDescriptorExtensions.cs
+++ b/src/HotChocolate/Filters/src/Types.Sorting/Extensions/SortObjectFieldDescriptorExtensions.cs
@@ -104,7 +104,8 @@ public static class SortObjectFieldDescriptorExtensions
d.Name = convention.ArgumentName;
},
argumentDefinition,
- ApplyConfigurationOn.Completion));
+ ApplyConfigurationOn.
+ BeforeCompletion));
definition.Arguments.Add(argumentDefinition);
definition.Configurations.Add(
@@ -116,7 +117,7 @@ public static class SortObjectFieldDescriptorExtensions
argumentTypeReference,
placeholder),
definition,
- ApplyConfigurationOn.Completion,
+ ApplyConfigurationOn.BeforeCompletion,
argumentTypeReference,
TypeDependencyKind.Completed));
});
@@ -187,4 +188,4 @@ public static class SortObjectFieldDescriptorExtensions
definition.MiddlewareDefinitions[index] =
new(middleware, key: WellKnownMiddleware.Sorting);
}
-}
\ No newline at end of file
+}