From ba97a1a4d5590631f8d070dcf13d4027418482f1 Mon Sep 17 00:00:00 2001 From: Jeremy Thomerson Date: Fri, 15 Feb 2019 09:38:07 -0500 Subject: [PATCH] Add header helpers: delete a header and caching helpers --- src/Response.ts | 37 +++++++++++ tests/Response.test.ts | 140 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 176 insertions(+), 1 deletion(-) diff --git a/src/Response.ts b/src/Response.ts index 23bbbc1..df53619 100644 --- a/src/Response.ts +++ b/src/Response.ts @@ -58,6 +58,14 @@ export default class Response { return this; } + /** + * Deletes a response header that may have been previously set. + */ + public delete(headerName: string): Response { + delete this._headers[headerName]; + return this; + } + /** * Appends one or more response header values to the response. For example: * @@ -261,6 +269,35 @@ export default class Response { return this.cookie(name, '', _.extend({ expires: new Date(1), path: '/' }, userOpts)); } + + public cacheForSeconds(seconds: number): Response { + const now = new Date(), + expiry = new Date(now.getTime() + (seconds * 1000)); + + if (seconds > 0) { + this.delete('Pragma'); + this.set({ + 'Expires': expiry.toUTCString(), + 'Cache-Control': `must-revalidate, max-age=${seconds}`, + }); + } else { + this.set({ + 'Expires': 'Thu, 19 Nov 1981 08:52:00 GMT', + 'Cache-Control': 'no-cache, max-age=0, must-revalidate', + 'Pragma': 'no-cache', + }); + } + return this; + } + + public cacheForMinutes(minutes: number): Response { + return this.cacheForSeconds(minutes * 60); + } + + public cacheForHours(hours: number): Response { + return this.cacheForMinutes(hours * 60); + } + // METHODS RELATED TO SENDING RESPONSES /** diff --git a/tests/Response.test.ts b/tests/Response.test.ts index 47ab957..f063dd3 100644 --- a/tests/Response.test.ts +++ b/tests/Response.test.ts @@ -34,7 +34,7 @@ describe('Request', () => { describe('header functionality', () => { - it('sets and appends headers correctly', () => { + it('sets, appends, and deletes headers correctly', () => { expect(sampleResp.getHeaders()).to.eql({}); sampleResp.set('Content-Type', 'text/plain'); expect(sampleResp.getHeaders()).to.eql({ @@ -100,6 +100,22 @@ describe('Request', () => { 'Waa': [ 'Hoo' ], }); + // delete + sampleResp.delete('Content-Type'); + expect(sampleResp.getHeaders()).to.eql({ + 'ETag': [ '54321' ], + 'Foo': [ 'Bar', 'Baz' ], + 'Baz': [ 'Boom' ], + 'Waa': [ 'Hoo' ], + }); + + sampleResp.delete('ETag'); + expect(sampleResp.getHeaders()).to.eql({ + 'Foo': [ 'Bar', 'Baz' ], + 'Baz': [ 'Boom' ], + 'Waa': [ 'Hoo' ], + }); + }); it('throws errors when set/append are called after a response is sent', () => { @@ -154,6 +170,128 @@ describe('Request', () => { }); + describe('caching helpers', () => { + let now = new Date(Date.UTC(1991, 10, 23, 12, 30, 59, 900)), // Sat, 23 Nov 1991 12:30:59 GMT + sandbox: SinonSandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + sandbox.useFakeTimers(now.getTime()); + }); + + afterEach(() => { + sandbox.restore(); + }); + + describe('cacheForSeconds', () => { + + it('sets the cache headers correctly', () => { + expect(sampleResp.getHeaders()).to.eql({}); + sampleResp.cacheForSeconds(180); + expect(sampleResp.getHeaders()).to.eql({ + 'Expires': [ 'Sat, 23 Nov 1991 12:33:59 GMT' ], + 'Cache-Control': [ 'must-revalidate, max-age=180' ], + }); + }); + + it('allows toggling them back and forth', () => { + expect(sampleResp.getHeaders()).to.eql({}); + + sampleResp.cacheForSeconds(180); + expect(sampleResp.getHeaders()).to.eql({ + 'Expires': [ 'Sat, 23 Nov 1991 12:33:59 GMT' ], + 'Cache-Control': [ 'must-revalidate, max-age=180' ], + }); + + sampleResp.cacheForSeconds(0); + expect(sampleResp.getHeaders()).to.eql({ + 'Expires': [ 'Thu, 19 Nov 1981 08:52:00 GMT' ], + 'Cache-Control': [ 'no-cache, max-age=0, must-revalidate' ], + 'Pragma': [ 'no-cache' ], + }); + + sampleResp.cacheForSeconds(241); + expect(sampleResp.getHeaders()).to.eql({ + 'Expires': [ 'Sat, 23 Nov 1991 12:35:00 GMT' ], + 'Cache-Control': [ 'must-revalidate, max-age=241' ], + }); + }); + + }); + + describe('cacheForMinutes', () => { + + it('sets the cache headers correctly', () => { + expect(sampleResp.getHeaders()).to.eql({}); + sampleResp.cacheForMinutes(90); + expect(sampleResp.getHeaders()).to.eql({ + 'Expires': [ 'Sat, 23 Nov 1991 14:00:59 GMT' ], + 'Cache-Control': [ 'must-revalidate, max-age=5400' ], + }); + }); + + it('allows toggling them back and forth', () => { + expect(sampleResp.getHeaders()).to.eql({}); + + sampleResp.cacheForMinutes(90); + expect(sampleResp.getHeaders()).to.eql({ + 'Expires': [ 'Sat, 23 Nov 1991 14:00:59 GMT' ], + 'Cache-Control': [ 'must-revalidate, max-age=5400' ], + }); + + sampleResp.cacheForMinutes(0); + expect(sampleResp.getHeaders()).to.eql({ + 'Expires': [ 'Thu, 19 Nov 1981 08:52:00 GMT' ], + 'Cache-Control': [ 'no-cache, max-age=0, must-revalidate' ], + 'Pragma': [ 'no-cache' ], + }); + + sampleResp.cacheForMinutes(5); + expect(sampleResp.getHeaders()).to.eql({ + 'Expires': [ 'Sat, 23 Nov 1991 12:35:59 GMT' ], + 'Cache-Control': [ 'must-revalidate, max-age=300' ], + }); + }); + + }); + + describe('cacheForHours', () => { + + it('sets the cache headers correctly', () => { + expect(sampleResp.getHeaders()).to.eql({}); + sampleResp.cacheForHours(2); + expect(sampleResp.getHeaders()).to.eql({ + 'Expires': [ 'Sat, 23 Nov 1991 14:30:59 GMT' ], + 'Cache-Control': [ 'must-revalidate, max-age=7200' ], + }); + }); + + it('allows toggling them back and forth', () => { + expect(sampleResp.getHeaders()).to.eql({}); + + sampleResp.cacheForHours(2); + expect(sampleResp.getHeaders()).to.eql({ + 'Expires': [ 'Sat, 23 Nov 1991 14:30:59 GMT' ], + 'Cache-Control': [ 'must-revalidate, max-age=7200' ], + }); + + sampleResp.cacheForHours(0); + expect(sampleResp.getHeaders()).to.eql({ + 'Expires': [ 'Thu, 19 Nov 1981 08:52:00 GMT' ], + 'Cache-Control': [ 'no-cache, max-age=0, must-revalidate' ], + 'Pragma': [ 'no-cache' ], + }); + + sampleResp.cacheForHours(1); + expect(sampleResp.getHeaders()).to.eql({ + 'Expires': [ 'Sat, 23 Nov 1991 13:30:59 GMT' ], + 'Cache-Control': [ 'must-revalidate, max-age=3600' ], + }); + }); + + }); + }); + }); describe('status codes and messages', () => {