Skip to content

Commit f3a9afa

Browse files
tyao1facebook-github-bot
authored andcommitted
Support normalized responses in OperationExecutor
Reviewed By: lynnshaoyu Differential Revision: D75038384 fbshipit-source-id: 819aca26e2583660db2642223ae6dfbc04bec174
1 parent 6f1394f commit f3a9afa

File tree

3 files changed

+136
-3
lines changed

3 files changed

+136
-3
lines changed

packages/relay-runtime/store/OperationExecutor.js

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -522,9 +522,10 @@ class Executor<TMutation: MutationParameters> {
522522
return;
523523
}
524524

525-
const [nonIncrementalResponses, incrementalResponses] =
525+
const [nonIncrementalResponses, incrementalResponses, normalizedResponses] =
526526
partitionGraphQLResponses(responsesWithData);
527527
const hasNonIncrementalResponses = nonIncrementalResponses.length > 0;
528+
const hasNormalizedResponses = normalizedResponses.length > 0;
528529

529530
// In theory this doesn't preserve the ordering of the batch.
530531
// The idea is that a batch is always:
@@ -559,6 +560,28 @@ class Executor<TMutation: MutationParameters> {
559560
this._processPayloadFollowups(payloadFollowups);
560561
}
561562

563+
if (hasNormalizedResponses) {
564+
for (let i = 0; i < normalizedResponses.length; i++) {
565+
const response = normalizedResponses[i];
566+
const source = new RelayRecordSource(
567+
response.data as $FlowExpectedError,
568+
);
569+
const payload: RelayResponsePayload = {
570+
errors: [],
571+
fieldPayloads: [],
572+
followupPayloads: [],
573+
incrementalPlaceholders: [],
574+
isFinal: response.extensions?.is_final === true,
575+
source,
576+
};
577+
this._getPublishQueueAndSaveActor().commitPayload(
578+
this._operation,
579+
payload,
580+
this._updater,
581+
);
582+
}
583+
}
584+
562585
if (incrementalResponses.length > 0) {
563586
const payloadFollowups =
564587
this._processIncrementalResponses(incrementalResponses);
@@ -584,7 +607,9 @@ class Executor<TMutation: MutationParameters> {
584607
// the publish queue here, which will later be passed to the store (via
585608
// notify) to indicate that this operation caused the store to update
586609
const updatedOwners = this._runPublishQueue(
587-
hasNonIncrementalResponses ? this._operation : undefined,
610+
hasNonIncrementalResponses || hasNormalizedResponses
611+
? this._operation
612+
: undefined,
588613
);
589614

590615
if (hasNonIncrementalResponses) {
@@ -1613,9 +1638,11 @@ function partitionGraphQLResponses(
16131638
): [
16141639
$ReadOnlyArray<GraphQLResponseWithData>,
16151640
$ReadOnlyArray<IncrementalGraphQLResponse>,
1641+
$ReadOnlyArray<GraphQLResponseWithData>,
16161642
] {
16171643
const nonIncrementalResponses: Array<GraphQLResponseWithData> = [];
16181644
const incrementalResponses: Array<IncrementalGraphQLResponse> = [];
1645+
const normalizedResponses: Array<GraphQLResponseWithData> = [];
16191646
responses.forEach(response => {
16201647
if (response.path != null || response.label != null) {
16211648
const {label, path} = response;
@@ -1633,11 +1660,13 @@ function partitionGraphQLResponses(
16331660
path,
16341661
response,
16351662
});
1663+
} else if (response.extensions?.is_normalized === true) {
1664+
normalizedResponses.push(response);
16361665
} else {
16371666
nonIncrementalResponses.push(response);
16381667
}
16391668
});
1640-
return [nonIncrementalResponses, incrementalResponses];
1669+
return [nonIncrementalResponses, incrementalResponses, normalizedResponses];
16411670
}
16421671

16431672
function stableStringify(value: mixed): string {

packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithDefer-test.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,63 @@ describe.each(['RelayModernEnvironment', 'MultiActorEnvironment'])(
251251
});
252252
});
253253

254+
it('processes deferred payloads mixed with normalized response payloads', () => {
255+
const initialSnapshot = environment.lookup(selector);
256+
const callback = jest.fn<[Snapshot], void>();
257+
environment.subscribe(initialSnapshot, callback);
258+
259+
environment.execute({operation}).subscribe(callbacks);
260+
dataSource.next({
261+
data: {
262+
node: {
263+
id: '1',
264+
__typename: 'User',
265+
},
266+
},
267+
});
268+
jest.runAllTimers();
269+
next.mockClear();
270+
callback.mockClear();
271+
272+
const extensionsPayload = {
273+
data: {
274+
'1': {
275+
__id: '1',
276+
__typename: 'User',
277+
extra_data: 'Zuck',
278+
},
279+
},
280+
extensions: {is_normalized: true},
281+
};
282+
dataSource.next(extensionsPayload);
283+
expect(callback).toBeCalledTimes(0);
284+
expect(next).toBeCalledTimes(1);
285+
expect(next.mock.calls[0][0]).toBe(extensionsPayload);
286+
next.mockClear();
287+
288+
dataSource.next({
289+
data: {
290+
id: '1',
291+
__typename: 'User',
292+
name: 'joe',
293+
},
294+
label:
295+
'RelayModernEnvironmentExecuteWithDeferTestUserQuery$defer$UserFragment',
296+
path: ['node'],
297+
});
298+
299+
expect(complete).toBeCalledTimes(0);
300+
expect(error).toBeCalledTimes(0);
301+
expect(next).toBeCalledTimes(1);
302+
expect(callback).toBeCalledTimes(1);
303+
const snapshot = callback.mock.calls[0][0];
304+
expect(snapshot.isMissingData).toBe(false);
305+
expect(snapshot.data).toEqual({
306+
id: '1',
307+
name: 'JOE',
308+
});
309+
});
310+
254311
describe('when using a scheduler', () => {
255312
let taskID;
256313
let tasks;

packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithSource-test.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,53 @@ describe.each(['RelayModernEnvironment', 'MultiActorEnvironment'])(
203203
},
204204
});
205205
});
206+
207+
it('calls next() and publishes normalized response to the store', () => {
208+
const selector = createReaderSelector(
209+
query.fragment,
210+
ROOT_ID,
211+
variables,
212+
operation.request,
213+
);
214+
const snapshot = environment.lookup(selector);
215+
const callback = jest.fn<[Snapshot], void>();
216+
environment.subscribe(snapshot, callback);
217+
218+
environment
219+
.executeWithSource({operation, source: fetchSource})
220+
.subscribe(callbacks);
221+
const payload = {
222+
data: {
223+
'842472': {
224+
__id: '842472',
225+
__typename: 'User',
226+
name: 'Joe',
227+
id: '842472',
228+
},
229+
'client:root': {
230+
__id: 'client:root',
231+
__typename: '__Root',
232+
me: {__ref: '842472'},
233+
},
234+
},
235+
extensions: {
236+
is_normalized: true,
237+
},
238+
};
239+
subject.next(payload);
240+
jest.runAllTimers();
241+
242+
expect(next.mock.calls.length).toBe(1);
243+
expect(next).toBeCalledWith(payload);
244+
expect(complete).not.toBeCalled();
245+
expect(error).not.toBeCalled();
246+
expect(callback.mock.calls.length).toBe(1);
247+
expect(callback.mock.calls[0][0].data).toEqual({
248+
me: {
249+
name: 'Joe',
250+
},
251+
});
252+
});
206253
});
207254
},
208255
);

0 commit comments

Comments
 (0)