1
0
mirror of synced 2025-12-23 03:44:00 -05:00
Files
docs/lib/redis-accessor.js
Kevin Heis 42e785b0a8 Migrate CommonJS to ESM (#20301)
* First run of script

* Get the app running --- ish

* Get NextJS working

* Remove `node:`

* Get more tests passing in unit directory

* Update FailBot test to use nock

* Update test.yml

* Update Dockerfile

* tests/content fixes

* Update page.js

* Update build-changelog.js

* updating tests/routing

* Update orphan-tests.js

* updating tests/rendering

* Update .eslintrc.js

* Update .eslintrc.js

* Install jest/globals

* "linting" tests

* staging update to server.mjs

* Change '.github/allowed-actions.js' to a ESM export

* Lint

* Fixes for the main package.json

* Move Jest to be last in the npm test command so we can pass args

* Just use 'npm run lint' in the npm test command

* update algolia label script

* update openapi script

* update require on openapi

* Update enterprise-algolia-label.js

* forgot JSON.parse

* Update lunr-search-index.js

* Always explicitly include process.cwd() for JSON file reads pathed from project root

* update graphql/update-files.js script

* Update other npm scripts using jest to pass ESM NODE_OPTIONS

* Update check-for-enterprise-issues-by-label.js for ESM

* Update create-enterprise-issue.js for ESM

* Import jest global for browser tests

* Convert 'script/deploy' to ESM

Co-authored-by: Grace Park <gracepark@github.com>
Co-authored-by: James M. Greene <jamesmgreene@github.com>
2021-07-14 13:49:18 -07:00

139 lines
3.6 KiB
JavaScript

import createRedisClient from './redis/create-client.js'
import InMemoryRedis from 'redis-mock'
import { promisify } from 'util'
const { CI, NODE_ENV, REDIS_URL } = process.env
// Do not use real a Redis client for CI, tests, or if the REDIS_URL is not provided
const useRealRedis = !CI && NODE_ENV !== 'test' && !!REDIS_URL
class RedisAccessor {
constructor ({
databaseNumber = 0,
prefix = null,
allowSetFailures = false,
allowGetFailures = false,
name = null
} = {}) {
const redisClient = useRealRedis
? createRedisClient({
url: REDIS_URL,
db: databaseNumber,
name: name || 'redis-accessor'
})
: InMemoryRedis.createClient()
this._client = redisClient
this._prefix = prefix ? prefix.replace(/:+$/, '') + ':' : ''
// Allow for graceful failures if a Redis SET operation fails?
this._allowSetFailures = allowSetFailures === true
// Allow for graceful failures if a Redis GET operation fails?
this._allowGetFailures = allowGetFailures === true
}
/** @private */
prefix (key) {
if (typeof key !== 'string' || !key) {
throw new TypeError(`Key must be a non-empty string but was: ${JSON.stringify(key)}`)
}
return this._prefix + key
}
static translateSetArguments (options = {}) {
const setArgs = []
const defaults = {
newOnly: false,
existingOnly: false,
expireIn: null, // No expiration
rollingExpiration: true
}
const opts = { ...defaults, ...options }
if (opts.newOnly === true) {
if (opts.existingOnly === true) {
throw new TypeError('Misconfiguration: entry cannot be both new and existing')
}
setArgs.push('NX')
} else if (opts.existingOnly === true) {
setArgs.push('XX')
}
if (Number.isFinite(opts.expireIn)) {
const ttl = Math.round(opts.expireIn)
if (ttl < 1) {
throw new TypeError('Misconfiguration: cannot set a TTL of less than 1 millisecond')
}
setArgs.push('PX')
setArgs.push(ttl)
}
// otherwise there is no expiration
if (opts.rollingExpiration === false) {
if (opts.newOnly === true) {
throw new TypeError('Misconfiguration: cannot keep an existing TTL on a new entry')
}
setArgs.push('KEEPTTL')
}
return setArgs
}
async set (key, value, options = {}) {
const setAsync = promisify(this._client.set).bind(this._client)
const fullKey = this.prefix(key)
if (typeof value !== 'string' || !value) {
throw new TypeError(`Value must be a non-empty string but was: ${JSON.stringify(value)}`)
}
// Handle optional arguments
const setArgs = this.constructor.translateSetArguments(options)
try {
const result = await setAsync(fullKey, value, ...setArgs)
return result === 'OK'
} catch (err) {
const errorText = `Failed to set value in Redis.
Key: ${fullKey}
Error: ${err.message}`
if (this._allowSetFailures === true) {
// Allow for graceful failure
console.error(errorText)
return false
}
throw new Error(errorText)
}
}
async get (key) {
const getAsync = promisify(this._client.get).bind(this._client)
const fullKey = this.prefix(key)
try {
const value = await getAsync(fullKey)
return value
} catch (err) {
const errorText = `Failed to get value from Redis.
Key: ${fullKey}
Error: ${err.message}`
if (this._allowGetFailures === true) {
// Allow for graceful failure
console.error(errorText)
return null
}
throw new Error(errorText)
}
}
}
export default RedisAccessor