Skip to content

Commit 2705e3d

Browse files
Ryan Holdrenfacebook-github-bot
Ryan Holdren
authored andcommitted
Add support for handling field errors on noncompliant lists
Reviewed By: captbaritone Differential Revision: D65632201 fbshipit-source-id: 7bf5d24afeae4f98828d86c693532476512f9d49
1 parent bdbe7e0 commit 2705e3d

5 files changed

+498
-1
lines changed

packages/relay-runtime/store/RelayResponseNormalizer.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const {
4040
ACTOR_IDENTIFIER_FIELD_NAME,
4141
getActorIdentifierFromPayload,
4242
} = require('../multi-actor-environment/ActorUtils');
43+
const RelayFeatureFlags = require('../util/RelayFeatureFlags');
4344
const {generateClientID, isClientID} = require('./ClientID');
4445
const {getLocalVariables} = require('./RelayConcreteVariables');
4546
const {
@@ -471,7 +472,12 @@ class RelayResponseNormalizer {
471472
const responseKey = selection.alias || selection.name;
472473
const storageKey = getStorageKey(selection, this._variables);
473474
const fieldValue = data[responseKey];
474-
if (fieldValue == null) {
475+
if (
476+
fieldValue == null ||
477+
(RelayFeatureFlags.ENABLE_NONCOMPLIANT_ERROR_HANDLING_ON_LISTS &&
478+
Array.isArray(fieldValue) &&
479+
fieldValue.length === 0)
480+
) {
475481
if (fieldValue === undefined) {
476482
// Fields may be missing in the response in two main cases:
477483
// - Inside a client extension: the server will not generally return

packages/relay-runtime/store/__tests__/RelayResponseNormalizer-test.js

+164
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const {
1515
getActorIdentifier,
1616
} = require('../../multi-actor-environment/ActorIdentifier');
1717
const {graphql} = require('../../query/GraphQLTag');
18+
const RelayFeatureFlags = require('../../util/RelayFeatureFlags');
1819
const defaultGetDataID = require('../defaultGetDataID');
1920
const {
2021
createOperationDescriptor,
@@ -4399,5 +4400,168 @@ describe('RelayResponseNormalizer', () => {
43994400
},
44004401
});
44014402
});
4403+
4404+
let wasNoncompliantErrorHandlingOnListsEnabled;
4405+
4406+
beforeEach(() => {
4407+
wasNoncompliantErrorHandlingOnListsEnabled =
4408+
RelayFeatureFlags.ENABLE_NONCOMPLIANT_ERROR_HANDLING_ON_LISTS;
4409+
});
4410+
4411+
afterEach(() => {
4412+
RelayFeatureFlags.ENABLE_NONCOMPLIANT_ERROR_HANDLING_ON_LISTS =
4413+
wasNoncompliantErrorHandlingOnListsEnabled;
4414+
});
4415+
4416+
describe('when noncompliant error handling on lists is disabled', () => {
4417+
beforeEach(() => {
4418+
RelayFeatureFlags.ENABLE_NONCOMPLIANT_ERROR_HANDLING_ON_LISTS = false;
4419+
});
4420+
4421+
it('ignores field errors on an empty list', () => {
4422+
const FooQuery = graphql`
4423+
query RelayResponseNormalizerTest39Query($id: ID!) {
4424+
node(id: $id) {
4425+
id
4426+
__typename
4427+
... on User {
4428+
friends(first: 3) {
4429+
edges {
4430+
cursor
4431+
}
4432+
}
4433+
}
4434+
}
4435+
}
4436+
`;
4437+
const payload = {
4438+
node: {
4439+
id: '1',
4440+
__typename: 'User',
4441+
friends: {
4442+
edges: [],
4443+
},
4444+
},
4445+
};
4446+
const errors = [
4447+
{
4448+
message: 'There was an error!',
4449+
path: ['node', 'friends', 'edges'],
4450+
},
4451+
];
4452+
const recordSource = new RelayRecordSource();
4453+
recordSource.set(ROOT_ID, RelayModernRecord.create(ROOT_ID, ROOT_TYPE));
4454+
normalize(
4455+
recordSource,
4456+
createNormalizationSelector(FooQuery.operation, ROOT_ID, {
4457+
id: '1',
4458+
size: 32,
4459+
}),
4460+
payload,
4461+
defaultOptions,
4462+
errors,
4463+
);
4464+
expect(recordSource.toJSON()).toEqual({
4465+
'1': {
4466+
__id: '1',
4467+
__typename: 'User',
4468+
'friends(first:3)': {
4469+
__ref: 'client:1:friends(first:3)',
4470+
},
4471+
id: '1',
4472+
},
4473+
'client:1:friends(first:3)': {
4474+
__id: 'client:1:friends(first:3)',
4475+
__typename: 'FriendsConnection',
4476+
edges: {
4477+
__refs: [],
4478+
},
4479+
},
4480+
'client:root': {
4481+
__id: 'client:root',
4482+
__typename: '__Root',
4483+
'node(id:"1")': {__ref: '1'},
4484+
},
4485+
});
4486+
});
4487+
});
4488+
4489+
describe('when noncompliant error handling on lists is enabled', () => {
4490+
beforeEach(() => {
4491+
RelayFeatureFlags.ENABLE_NONCOMPLIANT_ERROR_HANDLING_ON_LISTS = true;
4492+
});
4493+
4494+
it('stores field errors on an empty list', () => {
4495+
const FooQuery = graphql`
4496+
query RelayResponseNormalizerTest40Query($id: ID!) {
4497+
node(id: $id) {
4498+
id
4499+
__typename
4500+
... on User {
4501+
friends(first: 3) {
4502+
edges {
4503+
cursor
4504+
}
4505+
}
4506+
}
4507+
}
4508+
}
4509+
`;
4510+
const payload = {
4511+
node: {
4512+
id: '1',
4513+
__typename: 'User',
4514+
friends: {
4515+
edges: [],
4516+
},
4517+
},
4518+
};
4519+
const errors = [
4520+
{
4521+
message: 'There was an error!',
4522+
path: ['node', 'friends', 'edges'],
4523+
},
4524+
];
4525+
const recordSource = new RelayRecordSource();
4526+
recordSource.set(ROOT_ID, RelayModernRecord.create(ROOT_ID, ROOT_TYPE));
4527+
normalize(
4528+
recordSource,
4529+
createNormalizationSelector(FooQuery.operation, ROOT_ID, {
4530+
id: '1',
4531+
size: 32,
4532+
}),
4533+
payload,
4534+
defaultOptions,
4535+
errors,
4536+
);
4537+
expect(recordSource.toJSON()).toEqual({
4538+
'1': {
4539+
__id: '1',
4540+
__typename: 'User',
4541+
'friends(first:3)': {
4542+
__ref: 'client:1:friends(first:3)',
4543+
},
4544+
id: '1',
4545+
},
4546+
'client:1:friends(first:3)': {
4547+
__id: 'client:1:friends(first:3)',
4548+
__typename: 'FriendsConnection',
4549+
__errors: {
4550+
edges: [
4551+
{
4552+
message: 'There was an error!',
4553+
},
4554+
],
4555+
},
4556+
edges: null,
4557+
},
4558+
'client:root': {
4559+
__id: 'client:root',
4560+
__typename: '__Root',
4561+
'node(id:"1")': {__ref: '1'},
4562+
},
4563+
});
4564+
});
4565+
});
44024566
});
44034567
});

packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest39Query.graphql.js

+159
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)