Skip to content

Commit c4be7e0

Browse files
authored
Merge branch 'main' into dependabot-preview-shutdown
2 parents bb1ca99 + c66b44f commit c4be7e0

5 files changed

Lines changed: 91 additions & 6 deletions

File tree

lib/redis-accessor.js

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@ const { CI, NODE_ENV, REDIS_URL } = process.env
88
const useRealRedis = !CI && NODE_ENV !== 'test' && !!REDIS_URL
99

1010
class RedisAccessor {
11-
constructor ({ databaseNumber = 0, prefix = null, allowSetFailures = false, name = null } = {}) {
11+
constructor ({
12+
databaseNumber = 0,
13+
prefix = null,
14+
allowSetFailures = false,
15+
allowGetFailures = false,
16+
name = null
17+
} = {}) {
1218
const redisClient = useRealRedis
1319
? createRedisClient({
1420
url: REDIS_URL,
@@ -23,6 +29,9 @@ class RedisAccessor {
2329

2430
// Allow for graceful failures if a Redis SET operation fails?
2531
this._allowSetFailures = allowSetFailures === true
32+
33+
// Allow for graceful failures if a Redis GET operation fails?
34+
this._allowGetFailures = allowGetFailures === true
2635
}
2736

2837
/** @private */
@@ -105,8 +114,24 @@ Error: ${err.message}`
105114

106115
async get (key) {
107116
const getAsync = promisify(this._client.get).bind(this._client)
108-
const value = await getAsync(this.prefix(key))
109-
return value
117+
const fullKey = this.prefix(key)
118+
119+
try {
120+
const value = await getAsync(fullKey)
121+
return value
122+
} catch (err) {
123+
const errorText = `Failed to get value from Redis.
124+
Key: ${fullKey}
125+
Error: ${err.message}`
126+
127+
if (this._allowGetFailures === true) {
128+
// Allow for graceful failure
129+
console.error(errorText)
130+
return null
131+
}
132+
133+
throw new Error(errorText)
134+
}
110135
}
111136
}
112137

middleware/render-page.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ const pageCache = new RedisAccessor({
1717
prefix: (HEROKU_RELEASE_VERSION ? HEROKU_RELEASE_VERSION + ':' : '') + 'rp',
1818
// Allow for graceful failures if a Redis SET operation fails
1919
allowSetFailures: true,
20+
// Allow for graceful failures if a Redis GET operation fails
21+
allowGetFailures: true,
2022
name: 'page-cache'
2123
})
2224

script/rest/update-files.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ async function getDereferencedFiles () {
5959

6060
console.log(`\n🏃‍♀️🏃🏃‍♀️Running \`bin/openapi bundle\` in branch '${githubBranch}' of your github/github checkout to generate the dereferenced OpenAPI schema files.\n`)
6161
try {
62-
execSync(`${path.join(githubRepoDir, 'bin/openapi')} bundle -o ${tempDocsDir}`, { stdio: 'inherit' })
62+
execSync(`${path.join(githubRepoDir, 'bin/openapi')} bundle -o ${tempDocsDir} --include_unpublished`, { stdio: 'inherit' })
6363
} catch (error) {
6464
console.error(error)
6565
console.log('🛑 Whoops! It looks like the `bin/openapi bundle` command failed to run in your `github/github` repository checkout. To troubleshoot, ensure that your OpenAPI schema YAML is formatted correctly. A CI test runs on your `github/github` PR that flags malformed YAML. You can check the PR diff view for comments left by the openapi CI test to find and fix any formatting errors.')

tests/meta/repository-references.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,13 @@ describe('check for repository references', () => {
5757
'lib/excluded-links.js',
5858
'content/early-access',
5959
'data/early-access',
60-
'data/release-notes' // These include links to internal issues in Liquid comments
60+
'data/release-notes', // These include links to internal issues in Liquid comments.
61+
'**/*.png', // Do not check images or font files.
62+
'**/*.jpg', // We could just put all of assets/* here, but that would prevent any
63+
'**/*.gif', // READMEs or other text-based files from being checked.
64+
'**/*.pdf',
65+
'**/*.ico',
66+
'**/*.woff'
6167
]
6268
})
6369

tests/unit/redis-accessor.js

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,25 @@ describe('RedisAccessor', () => {
1111

1212
test('has expected instance properties', async () => {
1313
const instance = new RedisAccessor()
14-
expect(Object.keys(instance).sort()).toEqual(['_allowSetFailures', '_client', '_prefix'])
14+
expect(Object.keys(instance).sort()).toEqual(['_allowGetFailures', '_allowSetFailures', '_client', '_prefix'])
1515
})
1616

1717
test('has expected static methods', async () => {
1818
expect(typeof RedisAccessor.translateSetArguments).toBe('function')
1919
})
2020

21+
describe('#_allowGetFailures property', () => {
22+
test('defaults to false', async () => {
23+
const instance = new RedisAccessor()
24+
expect(instance._allowGetFailures).toBe(false)
25+
})
26+
27+
test('is expected value', async () => {
28+
const instance = new RedisAccessor({ allowGetFailures: true })
29+
expect(instance._allowGetFailures).toBe(true)
30+
})
31+
})
32+
2133
describe('#_allowSetFailures property', () => {
2234
test('defaults to false', async () => {
2335
const instance = new RedisAccessor()
@@ -299,5 +311,45 @@ Error: Redis ReplyError`
299311

300312
expect(callbackSpy).toHaveBeenCalledWith(null, 'myValue')
301313
})
314+
315+
test('resolves to null if Redis replies with an error and `allowGetFailures` option is set to true', async () => {
316+
// Temporarily override `console.error`
317+
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation()
318+
319+
const instance = new RedisAccessor({ prefix: 'myPrefix', allowGetFailures: true })
320+
instance._client.get = jest.fn((...args) => args.pop()(new Error('Redis ReplyError')))
321+
322+
const result = await instance.get('myKey', 'myValue')
323+
324+
expect(result).toBe(null)
325+
expect(consoleErrorSpy).toBeCalledWith(
326+
`Failed to get value from Redis.
327+
Key: myPrefix:myKey
328+
Error: Redis ReplyError`
329+
)
330+
331+
// Restore `console.error`
332+
consoleErrorSpy.mockRestore()
333+
})
334+
335+
test('rejects if Redis replies with an error and `allowGetFailures` option is not set to true', async () => {
336+
// Temporarily override `console.error`
337+
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation()
338+
339+
const instance = new RedisAccessor({ prefix: 'myPrefix' })
340+
instance._client.get = jest.fn((...args) => args.pop()(new Error('Redis ReplyError')))
341+
342+
await expect(instance.get('myKey')).rejects.toThrowError(
343+
new Error(`Failed to get value from Redis.
344+
Key: myPrefix:myKey
345+
Error: Redis ReplyError`
346+
)
347+
)
348+
349+
expect(consoleErrorSpy).not.toBeCalled()
350+
351+
// Restore `console.error`
352+
consoleErrorSpy.mockRestore()
353+
})
302354
})
303355
})

0 commit comments

Comments
 (0)