Skip to content
Open
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 @@ -104,10 +104,20 @@ private ApplyProjection CreateApplicator<TEntityType>()

var inMemory = IsInMemoryQuery<TEntityType>(input);

var runtimeType = context.Selection.Type.UnwrapRuntimeType();

// Explicit union fields can unwrap to object even if the resolver returns
// a concrete base type (e.g. IQueryable<Base>). Use the resolver element
// type to keep the projection lambda parameter type stable.
if (runtimeType == typeof(object) && typeof(TEntityType) != typeof(object))
{
runtimeType = typeof(TEntityType);
}

var visitorContext = new QueryableProjectionContext(
context,
context.ObjectType,
context.Selection.Type.UnwrapRuntimeType(),
runtimeType,
inMemory);

var visitor = new QueryableProjectionVisitor();
Expand Down
116 changes: 116 additions & 0 deletions src/HotChocolate/Data/test/Data.Tests/Issue6953Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
using HotChocolate.Execution;
using HotChocolate.Types;
using Microsoft.Extensions.DependencyInjection;

namespace HotChocolate.Data;

public class Issue6953Tests
{
[Fact]
public async Task UseProjection_On_List_Union_With_Fragments_Does_Not_Throw()
{
var executor = await CreateExecutorAsync();

var result = await executor.ExecuteAsync(
"""
{
unionTest {
__typename
... on ChildA {
a
}
... on ChildB {
b
}
}
}
""");

var operationResult = result.ExpectOperationResult();
Assert.Empty(operationResult.Errors ?? []);
}

[Fact]
public async Task UseProjection_On_List_Union_With_Typename_Only_Does_Not_Throw()
{
var executor = await CreateExecutorAsync();

var result = await executor.ExecuteAsync(
"""
{
unionTest {
__typename
}
}
""");

var operationResult = result.ExpectOperationResult();
Assert.Empty(operationResult.Errors ?? []);
}

private static ValueTask<IRequestExecutor> CreateExecutorAsync()
=> new ServiceCollection()
.AddGraphQL()
.AddProjections()
.AddQueryType<Query>()
.AddType<ChildAType>()
.AddType<ChildBType>()
.AddType<UnionTestType>()
.ModifyRequestOptions(o => o.IncludeExceptionDetails = true)
.BuildRequestExecutorAsync();

public class Query
{
[UseProjection]
[GraphQLType(typeof(ListType<UnionTestType>))]
public IQueryable<Base> GetUnionTest()
=> Data.AsQueryable();
}

public class Base
{
public string C { get; set; } = string.Empty;
}

public class ChildA : Base
{
public string A { get; set; } = string.Empty;
}

public class ChildB : Base
{
public string B { get; set; } = string.Empty;
}

public class ChildAType : ObjectType<ChildA>
{
protected override void Configure(IObjectTypeDescriptor<ChildA> descriptor)
{
descriptor.Field(x => x.A).Type<NonNullType<StringType>>();
}
}

public class ChildBType : ObjectType<ChildB>
{
protected override void Configure(IObjectTypeDescriptor<ChildB> descriptor)
{
descriptor.Field(x => x.B).Type<NonNullType<StringType>>();
}
}

public class UnionTestType : UnionType
{
protected override void Configure(IUnionTypeDescriptor descriptor)
{
descriptor.Name("UnionTestType");
descriptor.Type<ChildAType>();
descriptor.Type<ChildBType>();
}
}

private static readonly Base[] Data =
[
new ChildA { C = "shared-a", A = "value-a" },
new ChildB { C = "shared-b", B = "value-b" }
];
}
Loading