Skip to content

Commit

Permalink
Preserve cache-and-network fetchPolicy when refetching. (#4840)
Browse files Browse the repository at this point in the history
Should help with the problem reported by @supercranky in this comment:
#4636 (comment)

The apparent reasoning for preserving the no-cache and network-only fetch
policies is that refetching requires a "network" fetch policy, but I
believe cache-and-network should also count as a "network" fetch policy,
since it always attempts to fetch from the network in addition to fetching
from the cache. Even if that network request fails, it may be useful
(e.g. for offline usage) to return data from the cache.
  • Loading branch information
benjamn authored May 21, 2019
1 parent b78bb88 commit bcd3aff
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 4 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
- Preserve `networkStatus` for incomplete `cache-and-network` queries. <br/>
[@benjamn](https://github.com/benjamn) in [#4765](https://github.com/apollographql/apollo-client/pull/4765)

- Preserve `cache-and-network` `fetchPolicy` when refetching. <br/>
[@benjamn](https://github.com/benjamn) in [#4840](https://github.com/apollographql/apollo-client/pull/4840)

### Apollo Cache In-Memory

- Support `new InMemoryCache({ freezeResults: true })` to help enforce immutability. <br/>
Expand Down
8 changes: 5 additions & 3 deletions packages/apollo-client/src/core/ObservableQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,9 +296,11 @@ export class ObservableQuery<
));
}

// Override fetchPolicy for this call only
// only network-only and no-cache are safe to use
if (fetchPolicy !== 'no-cache') {
// Unless the provided fetchPolicy always consults the network
// (no-cache, network-only, or cache-and-network), override it with
// network-only to force the refetch for this fetchQuery call.
if (fetchPolicy !== 'no-cache' &&
fetchPolicy !== 'cache-and-network') {
fetchPolicy = 'network-only';
}

Expand Down
128 changes: 127 additions & 1 deletion packages/apollo-client/src/core/__tests__/ObservableQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import ApolloClient from '../../';
import wrap from '../../util/wrap';
import subscribeAndCount from '../../util/subscribeAndCount';
import { stripSymbols } from 'apollo-utilities';
import { ApolloError } from '../../errors/ApolloError';

describe('ObservableQuery', () => {
// Standard data for all these tests
Expand Down Expand Up @@ -1070,7 +1071,7 @@ describe('ObservableQuery', () => {
const observable = queryManager.watchQuery({
query: firstRequest.query,
variables: firstRequest.variables,
fetchPolicy: 'cache-and-network',
fetchPolicy: 'cache-first',
});

const origFetchQuery = queryManager.fetchQuery;
Expand Down Expand Up @@ -1200,6 +1201,131 @@ describe('ObservableQuery', () => {
}
});
});

it('cache-and-network refetch should run @client(always: true) resolvers when network request fails', done => {
const query = gql`
query MixedQuery {
counter @client(always: true)
name
}
`;

let count = 0;

let linkObservable = Observable.of({
data: {
name: 'Ben',
},
});

const intentionalNetworkFailure = new ApolloError({
networkError: new Error('intentional network failure'),
});

const errorObservable: typeof linkObservable = new Observable(
observer => {
observer.error(intentionalNetworkFailure);
},
);

const client = new ApolloClient({
link: new ApolloLink(request => linkObservable),
cache: new InMemoryCache(),
resolvers: {
Query: {
counter() {
return ++count;
},
},
},
});

const observable = client.watchQuery({
query,
fetchPolicy: 'cache-and-network',
returnPartialData: true,
});

let handleCount = 0;
observable.subscribe({
error(error) {
expect(error).toBe(intentionalNetworkFailure);
},

next(result) {
++handleCount;

if (handleCount === 1) {
expect(result).toEqual({
data: {},
loading: true,
networkStatus: NetworkStatus.loading,
stale: false,
});
} else if (handleCount === 2) {
expect(result).toEqual({
data: {
counter: 1,
},
loading: true,
networkStatus: NetworkStatus.loading,
stale: false,
});
} else if (handleCount === 3) {
expect(result).toEqual({
data: {
counter: 2,
name: 'Ben',
},
loading: false,
networkStatus: NetworkStatus.ready,
stale: false,
});

// Make the next network request fail.
linkObservable = errorObservable;

observable.refetch().then(
result => {
expect(result).toEqual({
data: {
counter: 3,
name: 'Ben',
},
});
},
error => {
expect(error).toBe(intentionalNetworkFailure);
},
);
} else if (handleCount === 4) {
expect(result).toEqual({
data: {
counter: 2,
name: 'Ben',
},
loading: true,
networkStatus: NetworkStatus.refetch,
stale: false,
});
} else if (handleCount === 5) {
expect(result).toEqual({
data: {
counter: 3,
name: 'Ben',
},
loading: true,
networkStatus: NetworkStatus.refetch,
stale: false,
});

done();
} else if (handleCount > 5) {
done.fail(new Error('should not get here'));
}
},
});
});
});

describe('currentResult', () => {
Expand Down

0 comments on commit bcd3aff

Please sign in to comment.