1
+ using HotChocolate . Language ;
1
2
using HotChocolate . Skimmed ;
2
3
using static HotChocolate . Fusion . Composition . MergeExtensions ;
3
4
@@ -64,6 +65,8 @@ public static void MergeField(
64
65
return ;
65
66
}
66
67
68
+ MergeSemanticNonNullability ( context , source , target ) ;
69
+
67
70
if ( ! mergedType . Equals ( target . Type , TypeComparison . Structural ) )
68
71
{
69
72
target . Type = mergedType ;
@@ -101,7 +104,7 @@ public static void MergeField(
101
104
return ;
102
105
}
103
106
104
- if ( ! targetArgument . Type . Equals ( mergedInputType , TypeComparison . Structural ) )
107
+ if ( ! targetArgument . Type . Equals ( mergedInputType , TypeComparison . Structural ) )
105
108
{
106
109
targetArgument . Type = mergedInputType ;
107
110
}
@@ -128,7 +131,7 @@ public static void MergeField(
128
131
// If the target field is not deprecated and the source field is deprecated, copy over the
129
132
target . MergeDeprecationWith ( source ) ;
130
133
131
- target . MergeDirectivesWith ( source , context ) ;
134
+ target . MergeDirectivesWith ( source , context , shouldApplySemanticNonNull : false ) ;
132
135
133
136
foreach ( var sourceArgument in source . Arguments )
134
137
{
@@ -151,4 +154,118 @@ public static void MergeField(
151
154
}
152
155
}
153
156
}
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
+ }
154
271
}
0 commit comments