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

fix(RequestController): support arbitrary objects in .errorWith() #676

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
8 changes: 8 additions & 0 deletions src/RequestController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ it('resolves the response promise with the error provided to "errorWith"', async
await expect(controller[kResponsePromise]).resolves.toEqual(error)
})

it('resolves the response promise with the object provided to "errorWith"', async () => {
const controller = new RequestController(new Request('http://localhost'))
const error = { message: 'Oops!' }
controller.errorWith(error)

await expect(controller[kResponsePromise]).resolves.toEqual(error)
})

it('throws when calling "respondWith" multiple times', () => {
const controller = new RequestController(new Request('http://localhost'))
controller.respondWith(new Response('hello world'))
Expand Down
7 changes: 4 additions & 3 deletions src/RequestController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export class RequestController {
* @note This promise cannot be rejected. It's either infinitely
* pending or resolved with whichever Response was passed to `respondWith()`.
*/
[kResponsePromise]: DeferredPromise<Response | Error | undefined>;
[kResponsePromise]: DeferredPromise<Response | Object | undefined>;

/**
* Internal flag indicating if this request has been handled.
Expand Down Expand Up @@ -46,7 +46,7 @@ export class RequestController {
this[kResponsePromise].resolve(response)

/**
* @note The request conrtoller doesn't do anything
* @note The request controller doesn't do anything
* apart from letting the interceptor await the response
* provided by the developer through the response promise.
* Each interceptor implements the actual respondWith/errorWith
Expand All @@ -59,8 +59,9 @@ export class RequestController {
* @example
* controller.errorWith()
* controller.errorWith(new Error('Oops!'))
* controller.errorWith({ message: 'Oops!'})
*/
public errorWith(error?: Error): void {
public errorWith(error?: Object): void {
invariant.as(
InterceptorError,
!this[kRequestHandled],
Expand Down
42 changes: 24 additions & 18 deletions src/utils/handleRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,31 @@ interface HandleRequestOptions {
export async function handleRequest(
options: HandleRequestOptions
): Promise<boolean> {
const handleResponse = async (response: Response | Error) => {
const onResolve = async (response: Response): Promise<boolean> => {
// Handle "Response.error()" instances.
if (response instanceof Error) {
options.onError(response)
}

// Handle "Response.error()" instances.
else if (isResponseError(response)) {
} else if (isResponseError(response)) {
options.onRequestError(response)
} else {
await options.onResponse(response)
await handleResponse(response)
}

return true
}
const onReject = async (error: unknown): Promise<boolean> => {
// Handle thrown responses.
if (error instanceof Response) {
await handleResponse(error)
return true
}

return await handleResponseError(error);
}

const handleResponse = async (response: Response) => {
await options.onResponse(response)
}

const handleResponseError = async (error: unknown): Promise<boolean> => {
// Forward the special interceptor error instances
Expand All @@ -69,12 +80,7 @@ export async function handleRequest(
if (isNodeLikeError(error)) {
options.onError(error)
return true
}

// Handle thrown responses.
if (error instanceof Response) {
return await handleResponse(error)
}
}

return false
}
Expand Down Expand Up @@ -116,7 +122,7 @@ export async function handleRequest(
// for that event are finished (e.g. async listeners awaited).
// By the end of this promise, the developer cannot affect the
// request anymore.
const requestListtenersPromise = emitAsync(options.emitter, 'request', {
const requestListenersPromise = emitAsync(options.emitter, 'request', {
requestId: options.requestId,
request: options.request,
controller: options.controller,
Expand All @@ -125,7 +131,7 @@ export async function handleRequest(
await Promise.race([
// Short-circuit the request handling promise if the request gets aborted.
requestAbortPromise,
requestListtenersPromise,
requestListenersPromise,
options.controller[kResponsePromise],
])

Expand All @@ -144,7 +150,7 @@ export async function handleRequest(
if (result.error) {
// Handle the error during the request listener execution.
// These can be thrown responses or request errors.
if (await handleResponseError(result.error)) {
if (await onReject(result.error)) {
return true
}

Expand Down Expand Up @@ -188,11 +194,11 @@ export async function handleRequest(
* emit of the same event. They are forwarded as-is.
*/
if (nextResult.error) {
return handleResponseError(nextResult.error)
return onReject(nextResult.error)
}

if (nextResult.data) {
return handleResponse(nextResult.data)
return onResolve(nextResult.data)
}
}

Expand All @@ -208,7 +214,7 @@ export async function handleRequest(
* unhandled exceptions from intended errors.
*/
if (result.data) {
return handleResponse(result.data)
return onResolve(result.data)
}

// In all other cases, consider the request unhandled.
Expand Down
Loading