Skip to content

Commit

Permalink
fix(client): New error event listener for handling connection errors (
Browse files Browse the repository at this point in the history
  • Loading branch information
enisdenjo committed Mar 11, 2021
1 parent 672df32 commit 127b69f
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 1 deletion.
19 changes: 18 additions & 1 deletion src/client.ts
Expand Up @@ -22,7 +22,8 @@ export * from './protocol';
export type EventConnecting = 'connecting';
export type EventConnected = 'connected'; // connected = socket opened + acknowledged
export type EventClosed = 'closed';
export type Event = EventConnecting | EventConnected | EventClosed;
export type EventError = 'error';
export type Event = EventConnecting | EventConnected | EventClosed | EventError;

/**
* The first argument is actually the `WebSocket`, but to avoid
Expand All @@ -46,12 +47,22 @@ export type EventConnectingListener = () => void;
*/
export type EventClosedListener = (event: unknown) => void;

/**
* The argument can be either an Error Event or an instance of Error, but to avoid
* bundling DOM typings because the client can run in Node env too, you should assert
* the type during implementation. Events dispatched from the WebSocket `onerror` can
* be handler in this listener.
*/
export type EventErrorListener = (error: unknown) => void;

export type EventListener<E extends Event> = E extends EventConnecting
? EventConnectingListener
: E extends EventConnected
? EventConnectedListener
: E extends EventClosed
? EventClosedListener
: E extends EventError
? EventErrorListener
: never;

/** Configuration used for the GraphQL over WebSocket client. */
Expand Down Expand Up @@ -261,6 +272,7 @@ export function createClient(options: ClientOptions): Client {
connecting: on?.connecting ? [on.connecting] : [],
connected: on?.connected ? [on.connected] : [],
closed: on?.closed ? [on.closed] : [],
error: on?.error ? [on.error] : [],
};

return {
Expand Down Expand Up @@ -305,6 +317,11 @@ export function createClient(options: ClientOptions): Client {
emitter.emit('connecting');
const socket = new WebSocketImpl(url, GRAPHQL_TRANSPORT_WS_PROTOCOL);

socket.onerror = (err) => {
// we let the onclose reject the promise for correct retry handling
emitter.emit('error', err);
};

socket.onclose = (event) => {
connecting = undefined;
emitter.emit('closed', event);
Expand Down
61 changes: 61 additions & 0 deletions src/tests/client.ts
Expand Up @@ -1075,4 +1075,65 @@ describe('events', () => {

client.dispose();
});

it('should emit the websocket connection error', (done) => {
expect.assertions(3);

createClient({
url: 'ws://localhost/i/dont/exist',
lazy: false,
retryAttempts: 0,
onNonLazyError: (err) => {
// handle the websocket close event
expect((err as CloseEvent).code).toBe(1006);
done();
},
on: {
closed: (err) => {
// websocket closed
expect((err as CloseEvent).code).toBe(1006);
},
error: (err) => {
// connection error
expect((err as ErrorEvent).message).toBe(
'connect ECONNREFUSED 127.0.0.1:80',
);
},
},
});
});

it('should emit the websocket connection error on first subscribe in lazy mode', (done) => {
expect.assertions(3);

const client = createClient({
url: 'ws://localhost/i/dont/exist',
retryAttempts: 0,
});

client.on('closed', (err) => {
// websocket closed
expect((err as CloseEvent).code).toBe(1006);
});

client.on('error', (err) => {
// connection error
expect((err as ErrorEvent).message).toBe(
'connect ECONNREFUSED 127.0.0.1:80',
);
});

client.subscribe(
{ query: '' },
{
next: noop,
complete: noop,
error: (err) => {
// handle the websocket close event
expect((err as CloseEvent).code).toBe(1006);
done();
},
},
);
});
});

0 comments on commit 127b69f

Please sign in to comment.