Skip to content

Commit 858b495

Browse files
authored
Fixed issue where Fusion was not handling unions. (#6375)
1 parent 3708c08 commit 858b495

File tree

33 files changed

+1915
-61
lines changed

33 files changed

+1915
-61
lines changed

src/HotChocolate/Core/src/Types/Types/Extensions/TypeExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ internal static bool IsType(this IType type, TypeKind kind1, TypeKind kind2, Typ
386386
{
387387
var innerKind = ((NonNullType) type).Type.Kind;
388388

389-
if (innerKind == kind1 || innerKind == kind2 || type.Kind == kind3)
389+
if (innerKind == kind1 || innerKind == kind2 || innerKind == kind3)
390390
{
391391
return true;
392392
}

src/HotChocolate/Fusion/src/Core/Planning/RequestFormatters/NodeRequestDocumentFormatter.cs

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Diagnostics;
2+
using System.Runtime.CompilerServices;
23
using HotChocolate.Execution.Processing;
34
using HotChocolate.Fusion.Metadata;
45
using HotChocolate.Language;
@@ -113,12 +114,13 @@ private SelectionSetNode CreateSelectionSetNode(
113114
var selectionNodes = new List<ISelectionNode>();
114115
var typeSelectionNodes = new List<ISelectionNode>();
115116
var entityType = _schema.GetType<ObjectType>(entityTypeName);
117+
var selectionSet = (SelectionSet)context.Operation.GetSelectionSet(parentSelection, entityType);
116118

117119
CreateSelectionNodes(
118120
context,
119121
executionStep,
120-
parentSelection,
121122
entityType,
123+
selectionSet,
122124
typeSelectionNodes);
123125

124126
AddInlineFragment(entityType);
@@ -161,44 +163,57 @@ void AddInlineFragment(IObjectType possibleType)
161163
}
162164
}
163165

164-
protected override void CreateSelectionNodes(
166+
protected override bool CreateSelectionNodes(
165167
QueryPlanContext context,
166168
SelectionExecutionStep executionStep,
167-
ISelection parentSelection,
168169
IObjectType possibleType,
170+
SelectionSet selectionSet,
169171
List<ISelectionNode> selectionNodes)
170172
{
173+
var onlyIntrospection = true;
171174
var typeContext = Configuration.GetType<ObjectTypeMetadata>(possibleType.Name);
172-
var selectionSet = context.Operation.GetSelectionSet(parentSelection, possibleType);
173-
174-
foreach (var selection in selectionSet.Selections)
175+
176+
ref var selection = ref selectionSet.GetSelectionsReference();
177+
ref var end = ref Unsafe.Add(ref selection, selectionSet.Selections.Count);
178+
179+
while(Unsafe.IsAddressLessThan(ref selection, ref end))
175180
{
176-
if (executionStep.AllSelections.Contains(selection) ||
177-
selection.Field.Name.EqualsOrdinal(IntrospectionFields.TypeName))
181+
if (!executionStep.AllSelections.Contains(selection) &&
182+
!selection.Field.Name.EqualsOrdinal(IntrospectionFields.TypeName))
183+
{
184+
goto NEXT;
185+
}
186+
187+
if (onlyIntrospection && !selection.Field.IsIntrospectionField)
178188
{
179-
selectionNodes.Add(
180-
CreateSelectionNode(
181-
context,
182-
executionStep,
183-
selection,
184-
typeContext.Fields[selection.Field.Name]));
185-
186-
if (!selection.Arguments.IsFullyCoercedNoErrors)
189+
onlyIntrospection = false;
190+
}
191+
192+
selectionNodes.Add(
193+
CreateSelectionNode(
194+
context,
195+
executionStep,
196+
selection,
197+
typeContext.Fields[selection.Field.Name]));
198+
199+
if (!selection.Arguments.IsFullyCoercedNoErrors)
200+
{
201+
foreach (var argument in selection.Arguments)
187202
{
188-
foreach (var argument in selection.Arguments)
203+
if (!argument.IsFullyCoerced)
189204
{
190-
if (!argument.IsFullyCoerced)
191-
{
192-
TryForwardVariable(
193-
context,
194-
executionStep.SubgraphName,
195-
null,
196-
argument,
197-
argument.Name);
198-
}
205+
TryForwardVariable(
206+
context,
207+
executionStep.SubgraphName,
208+
null,
209+
argument,
210+
argument.Name);
199211
}
200212
}
201213
}
214+
215+
NEXT:
216+
selection = ref Unsafe.Add(ref selection, 1);
202217
}
203218

204219
if (selectionSet.Selections.Count == 0 && selectionNodes.Count == 0)
@@ -219,5 +234,7 @@ protected override void CreateSelectionNodes(
219234
{
220235
throw ThrowHelper.RequestFormatter_SelectionSetEmpty();
221236
}
237+
238+
return onlyIntrospection;
222239
}
223240
}

src/HotChocolate/Fusion/src/Core/Planning/RequestFormatters/RequestDocumentFormatter.cs

Lines changed: 57 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Diagnostics;
2+
using System.Runtime.CompilerServices;
23
using HotChocolate.Execution.Processing;
34
using HotChocolate.Fusion.Metadata;
45
using HotChocolate.Language;
@@ -201,21 +202,25 @@ protected virtual SelectionSetNode CreateSelectionSetNode(
201202
var selectionNodes = new List<ISelectionNode>();
202203
var typeSelectionNodes = selectionNodes;
203204
var possibleTypes = context.Operation.GetPossibleTypes(parentSelection);
205+
var parentType = parentSelection.Type.NamedType();
204206

205207
using var typeEnumerator = possibleTypes.GetEnumerator();
206208
var next = typeEnumerator.MoveNext();
207209
var needsTypeNameField = true;
210+
var isAbstractType = parentType.Kind is TypeKind.Interface or TypeKind.Union;
208211

209212
while (next)
210213
{
211214
var possibleType = typeEnumerator.Current;
215+
var selectionSet = (SelectionSet)context.Operation.GetSelectionSet(parentSelection, possibleType);
212216

213-
CreateSelectionNodes(
214-
context,
215-
executionStep,
216-
parentSelection,
217-
possibleType,
218-
typeSelectionNodes);
217+
var onlyIntrospection =
218+
CreateSelectionNodes(
219+
context,
220+
executionStep,
221+
possibleType,
222+
selectionSet,
223+
typeSelectionNodes);
219224

220225
next = typeEnumerator.MoveNext();
221226

@@ -226,6 +231,14 @@ protected virtual SelectionSetNode CreateSelectionSetNode(
226231
selectionNodes = new List<ISelectionNode>();
227232
single = false;
228233
}
234+
else if (single && isAbstractType && !ReferenceEquals(parentType, possibleType))
235+
{
236+
if (!onlyIntrospection)
237+
{
238+
selectionNodes = new List<ISelectionNode>();
239+
single = false;
240+
}
241+
}
229242

230243
if (!single)
231244
{
@@ -257,44 +270,57 @@ void AddInlineFragment(IObjectType possibleType)
257270
}
258271
}
259272

260-
protected virtual void CreateSelectionNodes(
273+
protected virtual bool CreateSelectionNodes(
261274
QueryPlanContext context,
262275
SelectionExecutionStep executionStep,
263-
ISelection parentSelection,
264276
IObjectType possibleType,
277+
SelectionSet selectionSet,
265278
List<ISelectionNode> selectionNodes)
266279
{
280+
var onlyIntrospection = true;
267281
var typeContext = _config.GetType<ObjectTypeMetadata>(possibleType.Name);
268-
var selectionSet = context.Operation.GetSelectionSet(parentSelection, possibleType);
269282

270-
foreach (var selection in selectionSet.Selections)
283+
ref var selection = ref selectionSet.GetSelectionsReference();
284+
ref var end = ref Unsafe.Add(ref selection, selectionSet.Selections.Count);
285+
286+
while(Unsafe.IsAddressLessThan(ref selection, ref end))
271287
{
272-
if (executionStep.AllSelections.Contains(selection) ||
273-
selection.Field.Name.EqualsOrdinal(IntrospectionFields.TypeName))
288+
if (!executionStep.AllSelections.Contains(selection) &&
289+
!selection.Field.Name.EqualsOrdinal(IntrospectionFields.TypeName))
274290
{
275-
selectionNodes.Add(
276-
CreateSelectionNode(
277-
context,
278-
executionStep,
279-
selection,
280-
typeContext.Fields[selection.Field.Name]));
291+
goto NEXT;
292+
}
281293

282-
if (!selection.Arguments.IsFullyCoercedNoErrors)
294+
if (onlyIntrospection && !selection.Field.IsIntrospectionField)
295+
{
296+
onlyIntrospection = false;
297+
}
298+
299+
selectionNodes.Add(
300+
CreateSelectionNode(
301+
context,
302+
executionStep,
303+
selection,
304+
typeContext.Fields[selection.Field.Name]));
305+
306+
if (!selection.Arguments.IsFullyCoercedNoErrors)
307+
{
308+
foreach (var argument in selection.Arguments)
283309
{
284-
foreach (var argument in selection.Arguments)
310+
if (!argument.IsFullyCoerced)
285311
{
286-
if (!argument.IsFullyCoerced)
287-
{
288-
TryForwardVariable(
289-
context,
290-
executionStep.SubgraphName,
291-
null,
292-
argument,
293-
argument.Name);
294-
}
312+
TryForwardVariable(
313+
context,
314+
executionStep.SubgraphName,
315+
null,
316+
argument,
317+
argument.Name);
295318
}
296319
}
297320
}
321+
322+
NEXT:
323+
selection = ref Unsafe.Add(ref selection, 1);
298324
}
299325

300326
// append exports that were required by other execution steps.
@@ -307,6 +333,8 @@ protected virtual void CreateSelectionNodes(
307333
{
308334
throw ThrowHelper.RequestFormatter_SelectionSetEmpty();
309335
}
336+
337+
return onlyIntrospection;
310338
}
311339

312340
protected void ResolveRequirements(

src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/DemoIntegrationTests.Accounts_And_Reviews2_Products_With_Nodes.graphql

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,13 @@ type ProductDimension {
120120
@source(subgraph: "Products")
121121
}
122122

123+
type ProductNotFoundError implements Error {
124+
message: String!
125+
@source(subgraph: "Products")
126+
productId: Int!
127+
@source(subgraph: "Products")
128+
}
129+
123130
type Review implements Node
124131
@variable(subgraph: "Reviews2", name: "Review_id", select: "id")
125132
@resolver(subgraph: "Reviews2", select: "{ node(id: $Review_id) { ... on Review { ... Review } } }", arguments: [ { name: "Review_id", type: "ID!" } ])
@@ -144,6 +151,8 @@ type SomeData {
144151
type UploadProductPicturePayload {
145152
boolean: Boolean
146153
@source(subgraph: "Products")
154+
errors: [UploadProductPictureError!]
155+
@source(subgraph: "Products")
147156
}
148157

149158
type User implements Node
@@ -170,12 +179,18 @@ type User implements Node
170179
@source(subgraph: "Accounts")
171180
}
172181

182+
interface Error {
183+
message: String!
184+
}
185+
173186
interface Node {
174187
id: ID!
175188
}
176189

177190
union ReviewOrAuthor = User | Review
178191

192+
union UploadProductPictureError = ProductNotFoundError
193+
179194
input AddReviewInput {
180195
authorId: Int!
181196
body: String!

src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/DemoIntegrationTests.Accounts_And_Reviews_Products.graphql

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,13 @@ type ProductDimension {
101101
@source(subgraph: "Products")
102102
}
103103

104+
type ProductNotFoundError implements Error {
105+
message: String!
106+
@source(subgraph: "Products")
107+
productId: Int!
108+
@source(subgraph: "Products")
109+
}
110+
104111
type Review implements Node
105112
@variable(subgraph: "Reviews", name: "Review_id", select: "id")
106113
@resolver(subgraph: "Reviews", select: "{ node(id: $Review_id) { ... on Review { ... Review } } }", arguments: [ { name: "Review_id", type: "ID!" } ])
@@ -125,6 +132,8 @@ type SomeData {
125132
type UploadProductPicturePayload {
126133
boolean: Boolean
127134
@source(subgraph: "Products")
135+
errors: [UploadProductPictureError!]
136+
@source(subgraph: "Products")
128137
}
129138

130139
type User implements Node
@@ -152,12 +161,18 @@ type User implements Node
152161
@source(subgraph: "Accounts")
153162
}
154163

164+
interface Error {
165+
message: String!
166+
}
167+
155168
interface Node {
156169
id: ID!
157170
}
158171

159172
union ReviewOrAuthor = User | Review
160173

174+
union UploadProductPictureError = ProductNotFoundError
175+
161176
input AddReviewInput {
162177
authorId: Int!
163178
body: String!

src/HotChocolate/Fusion/test/Composition.Tests/__snapshots__/DemoIntegrationTests.Accounts_And_Reviews_Products_AutoCompose_With_Node.graphql

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,13 @@ type ProductDimension {
112112
@source(subgraph: "Products")
113113
}
114114

115+
type ProductNotFoundError implements Error {
116+
message: String!
117+
@source(subgraph: "Products")
118+
productId: Int!
119+
@source(subgraph: "Products")
120+
}
121+
115122
type Review implements Node
116123
@variable(subgraph: "Reviews", name: "Review_id", select: "id")
117124
@resolver(subgraph: "Reviews", select: "{ node(id: $Review_id) { ... on Review { ... Review } } }", arguments: [ { name: "Review_id", type: "ID!" } ])
@@ -136,6 +143,8 @@ type SomeData {
136143
type UploadProductPicturePayload {
137144
boolean: Boolean
138145
@source(subgraph: "Products")
146+
errors: [UploadProductPictureError!]
147+
@source(subgraph: "Products")
139148
}
140149

141150
type User implements Node
@@ -152,12 +161,18 @@ type User implements Node
152161
@source(subgraph: "Accounts")
153162
}
154163

164+
interface Error {
165+
message: String!
166+
}
167+
155168
interface Node {
156169
id: ID!
157170
}
158171

159172
union ReviewOrAuthor = Author | Review
160173

174+
union UploadProductPictureError = ProductNotFoundError
175+
161176
input AddReviewInput {
162177
authorId: Int!
163178
body: String!

0 commit comments

Comments
 (0)