Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -449,15 +449,16 @@ private void CollectSelection(
var field = selection.Field;
var namedType = field.Type.NamedType();

// A field is projectable if its resolver is the underlying member (a pure resolver
// declared on the parent runtime type, on an interface it implements, or on a base
// type) or if it explicitly replaces that member (fluent ResolveWith / [BindMember]).
var isPureMemberResolver = field.PureResolver is not null
// A field is projectable if it has a pure member resolver before middleware is
// compiled and the member is declared on the parent runtime type, a base type, or
// an implemented interface. Middleware prevents the pure resolver from being used
// for execution, but the resolver still reads the member, so it must be projected.
var isMemberResolver = field.Flags.HasFlag(CoreFieldFlags.HasPureResolver)
&& field.ResolverMember?.DeclaringType?.IsAssignableFrom(
field.DeclaringType.RuntimeType) == true;
var isMemberReplacement = field.Flags.HasFlag(CoreFieldFlags.MemberReplacement);

if (!isPureMemberResolver && !isMemberReplacement)
if (!isMemberResolver && !isMemberReplacement)
{
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,6 @@ internal enum CoreFieldFlags : long
BatchResolver = 1L << 33,
MemberReplacement = 1L << 34,
UseV15MutationFieldNameFormat = 1L << 35,
MutationPayload = 1L << 36
MutationPayload = 1L << 36,
HasPureResolver = 1L << 37
}
15 changes: 10 additions & 5 deletions src/HotChocolate/Core/src/Types/Types/ObjectField.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,12 +189,17 @@ options.FieldMiddleware is not FieldMiddlewareApplication.AllFields
var resolvers = definition.Resolvers;
Resolver = resolvers.Resolver;

if (resolvers.PureResolver is not null && IsPureContext())
if (resolvers.PureResolver is not null)
{
PureResolver = FieldMiddlewareCompiler.Compile(
definition.GetResultConverters(),
resolvers.PureResolver,
skipMiddleware);
Flags |= CoreFieldFlags.HasPureResolver;

if (IsPureContext())
{
PureResolver = FieldMiddlewareCompiler.Compile(
definition.GetResultConverters(),
resolvers.PureResolver,
skipMiddleware);
}
}

// by definition, fields with pure resolvers are parallel executable.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
using System.Linq.Expressions;
using HotChocolate.Execution;
using HotChocolate.Execution.Processing;
using HotChocolate.Types;
using Microsoft.Extensions.DependencyInjection;

namespace HotChocolate.Data;

public class GlobalFieldMiddlewareProjectionTests
{
[Fact]
public async Task AsSelector_Should_ProjectMemberFields_When_GlobalFieldMiddlewareIsApplied()
{
// arrange
var captured = new List<Expression<Func<Item, Item>>>();
var executor = await new ServiceCollection()
.AddSingleton(captured)
.AddGraphQL()
.AddQueryType<Query>()
.UseField(next => next)
.BuildRequestExecutorAsync(cancellationToken: TestContext.Current.CancellationToken);

// act
var result = await executor.ExecuteAsync(
"""
{
items {
name
}
}
""",
TestContext.Current.CancellationToken);

// assert
var selector = Assert.Single(captured);
var memberInit = Assert.IsType<MemberInitExpression>(selector.Body);
Assert.Contains(memberInit.Bindings, b => b.Member.Name == nameof(Item.Name));
result.MatchInlineSnapshot(
"""
{
"data": {
"items": [
{
"name": "Foo"
}
]
}
}
""");
}

[Fact]
public async Task AsSelector_Should_ExcludeMember_When_CustomResolverIsConfigured()
{
// arrange
var captured = new List<Expression<Func<Item, Item>>>();
var executor = await new ServiceCollection()
.AddSingleton(captured)
.AddGraphQL()
.AddQueryType<Query>()
.AddType<ItemType>()
.BuildRequestExecutorAsync(cancellationToken: TestContext.Current.CancellationToken);

// act
var result = await executor.ExecuteAsync(
"""
{
items {
name
}
}
""",
TestContext.Current.CancellationToken);

// assert
var selector = Assert.Single(captured);
var memberInit = Assert.IsType<MemberInitExpression>(selector.Body);
Assert.Collection(
memberInit.Bindings,
binding => Assert.Equal(nameof(Item.Id), binding.Member.Name));
result.MatchInlineSnapshot(
"""
{
"data": {
"items": [
{
"name": "Resolved"
}
]
}
}
""");
}

public class Query
{
public IQueryable<Item> GetItems(
ISelection selection,
[Service] List<Expression<Func<Item, Item>>> captured)
{
var selector = selection.AsSelector<Item>();
captured.Add(selector);
return new[] { new Item { Id = 1, Name = "Foo" } }.AsQueryable().Select(selector);
}
}

public class Item
{
public int Id { get; set; }

public string Name { get; set; } = default!;
}

public class ItemType : ObjectType<Item>
{
protected override void Configure(IObjectTypeDescriptor<Item> descriptor)
{
descriptor
.Field(t => t.Name)
.Resolve(_ => "Resolved");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
},
{
"Key": "exception.stacktrace",
"Value": "System.Net.Http.HttpRequestException: Response status code does not indicate success: 500 (Internal Server Error).\n at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()\n at HotChocolate.Fusion.Transport.Http.GraphQLHttpResponse.ReadAsResultAsync(IMemoryArena arena, CancellationToken cancellationToken) in GraphQLHttpResponse.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.ReadAsResultStreamAsync(OperationPlanContext context, ImmutableArray`1 variables, GraphQLHttpResponse response, CancellationToken cancellationToken)+MoveNext() in HttpSourceSchemaClient.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.ReadAsResultStreamAsync(OperationPlanContext context, ImmutableArray`1 variables, GraphQLHttpResponse response, CancellationToken cancellationToken)+System.Threading.Tasks.Sources.IValueTaskSource<System.Boolean>.GetResult()\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.WithResultCallback(IAsyncEnumerable`1 results, OperationPlanContext context, ExecutionNode node, Action`3 onSourceSchemaResult, CancellationToken cancellationToken)+MoveNext() in HttpSourceSchemaClient.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.WithResultCallback(IAsyncEnumerable`1 results, OperationPlanContext context, ExecutionNode node, Action`3 onSourceSchemaResult, CancellationToken cancellationToken)+MoveNext() in HttpSourceSchemaClient.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.WithResultCallback(IAsyncEnumerable`1 results, OperationPlanContext context, ExecutionNode node, Action`3 onSourceSchemaResult, CancellationToken cancellationToken)+System.Threading.Tasks.Sources.IValueTaskSource<System.Boolean>.GetResult()\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.<ExecuteAsync>g__CreateStreamAsync|13_0(OperationPlanContext context, SourceSchemaClientRequest request, CancellationToken cancellationToken)+MoveNext() in HttpSourceSchemaClient.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.<ExecuteAsync>g__CreateStreamAsync|13_0(OperationPlanContext context, SourceSchemaClientRequest request, CancellationToken cancellationToken)+MoveNext() in HttpSourceSchemaClient.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.<ExecuteAsync>g__CreateStreamAsync|13_0(OperationPlanContext context, SourceSchemaClientRequest request, CancellationToken cancellationToken)+System.Threading.Tasks.Sources.IValueTaskSource<System.Boolean>.GetResult()\n at HotChocolate.Fusion.Execution.Nodes.OperationExecutionNode.OnExecuteAsync(OperationPlanContext context, CancellationToken cancellationToken) in OperationExecutionNode.cs\n at HotChocolate.Fusion.Execution.Nodes.OperationExecutionNode.OnExecuteAsync(OperationPlanContext context, CancellationToken cancellationToken) in OperationExecutionNode.cs"
"Value": "System.Net.Http.HttpRequestException: Response status code does not indicate success: 500 (Internal Server Error).\n at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()\n at HotChocolate.Fusion.Transport.Http.GraphQLHttpResponse.ReadAsResultAsync(IMemoryArena arena, CancellationToken cancellationToken) in GraphQLHttpResponse.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.ReadAsResultStreamAsync(OperationPlanContext context, ImmutableArray`1 variables, GraphQLHttpResponse response, CancellationToken cancellationToken)+MoveNext() in HttpSourceSchemaClient.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.ReadAsResultStreamAsync(OperationPlanContext context, ImmutableArray`1 variables, GraphQLHttpResponse response, CancellationToken cancellationToken)+System.Threading.Tasks.Sources.IValueTaskSource<System.Boolean>.GetResult()\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.WithResultCallback(IAsyncEnumerable`1 results, OperationPlanContext context, ExecutionNode node, Action`3 onSourceSchemaResult, CancellationToken cancellationToken)+MoveNext() in HttpSourceSchemaClient.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.WithResultCallback(IAsyncEnumerable`1 results, OperationPlanContext context, ExecutionNode node, Action`3 onSourceSchemaResult, CancellationToken cancellationToken)+MoveNext() in HttpSourceSchemaClient.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.WithResultCallback(IAsyncEnumerable`1 results, OperationPlanContext context, ExecutionNode node, Action`3 onSourceSchemaResult, CancellationToken cancellationToken)+System.Threading.Tasks.Sources.IValueTaskSource<System.Boolean>.GetResult()\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.<ExecuteAsync>g__CreateStreamAsync|14_0(OperationPlanContext context, SourceSchemaClientRequest request, CancellationToken cancellationToken)+MoveNext() in HttpSourceSchemaClient.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.<ExecuteAsync>g__CreateStreamAsync|14_0(OperationPlanContext context, SourceSchemaClientRequest request, CancellationToken cancellationToken)+MoveNext() in HttpSourceSchemaClient.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.<ExecuteAsync>g__CreateStreamAsync|14_0(OperationPlanContext context, SourceSchemaClientRequest request, CancellationToken cancellationToken)+System.Threading.Tasks.Sources.IValueTaskSource<System.Boolean>.GetResult()\n at HotChocolate.Fusion.Execution.Nodes.OperationExecutionNode.OnExecuteAsync(OperationPlanContext context, CancellationToken cancellationToken) in OperationExecutionNode.cs\n at HotChocolate.Fusion.Execution.Nodes.OperationExecutionNode.OnExecuteAsync(OperationPlanContext context, CancellationToken cancellationToken) in OperationExecutionNode.cs"
}
]
}
Expand Down Expand Up @@ -239,7 +239,7 @@
},
{
"Key": "exception.stacktrace",
"Value": "System.Net.Http.HttpRequestException: Response status code does not indicate success: 500 (Internal Server Error).\n at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()\n at HotChocolate.Fusion.Transport.Http.GraphQLHttpResponse.ReadAsResultAsync(IMemoryArena arena, CancellationToken cancellationToken) in GraphQLHttpResponse.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.ReadAsResultStreamAsync(OperationPlanContext context, ImmutableArray`1 variables, GraphQLHttpResponse response, CancellationToken cancellationToken)+MoveNext() in HttpSourceSchemaClient.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.ReadAsResultStreamAsync(OperationPlanContext context, ImmutableArray`1 variables, GraphQLHttpResponse response, CancellationToken cancellationToken)+System.Threading.Tasks.Sources.IValueTaskSource<System.Boolean>.GetResult()\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.WithResultCallback(IAsyncEnumerable`1 results, OperationPlanContext context, ExecutionNode node, Action`3 onSourceSchemaResult, CancellationToken cancellationToken)+MoveNext() in HttpSourceSchemaClient.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.WithResultCallback(IAsyncEnumerable`1 results, OperationPlanContext context, ExecutionNode node, Action`3 onSourceSchemaResult, CancellationToken cancellationToken)+MoveNext() in HttpSourceSchemaClient.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.WithResultCallback(IAsyncEnumerable`1 results, OperationPlanContext context, ExecutionNode node, Action`3 onSourceSchemaResult, CancellationToken cancellationToken)+System.Threading.Tasks.Sources.IValueTaskSource<System.Boolean>.GetResult()\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.<ExecuteAsync>g__CreateStreamAsync|13_0(OperationPlanContext context, SourceSchemaClientRequest request, CancellationToken cancellationToken)+MoveNext() in HttpSourceSchemaClient.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.<ExecuteAsync>g__CreateStreamAsync|13_0(OperationPlanContext context, SourceSchemaClientRequest request, CancellationToken cancellationToken)+MoveNext() in HttpSourceSchemaClient.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.<ExecuteAsync>g__CreateStreamAsync|13_0(OperationPlanContext context, SourceSchemaClientRequest request, CancellationToken cancellationToken)+System.Threading.Tasks.Sources.IValueTaskSource<System.Boolean>.GetResult()\n at HotChocolate.Fusion.Execution.Nodes.OperationExecutionNode.OnExecuteAsync(OperationPlanContext context, CancellationToken cancellationToken) in OperationExecutionNode.cs\n at HotChocolate.Fusion.Execution.Nodes.OperationExecutionNode.OnExecuteAsync(OperationPlanContext context, CancellationToken cancellationToken) in OperationExecutionNode.cs"
"Value": "System.Net.Http.HttpRequestException: Response status code does not indicate success: 500 (Internal Server Error).\n at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()\n at HotChocolate.Fusion.Transport.Http.GraphQLHttpResponse.ReadAsResultAsync(IMemoryArena arena, CancellationToken cancellationToken) in GraphQLHttpResponse.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.ReadAsResultStreamAsync(OperationPlanContext context, ImmutableArray`1 variables, GraphQLHttpResponse response, CancellationToken cancellationToken)+MoveNext() in HttpSourceSchemaClient.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.ReadAsResultStreamAsync(OperationPlanContext context, ImmutableArray`1 variables, GraphQLHttpResponse response, CancellationToken cancellationToken)+System.Threading.Tasks.Sources.IValueTaskSource<System.Boolean>.GetResult()\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.WithResultCallback(IAsyncEnumerable`1 results, OperationPlanContext context, ExecutionNode node, Action`3 onSourceSchemaResult, CancellationToken cancellationToken)+MoveNext() in HttpSourceSchemaClient.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.WithResultCallback(IAsyncEnumerable`1 results, OperationPlanContext context, ExecutionNode node, Action`3 onSourceSchemaResult, CancellationToken cancellationToken)+MoveNext() in HttpSourceSchemaClient.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.WithResultCallback(IAsyncEnumerable`1 results, OperationPlanContext context, ExecutionNode node, Action`3 onSourceSchemaResult, CancellationToken cancellationToken)+System.Threading.Tasks.Sources.IValueTaskSource<System.Boolean>.GetResult()\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.<ExecuteAsync>g__CreateStreamAsync|14_0(OperationPlanContext context, SourceSchemaClientRequest request, CancellationToken cancellationToken)+MoveNext() in HttpSourceSchemaClient.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.<ExecuteAsync>g__CreateStreamAsync|14_0(OperationPlanContext context, SourceSchemaClientRequest request, CancellationToken cancellationToken)+MoveNext() in HttpSourceSchemaClient.cs\n at HotChocolate.Fusion.Execution.Clients.HttpSourceSchemaClient.<ExecuteAsync>g__CreateStreamAsync|14_0(OperationPlanContext context, SourceSchemaClientRequest request, CancellationToken cancellationToken)+System.Threading.Tasks.Sources.IValueTaskSource<System.Boolean>.GetResult()\n at HotChocolate.Fusion.Execution.Nodes.OperationExecutionNode.OnExecuteAsync(OperationPlanContext context, CancellationToken cancellationToken) in OperationExecutionNode.cs\n at HotChocolate.Fusion.Execution.Nodes.OperationExecutionNode.OnExecuteAsync(OperationPlanContext context, CancellationToken cancellationToken) in OperationExecutionNode.cs"
},
{
"Key": "graphql.operation.type",
Expand Down
Loading
Loading