From 88141112c52cbfa3d13bb6742420ee8b7453404d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Sun, 8 Dec 2019 01:49:41 +0100 Subject: [PATCH 1/2] fix(response): use never return type for abort method --- adonis-typings/response.ts | 2 +- src/Response/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/adonis-typings/response.ts b/adonis-typings/response.ts index 566c4f5..38a890f 100644 --- a/adonis-typings/response.ts +++ b/adonis-typings/response.ts @@ -101,7 +101,7 @@ declare module '@ioc:Adonis/Core/Response' { plainCookie (key: string, value: any, options?: Partial): this clearCookie (key: string): this - abort (body: any, status?: number): void + abort (body: any, status?: number): never abortIf (conditional: any, body: any, status?: number): void finish (): void diff --git a/src/Response/index.ts b/src/Response/index.ts index 972805e..10f4f21 100644 --- a/src/Response/index.ts +++ b/src/Response/index.ts @@ -819,7 +819,7 @@ export class Response extends Macroable implements ResponseContract { * Abort the request with custom body and a status code. 400 is * used when status is not defined */ - public abort (body: any, status?: number): void { + public abort (body: any, status?: number): never { throw HttpException.invoke(body, status || 400) } From de50e82672bf2ee1f1e29629913ecf45c6b5ae47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Thu, 19 Dec 2019 14:09:37 +0100 Subject: [PATCH 2/2] feat: add Response.abortUnless This method is the inverse of abortIf. It allows to use type assertions to make TypeScript narrow types if the function threw. --- adonis-typings/response.ts | 1 + src/Response/index.ts | 10 ++++++++++ test/response.spec.ts | 37 +++++++++++++++++++++++++++++++++++-- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/adonis-typings/response.ts b/adonis-typings/response.ts index 38a890f..7f06e77 100644 --- a/adonis-typings/response.ts +++ b/adonis-typings/response.ts @@ -103,6 +103,7 @@ declare module '@ioc:Adonis/Core/Response' { abort (body: any, status?: number): never abortIf (conditional: any, body: any, status?: number): void + abortUnless (conditional: any, body: any, status?: number): asserts conditional finish (): void } diff --git a/src/Response/index.ts b/src/Response/index.ts index 10f4f21..2038752 100644 --- a/src/Response/index.ts +++ b/src/Response/index.ts @@ -833,6 +833,16 @@ export class Response extends Macroable implements ResponseContract { } } + /** + * Abort the request with custom body and a status code when + * passed condition returns `false` + */ + public abortUnless (condition: any, body: any, status?: number): asserts condition { + if (!condition) { + this.abort(body, status) + } + } + /** * Set signed cookie as the response header. The inline options overrides * all options from the config (means they are not merged). diff --git a/test/response.spec.ts b/test/response.spec.ts index 8488f09..d275a13 100644 --- a/test/response.spec.ts +++ b/test/response.spec.ts @@ -1034,7 +1034,7 @@ test.group('Response', (group) => { assert.deepEqual(body, { message: 'Not allowed' }) }) - test('abort request when condition is truthy', async (assert) => { + test('abortIf: abort request when condition is truthy', async (assert) => { const server = createServer((req, res) => { const config = fakeConfig() const response = new Response(req, res, config) @@ -1051,7 +1051,7 @@ test.group('Response', (group) => { assert.deepEqual(body, { message: 'Not allowed' }) }) - test('do not abort request when condition is falsy', async () => { + test('abortIf: do not abort request when condition is falsy', async () => { const server = createServer((req, res) => { const config = fakeConfig() const response = new Response(req, res, config) @@ -1066,4 +1066,37 @@ test.group('Response', (group) => { await supertest(server).get('/').expect(200) }) + + test('abortUnless: abort request when condition is falsy', async (assert) => { + const server = createServer((req, res) => { + const config = fakeConfig() + const response: Response = new Response(req, res, config) + try { + response.abortUnless(false, { message: 'Not allowed' }, 401) + } catch (error) { + error.handle(error, { response }) + } + + response.finish() + }) + + const { body } = await supertest(server).get('/').expect(401) + assert.deepEqual(body, { message: 'Not allowed' }) + }) + + test('abortUnless: do not abort request when condition is truthy', async () => { + const server = createServer((req, res) => { + const config = fakeConfig() + const response: Response = new Response(req, res, config) + try { + response.abortUnless(true, { message: 'Not allowed' }, 401) + } catch (error) { + error.handle(error, { response }) + } + + response.finish() + }) + + await supertest(server).get('/').expect(200) + }) })