Skip to content

Commit

Permalink
1/n - Add field errors to Snapshot
Browse files Browse the repository at this point in the history
Reviewed By: captbaritone

Differential Revision: D49356526

fbshipit-source-id: 14c4a4ef96011906f22e184dff377cf7b078ffff
  • Loading branch information
itamark authored and facebook-github-bot committed Jan 10, 2024
1 parent 1a57f08 commit af4afbd
Show file tree
Hide file tree
Showing 11 changed files with 76 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ describe('ReactRelayFragmentContainer with fragment ownership', () => {
__fragmentOwner: ownerUser1.request,
},
missingRequiredFields: null,
errorResponseFields: null,
missingLiveResolverFields: [],
relayResolverErrors: [],
missingClientEdges: null,
Expand Down Expand Up @@ -315,6 +316,7 @@ describe('ReactRelayFragmentContainer with fragment ownership', () => {
__fragmentOwner: ownerUser2.request,
},
missingRequiredFields: null,
errorResponseFields: null,
missingLiveResolverFields: [],
relayResolverErrors: [],
missingClientEdges: null,
Expand Down Expand Up @@ -380,6 +382,7 @@ describe('ReactRelayFragmentContainer with fragment ownership', () => {
__fragmentOwner: ownerUser1WithCondVar.request,
},
missingRequiredFields: null,
errorResponseFields: null,
missingLiveResolverFields: [],
relayResolverErrors: [],
missingClientEdges: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ describe('ReactRelayFragmentContainer', () => {
name: 'Zuck',
},
missingRequiredFields: null,
errorResponseFields: null,
missingLiveResolverFields: [],
relayResolverErrors: [],
missingClientEdges: null,
Expand Down Expand Up @@ -355,6 +356,7 @@ describe('ReactRelayFragmentContainer', () => {
},
isMissingData: false,
missingRequiredFields: null,
errorResponseFields: null,
missingLiveResolverFields: [],
relayResolverErrors: [],
missingClientEdges: null,
Expand Down Expand Up @@ -408,6 +410,7 @@ describe('ReactRelayFragmentContainer', () => {
},
isMissingData: false,
missingRequiredFields: null,
errorResponseFields: null,
missingLiveResolverFields: [],
relayResolverErrors: [],
missingClientEdges: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ describe('ReactRelayPaginationContainer', () => {
data: expect.any(Object),
isMissingData: false,
missingRequiredFields: null,
errorResponseFields: null,
missingLiveResolverFields: [],
relayResolverErrors: [],
missingClientEdges: null,
Expand Down Expand Up @@ -441,6 +442,7 @@ describe('ReactRelayPaginationContainer', () => {
data: expect.any(Object),
isMissingData: false,
missingRequiredFields: null,
errorResponseFields: null,
missingLiveResolverFields: [],
relayResolverErrors: [],
missingClientEdges: null,
Expand Down Expand Up @@ -508,6 +510,7 @@ describe('ReactRelayPaginationContainer', () => {
data: expect.any(Object),
isMissingData: false,
missingRequiredFields: null,
errorResponseFields: null,
missingLiveResolverFields: [],
relayResolverErrors: [],
missingClientEdges: null,
Expand Down Expand Up @@ -606,6 +609,7 @@ describe('ReactRelayPaginationContainer', () => {
data: expect.any(Object),
isMissingData: false,
missingRequiredFields: null,
errorResponseFields: null,
missingLiveResolverFields: [],
relayResolverErrors: [],
missingClientEdges: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ describe('ReactRelayRefetchContainer', () => {
},
isMissingData: false,
missingRequiredFields: null,
errorResponseFields: null,
missingLiveResolverFields: [],
relayResolverErrors: [],
missingClientEdges: null,
Expand Down Expand Up @@ -364,6 +365,7 @@ describe('ReactRelayRefetchContainer', () => {
},
isMissingData: false,
missingRequiredFields: null,
errorResponseFields: null,
missingLiveResolverFields: [],
relayResolverErrors: [],
missingClientEdges: null,
Expand Down Expand Up @@ -418,6 +420,7 @@ describe('ReactRelayRefetchContainer', () => {
},
isMissingData: false,
missingRequiredFields: null,
errorResponseFields: null,
missingLiveResolverFields: [],
relayResolverErrors: [],
missingClientEdges: null,
Expand Down Expand Up @@ -496,6 +499,7 @@ describe('ReactRelayRefetchContainer', () => {
// Name is excluded since value of cond is now false
},
missingRequiredFields: null,
errorResponseFields: null,
missingLiveResolverFields: [],
relayResolverErrors: [],
missingClientEdges: null,
Expand Down
1 change: 1 addition & 0 deletions packages/react-relay/relay-hooks/FragmentResource.js
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,7 @@ class FragmentResourceImpl {
selector: currentSnapshot.selector,
missingRequiredFields: currentSnapshot.missingRequiredFields,
relayResolverErrors: currentSnapshot.relayResolverErrors,
errorResponseFields: currentSnapshot.errorResponseFields,
};
if (updatedData !== renderData) {
const result = getFragmentResult(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ function handleMissedUpdates(
selector: currentSnapshot.selector,
missingRequiredFields: currentSnapshot.missingRequiredFields,
relayResolverErrors: currentSnapshot.relayResolverErrors,
errorResponseFields: currentSnapshot.errorResponseFields,
};
return [
updatedData !== state.snapshot.data,
Expand All @@ -187,6 +188,7 @@ function handleMissedUpdates(
selector: currentSnapshot.selector,
missingRequiredFields: currentSnapshot.missingRequiredFields,
relayResolverErrors: currentSnapshot.relayResolverErrors,
errorResponseFields: currentSnapshot.errorResponseFields,
};
if (updatedData !== snapshot.data) {
didMissUpdates = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import type {ConcreteRequest} from '../util/RelayConcreteNode';
import type {Disposable, Variables} from '../util/RelayRuntimeTypes';
import type {
ErrorResponseFields,
FragmentMap,
FragmentSpecResolver,
FragmentSpecResults,
Expand Down Expand Up @@ -228,6 +229,7 @@ class SelectorResolver {
_environment: IEnvironment;
_isMissingData: boolean;
_missingRequiredFields: ?MissingRequiredFields;
_errorResponseFields: ?ErrorResponseFields;
_relayResolverErrors: RelayResolverErrors;
_rootIsQueryRenderer: boolean;
_selector: SingularReaderSelector;
Expand All @@ -245,6 +247,7 @@ class SelectorResolver {
this._data = snapshot.data;
this._isMissingData = snapshot.isMissingData;
this._missingRequiredFields = snapshot.missingRequiredFields;
this._errorResponseFields = snapshot.errorResponseFields;
this._relayResolverErrors = snapshot.relayResolverErrors;
this._environment = environment;
this._rootIsQueryRenderer = rootIsQueryRenderer;
Expand Down Expand Up @@ -346,6 +349,7 @@ class SelectorResolver {
this._data = recycleNodesInto(this._data, snapshot.data);
this._isMissingData = snapshot.isMissingData;
this._missingRequiredFields = snapshot.missingRequiredFields;
this._errorResponseFields = snapshot.errorResponseFields;
this._relayResolverErrors = snapshot.relayResolverErrors;
this._selector = selector;
this._subscription = this._environment.subscribe(snapshot, this._onChange);
Expand Down Expand Up @@ -383,6 +387,7 @@ class SelectorResolver {
this._data = snapshot.data;
this._isMissingData = snapshot.isMissingData;
this._missingRequiredFields = snapshot.missingRequiredFields;
this._errorResponseFields = snapshot.errorResponseFields;
this._relayResolverErrors = snapshot.relayResolverErrors;
this._callback();
};
Expand Down
41 changes: 39 additions & 2 deletions packages/relay-runtime/store/RelayReader.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import type {DataID, Variables} from '../util/RelayRuntimeTypes';
import type {
ClientEdgeTraversalInfo,
DataIDSet,
ErrorResponseFields,
MissingClientEdgeRequestInfo,
MissingLiveResolverField,
MissingRequiredFields,
Expand Down Expand Up @@ -116,6 +117,7 @@ class RelayReader {
_missingLiveResolverFields: Array<MissingLiveResolverField>;
_isWithinUnmatchedTypeRefinement: boolean;
_missingRequiredFields: ?MissingRequiredFields;
_errorResponseFields: ?ErrorResponseFields;
_owner: RequestDescriptor;
_recordSource: RecordSource;
_seenRecords: DataIDSet;
Expand All @@ -141,6 +143,7 @@ class RelayReader {
this._isMissingData = false;
this._isWithinUnmatchedTypeRefinement = false;
this._missingRequiredFields = null;
this._errorResponseFields = null;
this._owner = selector.owner;
this._recordSource = recordSource;
this._seenRecords = new Set();
Expand Down Expand Up @@ -226,9 +229,33 @@ class RelayReader {
selector: this._selector,
missingRequiredFields: this._missingRequiredFields,
relayResolverErrors: this._resolverErrors,
errorResponseFields: this._errorResponseFields,
};
}

_maybeAddErrorResponseFields(record: Record, storageKey: string): void {
if (!RelayFeatureFlags.ENABLE_FIELD_ERROR_HANDLING) {
return;
}
const errors = RelayModernRecord.getErrors(record, storageKey);

if (errors == null) {
return;
}
const owner = this._fragmentName;

if (this._errorResponseFields == null) {
this._errorResponseFields = [];
}
for (const error of errors) {
this._errorResponseFields.push({
owner,
path: (error.path ?? []).join('.'),
error,
});
}
}

_markDataAsMissing(): void {
this._isMissingData = true;
if (
Expand Down Expand Up @@ -256,6 +283,7 @@ class RelayReader {
): ?SelectorData {
const record = this._recordSource.get(dataID);
this._seenRecords.add(dataID);

if (record == null) {
if (record === undefined) {
this._markDataAsMissing();
Expand Down Expand Up @@ -811,7 +839,9 @@ class RelayReader {
const applicationName = field.alias ?? field.name;
const storageKey = getStorageKey(field, this._variables);
const value = RelayModernRecord.getValue(record, storageKey);
if (value === undefined) {
if (value === null) {
this._maybeAddErrorResponseFields(record, storageKey);
} else if (value === undefined) {
this._markDataAsMissing();
}
data[applicationName] = value;
Expand All @@ -828,7 +858,9 @@ class RelayReader {
const linkedID = RelayModernRecord.getLinkedRecordID(record, storageKey);
if (linkedID == null) {
data[applicationName] = linkedID;
if (linkedID === undefined) {
if (linkedID === null) {
this._maybeAddErrorResponseFields(record, storageKey);
} else if (linkedID === undefined) {
this._markDataAsMissing();
}
return linkedID;
Expand Down Expand Up @@ -865,6 +897,8 @@ class RelayReader {
data[applicationName] = externalRef;
if (externalRef === undefined) {
this._markDataAsMissing();
} else if (externalRef === null) {
this._maybeAddErrorResponseFields(record, storageKey);
}
return data[applicationName];
}
Expand Down Expand Up @@ -892,6 +926,9 @@ class RelayReader {
): ?mixed {
const storageKey = getStorageKey(field, this._variables);
const linkedIDs = RelayModernRecord.getLinkedRecordIDs(record, storageKey);
if (linkedIDs === null) {
this._maybeAddErrorResponseFields(record, storageKey);
}
return this._readLinkedIds(field, linkedIDs, record, data);
}

Expand Down
2 changes: 2 additions & 0 deletions packages/relay-runtime/store/RelayStoreSubscriptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ class RelayStoreSubscriptions implements StoreSubscriptions {
selector: backup.selector,
missingRequiredFields: backup.missingRequiredFields,
relayResolverErrors: backup.relayResolverErrors,
errorResponseFields: backup.errorResponseFields,
};
} else {
// This subscription was created during the optimisitic state. We should
Expand Down Expand Up @@ -175,6 +176,7 @@ class RelayStoreSubscriptions implements StoreSubscriptions {
selector: nextSnapshot.selector,
missingRequiredFields: nextSnapshot.missingRequiredFields,
relayResolverErrors: nextSnapshot.relayResolverErrors,
errorResponseFields: nextSnapshot.errorResponseFields,
}: Snapshot);
if (__DEV__) {
deepFreeze(nextSnapshot);
Expand Down
11 changes: 10 additions & 1 deletion packages/relay-runtime/store/RelayStoreTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
*/

'use strict';

import type {
ActorIdentifier,
IActorEnvironment,
Expand Down Expand Up @@ -50,6 +49,7 @@ import type {
UpdatableQuery,
Variables,
} from '../util/RelayRuntimeTypes';
import type {RelayFieldError} from './RelayErrorTrie';
import type {
Record as RelayModernRecord,
RecordJSON,
Expand Down Expand Up @@ -116,11 +116,18 @@ type FieldLocation = {
owner: string,
};

type ErrorFieldLocation = {
...FieldLocation,
error: RelayFieldError,
};

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

export type ErrorResponseFields = Array<ErrorFieldLocation>;

export type ClientEdgeTraversalInfo = {
+readerClientEdge: ReaderClientEdgeToServerObject,
+clientEdgeDestinationID: DataID,
Expand Down Expand Up @@ -158,6 +165,7 @@ export type Snapshot = {
+selector: SingularReaderSelector,
+missingRequiredFields: ?MissingRequiredFields,
+relayResolverErrors: RelayResolverErrors,
+errorResponseFields: ?ErrorResponseFields,
};

/**
Expand Down Expand Up @@ -1200,6 +1208,7 @@ export type RequiredFieldLoggerEvent =
+fieldPath: string,
+error: Error,
};

/**
* A handler for events related to @required fields. Currently reports missing
* fields with either `action: LOG` or `action: THROW`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ function cloneEventWithSets(event: LogEvent) {
},
seenRecords: new Set(Object.keys(data)),
missingRequiredFields: null,
errorResponseFields: null,
missingLiveResolverFields: [],
relayResolverErrors: [],
missingClientEdges: null,
Expand Down Expand Up @@ -338,6 +339,7 @@ function cloneEventWithSets(event: LogEvent) {
},
seenRecords: new Set(Object.keys(data)),
missingRequiredFields: null,
errorResponseFields: null,
missingLiveResolverFields: [],
relayResolverErrors: [],
missingClientEdges: null,
Expand Down Expand Up @@ -396,6 +398,7 @@ function cloneEventWithSets(event: LogEvent) {
},
seenRecords: new Set(['client:2', '4']),
missingRequiredFields: null,
errorResponseFields: null,
missingLiveResolverFields: [],
relayResolverErrors: [],
missingClientEdges: null,
Expand Down

0 comments on commit af4afbd

Please sign in to comment.