From 668f40204a4c93dee48ef3c5c4b483ef57aaa593 Mon Sep 17 00:00:00 2001 From: Kevin Whitley Date: Sat, 28 Oct 2023 11:23:25 -0500 Subject: [PATCH] flow work --- example/e2e middleware.ts | 20 +++++++++++++ src/flow.spec.ts | 59 +++++++++++++++++++++++++++++++++------ src/flow.ts | 16 +++++------ 3 files changed, 79 insertions(+), 16 deletions(-) create mode 100644 example/e2e middleware.ts diff --git a/example/e2e middleware.ts b/example/e2e middleware.ts new file mode 100644 index 00000000..dbc5a34f --- /dev/null +++ b/example/e2e middleware.ts @@ -0,0 +1,20 @@ +import { IRequest, Router } from 'Router' + +type FooRequest = { + foo: string +} & IRequest + +const router = Router() + +// MIDDLEWARE: adds foo to the request +const withFoo = (request: IRequest) => { + request.foo = 'bar' // no return = next handler gets fired (making this middleware) +} + +// ADD ROUTES +router + .all('*', withFoo) // we add Foo as global upstream middleware, but this could go anywhere + + .get('/foo-test', (request) => { + return request.foo // 'bar' + }) diff --git a/src/flow.spec.ts b/src/flow.spec.ts index 21c2392e..02abcc4a 100644 --- a/src/flow.spec.ts +++ b/src/flow.spec.ts @@ -12,7 +12,7 @@ let router beforeEach(() => { router = Router() .get('/', () => 'index') - .get('/items', () => []) + .get('/items', () => [1,2,3]) .get('/throw', (r) => r.a.b.c) }) @@ -30,7 +30,7 @@ describe('flow(router: RouterType, options: FlowOptions): RequestHandler', () => it('formats as json', async () => { let response = await flow(router)(request('/items')).then(r => r.json()) - expect(response).toEqual([]) + expect(response).toEqual([1,2,3]) }) it('catches errors', async () => { @@ -47,6 +47,49 @@ describe('flow(router: RouterType, options: FlowOptions): RequestHandler', () => }) describe('OPTIONS', () => { + describe('errors?: Function | false', () => { + it('should handle custom error function', async () => { + const customError = () => ({ status: 418, body: 'I\'m a teapot' }) + let response = await flow(router, { errors: customError })(request('/throw')) + expect(response.status).toBe(418) + expect(response.body).toBe('I\'m a teapot') + }) + + it('should not handle errors if set to false', async () => { + const errorHandler = vi.fn() + let response = await flow(router, { errors: false })(request('/throw')).catch(errorHandler) + expect(errorHandler).toHaveBeenCalled() + }) + }) + + describe('format?: Function | false', () => { + it('should handle custom format function', async () => { + const customFormat = (data) => ({ status: 200, body: `num items = ${data.length}` }) + let response = await flow(router, { format: customFormat })(request('/items')) + expect(response.status).toBe(200) + expect(response.body).toBe('num items = 3') + }) + + it('should not format response if set to false', async () => { + let response = await flow(router, { format: false })(request('/items')) + expect(response.body).toBeUndefined() + }) + }) + + describe('notFound?: RouteHandler | false', () => { + it('should handle custom notFound function', async () => { + const customNotFound = () => ({ status: 404, body: 'Custom Not Found' }) + let response = await flow(router, { notFound: customNotFound })(request('/missing')) + expect(response.status).toBe(404) + expect(response.body).toBe('Custom Not Found') + }) + + it('should not handle notFound if set to false', async () => { + let response = await flow(router, { notFound: false })(request('/missing')) + expect(response.status).toBeUndefined() + }) + }) + describe('cors?: CorsOptions | true', () => { it('will embed CORS headers if provided', async () => { let response = await flow(router, { @@ -68,14 +111,14 @@ describe('flow(router: RouterType, options: FlowOptions): RequestHandler', () => describe('error?: RouteHandler | false', () => { it('does not catch internally if set to false', async () => { let onError = vi.fn() - let response = await flow(router, { handleErrors: false })(request('/throw')).catch(onError) + let response = await flow(router, { errors: false })(request('/throw')).catch(onError) expect(onError).toHaveBeenCalled() }) it('can reshape errors if provided', async () => { let response = await flow(router, { - handleErrors: () => error(418, 'CUSTOM'), + errors: () => error(418, 'CUSTOM'), })(request('/throw')) expect(response.status).toBe(418) @@ -85,14 +128,14 @@ describe('flow(router: RouterType, options: FlowOptions): RequestHandler', () => describe('format?: ResponseFormatter | false', () => { it('does not catch internally if set to false', async () => { let onError = vi.fn() - let response = await flow(router, { handleErrors: false })(request('/throw')).catch(onError) + let response = await flow(router, { errors: false })(request('/throw')).catch(onError) expect(onError).toHaveBeenCalled() }) it('can reshape errors if provided', async () => { let response = await flow(router, { - handleErrors: () => error(418, 'CUSTOM'), + errors: () => error(418, 'CUSTOM'), })(request('/throw')) expect(response.status).toBe(418) @@ -102,7 +145,7 @@ describe('flow(router: RouterType, options: FlowOptions): RequestHandler', () => describe('notFound?: RouteHandler | false', () => { it('uses a custom 404 handler', async () => { let response = await flow(router, { - handleNotFound: () => error(418, 'CUSTOM'), + notFound: () => error(418, 'CUSTOM'), })(request('/missing')).then(r => r.json()) expect(response.status).toBe(418) @@ -111,7 +154,7 @@ describe('flow(router: RouterType, options: FlowOptions): RequestHandler', () => it('if set to false, will not add notFound handler (allow undefined passthrough)', async () => { let response = await flow(router, { - handleNotFound: false, + notFound: false, format: false, })(request('/missing')) diff --git a/src/flow.ts b/src/flow.ts index 17dc7cd2..9ea98b94 100644 --- a/src/flow.ts +++ b/src/flow.ts @@ -5,24 +5,24 @@ import { json } from './json' import { withParams } from './withParams' export type FlowOptions = { - format?: Function | false - handleErrors?: Function | false - handleNotFound?: RouteHandler | false cors?: CorsOptions | true + errors?: Function | false + format?: Function | false + notFound?: RouteHandler | false } export const flow = (router: RouterType, options: FlowOptions = {}) => { const { format = json, cors, - handleErrors = error, - handleNotFound = () => error(404), + errors = error, + notFound = () => error(404), } = options let corsHandlers: any // register a notFound route, if given - if (typeof handleNotFound === 'function') { - router.all('*', handleNotFound) + if (typeof notFound === 'function') { + router.all('*', notFound) } // Initialize CORS handlers if cors options are provided @@ -37,7 +37,7 @@ export const flow = (router: RouterType, options: FlowOptions = {}) => { // @ts-expect-error - add optional formatting response = format ? response.then(format) : response // @ts-expect-error - add optional error handling - response = handleErrors ? response.catch(handleErrors) : response + response = errors ? response.catch(errors) : response // add optional cors and return response return cors ? response.then(corsHandlers?.corsify) : response