Skip to content

Commit 319993e

Browse files
authored
Merge pull request #7761 from apollographql/7611-preserve-no-cache-fetchpolicy
Preserve no-cache fetchPolicy with notifyOnNetworkStatusChange
2 parents 6120401 + b4cd8fd commit 319993e

File tree

3 files changed

+190
-22
lines changed

3 files changed

+190
-22
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
- Maintain serial ordering of `asyncMap` mapping function calls, and prevent potential unhandled `Promise` rejection errors. <br/>
66
[@benjamn](https://github.com/benjamn) in [#7818](https://github.com/apollographql/apollo-client/pull/7818)
77

8+
- Preserve fetch policy even when `notifyOnNetworkStatusChange` is set <br />
9+
[@jcreighton](https://github.com/jcreighton) in [#7761](https://github.com/apollographql/apollo-client/pull/7761)
810
## Apollo Client 3.3.11
911

1012
### Bug fixes

src/core/QueryManager.ts

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -914,7 +914,6 @@ export class QueryManager<TStore> {
914914
const query = this.transform(options.query).document;
915915
const variables = this.getVariables(query, options.variables) as TVars;
916916
const queryInfo = this.getQuery(queryId);
917-
const oldNetworkStatus = queryInfo.networkStatus;
918917

919918
let {
920919
fetchPolicy = "cache-first" as WatchQueryFetchPolicy,
@@ -924,26 +923,6 @@ export class QueryManager<TStore> {
924923
context = {},
925924
} = options;
926925

927-
const mightUseNetwork =
928-
fetchPolicy === "cache-first" ||
929-
fetchPolicy === "cache-and-network" ||
930-
fetchPolicy === "network-only" ||
931-
fetchPolicy === "no-cache";
932-
933-
if (mightUseNetwork &&
934-
notifyOnNetworkStatusChange &&
935-
typeof oldNetworkStatus === "number" &&
936-
oldNetworkStatus !== networkStatus &&
937-
isNetworkRequestInFlight(networkStatus)) {
938-
// In order to force delivery of an incomplete cache result with
939-
// loading:true, we tweak the fetchPolicy to include the cache, and
940-
// pretend that returnPartialData was enabled.
941-
if (fetchPolicy !== "cache-first") {
942-
fetchPolicy = "cache-and-network";
943-
}
944-
returnPartialData = true;
945-
}
946-
947926
const normalized = Object.assign({}, options, {
948927
query,
949928
variables,
@@ -1038,8 +1017,11 @@ export class QueryManager<TStore> {
10381017
errorPolicy,
10391018
returnPartialData,
10401019
context,
1020+
notifyOnNetworkStatusChange,
10411021
} = options;
10421022

1023+
const oldNetworkStatus = queryInfo.networkStatus;
1024+
10431025
queryInfo.init({
10441026
document: query,
10451027
variables,
@@ -1092,6 +1074,13 @@ export class QueryManager<TStore> {
10921074
errorPolicy,
10931075
});
10941076

1077+
const shouldNotifyOnNetworkStatusChange = () => (
1078+
notifyOnNetworkStatusChange &&
1079+
typeof oldNetworkStatus === "number" &&
1080+
oldNetworkStatus !== networkStatus &&
1081+
isNetworkRequestInFlight(networkStatus)
1082+
);
1083+
10951084
switch (fetchPolicy) {
10961085
default: case "cache-first": {
10971086
const diff = readCache();
@@ -1109,6 +1098,13 @@ export class QueryManager<TStore> {
11091098
];
11101099
}
11111100

1101+
if (shouldNotifyOnNetworkStatusChange()) {
1102+
return [
1103+
resultsFromCache(diff),
1104+
resultsFromLink(true),
1105+
];
1106+
}
1107+
11121108
return [
11131109
resultsFromLink(true),
11141110
];
@@ -1117,7 +1113,7 @@ export class QueryManager<TStore> {
11171113
case "cache-and-network": {
11181114
const diff = readCache();
11191115

1120-
if (diff.complete || returnPartialData) {
1116+
if (diff.complete || returnPartialData || shouldNotifyOnNetworkStatusChange()) {
11211117
return [
11221118
resultsFromCache(diff),
11231119
resultsFromLink(true),
@@ -1135,9 +1131,22 @@ export class QueryManager<TStore> {
11351131
];
11361132

11371133
case "network-only":
1134+
if (shouldNotifyOnNetworkStatusChange()) {
1135+
const diff = readCache();
1136+
1137+
return [
1138+
resultsFromCache(diff),
1139+
resultsFromLink(true),
1140+
];
1141+
}
1142+
11381143
return [resultsFromLink(true)];
11391144

11401145
case "no-cache":
1146+
if (shouldNotifyOnNetworkStatusChange()) {
1147+
return [resultsFromCache(queryInfo.getDiff()), resultsFromLink(false)];
1148+
}
1149+
11411150
return [resultsFromLink(false)];
11421151

11431152
case "standby":

src/core/__tests__/fetchPolicies.ts

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,163 @@ describe('no-cache', () => {
324324
})
325325
.then(resolve, reject);
326326
});
327+
328+
describe('when notifyOnNetworkStatusChange is set', () => {
329+
itAsync('does not save the data to the cache on success', (resolve, reject) => {
330+
let called = 0;
331+
const inspector = new ApolloLink((operation, forward) => {
332+
called++;
333+
return forward(operation).map(result => {
334+
called++;
335+
return result;
336+
});
337+
});
338+
339+
const client = new ApolloClient({
340+
link: inspector.concat(createLink(reject)),
341+
cache: new InMemoryCache({ addTypename: false }),
342+
});
343+
344+
return client.query({ query, fetchPolicy: 'no-cache', notifyOnNetworkStatusChange: true }).then(
345+
() => client.query({ query }).then(actualResult => {
346+
expect(stripSymbols(actualResult.data)).toEqual(result);
347+
// the second query couldn't read anything from the cache
348+
expect(called).toBe(4);
349+
}),
350+
).then(resolve, reject);
351+
});
352+
353+
itAsync('does not save data to the cache on failure', (resolve, reject) => {
354+
let called = 0;
355+
const inspector = new ApolloLink((operation, forward) => {
356+
called++;
357+
return forward(operation).map(result => {
358+
called++;
359+
return result;
360+
});
361+
});
362+
363+
const client = new ApolloClient({
364+
link: inspector.concat(createFailureLink()),
365+
cache: new InMemoryCache({ addTypename: false }),
366+
});
367+
368+
let didFail = false;
369+
return client
370+
.query({ query, fetchPolicy: 'no-cache', notifyOnNetworkStatusChange: true })
371+
.catch(e => {
372+
expect(e.message).toMatch('query failed');
373+
didFail = true;
374+
})
375+
.then(() => client.query({ query }).then(actualResult => {
376+
expect(stripSymbols(actualResult.data)).toEqual(result);
377+
// the first error doesn't call .map on the inspector
378+
expect(called).toBe(3);
379+
expect(didFail).toBe(true);
380+
}))
381+
.then(resolve, reject);
382+
});
383+
384+
itAsync('gives appropriate networkStatus for watched queries', (resolve, reject) => {
385+
const client = new ApolloClient({
386+
link: ApolloLink.empty(),
387+
cache: new InMemoryCache(),
388+
resolvers: {
389+
Query: {
390+
hero(_data, args) {
391+
return {
392+
__typename: 'Hero',
393+
...args,
394+
name: 'Luke Skywalker',
395+
};
396+
},
397+
},
398+
},
399+
});
400+
401+
const observable = client.watchQuery({
402+
query: gql`
403+
query FetchLuke($id: String) {
404+
hero(id: $id) @client {
405+
id
406+
name
407+
}
408+
}
409+
`,
410+
fetchPolicy: 'no-cache',
411+
variables: { id: '1' },
412+
notifyOnNetworkStatusChange: true,
413+
});
414+
415+
function dataWithId(id: number | string) {
416+
return {
417+
hero: {
418+
__typename: 'Hero',
419+
id: String(id),
420+
name: 'Luke Skywalker',
421+
},
422+
};
423+
}
424+
425+
subscribeAndCount(reject, observable, (count, result) => {
426+
if (count === 1) {
427+
expect(result).toEqual({
428+
data: dataWithId(1),
429+
loading: false,
430+
networkStatus: NetworkStatus.ready,
431+
});
432+
expect(client.cache.extract(true)).toEqual({});
433+
return observable.setVariables({ id: '2' });
434+
} else if (count === 2) {
435+
expect(result).toEqual({
436+
data: {},
437+
loading: true,
438+
networkStatus: NetworkStatus.setVariables,
439+
partial: true,
440+
});
441+
} else if (count === 3) {
442+
expect(result).toEqual({
443+
data: dataWithId(2),
444+
loading: false,
445+
networkStatus: NetworkStatus.ready,
446+
});
447+
expect(client.cache.extract(true)).toEqual({});
448+
return observable.refetch();
449+
} else if (count === 4) {
450+
expect(result).toEqual({
451+
data: dataWithId(2),
452+
loading: true,
453+
networkStatus: NetworkStatus.refetch,
454+
});
455+
expect(client.cache.extract(true)).toEqual({});
456+
} else if (count === 5) {
457+
expect(result).toEqual({
458+
data: dataWithId(2),
459+
loading: false,
460+
networkStatus: NetworkStatus.ready,
461+
});
462+
expect(client.cache.extract(true)).toEqual({});
463+
return observable.refetch({ id: '3' });
464+
} else if (count === 6) {
465+
expect(result).toEqual({
466+
data: {},
467+
loading: true,
468+
networkStatus: NetworkStatus.setVariables,
469+
partial: true,
470+
});
471+
expect(client.cache.extract(true)).toEqual({});
472+
} else if (count === 7) {
473+
expect(result).toEqual({
474+
data: dataWithId(3),
475+
loading: false,
476+
networkStatus: NetworkStatus.ready,
477+
});
478+
expect(client.cache.extract(true)).toEqual({});
479+
resolve();
480+
}
481+
});
482+
});
483+
});
327484
});
328485

329486
describe('cache-first', () => {

0 commit comments

Comments
 (0)