Skip to content

Commit

Permalink
feat: automatically decorate etag
Browse files Browse the repository at this point in the history
  • Loading branch information
Kikobeats committed Mar 24, 2024
1 parent 6659425 commit 94c6259
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 29 deletions.
12 changes: 8 additions & 4 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ const cacheableResponse = ({
bypassQueryParameter = 'force',
cache = new Keyv({ namespace: 'ssr' }),
compress: enableCompression = false,
get,
get: rawGet,
key: getKey = createKey(bypassQueryParameter),
send,
staleTtl: rawStaleTtl = 3600000,
ttl: rawTtl = 86400000,
...compressOpts
} = {}) => {
assert(get, '.get required')
assert(rawGet, '.get required')
assert(send, '.send required')

const staleTtl = isFunction(rawStaleTtl)
Expand All @@ -34,6 +34,11 @@ const cacheableResponse = ({
...compressOpts
})

const get = opts => Promise.resolve(rawGet(opts)).then(result => {
result.etag = getEtag(serialize(result))
return result
})

const memoGet = memoize(get, cache, {
key: getKey,
objectMode: true,
Expand All @@ -55,13 +60,12 @@ const cacheableResponse = ({
const {
createdAt = Date.now(),
data = null,
etag: cachedEtag,
etag,
staleTtl = memoGet.staleTtl(result),
ttl = memoGet.ttl(result),
...props
} = result

const etag = cachedEtag || getEtag(serialize(result))
const ifNoneMatch = req.headers['if-none-match']
const isModified = etag !== ifNoneMatch

Expand Down
65 changes: 65 additions & 0 deletions test/etag.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
'use strict'

const test = require('ava')
const got = require('got')

const cacheableResponse = require('..')
const { runServer } = require('./helpers')

test('etag is present', async t => {
const url = await runServer(
t,
cacheableResponse({
staleTtl: false,
get: ({ req, res }) => {
return {
data: { foo: 'bar' },
ttl: 30000,
createdAt: Date.now(),
foo: { bar: true }
}
},
send: ({ data, headers, res, req, ...props }) => {
res.end('Hello World')
}
})
)
const { headers: headersOne } = await got(`${url}/kikobeats`)
t.is(headersOne['x-cache-status'], 'MISS')
const { headers: headersTwo } = await got(`${url}/kikobeats`)
t.is(headersTwo['x-cache-status'], 'HIT')
t.is(headersOne.etag, headersTwo.etag)
})

test('etag is different', async t => {
const url = await runServer(
t,
cacheableResponse({
staleTtl: false,
key: ({ req }) => [req.url, req.url.includes('force')],
get: ({ req, res }) => {
return {
data: { foo: 'bar' },
ttl: 30000,
createdAt: Date.now(),
foo: { bar: true }
}
},
send: ({ data, headers, res, req }) => {
res.end('Hello World')
}
})
)
const { headers: headersOne } = await got(`${url}/kikobeats`)
const etagOne = headersOne.etag

const { headers: headersTwo } = await got(`${url}/kikobeats`)
const etagTwo = headersTwo.etag

t.is(etagOne, etagTwo)

const { headers: headersThree } = await got(`${url}/kikobeats&force`)
const etagThree = headersThree.etag

t.not(etagOne, etagThree)
})
25 changes: 0 additions & 25 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,6 @@ const got = require('got')
const cacheableResponse = require('..')
const { runServer } = require('./helpers')

test('etag is present', async t => {
const url = await runServer(
t,
cacheableResponse({
staleTtl: false,
get: ({ req, res }) => {
return {
data: { foo: 'bar' },
ttl: 30000,
createdAt: Date.now(),
foo: { bar: true }
}
},
send: ({ data, headers, res, req, ...props }) => {
res.end('Hello World')
}
})
)
const { headers: headersOne } = await got(`${url}/kikobeats`)
t.is(headersOne['x-cache-status'], 'MISS')
const { headers: headersTwo } = await got(`${url}/kikobeats`)
t.is(headersTwo['x-cache-status'], 'HIT')
t.is(headersOne.etag, headersTwo.etag)
})

test('compress support', async t => {
const url = await runServer(
t,
Expand Down

0 comments on commit 94c6259

Please sign in to comment.