Skip to content

Commit

Permalink
Add timeout value and status code to TimeoutError
Browse files Browse the repository at this point in the history
  • Loading branch information
Dustin Callaway committed Jun 10, 2019
1 parent e7dbadf commit 877e07d
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 12 deletions.
4 changes: 3 additions & 1 deletion __tests__/timeoutLink.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ test('long request times out', done => {
done();
},
error(error) {
expect(error.message).toBe('Timeout exceeded');
expect(error.message).toEqual('Timeout exceeded');
expect(error.timeout).toEqual(100);
expect(error.statusCode).toEqual(408);
done();
}
});
Expand Down
11 changes: 10 additions & 1 deletion src/TimeoutError.ts
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
export default class TimeoutError extends Error {}
export default class TimeoutError extends Error {
public timeout: number;
public statusCode: number;

constructor(message: string, timeout?: number, statusCode?: number) {
super(message);
this.timeout = timeout;
this.statusCode = statusCode || 408;
}
}
16 changes: 6 additions & 10 deletions src/timeoutLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,9 @@ export default class TimeoutLink extends ApolloLink {

public request(operation: Operation, forward: NextLink) {
let controller: AbortController;
let ctxTimeout: number;

// override timeout from query context
ctxTimeout = operation.getContext().timeout || null;
if (ctxTimeout <= 0) {
ctxTimeout = null;
}
const requestTimeout = operation.getContext().timeout || this.timeout;

// add abort controller and signal object to fetchOptions if they don't already exist
if (typeof AbortController !== 'undefined') {
Expand All @@ -42,14 +38,14 @@ export default class TimeoutLink extends ApolloLink {
(def: DefinitionNode) => def.kind === 'OperationDefinition'
).operation;

if (this.timeout <= 0 || operationType === 'subscription') {
if (requestTimeout <= 0 || operationType === 'subscription') {
return chainObservable; // skip this link if timeout is zero or it's a subscription request
}

// create local observable with timeout functionality (unsubscibe from chain observable and
// return an error if the timeout expires before chain observable resolves)
const localObservable = new Observable(observer => {
let timer: NodeJS.Timer;
let timer: number;

// listen to chainObservable for result and pass to localObservable if received before timeout
const subscription = chainObservable.subscribe(
Expand All @@ -66,14 +62,14 @@ export default class TimeoutLink extends ApolloLink {
);

// if timeout expires before observable completes, abort call, unsubscribe, and return error
timer = setTimeout(() => {
timer = window.setTimeout(() => {
if (controller) {
controller.abort(); // abort fetch operation
}

observer.error(new TimeoutError('Timeout exceeded'));
observer.error(new TimeoutError('Timeout exceeded', requestTimeout));
subscription.unsubscribe();
}, ctxTimeout || this.timeout);
}, requestTimeout);

let ctxRef = operation.getContext().timeoutRef;

Expand Down

0 comments on commit 877e07d

Please sign in to comment.