relay-runtime 0.0.0-main-b6199194 → 0.0.0-main-042ceca3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/experimental.js +1 -1
- package/index.js +1 -1
- package/index.js.flow +0 -1
- package/lib/query/fetchQuery.js +1 -2
- package/lib/store/RelayErrorTrie.js +1 -17
- package/lib/store/RelayModernFragmentSpecResolver.js +1 -5
- package/lib/store/RelayReader.js +43 -77
- package/lib/store/RelayStoreSubscriptions.js +0 -2
- package/lib/store/ResolverFragments.js +4 -2
- package/lib/store/observeFragmentExperimental.js +1 -2
- package/lib/util/handlePotentialSnapshotErrors.js +44 -62
- package/package.json +1 -1
- package/query/fetchQuery.js.flow +1 -6
- package/relay-runtime-experimental.js +2 -2
- package/relay-runtime-experimental.min.js +2 -2
- package/relay-runtime.js +2 -2
- package/relay-runtime.min.js +2 -2
- package/store/RelayErrorTrie.js.flow +0 -12
- package/store/RelayModernFragmentSpecResolver.js.flow +1 -11
- package/store/RelayReader.js.flow +78 -97
- package/store/RelayStoreSubscriptions.js.flow +0 -2
- package/store/RelayStoreTypes.js.flow +30 -18
- package/store/ResolverCache.js.flow +2 -2
- package/store/ResolverFragments.js.flow +6 -3
- package/store/observeFragmentExperimental.js.flow +1 -6
- package/util/handlePotentialSnapshotErrors.js.flow +47 -62
|
@@ -16,16 +16,6 @@ import type {PayloadError} from '../network/RelayNetworkTypes';
|
|
|
16
16
|
// $FlowFixMe[recursive-definition]
|
|
17
17
|
const SELF: Self = Symbol('$SELF');
|
|
18
18
|
|
|
19
|
-
class RelayFieldError extends Error {
|
|
20
|
-
constructor(message: string, errors: Array<TRelayFieldErrorForDisplay> = []) {
|
|
21
|
-
super(message);
|
|
22
|
-
this.name = 'RelayFieldError';
|
|
23
|
-
this.message = message;
|
|
24
|
-
this.errors = errors;
|
|
25
|
-
}
|
|
26
|
-
errors: Array<TRelayFieldErrorForDisplay>;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
19
|
export opaque type Self = typeof SELF;
|
|
30
20
|
|
|
31
21
|
export type TRelayFieldErrorForDisplay = $ReadOnly<{
|
|
@@ -180,11 +170,9 @@ module.exports = ({
|
|
|
180
170
|
buildErrorTrie,
|
|
181
171
|
getNestedErrorTrieByKey,
|
|
182
172
|
getErrorsByKey,
|
|
183
|
-
RelayFieldError,
|
|
184
173
|
}: {
|
|
185
174
|
SELF: typeof SELF,
|
|
186
175
|
buildErrorTrie: typeof buildErrorTrie,
|
|
187
176
|
getNestedErrorTrieByKey: typeof getNestedErrorTrieByKey,
|
|
188
177
|
getErrorsByKey: typeof getErrorsByKey,
|
|
189
|
-
RelayFieldError: Class<RelayFieldError>,
|
|
190
178
|
});
|
|
@@ -19,7 +19,6 @@ import type {
|
|
|
19
19
|
FragmentSpecResolver,
|
|
20
20
|
FragmentSpecResults,
|
|
21
21
|
IEnvironment,
|
|
22
|
-
MissingRequiredFields,
|
|
23
22
|
PluralReaderSelector,
|
|
24
23
|
RelayContext,
|
|
25
24
|
SelectorData,
|
|
@@ -227,7 +226,6 @@ class SelectorResolver {
|
|
|
227
226
|
_data: ?SelectorData;
|
|
228
227
|
_environment: IEnvironment;
|
|
229
228
|
_isMissingData: boolean;
|
|
230
|
-
_missingRequiredFields: ?MissingRequiredFields;
|
|
231
229
|
_errorResponseFields: ?ErrorResponseFields;
|
|
232
230
|
_rootIsQueryRenderer: boolean;
|
|
233
231
|
_selector: SingularReaderSelector;
|
|
@@ -244,7 +242,6 @@ class SelectorResolver {
|
|
|
244
242
|
this._callback = callback;
|
|
245
243
|
this._data = snapshot.data;
|
|
246
244
|
this._isMissingData = snapshot.isMissingData;
|
|
247
|
-
this._missingRequiredFields = snapshot.missingRequiredFields;
|
|
248
245
|
this._errorResponseFields = snapshot.errorResponseFields;
|
|
249
246
|
this._environment = environment;
|
|
250
247
|
this._rootIsQueryRenderer = rootIsQueryRenderer;
|
|
@@ -326,12 +323,7 @@ class SelectorResolver {
|
|
|
326
323
|
}
|
|
327
324
|
}
|
|
328
325
|
}
|
|
329
|
-
handlePotentialSnapshotErrors(
|
|
330
|
-
this._environment,
|
|
331
|
-
this._missingRequiredFields,
|
|
332
|
-
this._errorResponseFields,
|
|
333
|
-
this._selector.node.metadata?.throwOnFieldError ?? false,
|
|
334
|
-
);
|
|
326
|
+
handlePotentialSnapshotErrors(this._environment, this._errorResponseFields);
|
|
335
327
|
return this._data;
|
|
336
328
|
}
|
|
337
329
|
|
|
@@ -346,7 +338,6 @@ class SelectorResolver {
|
|
|
346
338
|
const snapshot = this._environment.lookup(selector);
|
|
347
339
|
this._data = recycleNodesInto(this._data, snapshot.data);
|
|
348
340
|
this._isMissingData = snapshot.isMissingData;
|
|
349
|
-
this._missingRequiredFields = snapshot.missingRequiredFields;
|
|
350
341
|
this._errorResponseFields = snapshot.errorResponseFields;
|
|
351
342
|
this._selector = selector;
|
|
352
343
|
this._subscription = this._environment.subscribe(snapshot, this._onChange);
|
|
@@ -383,7 +374,6 @@ class SelectorResolver {
|
|
|
383
374
|
_onChange = (snapshot: Snapshot): void => {
|
|
384
375
|
this._data = snapshot.data;
|
|
385
376
|
this._isMissingData = snapshot.isMissingData;
|
|
386
|
-
this._missingRequiredFields = snapshot.missingRequiredFields;
|
|
387
377
|
this._errorResponseFields = snapshot.errorResponseFields;
|
|
388
378
|
this._callback();
|
|
389
379
|
};
|
|
@@ -35,7 +35,6 @@ import type {
|
|
|
35
35
|
ErrorResponseFields,
|
|
36
36
|
MissingClientEdgeRequestInfo,
|
|
37
37
|
MissingLiveResolverField,
|
|
38
|
-
MissingRequiredFields,
|
|
39
38
|
Record,
|
|
40
39
|
RecordSource,
|
|
41
40
|
RequestDescriptor,
|
|
@@ -99,7 +98,6 @@ class RelayReader {
|
|
|
99
98
|
_missingClientEdges: Array<MissingClientEdgeRequestInfo>;
|
|
100
99
|
_missingLiveResolverFields: Array<MissingLiveResolverField>;
|
|
101
100
|
_isWithinUnmatchedTypeRefinement: boolean;
|
|
102
|
-
_missingRequiredFields: ?MissingRequiredFields;
|
|
103
101
|
_errorResponseFields: ?ErrorResponseFields;
|
|
104
102
|
_owner: RequestDescriptor;
|
|
105
103
|
_recordSource: RecordSource;
|
|
@@ -124,7 +122,6 @@ class RelayReader {
|
|
|
124
122
|
this._missingLiveResolverFields = [];
|
|
125
123
|
this._isMissingData = false;
|
|
126
124
|
this._isWithinUnmatchedTypeRefinement = false;
|
|
127
|
-
this._missingRequiredFields = null;
|
|
128
125
|
this._errorResponseFields = null;
|
|
129
126
|
this._owner = selector.owner;
|
|
130
127
|
this._recordSource = recordSource;
|
|
@@ -193,7 +190,6 @@ class RelayReader {
|
|
|
193
190
|
missingLiveResolverFields: this._missingLiveResolverFields,
|
|
194
191
|
seenRecords: this._seenRecords,
|
|
195
192
|
selector: this._selector,
|
|
196
|
-
missingRequiredFields: this._missingRequiredFields,
|
|
197
193
|
errorResponseFields: this._errorResponseFields,
|
|
198
194
|
};
|
|
199
195
|
}
|
|
@@ -216,6 +212,7 @@ class RelayReader {
|
|
|
216
212
|
fieldPath: (error.path ?? []).join('.'),
|
|
217
213
|
error,
|
|
218
214
|
shouldThrow: this._selector.node.metadata?.throwOnFieldError ?? false,
|
|
215
|
+
handled: false,
|
|
219
216
|
});
|
|
220
217
|
}
|
|
221
218
|
}
|
|
@@ -231,7 +228,12 @@ class RelayReader {
|
|
|
231
228
|
|
|
232
229
|
this._errorResponseFields.push(
|
|
233
230
|
this._selector.node.metadata?.throwOnFieldError ?? false
|
|
234
|
-
? {
|
|
231
|
+
? {
|
|
232
|
+
kind: 'missing_expected_data.throw',
|
|
233
|
+
owner,
|
|
234
|
+
fieldPath,
|
|
235
|
+
handled: false,
|
|
236
|
+
}
|
|
235
237
|
: {kind: 'missing_expected_data.log', owner, fieldPath},
|
|
236
238
|
);
|
|
237
239
|
|
|
@@ -284,33 +286,27 @@ class RelayReader {
|
|
|
284
286
|
}
|
|
285
287
|
|
|
286
288
|
_maybeReportUnexpectedNull(fieldPath: string, action: 'LOG' | 'THROW') {
|
|
287
|
-
if (this._missingRequiredFields?.action === 'THROW') {
|
|
288
|
-
// Chained @required directives may cause a parent `@required(action:
|
|
289
|
-
// THROW)` field to become null, so the first missing field we
|
|
290
|
-
// encounter is likely to be the root cause of the error.
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
289
|
const owner = this._fragmentName;
|
|
294
290
|
|
|
291
|
+
if (this._errorResponseFields == null) {
|
|
292
|
+
this._errorResponseFields = [];
|
|
293
|
+
}
|
|
294
|
+
|
|
295
295
|
switch (action) {
|
|
296
296
|
case 'THROW':
|
|
297
|
-
this.
|
|
297
|
+
this._errorResponseFields.push({
|
|
298
|
+
kind: 'missing_required_field.throw',
|
|
299
|
+
fieldPath,
|
|
300
|
+
owner,
|
|
301
|
+
handled: false,
|
|
302
|
+
});
|
|
298
303
|
return;
|
|
299
304
|
case 'LOG':
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
} else {
|
|
306
|
-
this._missingRequiredFields = {
|
|
307
|
-
action,
|
|
308
|
-
fields: [
|
|
309
|
-
...this._missingRequiredFields.fields,
|
|
310
|
-
{path: fieldPath, owner},
|
|
311
|
-
],
|
|
312
|
-
};
|
|
313
|
-
}
|
|
305
|
+
this._errorResponseFields.push({
|
|
306
|
+
kind: 'missing_required_field.log',
|
|
307
|
+
fieldPath,
|
|
308
|
+
owner,
|
|
309
|
+
});
|
|
314
310
|
return;
|
|
315
311
|
default:
|
|
316
312
|
(action: empty);
|
|
@@ -333,43 +329,40 @@ class RelayReader {
|
|
|
333
329
|
"Couldn't determine field name for this field. It might be a ReaderClientExtension - which is not yet supported.",
|
|
334
330
|
);
|
|
335
331
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
errors.push(missingFieldError);
|
|
371
|
-
}
|
|
372
|
-
}
|
|
332
|
+
const errors = this._errorResponseFields
|
|
333
|
+
?.map(error => {
|
|
334
|
+
switch (error.kind) {
|
|
335
|
+
case 'relay_field_payload.error':
|
|
336
|
+
const {message, ...displayError} = error.error;
|
|
337
|
+
return displayError;
|
|
338
|
+
case 'missing_expected_data.throw':
|
|
339
|
+
case 'missing_expected_data.log':
|
|
340
|
+
return {
|
|
341
|
+
path: error.fieldPath.split('.'),
|
|
342
|
+
};
|
|
343
|
+
case 'relay_resolver.error':
|
|
344
|
+
return {
|
|
345
|
+
message: `Relay: Error in resolver for field at ${error.fieldPath} in ${error.owner}`,
|
|
346
|
+
};
|
|
347
|
+
case 'missing_required_field.throw':
|
|
348
|
+
// If we have a nested @required(THROW) that will throw,
|
|
349
|
+
// we want to catch that error and provide it
|
|
350
|
+
return {
|
|
351
|
+
message: `Relay: Missing @required value at path '${error.fieldPath}' in '${error.owner}'.`,
|
|
352
|
+
};
|
|
353
|
+
case 'missing_required_field.log':
|
|
354
|
+
// For backwards compatibility, we don't surface log level missing required fields
|
|
355
|
+
return null;
|
|
356
|
+
default:
|
|
357
|
+
(error.kind: empty);
|
|
358
|
+
invariant(
|
|
359
|
+
false,
|
|
360
|
+
'Unexpected error errorResponseField kind: %s',
|
|
361
|
+
error.kind,
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
})
|
|
365
|
+
.filter(Boolean);
|
|
373
366
|
|
|
374
367
|
data[fieldName] = errors != null ? {ok: false, errors} : {ok: true, value};
|
|
375
368
|
}
|
|
@@ -412,10 +405,8 @@ class RelayReader {
|
|
|
412
405
|
break;
|
|
413
406
|
case 'CatchField': {
|
|
414
407
|
const previousResponseFields = this._errorResponseFields;
|
|
415
|
-
const previousMissingRequiredFields = this._missingRequiredFields;
|
|
416
408
|
|
|
417
409
|
this._errorResponseFields = null;
|
|
418
|
-
this._missingRequiredFields = null;
|
|
419
410
|
|
|
420
411
|
const catchFieldValue = this._readClientSideDirectiveField(
|
|
421
412
|
selection,
|
|
@@ -427,15 +418,23 @@ class RelayReader {
|
|
|
427
418
|
this._handleCatchToResult(selection, record, data, catchFieldValue);
|
|
428
419
|
}
|
|
429
420
|
|
|
430
|
-
const
|
|
421
|
+
const childrenErrorResponseFields = this._errorResponseFields;
|
|
431
422
|
|
|
432
423
|
this._errorResponseFields = previousResponseFields;
|
|
433
|
-
this._missingRequiredFields = previousMissingRequiredFields;
|
|
434
424
|
|
|
435
425
|
// If we encountered non-throwing @required fields, in the children,
|
|
436
426
|
// we want to preserve those errors in the snapshot.
|
|
437
|
-
if (
|
|
438
|
-
|
|
427
|
+
if (childrenErrorResponseFields != null) {
|
|
428
|
+
for (let i = 0; i < childrenErrorResponseFields.length; i++) {
|
|
429
|
+
const event = childrenErrorResponseFields[i];
|
|
430
|
+
if (event.kind === 'missing_required_field.log') {
|
|
431
|
+
if (this._errorResponseFields == null) {
|
|
432
|
+
this._errorResponseFields = [event];
|
|
433
|
+
} else {
|
|
434
|
+
this._errorResponseFields.push(event);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
439
438
|
}
|
|
440
439
|
|
|
441
440
|
break;
|
|
@@ -647,7 +646,7 @@ class RelayReader {
|
|
|
647
646
|
return {
|
|
648
647
|
data: snapshot.data,
|
|
649
648
|
isMissingData: snapshot.isMissingData,
|
|
650
|
-
|
|
649
|
+
errorResponseFields: snapshot.errorResponseFields,
|
|
651
650
|
};
|
|
652
651
|
}
|
|
653
652
|
|
|
@@ -660,7 +659,7 @@ class RelayReader {
|
|
|
660
659
|
return {
|
|
661
660
|
data: snapshot.data,
|
|
662
661
|
isMissingData: snapshot.isMissingData,
|
|
663
|
-
|
|
662
|
+
errorResponseFields: snapshot.errorResponseFields,
|
|
664
663
|
};
|
|
665
664
|
};
|
|
666
665
|
|
|
@@ -744,9 +743,6 @@ class RelayReader {
|
|
|
744
743
|
// errors, or be in a suspended state. Here we propagate those cases
|
|
745
744
|
// upwards to mimic the behavior of having traversed into that fragment directly.
|
|
746
745
|
if (cachedSnapshot != null) {
|
|
747
|
-
if (cachedSnapshot.missingRequiredFields != null) {
|
|
748
|
-
this._addMissingRequiredFields(cachedSnapshot.missingRequiredFields);
|
|
749
|
-
}
|
|
750
746
|
if (cachedSnapshot.missingClientEdges != null) {
|
|
751
747
|
for (const missing of cachedSnapshot.missingClientEdges) {
|
|
752
748
|
this._missingClientEdges.push(missing);
|
|
@@ -767,9 +763,13 @@ class RelayReader {
|
|
|
767
763
|
}
|
|
768
764
|
for (const error of cachedSnapshot.errorResponseFields) {
|
|
769
765
|
// TODO: In reality we should propagate _all_ errors, but
|
|
770
|
-
// for now we're only propagating resolver errors
|
|
771
|
-
// compatibility with previous behavior.
|
|
772
|
-
if (
|
|
766
|
+
// for now we're only propagating resolver errors and missing field
|
|
767
|
+
// errors for backwards compatibility with previous behavior.
|
|
768
|
+
if (
|
|
769
|
+
error.kind === 'relay_resolver.error' ||
|
|
770
|
+
error.kind === 'missing_required_field.throw' ||
|
|
771
|
+
error.kind === 'missing_required_field.log'
|
|
772
|
+
) {
|
|
773
773
|
this._errorResponseFields.push(error);
|
|
774
774
|
}
|
|
775
775
|
}
|
|
@@ -789,6 +789,7 @@ class RelayReader {
|
|
|
789
789
|
shouldThrow:
|
|
790
790
|
this._selector.node.metadata?.throwOnFieldError ??
|
|
791
791
|
RelayFeatureFlags.ENABLE_FIELD_ERROR_HANDLING_THROW_BY_DEFAULT,
|
|
792
|
+
handled: false,
|
|
792
793
|
};
|
|
793
794
|
if (this._errorResponseFields == null) {
|
|
794
795
|
this._errorResponseFields = [errorEvent];
|
|
@@ -1392,26 +1393,6 @@ class RelayReader {
|
|
|
1392
1393
|
fragmentPointers[fragmentSpreadOrFragment.name] = inlineData;
|
|
1393
1394
|
}
|
|
1394
1395
|
|
|
1395
|
-
_addMissingRequiredFields(additional: MissingRequiredFields) {
|
|
1396
|
-
if (this._missingRequiredFields == null) {
|
|
1397
|
-
this._missingRequiredFields = additional;
|
|
1398
|
-
return;
|
|
1399
|
-
}
|
|
1400
|
-
|
|
1401
|
-
if (this._missingRequiredFields.action === 'THROW') {
|
|
1402
|
-
return;
|
|
1403
|
-
}
|
|
1404
|
-
if (additional.action === 'THROW') {
|
|
1405
|
-
this._missingRequiredFields = additional;
|
|
1406
|
-
return;
|
|
1407
|
-
}
|
|
1408
|
-
|
|
1409
|
-
this._missingRequiredFields = {
|
|
1410
|
-
action: 'LOG',
|
|
1411
|
-
fields: [...this._missingRequiredFields.fields, ...additional.fields],
|
|
1412
|
-
};
|
|
1413
|
-
}
|
|
1414
|
-
|
|
1415
1396
|
_implementsInterface(record: Record, abstractKey: string): ?boolean {
|
|
1416
1397
|
const typeName = RelayModernRecord.getType(record);
|
|
1417
1398
|
const typeRecord = this._recordSource.get(generateTypeID(typeName));
|
|
@@ -115,7 +115,6 @@ class RelayStoreSubscriptions implements StoreSubscriptions {
|
|
|
115
115
|
missingLiveResolverFields: backup.missingLiveResolverFields,
|
|
116
116
|
seenRecords: backup.seenRecords,
|
|
117
117
|
selector: backup.selector,
|
|
118
|
-
missingRequiredFields: backup.missingRequiredFields,
|
|
119
118
|
errorResponseFields: backup.errorResponseFields,
|
|
120
119
|
};
|
|
121
120
|
} else {
|
|
@@ -186,7 +185,6 @@ class RelayStoreSubscriptions implements StoreSubscriptions {
|
|
|
186
185
|
missingLiveResolverFields: nextSnapshot.missingLiveResolverFields,
|
|
187
186
|
seenRecords: nextSnapshot.seenRecords,
|
|
188
187
|
selector: nextSnapshot.selector,
|
|
189
|
-
missingRequiredFields: nextSnapshot.missingRequiredFields,
|
|
190
188
|
errorResponseFields: nextSnapshot.errorResponseFields,
|
|
191
189
|
}: Snapshot);
|
|
192
190
|
if (__DEV__) {
|
|
@@ -116,22 +116,15 @@ export type NormalizationSelector = {
|
|
|
116
116
|
+variables: Variables,
|
|
117
117
|
};
|
|
118
118
|
|
|
119
|
-
type
|
|
120
|
-
path: string,
|
|
121
|
-
owner: string,
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
export type MissingRequiredFields = $ReadOnly<
|
|
125
|
-
| {action: 'THROW', field: FieldLocation}
|
|
126
|
-
| {action: 'LOG', fields: Array<FieldLocation>},
|
|
127
|
-
>;
|
|
128
|
-
|
|
129
|
-
export type ErrorResponseFields = Array<
|
|
119
|
+
export type ErrorResponseField =
|
|
130
120
|
| RelayFieldPayloadErrorEvent
|
|
131
121
|
| MissingExpectedDataLogEvent
|
|
132
122
|
| MissingExpectedDataThrowEvent
|
|
133
|
-
| RelayResolverErrorEvent
|
|
134
|
-
|
|
123
|
+
| RelayResolverErrorEvent
|
|
124
|
+
| MissingRequiredFieldLogEvent
|
|
125
|
+
| MissingRequiredFieldThrowEvent;
|
|
126
|
+
|
|
127
|
+
export type ErrorResponseFields = Array<ErrorResponseField>;
|
|
135
128
|
|
|
136
129
|
export type ClientEdgeTraversalInfo = {
|
|
137
130
|
+readerClientEdge: ReaderClientEdgeToServerObject,
|
|
@@ -161,7 +154,6 @@ export type Snapshot = {
|
|
|
161
154
|
+missingClientEdges: null | $ReadOnlyArray<MissingClientEdgeRequestInfo>,
|
|
162
155
|
+seenRecords: DataIDSet,
|
|
163
156
|
+selector: SingularReaderSelector,
|
|
164
|
-
+missingRequiredFields: ?MissingRequiredFields,
|
|
165
157
|
+errorResponseFields: ?ErrorResponseFields,
|
|
166
158
|
};
|
|
167
159
|
|
|
@@ -1292,18 +1284,23 @@ export type MissingExpectedDataLogEvent = {
|
|
|
1292
1284
|
*
|
|
1293
1285
|
* Relay will throw immediately after logging this event. If you wish to
|
|
1294
1286
|
* customize the error being thrown, you may throw your own error.
|
|
1287
|
+
*
|
|
1288
|
+
* *NOTE*: Only throw on this event if `handled` is false. Errors that have been
|
|
1289
|
+
* handled by a `@catch` directive or by making a resolver null will have
|
|
1290
|
+
* `handled: true` and should not trigger a throw.
|
|
1295
1291
|
*/
|
|
1296
1292
|
export type MissingExpectedDataThrowEvent = {
|
|
1297
1293
|
+kind: 'missing_expected_data.throw',
|
|
1298
1294
|
+owner: string,
|
|
1299
1295
|
+fieldPath: string,
|
|
1296
|
+
+handled: boolean,
|
|
1300
1297
|
};
|
|
1301
1298
|
|
|
1302
1299
|
/**
|
|
1303
1300
|
* A field was marked as @required(action: LOG) but was null or missing in the
|
|
1304
1301
|
* store.
|
|
1305
1302
|
*/
|
|
1306
|
-
export type
|
|
1303
|
+
export type MissingRequiredFieldLogEvent = {
|
|
1307
1304
|
+kind: 'missing_required_field.log',
|
|
1308
1305
|
+owner: string,
|
|
1309
1306
|
+fieldPath: string,
|
|
@@ -1315,11 +1312,16 @@ export type MissingFieldLogEvent = {
|
|
|
1315
1312
|
*
|
|
1316
1313
|
* Relay will throw immediately after logging this event. If you wish to
|
|
1317
1314
|
* customize the error being thrown, you may throw your own error.
|
|
1315
|
+
*
|
|
1316
|
+
* *NOTE*: Only throw on this event if `handled` is false. Errors that have been
|
|
1317
|
+
* handled by a `@catch` directive or by making a resolver null will have
|
|
1318
|
+
* `handled: true` and should not trigger a throw.
|
|
1318
1319
|
*/
|
|
1319
|
-
export type
|
|
1320
|
+
export type MissingRequiredFieldThrowEvent = {
|
|
1320
1321
|
+kind: 'missing_required_field.throw',
|
|
1321
1322
|
+owner: string,
|
|
1322
1323
|
+fieldPath: string,
|
|
1324
|
+
+handled: boolean,
|
|
1323
1325
|
};
|
|
1324
1326
|
|
|
1325
1327
|
/**
|
|
@@ -1329,6 +1331,10 @@ export type MissingFieldThrowEvent = {
|
|
|
1329
1331
|
*
|
|
1330
1332
|
* If `@throwOnFieldError` was used on the parent query/fragment/mutation, you
|
|
1331
1333
|
* will also receive a TODO
|
|
1334
|
+
*
|
|
1335
|
+
* *NOTE*: Only throw on this event if `handled` is false. Errors that have been
|
|
1336
|
+
* handled by a `@catch` directive or by making a resolver null will have
|
|
1337
|
+
* `handled: true` and should not trigger a throw.
|
|
1332
1338
|
*/
|
|
1333
1339
|
export type RelayResolverErrorEvent = {
|
|
1334
1340
|
+kind: 'relay_resolver.error',
|
|
@@ -1336,6 +1342,7 @@ export type RelayResolverErrorEvent = {
|
|
|
1336
1342
|
+fieldPath: string,
|
|
1337
1343
|
+error: Error,
|
|
1338
1344
|
+shouldThrow: boolean,
|
|
1345
|
+
+handled: boolean,
|
|
1339
1346
|
};
|
|
1340
1347
|
|
|
1341
1348
|
/**
|
|
@@ -1350,6 +1357,10 @@ export type RelayResolverErrorEvent = {
|
|
|
1350
1357
|
*
|
|
1351
1358
|
* https://relay.dev/docs/next/guides/catch-directive/
|
|
1352
1359
|
* https://relay.dev/docs/next/guides/throw-on-field-error-directive/
|
|
1360
|
+
*
|
|
1361
|
+
* *NOTE*: Only throw on this event if `handled` is false. Errors that have been
|
|
1362
|
+
* handled by a `@catch` directive or by making a resolver null will have
|
|
1363
|
+
* `handled: true` and should not trigger a throw.
|
|
1353
1364
|
*/
|
|
1354
1365
|
export type RelayFieldPayloadErrorEvent = {
|
|
1355
1366
|
+kind: 'relay_field_payload.error',
|
|
@@ -1357,6 +1368,7 @@ export type RelayFieldPayloadErrorEvent = {
|
|
|
1357
1368
|
+fieldPath: string,
|
|
1358
1369
|
+error: TRelayFieldError,
|
|
1359
1370
|
+shouldThrow: boolean,
|
|
1371
|
+
+handled: boolean,
|
|
1360
1372
|
};
|
|
1361
1373
|
|
|
1362
1374
|
/**
|
|
@@ -1365,8 +1377,8 @@ export type RelayFieldPayloadErrorEvent = {
|
|
|
1365
1377
|
export type RelayFieldLoggerEvent =
|
|
1366
1378
|
| MissingExpectedDataLogEvent
|
|
1367
1379
|
| MissingExpectedDataThrowEvent
|
|
1368
|
-
|
|
|
1369
|
-
|
|
|
1380
|
+
| MissingRequiredFieldLogEvent
|
|
1381
|
+
| MissingRequiredFieldThrowEvent
|
|
1370
1382
|
| RelayResolverErrorEvent
|
|
1371
1383
|
| RelayFieldPayloadErrorEvent;
|
|
1372
1384
|
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
|
|
12
12
|
'use strict';
|
|
13
13
|
|
|
14
|
-
import type {MissingRequiredFields} from '..';
|
|
15
14
|
import type {
|
|
16
15
|
ReaderRelayLiveResolver,
|
|
17
16
|
ReaderRelayResolver,
|
|
@@ -19,6 +18,7 @@ import type {
|
|
|
19
18
|
import type {DataID, Variables} from '../util/RelayRuntimeTypes';
|
|
20
19
|
import type {
|
|
21
20
|
DataIDSet,
|
|
21
|
+
ErrorResponseFields,
|
|
22
22
|
MutableRecordSource,
|
|
23
23
|
Record,
|
|
24
24
|
SingularReaderSelector,
|
|
@@ -52,7 +52,7 @@ export type EvaluationResult<T> = {
|
|
|
52
52
|
export type ResolverFragmentResult = {
|
|
53
53
|
data: mixed,
|
|
54
54
|
isMissingData: boolean,
|
|
55
|
-
|
|
55
|
+
errorResponseFields: ?ErrorResponseFields,
|
|
56
56
|
};
|
|
57
57
|
|
|
58
58
|
export type GetDataForResolverFragmentFn =
|
|
@@ -111,13 +111,16 @@ function readFragment(
|
|
|
111
111
|
fragmentSelector.kind === 'SingularReaderSelector',
|
|
112
112
|
`Expected a singular reader selector for the fragment of the resolver ${fragmentNode.name}, but it was plural.`,
|
|
113
113
|
);
|
|
114
|
-
const {data, isMissingData,
|
|
114
|
+
const {data, isMissingData, errorResponseFields} =
|
|
115
115
|
context.getDataForResolverFragment(fragmentSelector, fragmentKey);
|
|
116
116
|
|
|
117
117
|
if (
|
|
118
118
|
isMissingData ||
|
|
119
|
-
(
|
|
120
|
-
|
|
119
|
+
(errorResponseFields != null &&
|
|
120
|
+
errorResponseFields.some(
|
|
121
|
+
// TODO: Also consider @throwOnFieldError
|
|
122
|
+
event => event.kind === 'missing_required_field.throw',
|
|
123
|
+
))
|
|
121
124
|
) {
|
|
122
125
|
throw RESOLVER_FRAGMENT_ERRORED_SENTINEL;
|
|
123
126
|
}
|
|
@@ -210,12 +210,7 @@ function snapshotToFragmentState<TFragmentType: FragmentType, TData>(
|
|
|
210
210
|
}
|
|
211
211
|
|
|
212
212
|
try {
|
|
213
|
-
handlePotentialSnapshotErrors(
|
|
214
|
-
environment,
|
|
215
|
-
snapshot.missingRequiredFields,
|
|
216
|
-
snapshot.errorResponseFields,
|
|
217
|
-
snapshot.selector.node.metadata?.throwOnFieldError ?? false,
|
|
218
|
-
);
|
|
213
|
+
handlePotentialSnapshotErrors(environment, snapshot.errorResponseFields);
|
|
219
214
|
} catch (error) {
|
|
220
215
|
return {error, state: 'error'};
|
|
221
216
|
}
|