From c46c5d83c30db002a1e22de47e97fd116170297c Mon Sep 17 00:00:00 2001 From: Dmitriy Garanzha Date: Wed, 17 Jan 2018 17:33:36 +0200 Subject: [PATCH] Emit event when an websocket error occurs. --- CHANGELOG.md | 1 + README.md | 7 ++++++- src/client.ts | 7 ++++++- src/test/tests.ts | 20 ++++++++++++++++++++ 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 869d8be30..d67bb7a76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ### vNEXT +- added `error` event to handle connection errors and debug network troubles [PR #341](https://github.com/apollographql/subscriptions-transport-ws/pull/341). ### v0.9.7 - change default timeout from 10s to 30s [PR #368](https://github.com/apollographql/subscriptions-transport-ws/pull/368) diff --git a/README.md b/README.md index 78aa3e65e..673b74d6f 100644 --- a/README.md +++ b/README.md @@ -255,7 +255,7 @@ ReactDOM.render( #### `unsubscribeAll() => void` - unsubscribes from all active subscriptions. #### `on(eventName, callback, thisContext) => Function` -- `eventName: string`: the name of the event, available events are: `connecting`, `connected`, `reconnecting`, `reconnected` and `disconnected` +- `eventName: string`: the name of the event, available events are: `connecting`, `connected`, `reconnecting`, `reconnected`, `disconnected` and `error` - `callback: Function`: function to be called when websocket connects and initialized. - `thisContext: any`: `this` context to use when calling the callback function. - => Returns an `off` method to cancel the event subscription. @@ -285,6 +285,11 @@ ReactDOM.render( - `thisContext: any`: `this` context to use when calling the callback function. - => Returns an `off` method to cancel the event subscription. +#### `onError(callback, thisContext) => Function` - shorthand for `.on('error', ...)` +- `callback: Function`: function to be called when an error occurs. +- `thisContext: any`: `this` context to use when calling the callback function. +- => Returns an `off` method to cancel the event subscription. + ### `close() => void` - closes the WebSocket connection manually, and ignores `reconnect` logic if it was set to `true`. ### `use(middlewares: MiddlewareInterface[]) => SubscriptionClient` - adds middleware to modify `OperationOptions` per each request diff --git a/src/client.ts b/src/client.ts index 058a24ecf..0082c9d39 100644 --- a/src/client.ts +++ b/src/client.ts @@ -234,6 +234,10 @@ export class SubscriptionClient { return this.on('reconnecting', callback, context); } + public onError(callback: ListenerFn, context?: any): Function { + return this.on('error', callback, context); + } + public unsubscribeAll() { Object.keys(this.operations).forEach( subId => { this.unsubscribe(subId); @@ -514,9 +518,10 @@ export class SubscriptionClient { } }; - this.client.onerror = () => { + this.client.onerror = (err: Error) => { // Capture and ignore errors to prevent unhandled exceptions, wait for // onclose to fire before attempting a reconnect. + this.eventEmitter.emit('error', err); }; this.client.onmessage = ({ data }: {data: any}) => { diff --git a/src/test/tests.ts b/src/test/tests.ts index 345b9ad68..cc4214c69 100644 --- a/src/test/tests.ts +++ b/src/test/tests.ts @@ -948,6 +948,26 @@ describe('Client', function () { }, 1000); }); + it('should emit event when an websocket error occurs', function (done) { + const client = new SubscriptionClient(`ws://localhost:${ERROR_TEST_PORT}/`); + + client.request({ + query: `subscription useInfo{ + invalid + }`, + variables: {}, + }).subscribe({ + next: () => { + assert(false); + }, + }); + + client.onError((err: Error) => { + expect(err.message).to.be.equal(`connect ECONNREFUSED 127.0.0.1:${ERROR_TEST_PORT}`); + done(); + }); + }); + it('should stop trying to reconnect to the server', function (done) { wsServer.on('connection', (connection: WebSocket) => { connection.close();