Skip to content

Commit

Permalink
Use field error events on snapshot (#4798)
Browse files Browse the repository at this point in the history
Summary:
Previously we had two different object shapes which were nearly the same. One for stashing on the snapshot, and another for logging. By unifying the two shapes we simplify things a bit and avoid the transformation.

As part of this simplification, I've also removed the special case handling for snapshots which contain only missing data errors. This means we will end up with duplicate events if the snapshot is missing multiple fields. This is noisier, but perhaps more correct because it captures the fact that multiple fields were missing. Once/when/if we add runtime pathName to RelayReader, these multiple events will be even more helpful.

Pull Request resolved: #4798

Reviewed By: gordyf

Differential Revision: D63032950

Pulled By: captbaritone

fbshipit-source-id: 30fc941134e641eb31f1440f2d9e066f23f07621
  • Loading branch information
captbaritone authored and facebook-github-bot committed Sep 19, 2024
1 parent 790d468 commit 55795a1
Show file tree
Hide file tree
Showing 10 changed files with 149 additions and 177 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -503,9 +503,9 @@ describe.each([true, false])(
expect(snapshot.relayResolverErrors).toEqual([
{
error: Error(ERROR_MESSAGE),
kind: 'relay_resolver.error',
owner: 'RelayResolverNullableModelClientEdgeTest_ErrorModel_Query',
fieldPath: 'edge_to_model_that_throws.__relay_model_instance',
kind: 'relay_resolver.error',
},
]);
const data: $FlowExpectedError = snapshot.data;
Expand All @@ -527,17 +527,17 @@ describe.each([true, false])(
expect(snapshot.relayResolverErrors).toEqual([
{
error: Error(ERROR_MESSAGE),
kind: 'relay_resolver.error',
owner:
'RelayResolverNullableModelClientEdgeTest_PluralErrorModel_Query',
fieldPath: 'edge_to_plural_models_that_throw.__relay_model_instance',
kind: 'relay_resolver.error',
},
{
error: Error(ERROR_MESSAGE),
kind: 'relay_resolver.error',
owner:
'RelayResolverNullableModelClientEdgeTest_PluralErrorModel_Query',
fieldPath: 'edge_to_plural_models_that_throw.__relay_model_instance',
kind: 'relay_resolver.error',
},
]);
const data: $FlowExpectedError = snapshot.data;
Expand All @@ -559,10 +559,10 @@ describe.each([true, false])(
expect(snapshot.relayResolverErrors).toEqual([
{
error: Error(ERROR_MESSAGE),
kind: 'relay_resolver.error',
owner:
'RelayResolverNullableModelClientEdgeTest_PluralSomeErrorModel_Query',
fieldPath: 'edge_to_plural_models_some_throw.__relay_model_instance',
kind: 'relay_resolver.error',
},
]);
const data: $FlowExpectedError = snapshot.data;
Expand Down
58 changes: 33 additions & 25 deletions packages/relay-runtime/store/RelayReader.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,39 +215,30 @@ class RelayReader {
}
for (const error of errors) {
this._errorResponseFields.push({
kind: 'relay_field_payload.error',
owner,
path: (error.path ?? []).join('.'),
type: 'PAYLOAD_ERROR',
fieldPath: (error.path ?? []).join('.'),
error,
shouldThrow: this._selector.node.metadata?.throwOnFieldError ?? false,
});
}
}

_markDataAsMissing(): void {
// We want to mark multiple missing_data - but not duplicate ones.
const alreadyInErrors = Boolean(
this._errorResponseFields?.some(
err => err.owner === this._fragmentName && err.type === 'MISSING_DATA',
),
);

if (!alreadyInErrors) {
if (this._errorResponseFields == null) {
this._errorResponseFields = [];
}
this._errorResponseFields.push({
owner: this._fragmentName,
// we will add the path later
path: '',
type: 'MISSING_DATA',
error: {
message:
'Relay: Missing data for one or more fields in ' +
this._fragmentName,
},
});
if (this._errorResponseFields == null) {
this._errorResponseFields = [];
}

// we will add the path later
const fieldPath = '';
const owner = this._fragmentName;

this._errorResponseFields.push(
this._selector.node.metadata?.throwOnFieldError ?? false
? {kind: 'missing_expected_data.throw', owner, fieldPath}
: {kind: 'missing_expected_data.log', owner, fieldPath},
);

this._isMissingData = true;

if (this._clientEdgeTraversalPath.length) {
Expand Down Expand Up @@ -346,7 +337,24 @@ class RelayReader {
"Couldn't determine field name for this field. It might be a ReaderClientExtension - which is not yet supported.",
);

let errors = this._errorResponseFields?.map(error => error.error);
let errors = this._errorResponseFields?.map(error => {
switch (error.kind) {
case 'relay_field_payload.error':
return error.error;
case 'missing_expected_data.throw':
case 'missing_expected_data.log':
return {
message: `Relay: Missing data for one or more fields in ${error.owner}`,
};
default:
(error.kind: empty);
invariant(
false,
'Unexpected error errorResponseField kind: %s',
error.kind,
);
}
});

if (this._resolverErrors.length > 0) {
if (errors == null) {
Expand Down
17 changes: 6 additions & 11 deletions packages/relay-runtime/store/RelayStoreTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import type {
NormalizationSelectableNode,
} from '../util/NormalizationNode';
import type {
CatchFieldTo,
ReaderClientEdgeToServerObject,
ReaderFragment,
ReaderLinkedField,
Expand Down Expand Up @@ -122,19 +121,16 @@ type FieldLocation = {
owner: string,
};

type ErrorFieldLocation = {
...FieldLocation,
error: TRelayFieldError,
type: FieldErrorType,
to?: CatchFieldTo,
};

export type MissingRequiredFields = $ReadOnly<
| {action: 'THROW', field: FieldLocation}
| {action: 'LOG', fields: Array<FieldLocation>},
>;

export type ErrorResponseFields = Array<ErrorFieldLocation>;
export type ErrorResponseFields = Array<
| RelayFieldPayloadErrorEvent
| MissingExpectedDataLogEvent
| MissingExpectedDataThrowEvent,
>;

export type ClientEdgeTraversalInfo = {
+readerClientEdge: ReaderClientEdgeToServerObject,
Expand Down Expand Up @@ -1361,8 +1357,7 @@ export type RelayFieldPayloadErrorEvent = {
+owner: string,
+fieldPath: string,
+error: TRelayFieldError,
// TODO: Should we add `shouldThrow` as a flag here, or perhaps have two
// different event types?
+shouldThrow: boolean,
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -419,13 +419,14 @@ function cloneEventWithSets(event: LogEvent) {
isMissingData: true,
errorResponseFields: [
{
error: {
message:
'Relay: Missing data for one or more fields in RelayModernStoreSubscriptionsTest1Fragment',
},
fieldPath: '',
kind: 'missing_expected_data.log',
owner: 'RelayModernStoreSubscriptionsTest1Fragment',
},
{
fieldPath: '',
kind: 'missing_expected_data.log',
owner: 'RelayModernStoreSubscriptionsTest1Fragment',
type: 'MISSING_DATA',
path: '',
},
],
seenRecords: new Set(Object.keys(nextSource.toJSON())),
Expand Down Expand Up @@ -468,13 +469,14 @@ function cloneEventWithSets(event: LogEvent) {
missingRequiredFields: null,
errorResponseFields: [
{
error: {
message:
'Relay: Missing data for one or more fields in RelayModernStoreSubscriptionsTest1Fragment',
},
fieldPath: '',
kind: 'missing_expected_data.log',
owner: 'RelayModernStoreSubscriptionsTest1Fragment',
},
{
fieldPath: '',
kind: 'missing_expected_data.log',
owner: 'RelayModernStoreSubscriptionsTest1Fragment',
type: 'MISSING_DATA',
path: '',
},
],
missingLiveResolverFields: [],
Expand Down
25 changes: 19 additions & 6 deletions packages/relay-runtime/store/__tests__/RelayModernStore-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,18 @@ function cloneEventWithSets(event: LogEvent) {
name: 'Joe',
profilePicture: undefined,
},
errorResponseFields: [
{
owner: 'RelayModernStoreTest5Fragment',
kind: 'missing_expected_data.log',
fieldPath: '',
},
{
owner: 'RelayModernStoreTest5Fragment',
kind: 'missing_expected_data.log',
fieldPath: '',
},
],
missingRequiredFields: null,
missingLiveResolverFields: [],
missingClientEdges: null,
Expand Down Expand Up @@ -774,13 +786,14 @@ function cloneEventWithSets(event: LogEvent) {
isMissingData: true,
errorResponseFields: [
{
error: {
message:
'Relay: Missing data for one or more fields in RelayModernStoreTest5Fragment',
},
owner: 'RelayModernStoreTest5Fragment',
type: 'MISSING_DATA',
path: '',
kind: 'missing_expected_data.log',
fieldPath: '',
},
{
owner: 'RelayModernStoreTest5Fragment',
kind: 'missing_expected_data.log',
fieldPath: '',
},
],
seenRecords: new Set(['842472']),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,14 @@ describe('RelayReader @catch', () => {

expect(errorResponseFields).toEqual([
{
path: 'me.lastName',
fieldPath: 'me.lastName',
error: {
message: 'There was an error!',
path: ['me', 'lastName'],
},
owner: 'RelayReaderCatchFieldsTestSiblingErrorQuery',
type: 'PAYLOAD_ERROR',
kind: 'relay_field_payload.error',
shouldThrow: false,
},
]);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,13 @@ describe('RelayReader error fields', () => {
expect(errorResponseFields).toEqual([
{
owner: 'RelayReaderRelayErrorHandlingTest1Query',
path: 'me.lastName',
fieldPath: 'me.lastName',
error: {
message: 'There was an error!',
path: ['me', 'lastName'],
},
type: 'PAYLOAD_ERROR',
kind: 'relay_field_payload.error',
shouldThrow: false,
},
]);
});
Expand Down Expand Up @@ -104,21 +105,18 @@ describe('RelayReader error fields', () => {
expect(errorResponseFields).toEqual([
{
owner: 'RelayReaderRelayErrorHandlingTest4Query',
path: 'me.lastName',
type: 'PAYLOAD_ERROR',
fieldPath: 'me.lastName',
kind: 'relay_field_payload.error',
error: {
message: 'There was an error!',
path: ['me', 'lastName'],
},
shouldThrow: true,
},
{
owner: 'RelayReaderRelayErrorHandlingTest4Query',
path: '',
type: 'MISSING_DATA',
error: {
message:
'Relay: Missing data for one or more fields in RelayReaderRelayErrorHandlingTest4Query',
},
fieldPath: '',
kind: 'missing_expected_data.throw',
},
]);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,10 +208,11 @@ describe.each([true, false])(
const {errorResponseFields} = store.lookup(operation.fragment);
expect(errorResponseFields).toEqual([
{
fieldPath: 'me.lastName',
kind: 'relay_field_payload.error',
error: {message: 'There was an error!', path: ['me', 'lastName']},
owner: 'RelayReaderResolverTestFieldErrorQuery',
type: 'PAYLOAD_ERROR',
path: 'me.lastName',
shouldThrow: false,
},
]);
});
Expand Down
Loading

0 comments on commit 55795a1

Please sign in to comment.