Skip to content

Commit b07cb02

Browse files
[Fusion] @semanticNonNull composition support (#7731)
1 parent 5e16607 commit b07cb02

File tree

6 files changed

+1622
-3
lines changed

6 files changed

+1622
-3
lines changed

src/HotChocolate/Fusion/src/Composition/Extensions/ComplexTypeMergeExtensions.cs

Lines changed: 119 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using HotChocolate.Language;
12
using HotChocolate.Skimmed;
23
using static HotChocolate.Fusion.Composition.MergeExtensions;
34

@@ -64,6 +65,8 @@ public static void MergeField(
6465
return;
6566
}
6667

68+
MergeSemanticNonNullability(context, source, target);
69+
6770
if (!mergedType.Equals(target.Type, TypeComparison.Structural))
6871
{
6972
target.Type = mergedType;
@@ -101,7 +104,7 @@ public static void MergeField(
101104
return;
102105
}
103106

104-
if(!targetArgument.Type.Equals(mergedInputType, TypeComparison.Structural))
107+
if (!targetArgument.Type.Equals(mergedInputType, TypeComparison.Structural))
105108
{
106109
targetArgument.Type = mergedInputType;
107110
}
@@ -128,7 +131,7 @@ public static void MergeField(
128131
// If the target field is not deprecated and the source field is deprecated, copy over the
129132
target.MergeDeprecationWith(source);
130133

131-
target.MergeDirectivesWith(source, context);
134+
target.MergeDirectivesWith(source, context, shouldApplySemanticNonNull: false);
132135

133136
foreach (var sourceArgument in source.Arguments)
134137
{
@@ -151,4 +154,118 @@ public static void MergeField(
151154
}
152155
}
153156
}
157+
158+
private static void MergeSemanticNonNullability(
159+
this CompositionContext context,
160+
OutputFieldDefinition source,
161+
OutputFieldDefinition target)
162+
{
163+
var sourceSemanticNonNullLevels = GetSemanticNonNullLevels(source);
164+
var targetSemanticNonNullLevels = GetSemanticNonNullLevels(target);
165+
166+
if (sourceSemanticNonNullLevels.Count < 1 && targetSemanticNonNullLevels.Count < 1)
167+
{
168+
return;
169+
}
170+
171+
List<int> levels = [];
172+
173+
var currentLevel = 0;
174+
var currentSourceType = source.Type;
175+
var currentTargetType = target.Type;
176+
while (true)
177+
{
178+
if (currentTargetType is NonNullTypeDefinition targetNonNullType)
179+
{
180+
if (currentSourceType is not NonNullTypeDefinition)
181+
{
182+
if (sourceSemanticNonNullLevels.Contains(currentLevel))
183+
{
184+
// Non-Null + Semantic Non-Null case
185+
levels.Add(currentLevel);
186+
}
187+
}
188+
189+
currentTargetType = targetNonNullType.NullableType;
190+
}
191+
else if (targetSemanticNonNullLevels.Contains(currentLevel))
192+
{
193+
if (currentSourceType is NonNullTypeDefinition || sourceSemanticNonNullLevels.Contains(currentLevel))
194+
{
195+
// Semantic Non-Null + (Non-Null || Semantic Non-Null) case
196+
levels.Add(currentLevel);
197+
}
198+
}
199+
200+
if (currentSourceType is NonNullTypeDefinition sourceNonNullType)
201+
{
202+
currentSourceType = sourceNonNullType.NullableType;
203+
}
204+
205+
if (currentTargetType is ListTypeDefinition targetListType)
206+
{
207+
currentTargetType = targetListType.ElementType;
208+
209+
if (currentSourceType is ListTypeDefinition sourceListType)
210+
{
211+
currentSourceType = sourceListType.ElementType;
212+
}
213+
214+
currentLevel++;
215+
216+
continue;
217+
}
218+
219+
break;
220+
}
221+
222+
var targetSemanticNonNullDirective = target.Directives
223+
.FirstOrDefault(d => d.Name == BuiltIns.SemanticNonNull.Name);
224+
225+
if (targetSemanticNonNullDirective is not null)
226+
{
227+
target.Directives.Remove(targetSemanticNonNullDirective);
228+
}
229+
230+
if (levels.Count < 1)
231+
{
232+
return;
233+
}
234+
235+
if (context.FusionGraph.DirectiveDefinitions.TryGetDirective(BuiltIns.SemanticNonNull.Name,
236+
out var semanticNonNullDirectiveDefinition))
237+
{
238+
if (levels is [0])
239+
{
240+
target.Directives.Add(new Directive(semanticNonNullDirectiveDefinition));
241+
}
242+
else
243+
{
244+
var levelsValueNode = new ListValueNode(levels.Select(l => new IntValueNode(l)).ToList());
245+
var levelsArgument = new ArgumentAssignment(BuiltIns.SemanticNonNull.Levels, levelsValueNode);
246+
target.Directives.Add(new Directive(semanticNonNullDirectiveDefinition, levelsArgument));
247+
}
248+
}
249+
}
250+
251+
private static List<int> GetSemanticNonNullLevels(IDirectivesProvider provider)
252+
{
253+
var directive = provider.Directives
254+
.FirstOrDefault(d => d.Name == BuiltIns.SemanticNonNull.Name);
255+
256+
if (directive is null)
257+
{
258+
return [];
259+
}
260+
261+
if (directive.Arguments.TryGetValue(BuiltIns.SemanticNonNull.Levels, out var levelsArg))
262+
{
263+
if (levelsArg is ListValueNode listValueNode)
264+
{
265+
return listValueNode.Items.Cast<IntValueNode>().Select(i => i.ToInt32()).ToList();
266+
}
267+
}
268+
269+
return [0];
270+
}
154271
}

src/HotChocolate/Fusion/src/Composition/Extensions/MergeExtensions.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ internal static void MergeDescriptionWith(this INamedTypeDefinition target, INam
170170
internal static void MergeDirectivesWith(
171171
this IDirectivesProvider target,
172172
IDirectivesProvider source,
173-
CompositionContext context)
173+
CompositionContext context,
174+
bool shouldApplySemanticNonNull = true)
174175
{
175176
foreach (var directive in source.Directives)
176177
{
@@ -183,6 +184,11 @@ internal static void MergeDirectivesWith(
183184
continue;
184185
}
185186

187+
if (directive.Name == BuiltIns.SemanticNonNull.Name && !shouldApplySemanticNonNull)
188+
{
189+
continue;
190+
}
191+
186192
context.FusionGraph.DirectiveDefinitions.TryGetDirective(directive.Name, out var directiveDefinition);
187193

188194
if (!target.Directives.ContainsName(directive.Name))

0 commit comments

Comments
 (0)