diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dc639fd837..1ec0bf0030a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Expect active development and potentially significant breaking changes in the `0 (https://github.com/apollostack/apollo-client/pull/866) - Expose `SubscriptionOptions` [PR #868](https://github.com/apollostack/apollo-client/pull/868) - Fix issue with `currentResult` and optimistic responses [Issue #877](https://github.com/apollostack/apollo-client/issues/877) +- Provide an onError callback for subscribeToMore [PR #886](https://github.com/apollostack/apollo-client/issues/886) - Bind `resetStore` to ApolloClient [PR #882](https://github.com/apollostack/apollo-client/issues/882) - Fix a bug with `resetStore` that caused existing queries to fail [PR #885](https://github.com/apollostack/apollo-client/issues/885) diff --git a/src/core/ObservableQuery.ts b/src/core/ObservableQuery.ts index 56392bf26d5..ddf211dfba2 100644 --- a/src/core/ObservableQuery.ts +++ b/src/core/ObservableQuery.ts @@ -243,8 +243,11 @@ export class ObservableQuery extends Observable { this.updateQuery(mapFn); }, error: (err) => { - // TODO implement something smart here when improving error handling - console.error(err); + if (options.onError) { + options.onError(err); + } else { + console.error('Unhandled GraphQL subscription errror', err); + } }, }); diff --git a/src/core/watchQueryOptions.ts b/src/core/watchQueryOptions.ts index 443e57d9cb8..fd2dc73fe06 100644 --- a/src/core/watchQueryOptions.ts +++ b/src/core/watchQueryOptions.ts @@ -95,6 +95,7 @@ export type SubscribeToMoreOptions = { subscriptionData: { data: any }, variables: { [key: string]: any }, }) => Object; + onError?: (error: Error) => void; } export interface DeprecatedSubscriptionOptions { diff --git a/test/subscribeToMore.ts b/test/subscribeToMore.ts index 98013780e81..8228d5478ad 100644 --- a/test/subscribeToMore.ts +++ b/test/subscribeToMore.ts @@ -45,6 +45,40 @@ describe('subscribeToMore', () => { results: [...results], }; + const results2 = [ + { error: new Error('You cant touch this'), delay: 10 }, + { result: { name: 'Amanda Liu' }, delay: 10 }, + ]; + + const sub2 = { + request: { + query: gql` + subscription newValues { + notAnActualField + } + `, + }, + id: 0, + results: [...results2], + }; + + const results3 = [ + { error: new Error('You cant touch this'), delay: 10 }, + { result: { name: 'Amanda Liu' }, delay: 10 }, + ]; + + const sub3 = { + request: { + query: gql` + subscription newValues { + notAnActualField + } + `, + }, + id: 0, + results: [...results3], + }; + it('triggers new result from subscription data', (done) => { let latestResult: any = null; const networkInterface = mockSubscriptionNetworkInterface([sub1], req1); @@ -91,5 +125,108 @@ describe('subscribeToMore', () => { } }); + + it('calls error callback on error', (done) => { + let latestResult: any = null; + const networkInterface = mockSubscriptionNetworkInterface([sub2], req1); + let counter = 0; + + const client = new ApolloClient({ + networkInterface, + addTypename: false, + }); + + const obsHandle = client.watchQuery({ + query, + }); + const sub = obsHandle.subscribe({ + next(queryResult) { + latestResult = queryResult; + counter++; + }, + }); + + let errorCount = 0; + + obsHandle.subscribeToMore({ + document: gql` + subscription newValues { + notAnActualField + } + `, + updateQuery: (prev, { subscriptionData }) => { + return { entry: { value: subscriptionData.data.name } }; + }, + onError: (err) => { errorCount += 1; }, + }); + + setTimeout(() => { + sub.unsubscribe(); + assert.equal(counter, 2); + assert.deepEqual( + latestResult, + { data: { entry: { value: 'Amanda Liu' } }, loading: false, networkStatus: 7 } + ); + assert.equal(errorCount, 1); + done(); + }, 50); + + for (let i = 0; i < 2; i++) { + networkInterface.fireResult(0); // 0 is the id of the subscription for the NI + } + }); + + it('prints unhandled subscription errors to the console', (done) => { + let latestResult: any = null; + const networkInterface = mockSubscriptionNetworkInterface([sub3], req1); + let counter = 0; + + const client = new ApolloClient({ + networkInterface, + addTypename: false, + }); + + const obsHandle = client.watchQuery({ + query, + }); + const sub = obsHandle.subscribe({ + next(queryResult) { + latestResult = queryResult; + counter++; + }, + }); + + let errorCount = 0; + const consoleErr = console.error; + console.error = (err: Error) => { errorCount += 1; }; + + obsHandle.subscribeToMore({ + document: gql` + subscription newValues { + notAnActualField + } + `, + updateQuery: (prev, { subscriptionData }) => { + return { entry: { value: subscriptionData.data.name } }; + }, + }); + + setTimeout(() => { + sub.unsubscribe(); + assert.equal(counter, 2); + assert.deepEqual( + latestResult, + { data: { entry: { value: 'Amanda Liu' } }, loading: false, networkStatus: 7 } + ); + assert.equal(errorCount, 1); + console.error = consoleErr; + done(); + }, 50); + + for (let i = 0; i < 2; i++) { + networkInterface.fireResult(0); // 0 is the id of the subscription for the NI + } + }); + // TODO add a test that checks that subscriptions are cancelled when obs is unsubscribed from. });