Skip to content

Commit

Permalink
Add support for handling field errors on noncompliant lists
Browse files Browse the repository at this point in the history
Reviewed By: captbaritone

Differential Revision: D65632201

fbshipit-source-id: 7bf5d24afeae4f98828d86c693532476512f9d49
  • Loading branch information
Ryan Holdren authored and facebook-github-bot committed Nov 8, 2024
1 parent bdbe7e0 commit 2705e3d
Show file tree
Hide file tree
Showing 5 changed files with 498 additions and 1 deletion.
8 changes: 7 additions & 1 deletion packages/relay-runtime/store/RelayResponseNormalizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const {
ACTOR_IDENTIFIER_FIELD_NAME,
getActorIdentifierFromPayload,
} = require('../multi-actor-environment/ActorUtils');
const RelayFeatureFlags = require('../util/RelayFeatureFlags');
const {generateClientID, isClientID} = require('./ClientID');
const {getLocalVariables} = require('./RelayConcreteVariables');
const {
Expand Down Expand Up @@ -471,7 +472,12 @@ class RelayResponseNormalizer {
const responseKey = selection.alias || selection.name;
const storageKey = getStorageKey(selection, this._variables);
const fieldValue = data[responseKey];
if (fieldValue == null) {
if (
fieldValue == null ||
(RelayFeatureFlags.ENABLE_NONCOMPLIANT_ERROR_HANDLING_ON_LISTS &&
Array.isArray(fieldValue) &&
fieldValue.length === 0)
) {
if (fieldValue === undefined) {
// Fields may be missing in the response in two main cases:
// - Inside a client extension: the server will not generally return
Expand Down
164 changes: 164 additions & 0 deletions packages/relay-runtime/store/__tests__/RelayResponseNormalizer-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const {
getActorIdentifier,
} = require('../../multi-actor-environment/ActorIdentifier');
const {graphql} = require('../../query/GraphQLTag');
const RelayFeatureFlags = require('../../util/RelayFeatureFlags');
const defaultGetDataID = require('../defaultGetDataID');
const {
createOperationDescriptor,
Expand Down Expand Up @@ -4399,5 +4400,168 @@ describe('RelayResponseNormalizer', () => {
},
});
});

let wasNoncompliantErrorHandlingOnListsEnabled;

beforeEach(() => {
wasNoncompliantErrorHandlingOnListsEnabled =
RelayFeatureFlags.ENABLE_NONCOMPLIANT_ERROR_HANDLING_ON_LISTS;
});

afterEach(() => {
RelayFeatureFlags.ENABLE_NONCOMPLIANT_ERROR_HANDLING_ON_LISTS =
wasNoncompliantErrorHandlingOnListsEnabled;
});

describe('when noncompliant error handling on lists is disabled', () => {
beforeEach(() => {
RelayFeatureFlags.ENABLE_NONCOMPLIANT_ERROR_HANDLING_ON_LISTS = false;
});

it('ignores field errors on an empty list', () => {
const FooQuery = graphql`
query RelayResponseNormalizerTest39Query($id: ID!) {
node(id: $id) {
id
__typename
... on User {
friends(first: 3) {
edges {
cursor
}
}
}
}
}
`;
const payload = {
node: {
id: '1',
__typename: 'User',
friends: {
edges: [],
},
},
};
const errors = [
{
message: 'There was an error!',
path: ['node', 'friends', 'edges'],
},
];
const recordSource = new RelayRecordSource();
recordSource.set(ROOT_ID, RelayModernRecord.create(ROOT_ID, ROOT_TYPE));
normalize(
recordSource,
createNormalizationSelector(FooQuery.operation, ROOT_ID, {
id: '1',
size: 32,
}),
payload,
defaultOptions,
errors,
);
expect(recordSource.toJSON()).toEqual({
'1': {
__id: '1',
__typename: 'User',
'friends(first:3)': {
__ref: 'client:1:friends(first:3)',
},
id: '1',
},
'client:1:friends(first:3)': {
__id: 'client:1:friends(first:3)',
__typename: 'FriendsConnection',
edges: {
__refs: [],
},
},
'client:root': {
__id: 'client:root',
__typename: '__Root',
'node(id:"1")': {__ref: '1'},
},
});
});
});

describe('when noncompliant error handling on lists is enabled', () => {
beforeEach(() => {
RelayFeatureFlags.ENABLE_NONCOMPLIANT_ERROR_HANDLING_ON_LISTS = true;
});

it('stores field errors on an empty list', () => {
const FooQuery = graphql`
query RelayResponseNormalizerTest40Query($id: ID!) {
node(id: $id) {
id
__typename
... on User {
friends(first: 3) {
edges {
cursor
}
}
}
}
}
`;
const payload = {
node: {
id: '1',
__typename: 'User',
friends: {
edges: [],
},
},
};
const errors = [
{
message: 'There was an error!',
path: ['node', 'friends', 'edges'],
},
];
const recordSource = new RelayRecordSource();
recordSource.set(ROOT_ID, RelayModernRecord.create(ROOT_ID, ROOT_TYPE));
normalize(
recordSource,
createNormalizationSelector(FooQuery.operation, ROOT_ID, {
id: '1',
size: 32,
}),
payload,
defaultOptions,
errors,
);
expect(recordSource.toJSON()).toEqual({
'1': {
__id: '1',
__typename: 'User',
'friends(first:3)': {
__ref: 'client:1:friends(first:3)',
},
id: '1',
},
'client:1:friends(first:3)': {
__id: 'client:1:friends(first:3)',
__typename: 'FriendsConnection',
__errors: {
edges: [
{
message: 'There was an error!',
},
],
},
edges: null,
},
'client:root': {
__id: 'client:root',
__typename: '__Root',
'node(id:"1")': {__ref: '1'},
},
});
});
});
});
});

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 2705e3d

Please sign in to comment.