Skip to content

Commit

Permalink
Catch errors thrown during .read()
Browse files Browse the repository at this point in the history
Reviewed By: josephsavona

Differential Revision: D46500160

fbshipit-source-id: 958d87878faa941bc790500362a4e52f5ca651a0
  • Loading branch information
captbaritone authored and facebook-github-bot committed Jun 13, 2023
1 parent 161b5ec commit fca7a9c
Show file tree
Hide file tree
Showing 6 changed files with 417 additions and 21 deletions.
33 changes: 27 additions & 6 deletions packages/react-relay/__tests__/LiveResolvers-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -980,12 +980,33 @@ describe.each([
requiredFieldLogger.mockReset();

// Render with complete data
expect(() => {
TestRenderer.create(<TestComponent environment={environment} id="2" />);
}).toThrow(
'The resolver should throw earlier. It should have missing data.',
let renderer;
TestRenderer.act(() => {
renderer = TestRenderer.create(
<TestComponent environment={environment} id="2" />,
);
});

if (renderer == null) {
throw new Error('Renderer is expected to be defined.');
}

expect(requiredFieldLogger.mock.calls).toEqual([
[
{
error: new Error(
'The resolver should throw earlier. It should have missing data.',
),
fieldPath: 'node.resolver_that_throws',
kind: 'relay_resolver.error',
owner: 'LiveResolversTest8Query',
},
],
]);

expect(renderer.toJSON()).toEqual(
'Bob: Unknown resolver_that_throws value',
);
expect(requiredFieldLogger.mock.calls).toEqual([]);
});

test('apply optimistic updates to live resolver field', () => {
Expand Down Expand Up @@ -1462,7 +1483,7 @@ describe.each([
expect(() => {
GLOBAL_STORE.dispatch({type: 'INCREMENT'});
}).toThrowError(
'Unexpected LiveState value returned from Relay Resolver internal field `RELAY_RESOLVER_LIVE_STATE_VALUE`. It is likely a bug in Relay, or a corrupt state of the relay store state Field Path `counter_suspends_when_odd`. Record `{"__id":"client:1:counter_suspends_when_odd","__typename":"__RELAY_RESOLVER__","__resolverValue":{"__LIVE_RESOLVER_SUSPENSE_SENTINEL":true},"__resolverLiveStateDirty":true,"__resolverError":null}`',
'Unexpected LiveState value returned from Relay Resolver internal field `RELAY_RESOLVER_LIVE_STATE_VALUE`. It is likely a bug in Relay, or a corrupt state of the relay store state Field Path `counter_suspends_when_odd`. Record `{"__id":"client:1:counter_suspends_when_odd","__typename":"__RELAY_RESOLVER__","__resolverError":null,"__resolverValue":{"__LIVE_RESOLVER_SUSPENSE_SENTINEL":true},"__resolverLiveStateDirty":true}`.',
);
expect(renderer.toJSON()).toEqual('Loading...');
});
Expand Down
103 changes: 103 additions & 0 deletions packages/relay-runtime/store/__tests__/resolvers/LiveResolvers-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,106 @@ test('Updates can be batched', () => {
'liveresolver.batch.end',
]);
});

test('Errors thrown during _initial_ read() are caught as resolver errors', () => {
GLOBAL_STORE.dispatch({type: 'INCREMENT'});
const source = RelayRecordSource.create({
'client:root': {
__id: 'client:root',
__typename: '__Root',
},
});
const operation = createOperationDescriptor(
graphql`
query LiveResolversTestHandlesErrorOnReadQuery {
counter_throws_when_odd
}
`,
{},
);
const store = new LiveResolverStore(source, {
gcReleaseBufferSize: 0,
});
const environment = new RelayModernEnvironment({
network: RelayNetwork.create(jest.fn()),
store,
});

const snapshot = environment.lookup(operation.fragment);
expect(snapshot.relayResolverErrors).toEqual([
{
error: Error('What?'),
field: {
owner: 'LiveResolversTestHandlesErrorOnReadQuery',
path: 'counter_throws_when_odd',
},
},
]);
const data: $FlowExpectedError = snapshot.data;
expect(data.counter_throws_when_odd).toBe(null);
});

test('Errors thrown during read() _after update_ are caught as resolver errors', () => {
const source = RelayRecordSource.create({
'client:root': {
__id: 'client:root',
__typename: '__Root',
},
});
const operation = createOperationDescriptor(
graphql`
query LiveResolversTestHandlesErrorOnUpdateQuery {
counter_throws_when_odd
}
`,
{},
);
const store = new LiveResolverStore(source, {
gcReleaseBufferSize: 0,
});
const environment = new RelayModernEnvironment({
network: RelayNetwork.create(jest.fn()),
store,
});

const snapshot = environment.lookup(operation.fragment);

const handler = jest.fn<[Snapshot], void>();
environment.subscribe(snapshot, handler);

// Confirm there are no initial errors
expect(snapshot.relayResolverErrors).toEqual([]);
const data: $FlowExpectedError = snapshot.data;
expect(data.counter_throws_when_odd).toBe(0);

// This should trigger a read that throws
GLOBAL_STORE.dispatch({type: 'INCREMENT'});

expect(handler).toHaveBeenCalled();

const nextSnapshot = handler.mock.calls[0][0];

expect(nextSnapshot.relayResolverErrors).toEqual([
{
error: Error('What?'),
field: {
owner: 'LiveResolversTestHandlesErrorOnUpdateQuery',
path: 'counter_throws_when_odd',
},
},
]);
const nextData: $FlowExpectedError = nextSnapshot.data;
expect(nextData.counter_throws_when_odd).toBe(null);

handler.mockReset();

// Put the live resolver back into a state where it is valid
GLOBAL_STORE.dispatch({type: 'INCREMENT'});

const finalSnapshot = handler.mock.calls[0][0];

// Confirm there are no initial errors
expect(finalSnapshot.relayResolverErrors).toEqual([]);
const finalData: $FlowExpectedError = finalSnapshot.data;
expect(finalData.counter_throws_when_odd).toBe(2);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* 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.
*
* @flow strict-local
* @format
* @oncall relay
*/

'use strict';

import type {LiveState} from '../../experimental-live-resolvers/LiveResolverStore';

const {GLOBAL_STORE, Selectors} = require('./ExampleExternalStateStore');

/**
* @RelayResolver Query.counter_throws_when_odd: Int
* @live
*
* A @live resolver that throws when counter is odd. Useful for testing
* behavior of live resolvers that throw on read.
*/

function counter_throws_when_odd(): LiveState<number> {
return {
read() {
const number = Selectors.getNumber(GLOBAL_STORE.getState());
if (number % 2 !== 0) {
throw new Error('What?');
} else {
return number;
}
},
subscribe(cb): () => void {
// Here we could try to run the selector and short-circuit if
// the value has not changed, but for now we'll over-notify.
return GLOBAL_STORE.subscribe(cb);
},
};
}

module.exports = {
counter_throws_when_odd,
};

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

Loading

0 comments on commit fca7a9c

Please sign in to comment.