4
4
using System . Text . Json ;
5
5
using HotChocolate . Execution . Processing ;
6
6
using HotChocolate . Fusion . Execution . Nodes ;
7
+ using HotChocolate . Fusion . Execution . Pipeline ;
7
8
using HotChocolate . Fusion . Metadata ;
8
9
using HotChocolate . Fusion . Planning ;
9
10
using HotChocolate . Fusion . Utilities ;
12
13
using HotChocolate . Types ;
13
14
using HotChocolate . Utilities ;
14
15
using static HotChocolate . Execution . Processing . Selection ;
16
+ using ErrorHelper = HotChocolate . Utilities . ErrorHelper ;
15
17
using IType = HotChocolate . Types . IType ;
16
18
using ObjectType = HotChocolate . Types . ObjectType ;
17
19
@@ -41,7 +43,8 @@ private static void ComposeResult(
41
43
SelectionSet selectionSet ,
42
44
SelectionData [ ] selectionSetData ,
43
45
ObjectResult selectionSetResult ,
44
- bool partialResult = false )
46
+ bool partialResult = false ,
47
+ int level = 0 )
45
48
{
46
49
if ( selectionSetResult . IsInvalidated )
47
50
{
@@ -69,14 +72,19 @@ private static void ComposeResult(
69
72
70
73
if ( ! field . IsIntrospectionField )
71
74
{
72
- var nullable = selection . TypeKind is not TypeKind . NonNull ;
73
- var namedType = selectionType . NamedType ( ) ;
75
+ var isSemanticNonNull = IsSemanticNonNull ( selection , level ) ;
76
+ var nullable = selectionType . IsNullableType ( ) ;
77
+ var nullableType = selectionType . NullableType ( ) ;
74
78
75
79
if ( ! data . HasValue )
76
80
{
77
81
if ( ! partialResult )
78
82
{
79
- if ( ! nullable )
83
+ if ( isSemanticNonNull )
84
+ {
85
+ AddSemanticNonNullViolation ( context . Result , selection , selectionSetResult , responseIndex ) ;
86
+ }
87
+ else if ( ! nullable )
80
88
{
81
89
PropagateNullValues ( context . Result , selection , selectionSetResult , responseIndex ) ;
82
90
break ;
@@ -85,14 +93,21 @@ private static void ComposeResult(
85
93
result . Set ( responseName , null , nullable ) ;
86
94
}
87
95
}
88
- else if ( namedType . IsType ( TypeKind . Scalar ) )
96
+ else if ( nullableType . IsType ( TypeKind . Scalar ) )
89
97
{
90
98
var value = data . Single . Element ;
91
99
92
- if ( value . ValueKind is JsonValueKind . Null or JsonValueKind . Undefined && ! nullable )
100
+ if ( value . ValueKind is JsonValueKind . Null or JsonValueKind . Undefined )
93
101
{
94
- PropagateNullValues ( context . Result , selection , selectionSetResult , responseIndex ) ;
95
- break ;
102
+ if ( isSemanticNonNull )
103
+ {
104
+ AddSemanticNonNullViolation ( context . Result , selection , selectionSetResult , responseIndex ) ;
105
+ }
106
+ else if ( ! nullable )
107
+ {
108
+ PropagateNullValues ( context . Result , selection , selectionSetResult , responseIndex ) ;
109
+ break ;
110
+ }
96
111
}
97
112
98
113
result . Set ( responseName , value , nullable ) ;
@@ -105,15 +120,21 @@ private static void ComposeResult(
105
120
result . Set ( responseName , reformattedId , nullable ) ;
106
121
}
107
122
}
108
- else if ( namedType . IsType ( TypeKind . Enum ) )
123
+ else if ( nullableType . IsType ( TypeKind . Enum ) )
109
124
{
110
- // we might need to map the enum value!
111
125
var value = data . Single . Element ;
112
126
113
- if ( value . ValueKind is JsonValueKind . Null or JsonValueKind . Undefined && ! nullable )
127
+ if ( value . ValueKind is JsonValueKind . Null or JsonValueKind . Undefined )
114
128
{
115
- PropagateNullValues ( context . Result , selection , selectionSetResult , responseIndex ) ;
116
- break ;
129
+ if ( isSemanticNonNull )
130
+ {
131
+ AddSemanticNonNullViolation ( context . Result , selection , selectionSetResult , responseIndex ) ;
132
+ }
133
+ else if ( ! nullable )
134
+ {
135
+ PropagateNullValues ( context . Result , selection , selectionSetResult , responseIndex ) ;
136
+ break ;
137
+ }
117
138
}
118
139
119
140
result . Set ( responseName , value , nullable ) ;
@@ -122,7 +143,7 @@ private static void ComposeResult(
122
143
{
123
144
if ( ! result . IsInitialized )
124
145
{
125
- // we add a placeholder here so if the ComposeObject propagates an error
146
+ // we add a placeholder here so if ComposeObject propagates an error
126
147
// there is a value here.
127
148
result . Set ( responseName , null , nullable ) ;
128
149
@@ -131,12 +152,20 @@ private static void ComposeResult(
131
152
selectionSetResult ,
132
153
responseIndex ,
133
154
selection ,
134
- data ) ;
155
+ data ,
156
+ level ) ;
135
157
136
- if ( value is null && ! nullable )
158
+ if ( value is null )
137
159
{
138
- PropagateNullValues ( context . Result , selection , selectionSetResult , responseIndex ) ;
139
- break ;
160
+ if ( isSemanticNonNull )
161
+ {
162
+ AddSemanticNonNullViolation ( context . Result , selection , selectionSetResult , responseIndex ) ;
163
+ }
164
+ else if ( ! nullable )
165
+ {
166
+ PropagateNullValues ( context . Result , selection , selectionSetResult , responseIndex ) ;
167
+ break ;
168
+ }
140
169
}
141
170
142
171
result . Set ( responseName , value , nullable ) ;
@@ -146,18 +175,30 @@ private static void ComposeResult(
146
175
{
147
176
if ( ! result . IsInitialized )
148
177
{
178
+ // we add a placeholder here so if ComposeList propagates an error
179
+ // there is a value here.
180
+ result . Set ( responseName , null , nullable ) ;
181
+
149
182
var value = ComposeList (
150
183
context ,
151
184
selectionSetResult ,
152
185
responseIndex ,
153
186
selection ,
154
187
data ,
155
- selectionType ) ;
188
+ selectionType ,
189
+ level + 1 ) ;
156
190
157
- if ( value is null && ! nullable )
191
+ if ( value is null )
158
192
{
159
- PropagateNullValues ( context . Result , selection , selectionSetResult , responseIndex ) ;
160
- break ;
193
+ if ( isSemanticNonNull )
194
+ {
195
+ AddSemanticNonNullViolation ( context . Result , selection , selectionSetResult , responseIndex ) ;
196
+ }
197
+ else if ( ! nullable )
198
+ {
199
+ PropagateNullValues ( context . Result , selection , selectionSetResult , responseIndex ) ;
200
+ break ;
201
+ }
161
202
}
162
203
163
204
result . Set ( responseName , value , nullable ) ;
@@ -191,7 +232,8 @@ private static void ComposeResult(
191
232
int parentIndex ,
192
233
Selection selection ,
193
234
SelectionData selectionData ,
194
- IType type )
235
+ IType type ,
236
+ int level )
195
237
{
196
238
if ( selectionData . IsNull ( ) )
197
239
{
@@ -206,14 +248,15 @@ private static void ComposeResult(
206
248
var index = 0 ;
207
249
var elementType = type . ElementType ( ) ;
208
250
var nullable = elementType . IsNullableType ( ) ;
251
+ var isSemanticNonNull = IsSemanticNonNull ( selection , level ) ;
209
252
var result = context . Result . RentList ( json . GetArrayLength ( ) ) ;
210
253
211
254
result . IsNullable = nullable ;
212
255
result . SetParent ( parent , parentIndex ) ;
213
256
214
257
foreach ( var item in json . EnumerateArray ( ) )
215
258
{
216
- // we add a placeholder here so if the ComposeElement propagates an error
259
+ // we add a placeholder here so if ComposeElement propagates an error
217
260
// there is a value here.
218
261
result . AddUnsafe ( null ) ;
219
262
@@ -223,12 +266,20 @@ private static void ComposeResult(
223
266
index ,
224
267
selection ,
225
268
new SelectionData ( new JsonResult ( schemaName , item ) ) ,
226
- elementType ) ;
269
+ elementType ,
270
+ level ) ;
227
271
228
- if ( ! nullable && element is null )
272
+ if ( element is null )
229
273
{
230
- PropagateNullValues ( context . Result , selection , result , index ) ;
231
- return null ;
274
+ if ( isSemanticNonNull )
275
+ {
276
+ AddSemanticNonNullViolation ( context . Result , selection , result , index ) ;
277
+ }
278
+ else if ( ! nullable )
279
+ {
280
+ PropagateNullValues ( context . Result , selection , result , index ) ;
281
+ break ;
282
+ }
232
283
}
233
284
234
285
result . SetUnsafe ( index ++ , element ) ;
@@ -248,16 +299,17 @@ private static void ComposeResult(
248
299
int parentIndex ,
249
300
Selection selection ,
250
301
SelectionData selectionData ,
251
- IType valueType )
302
+ IType valueType ,
303
+ int level )
252
304
{
253
- var namedType = valueType . NamedType ( ) ;
305
+ var nullableType = valueType . NullableType ( ) ;
254
306
255
307
if ( ! selectionData . HasValue )
256
308
{
257
309
return null ;
258
310
}
259
311
260
- if ( namedType . IsType ( TypeKind . Scalar ) )
312
+ if ( nullableType . IsType ( TypeKind . Scalar ) )
261
313
{
262
314
var value = selectionData . Single . Element ;
263
315
@@ -276,7 +328,7 @@ private static void ComposeResult(
276
328
return value ;
277
329
}
278
330
279
- if ( namedType . IsType ( TypeKind . Enum ) )
331
+ if ( nullableType . IsType ( TypeKind . Enum ) )
280
332
{
281
333
var value = selectionData . Single . Element ;
282
334
@@ -288,17 +340,18 @@ private static void ComposeResult(
288
340
return value ;
289
341
}
290
342
291
- return TypeExtensions . IsCompositeType ( valueType )
292
- ? ComposeObject ( context , parent , parentIndex , selection , selectionData )
293
- : ComposeList ( context , parent , parentIndex , selection , selectionData , valueType ) ;
343
+ return nullableType . IsCompositeType ( )
344
+ ? ComposeObject ( context , parent , parentIndex , selection , selectionData , 0 )
345
+ : ComposeList ( context , parent , parentIndex , selection , selectionData , valueType , level + 1 ) ;
294
346
}
295
347
296
348
private static ObjectResult ? ComposeObject (
297
349
FusionExecutionContext context ,
298
350
ResultData parent ,
299
351
int parentIndex ,
300
352
ISelection selection ,
301
- SelectionData selectionData )
353
+ SelectionData selectionData ,
354
+ int level )
302
355
{
303
356
if ( selectionData . IsNull ( ) )
304
357
{
@@ -330,13 +383,13 @@ private static void ComposeResult(
330
383
331
384
var childSelectionResults = new SelectionData [ selectionCount ] ;
332
385
ExtractSelectionResults ( selectionData , selectionSet , childSelectionResults ) ;
333
- ComposeResult ( context , selectionSet , childSelectionResults , result , true ) ;
386
+ ComposeResult ( context , selectionSet , childSelectionResults , result , true , level ) ;
334
387
}
335
388
else
336
389
{
337
390
var childSelectionResults = new SelectionData [ selectionCount ] ;
338
391
ExtractSelectionResults ( selectionData , selectionSet , childSelectionResults ) ;
339
- ComposeResult ( context , selectionSet , childSelectionResults , result ) ;
392
+ ComposeResult ( context , selectionSet , childSelectionResults , result , false , level ) ;
340
393
}
341
394
342
395
return result . IsInvalidated ? null : result ;
@@ -716,6 +769,17 @@ public static void ExtractVariables(
716
769
}
717
770
}
718
771
772
+ private static void AddSemanticNonNullViolation (
773
+ ResultBuilder resultBuilder ,
774
+ Selection selection ,
775
+ ResultData selectionSetResult ,
776
+ int responseIndex )
777
+ {
778
+ var path = PathHelper . CreatePathFromContext ( selection , selectionSetResult , responseIndex ) ;
779
+ var error = ErrorHelper . CreateSemanticNonNullViolationError ( path , selection ) ;
780
+ resultBuilder . AddError ( error ) ;
781
+ }
782
+
719
783
private static void PropagateNullValues (
720
784
ResultBuilder resultBuilder ,
721
785
Selection selection ,
@@ -727,6 +791,25 @@ private static void PropagateNullValues(
727
791
ValueCompletion . PropagateNullValues ( selectionSetResult ) ;
728
792
}
729
793
794
+ private static readonly CustomOptionsFlags [ ] _levelOptions =
795
+ [
796
+ CustomOptionsFlags . Option5 ,
797
+ CustomOptionsFlags . Option6 ,
798
+ CustomOptionsFlags . Option7
799
+ ] ;
800
+
801
+ private static bool IsSemanticNonNull ( Selection selection , int level )
802
+ {
803
+ if ( level >= SemanticNonNullOptimizer . MaxLevels )
804
+ {
805
+ return true ;
806
+ }
807
+
808
+ var optionForLevel = _levelOptions [ level ] ;
809
+
810
+ return selection . CustomOptions . HasFlag ( optionForLevel ) ;
811
+ }
812
+
730
813
private sealed class ErrorPathContext
731
814
{
732
815
public string Current { get ; set ; } = string . Empty ;
0 commit comments