Skip to content

Commit

Permalink
Add RESULT errors to snapshot and catch values boolean ok!
Browse files Browse the repository at this point in the history
Reviewed By: tyao1

Differential Revision: D55992996

fbshipit-source-id: d73cfb27ae8b6d9c9aa42760482aa4e7cd5fc9d1
  • Loading branch information
itamark authored and facebook-github-bot committed Apr 15, 2024
1 parent 696a1b3 commit 2a2a9a9
Show file tree
Hide file tree
Showing 5 changed files with 299 additions and 9 deletions.
44 changes: 41 additions & 3 deletions packages/relay-runtime/store/RelayReader.js
Original file line number Diff line number Diff line change
Expand Up @@ -345,8 +345,22 @@ class RelayReader {
}
}

_handleCatchFieldValue(selection: ReaderCatchField) {
_handleCatchFieldValue(
selection: ReaderCatchField,
record: Record,
data: SelectorData,
value: mixed,
) {
const {to} = selection;
const field = selection.field?.backingField ?? selection.field;
const applicationName = field?.alias ?? field?.name;

// ReaderClientExtension doesn't have `alias` or `name`
// so we don't support this yet
invariant(
applicationName != null,
"Couldn't determine field name for this field. It might be a ReaderClientExtension - which is not yet supported.",
);

if (this._errorResponseFields != null) {
for (let i = 0; i < this._errorResponseFields.length; i++) {
Expand Down Expand Up @@ -382,6 +396,21 @@ class RelayReader {
return;
}

if (this._errorResponseFields != null) {
const errors = this._errorResponseFields.map(error => error.error);

data[applicationName] = {
ok: false,
errors,
};
return;
}

data[applicationName] = {
ok: true,
value,
};

// we do nothing if to is 'NULL'
}

Expand Down Expand Up @@ -434,12 +463,21 @@ class RelayReader {
}
break;
case CATCH_FIELD:
this._readClientSideDirectiveField(selection, record, data);
const catchFieldValue = this._readClientSideDirectiveField(
selection,
record,
data,
);
if (RelayFeatureFlags.ENABLE_FIELD_ERROR_HANDLING_CATCH_DIRECTIVE) {
/* NULL is old behavior. do nothing. */
if (selection.to != 'NULL') {
/* @catch(to: RESULT) is the default */
this._handleCatchFieldValue(selection);
this._handleCatchFieldValue(
selection,
record,
data,
catchFieldValue,
);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ describe('RelayReader @catch', () => {
expect(data).toEqual({me: {lastName: null}});
});

it('if scalar has @catch(to: RESULT) - scalar value should provide the error', () => {
it('if scalar has catch to RESULT - scalar value should provide the error', () => {
const source = RelayRecordSource.create({
'client:root': {
__id: 'client:root',
Expand Down Expand Up @@ -99,7 +99,15 @@ describe('RelayReader @catch', () => {
const {data, errorResponseFields} = read(source, operation.fragment);
expect(data).toEqual({
me: {
lastName: null,
lastName: {
ok: false,
errors: [
{
message: 'There was an error!',
path: ['me', 'lastName'],
},
],
},
},
});

Expand Down Expand Up @@ -307,6 +315,48 @@ describe('RelayReader @catch', () => {
});
expect(errorResponseFields).toBeNull();
});

it('if scalar has catch to RESULT with nested required THROW - do nothing', () => {
const source = RelayRecordSource.create({
'client:root': {
__id: 'client:root',
__typename: '__Root',
me: {__ref: '1'},
},
'1': {
__id: '1',
id: '1',
__typename: 'User',
lastName: null,
},
});

// Mocking the query below with RelayReaderCatchFieldsTest0Query
// const FooQuery = graphql`
// query RelayReaderCatchFieldsTest1Query {
// me @catch {
// lastName @required(action: THROW)
// }
// }
// `;
const operation = createOperationDescriptor(
RelayReaderCatchFieldsTest2Query,
{id: '1'},
);
const {data, errorResponseFields, missingRequiredFields} = read(
source,
operation.fragment,
);
expect(data).toEqual({
me: null,
});

expect(missingRequiredFields).toEqual({
action: 'THROW',
field: {owner: 'RelayReaderCatchFieldsTest2Query', path: 'me.lastName'},
});
expect(errorResponseFields).toBeNull();
});
afterAll(() => {
RelayFeatureFlags.ENABLE_FIELD_ERROR_HANDLING =
wasFieldErrorHandlingEnabled;
Expand All @@ -316,3 +366,4 @@ describe('RelayReader @catch', () => {
});
});
});
// RelayReaderCatchFieldsTest3Query
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @oncall relay
* @flow
* @lightSyntaxTransform
* @nogrep
*/

/* eslint-disable */

'use strict';

/*::
import type { Fragment, ReaderFragment } from 'relay-runtime';
import type { FragmentType } from "relay-runtime";
declare export opaque type RelayReaderCatchFieldsTest3Fragment$fragmentType: FragmentType;
export type RelayReaderCatchFieldsTest3Fragment$data = {|
+profilePicture: ?{|
+uri: ?string,
|},
+$fragmentType: RelayReaderCatchFieldsTest3Fragment$fragmentType,
|};
export type RelayReaderCatchFieldsTest3Fragment$key = {
+$data?: RelayReaderCatchFieldsTest3Fragment$data,
+$fragmentSpreads: RelayReaderCatchFieldsTest3Fragment$fragmentType,
...
};
*/

var node/*: ReaderFragment*/ = {
"argumentDefinitions": [],
"kind": "Fragment",
"metadata": null,
"name": "RelayReaderCatchFieldsTest3Fragment",
"selections": [
{
"alias": "profilePicture",
"args": null,
"concreteType": "Image",
"kind": "LinkedField",
"name": "__profilePicture_test",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "uri",
"storageKey": null
}
],
"storageKey": null
}
],
"type": "User",
"abstractKey": null
};

if (__DEV__) {
(node/*: any*/).hash = "9a91f81e017f3267c21ec7f465854acf";
}

module.exports = ((node/*: any*/)/*: Fragment<
RelayReaderCatchFieldsTest3Fragment$fragmentType,
RelayReaderCatchFieldsTest3Fragment$data,
>*/);
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @oncall relay
* @flow
* @lightSyntaxTransform
* @nogrep
*/

/* eslint-disable */

'use strict';

/*::
import type { ConcreteRequest, Query } from 'relay-runtime';
export type RelayReaderCatchFieldsTest3Query$variables = {||};
export type RelayReaderCatchFieldsTest3Query$data = {|
+me: ?{|
+profilePicture: ?{|
+uri: ?string,
|},
|},
|};
export type RelayReaderCatchFieldsTest3Query = {|
response: RelayReaderCatchFieldsTest3Query$data,
variables: RelayReaderCatchFieldsTest3Query$variables,
|};
*/

var node/*: ConcreteRequest*/ = (function(){
var v0 = [
{
"kind": "Literal",
"name": "size",
"value": 32
}
];
return {
"fragment": {
"argumentDefinitions": [],
"kind": "Fragment",
"metadata": null,
"name": "RelayReaderCatchFieldsTest3Query",
"selections": [
{
"alias": null,
"args": null,
"concreteType": "User",
"kind": "LinkedField",
"name": "me",
"plural": false,
"selections": [
{
"alias": null,
"args": (v0/*: any*/),
"concreteType": "Image",
"kind": "LinkedField",
"name": "profilePicture",
"plural": false,
"selections": [
{
"args": null,
"kind": "FragmentSpread",
"name": "RelayReaderCatchFieldsTest3Fragment"
}
],
"storageKey": "profilePicture(size:32)"
}
],
"storageKey": null
}
],
"type": "Query",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": [],
"kind": "Operation",
"name": "RelayReaderCatchFieldsTest3Query",
"selections": [
{
"alias": null,
"args": null,
"concreteType": "User",
"kind": "LinkedField",
"name": "me",
"plural": false,
"selections": [
{
"alias": null,
"args": (v0/*: any*/),
"concreteType": "Image",
"kind": "LinkedField",
"name": "profilePicture",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "uri",
"storageKey": null
}
],
"storageKey": "profilePicture(size:32)"
}
],
"storageKey": null
}
]
},
"params": {
"cacheID": "25d9b9bf8df79f73e41a4671ce1d207d",
"id": null,
"metadata": {},
"name": "RelayReaderCatchFieldsTest3Query",
"operationKind": "query",
"text": "query RelayReaderCatchFieldsTest3Query {\n me {\n profilePicture(size: 32) {\n uri\n }\n id\n }\n}\n"
}
};
})();

if (__DEV__) {
(node/*: any*/).hash = "7cd54b0080d8dab528631b15888562dc";
}

module.exports = ((node/*: any*/)/*: Query<
RelayReaderCatchFieldsTest3Query$variables,
RelayReaderCatchFieldsTest3Query$data,
>*/);
5 changes: 1 addition & 4 deletions packages/relay-runtime/util/ReaderNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,10 +240,7 @@ export type CatchFieldTo = 'RESULT' | 'NULL';

export type ReaderCatchField = {
+kind: 'CatchField',
+field:
| ReaderField
| ReaderClientEdgeToClientObject
| ReaderClientEdgeToServerObject,
+field: ReaderField | ReaderClientEdge,
+to: CatchFieldTo,
+path: string,
};
Expand Down

0 comments on commit 2a2a9a9

Please sign in to comment.