diff --git a/packages/keyv-mongo/src/index.js b/packages/keyv-mongo/src/index.js index ff808d2..5a1e63b 100644 --- a/packages/keyv-mongo/src/index.js +++ b/packages/keyv-mongo/src/index.js @@ -20,7 +20,8 @@ class KeyvMongo extends EventEmitter { this.options = Object.assign( { url: 'mongodb://127.0.0.1:27017', - collection: 'keyv' + collection: 'keyv', + emitErrors: true }, url, options @@ -40,7 +41,7 @@ class KeyvMongo extends EventEmitter { this.options.mongoOptions ) } catch (error) { - this.emit('error', error) + if (this.options.emitErrors) this.emit('error', error) } this.mongo = {} @@ -76,13 +77,17 @@ class KeyvMongo extends EventEmitter { } if (!listeningEvents) { - this.client.on('error', error => this.emit('error', error)) - listeningEvents = true + if (this.options.emitErrors) { + this.client.on('error', error => this.emit('error', error)) + listeningEvents = true + } } resolve(this.store) }) - .catch(error => this.emit('error', error)) + .catch(error => { + if (this.options.emitErrors) this.emit('error', error) + }) }) } diff --git a/packages/keyv-redis/src/index.js b/packages/keyv-redis/src/index.js index 0e09c8f..ae752f7 100644 --- a/packages/keyv-redis/src/index.js +++ b/packages/keyv-redis/src/index.js @@ -5,7 +5,7 @@ const pEvent = require('p-event') const Redis = require('ioredis') class KeyvRedis extends EventEmitter { - constructor (uri, options) { + constructor (uri, { emitErrors = true, ...options }) { super() if (uri instanceof Redis) { @@ -19,7 +19,9 @@ class KeyvRedis extends EventEmitter { 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) { diff --git a/packages/keyv-sql/src/index.js b/packages/keyv-sql/src/index.js index 0f13091..2020283 100644 --- a/packages/keyv-sql/src/index.js +++ b/packages/keyv-sql/src/index.js @@ -6,33 +6,45 @@ class KeyvSql extends EventEmitter { super() this.ttlSupport = false - this.options = Object.assign({ - table: 'keyv', - keySize: 255, - iterationLimit: 10 - }, options) - - 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() + this.options = Object.assign( + { + table: 'keyv', + keySize: 255, + iterationLimit: 10 + }, + options + ) + + 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)) - .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 - .then(query => query(sqlString)) + this.query = sqlString => connected.then(query => query(sqlString)) } 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}')` - return this.query(select) - .then(rows => { - const row = rows[0] - if (row === undefined) { - return undefined - } + 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}')` + return this.query(select).then(rows => { + const row = rows[0] + if (row === undefined) { + return undefined + } - return row.value - }) + return row.value + }) } set (key, value) { @@ -40,39 +52,61 @@ class KeyvSql extends EventEmitter { value = value.replace(/\\/g, '\\\\') } - const upsert = this.options.dialect === 'postgres' - ? `INSERT INTO "${this.options.table}" ("key", "value") VALUES ('${key}', '${value}') ON CONFLICT ("key") DO UPDATE SET "value" = EXCLUDED."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}')`) + const upsert = + this.options.dialect === 'postgres' + ? `INSERT INTO "${this.options.table}" ("key", "value") VALUES ('${key}', '${value}') ON CONFLICT ("key") DO UPDATE SET "value" = EXCLUDED."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}')` return this.query(upsert) } 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 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}')` - return this.query(select) - .then(rows => { - const row = rows[0] - if (row === undefined) { - return false - } + 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 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}')` + return this.query(select).then(rows => { + const row = rows[0] + if (row === undefined) { + return false + } - return this.query(del) - .then(() => true) - }) + return this.query(del).then(() => true) + }) } 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 + ':' : ''}%')` - return this.query(del) - .then(() => undefined) + 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 + ':' : ''}%')` + return this.query(del).then(() => undefined) } async * iterator () { 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) { const entries = await query(selectChunk + offset) diff --git a/packages/keyv/README.md b/packages/keyv/README.md index 6f57838..c46527c 100644 --- a/packages/keyv/README.md +++ b/packages/keyv/README.md @@ -232,6 +232,13 @@ Default: `JSONB.parse` A custom deserialization function. +#### options.emitErrors + +Type: `Boolean`
+Default: `true` + +When it's `true`, errors on `options.store` will be emitted at keyv level. + #### options.store Type: `Storage adapter instance`
diff --git a/packages/keyv/src/index.js b/packages/keyv/src/index.js index 0e69d72..f5956c2 100644 --- a/packages/keyv/src/index.js +++ b/packages/keyv/src/index.js @@ -4,22 +4,22 @@ const EventEmitter = require('events') const JSONB = require('json-buffer') class Keyv extends EventEmitter { - constructor (options) { + constructor ({ emitErrors = true, ...options }) { super() this.options = Object.assign( { namespace: 'keyv', serialize: JSONB.stringify, - deserialize: JSONB.parse + deserialize: JSONB.parse, + emitErrors: true }, options ) this.store = this.options.store || new Map() - this.store.namespace = this.options.namespace - if (typeof this.store.on === 'function') { + if (emitErrors) { this.store.on('error', error => this.emit('error', error)) } diff --git a/packages/keyv/test/keyv.js b/packages/keyv/test/keyv.js index ea66037..37f3843 100644 --- a/packages/keyv/test/keyv.js +++ b/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) }) +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() keyvTestSuite(test, Keyv, store)