Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make ObservableQuery.lastResult keep track of errors #4992

Merged
merged 3 commits into from
Aug 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@

### Apollo Client (vNext)

- Modify `ObservableQuery` to allow queries with `notifyOnNetworkStatusChange`
to be notified when loading after an error occurs. <br />
[@jasonpaulos](https://github.com/jasonpaulos) in [#4992](https://github.com/apollographql/apollo-client/pull/4992)
- Documentation updates. </br>
[@raibima](https://github.com/raibima) in [#5132](https://github.com/apollographql/apollo-client/pull/5132) <br/>
[@hwillson](https://github.com/hwillson) in [#5141](https://github.com/apollographql/apollo-client/pull/5141)


## Apollo Client (2.6.3)

Expand Down
116 changes: 116 additions & 0 deletions packages/apollo-client/src/__tests__/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2603,6 +2603,122 @@ describe('client', () => {
});
});

it.only('should be able to refetch after there was a network error', done => {
const query: DocumentNode = gql`
query somethingelse {
allPeople(first: 1) {
people {
name
}
}
}
`;

const data = { allPeople: { people: [{ name: 'Luke Skywalker' }] } };
const dataTwo = { allPeople: { people: [{ name: 'Princess Leia' }] } };
const link = mockSingleLink(
{ request: { query }, result: { data } },
{ request: { query }, error: new Error('This is an error!') },
{ request: { query }, result: { data: dataTwo } },
);
const client = new ApolloClient({
link,
cache: new InMemoryCache({ addTypename: false }),
});

let count = 0;
const noop = () => null;

const observable = client.watchQuery({
query,
notifyOnNetworkStatusChange: true,
});

let subscription: any = null;

const observerOptions = {
next(result: any) {
try {
switch (count++) {
case 0:
if (!result.data!.allPeople) {
done.fail('Should have data by this point');
break;
}
// First result is loaded, run a refetch to get the second result
// which is an error.
expect(result.loading).toBeFalsy();
expect(result.networkStatus).toBe(7);
expect(stripSymbols(result.data!.allPeople)).toEqual(
data.allPeople,
);
setTimeout(() => {
observable.refetch().then(() => {
done.fail('Expected error value on first refetch.');
}, noop);
}, 0);
break;
case 1:
// Waiting for the second result to load
expect(result.loading).toBeTruthy();
expect(result.networkStatus).toBe(4);
break;
// case 2 is handled by the error callback
case 3:
expect(result.loading).toBeTruthy();
expect(result.networkStatus).toBe(4);
expect(result.errors).toBeFalsy();
break;
case 4:
// Third result's data is loaded
expect(result.loading).toBeFalsy();
expect(result.networkStatus).toBe(7);
expect(result.errors).toBeFalsy();
if (!result.data) {
done.fail('Should have data by this point');
break;
}
expect(stripSymbols(result.data.allPeople)).toEqual(
dataTwo.allPeople,
);
done();
break;
default:
throw new Error('Unexpected fall through');
}
} catch (e) {
done.fail(e);
}
},
error(error) {
expect(count++).toBe(2);
expect(error.message).toBe('Network error: This is an error!');

subscription.unsubscribe();

const lastError = observable.getLastError();
const lastResult = observable.getLastResult();

expect(lastResult.loading).toBeFalsy();
expect(lastResult.networkStatus).toBe(8);

observable.resetLastResults();
subscription = observable.subscribe(observerOptions);
Object.assign(observable, { lastError, lastResult });

// The error arrived, run a refetch to get the third result
// which should now contain valid data.
setTimeout(() => {
observable.refetch().catch(() => {
done.fail('Expected good data on second refetch.');
});
}, 0);
},
};

subscription = observable.subscribe(observerOptions);
});

it('should throw a GraphQL error', () => {
const query = gql`
query {
Expand Down
8 changes: 8 additions & 0 deletions packages/apollo-client/src/core/ObservableQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,14 @@ export class ObservableQuery<
}

const onError = (error: ApolloError) => {
// Since we don't get the current result on errors, only the error, we
// must mirror the updates that occur in QueryStore.markQueryError here
this.updateLastResult({
...this.lastResult,
errors: error.graphQLErrors,
networkStatus: NetworkStatus.error,
loading: false,
});
iterateObserversSafely(this.observers, 'error', this.lastError = error);
};

Expand Down