Skip to content

Commit 15071ee

Browse files
authored
Fixed issue with abstract types in operation compiler. (#7322)
1 parent 9ca20f0 commit 15071ee

File tree

4 files changed

+178
-9
lines changed

4 files changed

+178
-9
lines changed

src/HotChocolate/Core/src/Execution/Processing/OperationCompiler.cs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ public IOperation Compile(
111111

112112
// collect root fields
113113
var rootPath = SelectionPath.Root;
114-
var id = GetOrCreateSelectionSetRefId(operationDefinition.SelectionSet, rootPath);
114+
var id = GetOrCreateSelectionSetRefId(operationDefinition.SelectionSet, operationType.Name, rootPath);
115115
var variants = GetOrCreateSelectionVariants(id);
116116
SelectionSetInfo[] infos = [new(operationDefinition.SelectionSet, 0),];
117117

@@ -356,7 +356,7 @@ private void CompleteSelectionSet(CompilerContext context)
356356
}
357357

358358
var selectionPath = context.Path.Append(selection.ResponseName);
359-
selectionSetId = GetOrCreateSelectionSetRefId(selection.SelectionSet, selectionPath);
359+
selectionSetId = GetOrCreateSelectionSetRefId(selection.SelectionSet, fieldType.Name, selectionPath);
360360
var possibleTypes = context.Schema.GetPossibleTypes(fieldType);
361361

362362
if (_enqueuedSelectionSets.Add(selectionSetId))
@@ -598,7 +598,8 @@ private void ResolveFragment(
598598
ifConditionFlags = GetSelectionIncludeCondition(ifCondition, includeCondition);
599599
}
600600

601-
var id = GetOrCreateSelectionSetRefId(selectionSet, context.Path);
601+
var typeName = typeCondition?.Name.Value ?? context.Type.Name;
602+
var id = GetOrCreateSelectionSetRefId(selectionSet, typeName, context.Path);
602603
var variants = GetOrCreateSelectionVariants(id);
603604
var infos = new SelectionSetInfo[] { new(selectionSet, includeCondition), };
604605

@@ -683,9 +684,12 @@ private FragmentDefinitionNode GetFragmentDefinition(
683684

684685
private int GetNextFragmentId() => _nextFragmentId++;
685686

686-
private int GetOrCreateSelectionSetRefId(SelectionSetNode selectionSet, SelectionPath path)
687+
private int GetOrCreateSelectionSetRefId(
688+
SelectionSetNode selectionSet,
689+
string selectionSetType,
690+
SelectionPath path)
687691
{
688-
var selectionSetRef = new SelectionSetRef(selectionSet, path);
692+
var selectionSetRef = new SelectionSetRef(selectionSet, selectionSetType, path);
689693

690694
if (!_selectionSetIdLookup.TryGetValue(selectionSetRef, out var selectionSetId))
691695
{
@@ -849,23 +853,28 @@ internal void RegisterNewSelection(Selection newSelection)
849853

850854
private readonly struct SelectionSetRef(
851855
SelectionSetNode selectionSet,
856+
string selectionSetTypeName,
852857
SelectionPath path)
853858
: IEquatable<SelectionSetRef>
854859
{
855860
public SelectionSetNode SelectionSet { get; } = selectionSet;
856861

857862
public SelectionPath Path { get; } = path;
858863

864+
public string SelectionSetTypeName { get; } = selectionSetTypeName;
865+
859866
public bool Equals(SelectionSetRef other)
860867
=> SyntaxComparer.BySyntax.Equals(SelectionSet, other.SelectionSet)
861-
&& Path.Equals(other.Path);
868+
&& Path.Equals(other.Path)
869+
&& Ordinal.Equals(SelectionSetTypeName, other.SelectionSetTypeName);
862870

863871
public override bool Equals(object? obj)
864872
=> obj is SelectionSetRef other && Equals(other);
865873

866874
public override int GetHashCode()
867875
=> HashCode.Combine(
868876
SyntaxComparer.BySyntax.GetHashCode(SelectionSet),
869-
Path.GetHashCode());
877+
Path.GetHashCode(),
878+
Ordinal.GetHashCode(SelectionSetTypeName));
870879
}
871880
}

src/HotChocolate/Core/test/Execution.Tests/Processing/OperationCompilerTests.cs

Lines changed: 112 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
using System.Text;
2-
using ChilliCream.Testing;
32
using HotChocolate.Language;
43
using HotChocolate.StarWars;
54
using HotChocolate.Types;
65
using HotChocolate.Utilities;
76
using Microsoft.Extensions.DependencyInjection;
87
using Moq;
9-
using Snapshooter.Xunit;
8+
using CookieCrumble;
109

1110
namespace HotChocolate.Execution.Processing;
1211

@@ -1149,6 +1148,89 @@ public async Task Crypto_List_Test()
11491148
MatchSnapshot(document, operation);
11501149
}
11511150

1151+
[Fact]
1152+
public async Task Resolve_Concrete_Types_From_Unions()
1153+
{
1154+
// arrange
1155+
var schema =
1156+
await new ServiceCollection()
1157+
.AddGraphQLServer()
1158+
.AddQueryType<UnionQuery>()
1159+
.AddType<TypeOne>()
1160+
.AddType<TypeTwo>()
1161+
.UseField(next => next)
1162+
.BuildSchemaAsync();
1163+
1164+
var document = Utf8GraphQLParser.Parse(
1165+
"""
1166+
query QueryName {
1167+
oneOrTwo {
1168+
...TypeOneParts
1169+
...TypeTwoParts
1170+
}
1171+
}
1172+
1173+
fragment TypeOneParts on TypeOne {
1174+
field1 { name }
1175+
}
1176+
1177+
fragment TypeTwoParts on TypeTwo {
1178+
field1 { name }
1179+
}
1180+
""");
1181+
1182+
var operationDefinition = document.Definitions.OfType<OperationDefinitionNode>().Single();
1183+
1184+
// act
1185+
var compiler = new OperationCompiler(new InputParser());
1186+
var operation = compiler.Compile(
1187+
"opid",
1188+
operationDefinition,
1189+
schema.QueryType,
1190+
document,
1191+
schema);
1192+
1193+
// assert
1194+
MatchSnapshot(document, operation);
1195+
}
1196+
1197+
[Fact]
1198+
public async Task Resolve_Concrete_Types_From_Unions_Execute()
1199+
{
1200+
// arrange
1201+
var executor =
1202+
await new ServiceCollection()
1203+
.AddGraphQLServer()
1204+
.AddQueryType<UnionQuery>()
1205+
.AddType<TypeOne>()
1206+
.AddType<TypeTwo>()
1207+
.BuildRequestExecutorAsync();
1208+
1209+
var document = Utf8GraphQLParser.Parse(
1210+
"""
1211+
query QueryName {
1212+
oneOrTwo {
1213+
...TypeOneParts
1214+
...TypeTwoParts
1215+
}
1216+
}
1217+
1218+
fragment TypeOneParts on TypeOne {
1219+
field1 { name }
1220+
}
1221+
1222+
fragment TypeTwoParts on TypeTwo {
1223+
field1 { name }
1224+
}
1225+
""");
1226+
1227+
// act
1228+
var result = await executor.ExecuteAsync(builder => builder.SetQuery(document));
1229+
1230+
// assert
1231+
result.MatchSnapshot();
1232+
}
1233+
11521234
private static void MatchSnapshot(DocumentNode original, IOperation compiled)
11531235
{
11541236
var sb = new StringBuilder();
@@ -1201,4 +1283,32 @@ public void OptimizeSelectionSet(SelectionSetOptimizerContext context)
12011283
}
12021284
}
12031285
}
1286+
1287+
public class UnionQuery
1288+
{
1289+
public IOneOrTwo OneOrTwo() => new TypeOne();
1290+
}
1291+
1292+
public class TypeOne : IOneOrTwo
1293+
{
1294+
public FieldOne1 Field1 => new();
1295+
}
1296+
1297+
public class TypeTwo : IOneOrTwo
1298+
{
1299+
public FieldTwo1 Field1 => new();
1300+
}
1301+
1302+
[UnionType]
1303+
public interface IOneOrTwo;
1304+
1305+
public class FieldOne1
1306+
{
1307+
public string Name => "Name";
1308+
}
1309+
1310+
public class FieldTwo1
1311+
{
1312+
public string Name => "Name";
1313+
}
12041314
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
query QueryName {
2+
oneOrTwo {
3+
... TypeOneParts
4+
... TypeTwoParts
5+
}
6+
}
7+
8+
fragment TypeOneParts on TypeOne {
9+
field1 {
10+
name
11+
}
12+
}
13+
14+
fragment TypeTwoParts on TypeTwo {
15+
field1 {
16+
name
17+
}
18+
}
19+
20+
---------------------------------------------------------
21+
22+
query QueryName {
23+
... on UnionQuery {
24+
oneOrTwo @__execute(id: 0, kind: DEFAULT, type: COMPOSITE) {
25+
... on TypeOne {
26+
field1 @__execute(id: 1, kind: DEFAULT, type: COMPOSITE) {
27+
... on FieldOne1 {
28+
name @__execute(id: 2, kind: DEFAULT, type: LEAF)
29+
}
30+
}
31+
}
32+
... on TypeTwo {
33+
field1 @__execute(id: 3, kind: DEFAULT, type: COMPOSITE) {
34+
... on FieldTwo1 {
35+
name @__execute(id: 4, kind: DEFAULT, type: LEAF)
36+
}
37+
}
38+
}
39+
}
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"data": {
3+
"oneOrTwo": {
4+
"field1": {
5+
"name": "Name"
6+
}
7+
}
8+
}
9+
}

0 commit comments

Comments
 (0)