Browse Source

feat: add emitErrors option

master
Kiko Beats 3 years ago
parent
commit
0c13a187b6
No known key found for this signature in database GPG Key ID: 8FA93B22CCF04B96
  1. 15
      packages/keyv-mongo/src/index.js
  2. 6
      packages/keyv-redis/src/index.js
  3. 116
      packages/keyv-sql/src/index.js
  4. 7
      packages/keyv/README.md
  5. 8
      packages/keyv/src/index.js
  6. 14
      packages/keyv/test/keyv.js

15
packages/keyv-mongo/src/index.js

@ -20,7 +20,8 @@ class KeyvMongo extends EventEmitter {
this.options = Object.assign( this.options = Object.assign(
{ {
url: 'mongodb://127.0.0.1:27017', url: 'mongodb://127.0.0.1:27017',
collection: 'keyv' collection: 'keyv',
emitErrors: true
}, },
url, url,
options options
@ -40,7 +41,7 @@ class KeyvMongo extends EventEmitter {
this.options.mongoOptions this.options.mongoOptions
) )
} catch (error) { } catch (error) {
this.emit('error', error) if (this.options.emitErrors) this.emit('error', error)
} }
this.mongo = {} this.mongo = {}
@ -76,13 +77,17 @@ class KeyvMongo extends EventEmitter {
} }
if (!listeningEvents) { if (!listeningEvents) {
this.client.on('error', error => this.emit('error', error)) if (this.options.emitErrors) {
listeningEvents = true this.client.on('error', error => this.emit('error', error))
listeningEvents = true
}
} }
resolve(this.store) resolve(this.store)
}) })
.catch(error => this.emit('error', error)) .catch(error => {
if (this.options.emitErrors) this.emit('error', error)
})
}) })
} }

6
packages/keyv-redis/src/index.js

@ -5,7 +5,7 @@ const pEvent = require('p-event')
const Redis = require('ioredis') const Redis = require('ioredis')
class KeyvRedis extends EventEmitter { class KeyvRedis extends EventEmitter {
constructor (uri, options) { constructor (uri, { emitErrors = true, ...options }) {
super() super()
if (uri instanceof Redis) { if (uri instanceof Redis) {
@ -19,7 +19,9 @@ class KeyvRedis extends EventEmitter {
this.redis = new Redis(options.uri, options) this.redis = new Redis(options.uri, options)
} }
this.redis.on('error', error => this.emit('error', error)) if (emitErrors) {
this.redis.on('error', error => this.emit('error', error))
}
} }
async get (key) { async get (key) {

116
packages/keyv-sql/src/index.js

@ -6,33 +6,45 @@ class KeyvSql extends EventEmitter {
super() super()
this.ttlSupport = false this.ttlSupport = false
this.options = Object.assign({ this.options = Object.assign(
table: 'keyv', {
keySize: 255, table: 'keyv',
iterationLimit: 10 keySize: 255,
}, options) iterationLimit: 10
},
const createTable = this.options.dialect === 'mysql' ? `CREATE TABLE IF NOT EXISTS \`${this.options.table}\` (\`key\` VARCHAR(${this.options.keySize}) PRIMARY KEY, \`value\` TEXT)` : `CREATE TABLE IF NOT EXISTS "${this.options.table}" ("key" VARCHAR(${this.options.keySize}) PRIMARY KEY, "value" TEXT)` options
)
const connected = this.options.connect()
const createTable =
this.options.dialect === 'mysql'
? `CREATE TABLE IF NOT EXISTS \`${this.options.table}\` (\`key\` VARCHAR(${this.options.keySize}) PRIMARY KEY, \`value\` TEXT)`
: `CREATE TABLE IF NOT EXISTS "${this.options.table}" ("key" VARCHAR(${this.options.keySize}) PRIMARY KEY, "value" TEXT)`
const connected = this.options
.connect()
.then(query => query(createTable).then(() => query)) .then(query => query(createTable).then(() => query))
.catch(error => this.emit('error', error)) .catch(error => {
if (this.options.emitErrors && typeof this.store.on === 'function') {
this.emit('error', error)
}
})
this.query = sqlString => connected this.query = sqlString => connected.then(query => query(sqlString))
.then(query => query(sqlString))
} }
get (key) { get (key) {
const select = this.options.dialect === 'mysql' ? `SELECT \`${this.options.table}\`.* FROM \`${this.options.table}\` WHERE (\`${this.options.table}\`.\`key\` = '${key}')` : `SELECT "${this.options.table}".* FROM "${this.options.table}" WHERE ("${this.options.table}"."key" = '${key}')` const select =
return this.query(select) this.options.dialect === 'mysql'
.then(rows => { ? `SELECT \`${this.options.table}\`.* FROM \`${this.options.table}\` WHERE (\`${this.options.table}\`.\`key\` = '${key}')`
const row = rows[0] : `SELECT "${this.options.table}".* FROM "${this.options.table}" WHERE ("${this.options.table}"."key" = '${key}')`
if (row === undefined) { return this.query(select).then(rows => {
return undefined const row = rows[0]
} if (row === undefined) {
return undefined
}
return row.value return row.value
}) })
} }
set (key, value) { set (key, value) {
@ -40,39 +52,61 @@ class KeyvSql extends EventEmitter {
value = value.replace(/\\/g, '\\\\') value = value.replace(/\\/g, '\\\\')
} }
const upsert = this.options.dialect === 'postgres' const upsert =
? `INSERT INTO "${this.options.table}" ("key", "value") VALUES ('${key}', '${value}') ON CONFLICT ("key") DO UPDATE SET "value" = EXCLUDED."value"` this.options.dialect === 'postgres'
: (this.options.dialect === 'mysql' ? `INSERT INTO "${this.options.table}" ("key", "value") VALUES ('${key}', '${value}') ON CONFLICT ("key") DO UPDATE SET "value" = EXCLUDED."value"`
? `REPLACE INTO \`${this.options.table}\` (\`key\`, \`value\`) VALUES ('${key}', '${value}')` : this.options.dialect === 'mysql'
: `REPLACE INTO "${this.options.table}" ("key", "value") VALUES ('${key}', '${value}')`) ? `REPLACE INTO \`${this.options.table}\` (\`key\`, \`value\`) VALUES ('${key}', '${value}')`
: `REPLACE INTO "${this.options.table}" ("key", "value") VALUES ('${key}', '${value}')`
return this.query(upsert) return this.query(upsert)
} }
delete (key) { delete (key) {
const select = this.options.dialect === 'mysql' ? `SELECT \`${this.options.table}\`.* FROM \`${this.options.table}\` WHERE (\`${this.options.table}\`.\`key\` = '${key}')` : `SELECT "${this.options.table}".* FROM "${this.options.table}" WHERE ("${this.options.table}"."key" = '${key}')` const select =
const del = this.options.dialect === 'mysql' ? `DELETE FROM \`${this.options.table}\` WHERE (\`${this.options.table}\`.\`key\` = '${key}')` : `DELETE FROM "${this.options.table}" WHERE ("${this.options.table}"."key" = '${key}')` this.options.dialect === 'mysql'
return this.query(select) ? `SELECT \`${this.options.table}\`.* FROM \`${this.options.table}\` WHERE (\`${this.options.table}\`.\`key\` = '${key}')`
.then(rows => { : `SELECT "${this.options.table}".* FROM "${this.options.table}" WHERE ("${this.options.table}"."key" = '${key}')`
const row = rows[0] const del =
if (row === undefined) { this.options.dialect === 'mysql'
return false ? `DELETE FROM \`${this.options.table}\` WHERE (\`${this.options.table}\`.\`key\` = '${key}')`
} : `DELETE FROM "${this.options.table}" WHERE ("${this.options.table}"."key" = '${key}')`
return this.query(select).then(rows => {
const row = rows[0]
if (row === undefined) {
return false
}
return this.query(del) return this.query(del).then(() => true)
.then(() => true) })
})
} }
clear () { clear () {
const del = this.options.dialect === 'mysql' ? `DELETE FROM \`${this.options.table}\` WHERE (\`${this.options.table}\`.\`key\` LIKE '${this.namespace ? this.namespace + ':' : ''}%')` : `DELETE FROM "${this.options.table}" WHERE ("${this.options.table}"."key" LIKE '${this.namespace ? this.namespace + ':' : ''}%')` const del =
return this.query(del) this.options.dialect === 'mysql'
.then(() => undefined) ? `DELETE FROM \`${this.options.table}\` WHERE (\`${
this.options.table
}\`.\`key\` LIKE '${this.namespace ? this.namespace + ':' : ''}%')`
: `DELETE FROM "${this.options.table}" WHERE ("${
this.options.table
}"."key" LIKE '${this.namespace ? this.namespace + ':' : ''}%')`
return this.query(del).then(() => undefined)
} }
async * iterator () { async * iterator () {
const limit = Number.parseInt(this.options.iterationLimit, 10) const limit = Number.parseInt(this.options.iterationLimit, 10)
const selectChunk = this.options.dialect === 'mysql' ? `SELECT * FROM \`${this.options.table}\` WHERE (\`${this.options.table}\`.\`key\` LIKE '${this.namespace ? this.namespace + ':' : ''}%') LIMIT ${limit} OFFSET ` : `SELECT * FROM "${this.options.table}" WHERE ("${this.options.table}"."key" LIKE '${this.namespace ? this.namespace + ':' : ''}%') LIMIT ${limit} OFFSET ` const selectChunk =
this.options.dialect === 'mysql'
? `SELECT * FROM \`${this.options.table}\` WHERE (\`${
this.options.table
}\`.\`key\` LIKE '${
this.namespace ? this.namespace + ':' : ''
}%') LIMIT ${limit} OFFSET `
: `SELECT * FROM "${this.options.table}" WHERE ("${
this.options.table
}"."key" LIKE '${
this.namespace ? this.namespace + ':' : ''
}%') LIMIT ${limit} OFFSET `
async function * iterate (offset, query) { async function * iterate (offset, query) {
const entries = await query(selectChunk + offset) const entries = await query(selectChunk + offset)

7
packages/keyv/README.md

@ -232,6 +232,13 @@ Default: `JSONB.parse`
A custom deserialization function. A custom deserialization function.
#### options.emitErrors
Type: `Boolean`<br>
Default: `true`
When it's `true`, errors on `options.store` will be emitted at keyv level.
#### options.store #### options.store
Type: `Storage adapter instance`<br> Type: `Storage adapter instance`<br>

8
packages/keyv/src/index.js

@ -4,22 +4,22 @@ const EventEmitter = require('events')
const JSONB = require('json-buffer') const JSONB = require('json-buffer')
class Keyv extends EventEmitter { class Keyv extends EventEmitter {
constructor (options) { constructor ({ emitErrors = true, ...options }) {
super() super()
this.options = Object.assign( this.options = Object.assign(
{ {
namespace: 'keyv', namespace: 'keyv',
serialize: JSONB.stringify, serialize: JSONB.stringify,
deserialize: JSONB.parse deserialize: JSONB.parse,
emitErrors: true
}, },
options options
) )
this.store = this.options.store || new Map() this.store = this.options.store || new Map()
this.store.namespace = this.options.namespace this.store.namespace = this.options.namespace
if (typeof this.store.on === 'function') { if (emitErrors) {
this.store.on('error', error => this.emit('error', error)) this.store.on('error', error => this.emit('error', error))
} }

14
packages/keyv/test/keyv.js

@ -157,5 +157,19 @@ test.serial('An empty namespace stores the key as-is', async t => {
t.is([...store.keys()][0], 42) t.is([...store.keys()][0], 42)
}) })
test('emit errors by default', async t => {
const store = new Keyv()
const keyv = new Keyv({ store, namespace: '' })
await keyv.set(42, 'foo')
t.is(store.listenerCount('error'), 1)
})
test('disable emit errors', async t => {
const store = new Keyv({ emitErrors: false })
const keyv = new Keyv({ store, emitErrors: false, namespace: '' })
await keyv.set(42, 'foo')
t.is(keyv.listenerCount('error'), 0)
})
const store = () => new Map() const store = () => new Map()
keyvTestSuite(test, Keyv, store) keyvTestSuite(test, Keyv, store)

Loading…
Cancel
Save