@goodware/mysql
Advanced tools
Comparing version 2.0.4 to 2.0.5
90
index.js
@@ -9,3 +9,3 @@ /* eslint-disable no-param-reassign */ | ||
/** | ||
/** | ||
* Testing | ||
@@ -17,17 +17,17 @@ * | ||
/** | ||
* To Do | ||
* | ||
* 1. In order for decimals to be returned as numbers instead of strings, add an option and add this code | ||
* conditionally. See | ||
* https://github.com/sidorares/node-mysql2/issues/795 | ||
* I tried this when creating a new connection and it didn't work: | ||
* typeCast: (field, next) => { | ||
* if (field.type === "DECIMAL") { | ||
* const value = field.string(); | ||
* return (value === null) ? null : Number(value); | ||
* } | ||
* return next(); | ||
* } | ||
*/ | ||
/** | ||
* To Do | ||
* | ||
* 1. In order for decimals to be returned as numbers instead of strings, add an option and add this code | ||
* conditionally. See | ||
* https://github.com/sidorares/node-mysql2/issues/795 | ||
* I tried this when creating a new connection and it didn't work: | ||
* typeCast: (field, next) => { | ||
* if (field.type === "DECIMAL") { | ||
* const value = field.string(); | ||
* return (value === null) ? null : Number(value); | ||
* } | ||
* return next(); | ||
* } | ||
*/ | ||
@@ -57,3 +57,3 @@ /** | ||
keepAliveInitialDelay: Joi.number().integer().min(0).default(10000), | ||
logger: Joi.object(), | ||
logger: Joi.alternatives(Joi.object(), Joi.function()), | ||
// eslint-disable-next-line quotes | ||
@@ -68,7 +68,3 @@ maxConnectDelay: Joi.number() | ||
multipleStatements: Joi.boolean(), | ||
password: Joi.string().when('useIAM', { | ||
is: true, | ||
then: Joi.optional(), | ||
otherwise: Joi.string().required(), | ||
}), | ||
password: Joi.string().allow(''), | ||
port: Joi.number().integer().default(3306), | ||
@@ -83,9 +79,28 @@ queueLimit: Joi.number().integer().min(0).default(0), | ||
useIAM: Joi.boolean(), | ||
user: Joi.string().required(), | ||
user: Joi.string().required().allow(''), | ||
usePool: Joi.boolean(), | ||
}); | ||
/** | ||
* @private | ||
* @description This tag is always logged | ||
*/ | ||
const logTag = 'mysql'; | ||
/** | ||
* @private | ||
* @description Logs a message | ||
* @param {Object | Function} logger | ||
* @param {string []} tags | ||
* @param {Object} message | ||
*/ | ||
function log(logger, tags, message) { | ||
if (typeof logger === 'object') { | ||
logger.log(tags, message); | ||
} else { | ||
logger(tags, message); | ||
} | ||
} | ||
/** | ||
* @description Creates mysql2-promise Connections, optionally from a pool. If a database connection can not be acquired | ||
@@ -110,4 +125,6 @@ * due to a timeout specified via the 'connectTimeout' options setting, the methods try again using exponential backoff | ||
* @param {Object} options Database connection options | ||
* @param {Object} [logger] | ||
*/ | ||
constructor(options) { | ||
constructor(options, logger) { | ||
if (logger && !options.logger) options = { ...options, logger }; | ||
const validation = optionsSchema.validate(options); | ||
@@ -188,9 +205,10 @@ if (validation.error) throw new Error(validation.error.message); | ||
* @description Runs a bogus SQL statement to check the database connection | ||
* @param {Object} [logger] | ||
* @return {Promise} Resolves to true or rejects in case of connection failure | ||
*/ | ||
connect() { | ||
connect(logger) { | ||
return this.connectForTask((connection) => { | ||
connection.query('SELECT 1'); | ||
return true; | ||
}); | ||
}, logger); | ||
} | ||
@@ -204,3 +222,3 @@ | ||
* @param {Object} [logger] | ||
* @return {Promise} The value returned by task(), if the database connection is successful | ||
* @return {Promise} Resolves to the value returned by task(), if the database connection is successful | ||
* @throws {Error} The error caught while: | ||
@@ -221,3 +239,3 @@ * 1. connecting to the database | ||
* @param {Object} [logger] | ||
* @return {Promise} The value returned by task(), if the database connection is successful | ||
* @return {Promise} Resolves to the value returned by task(), if the database connection is successful | ||
* @throws {Error} The error caught while: | ||
@@ -239,3 +257,3 @@ * 1. connecting to the database | ||
* @param {Boolean} useTransaction Defaults to true | ||
* @return {Promise} The value returned by task(), if the database connection is successful | ||
* @return {Promise} Resolves to the value returned by task(), if the database connection is successful | ||
* @throws {Error} The error caught while: | ||
@@ -296,3 +314,3 @@ * 1. connecting to the database | ||
if (logger) { | ||
logger.log(['warn', logTag], { | ||
log(logger, ['warn', logTag], { | ||
message: `Rollback transaction on '${this.connectOptions.host}'`, | ||
@@ -307,3 +325,3 @@ host: this.connectOptions.host, | ||
if (logger) { | ||
logger.log(['error', logTag], { | ||
log(logger, ['error', logTag], { | ||
message: `Rollback transaction failed on '${this.connectOptions.host}'`, | ||
@@ -319,3 +337,3 @@ host: this.connectOptions.host, | ||
if (logger) { | ||
logger.log(['error', logTag], { | ||
log(logger, ['error', logTag], { | ||
message: `Destroying connection failed on '${this.connectOptions.host}'`, | ||
@@ -337,3 +355,3 @@ host: this.connectOptions.host, | ||
if (logger) { | ||
logger.log(['error', logTag], { | ||
log(logger, ['error', logTag], { | ||
message: `Dead connection detected on '${this.connectOptions.host}'`, | ||
@@ -349,3 +367,3 @@ host: this.connectOptions.host, | ||
if (logger) { | ||
logger.log(['error', logTag], { | ||
log(logger, ['error', logTag], { | ||
message: `Destroying connection failed on '${this.connectOptions.host}'`, | ||
@@ -388,3 +406,3 @@ host: this.connectOptions.host, | ||
logger.log(['warn', logTag], { | ||
log(logger, ['warn', logTag], { | ||
message: `Waiting ${humanizeDuration(delayMs)} for '${user}@${host}:${port}/${database}' IAM: ${useIAM}`, | ||
@@ -412,3 +430,3 @@ host, | ||
if (logger) { | ||
logger.log(['error', logTag], { | ||
log(logger, ['error', logTag], { | ||
message: `Releasing connection failed on '${this.connectOptions.host}'`, | ||
@@ -415,0 +433,0 @@ host: this.connectOptions.host, |
{ | ||
"name": "@goodware/mysql", | ||
"version": "2.0.4", | ||
"version": "2.0.5", | ||
"description": "A mysql2-based connection helper", | ||
@@ -23,11 +23,9 @@ "keywords": [ | ||
"dependencies": { | ||
"aws-sdk": "^2.773.0", | ||
"docs": "^0.3.2-canary.0", | ||
"humanize-duration": "^3.24.0", | ||
"joi": "^17.2.1", | ||
"mysql2": "^2.2.5" | ||
"aws-sdk": "^2.0.0", | ||
"humanize-duration": "^3.0.0", | ||
"joi": "^17.0.0", | ||
"mysql2": "^2.0.0" | ||
}, | ||
"devDependencies": { | ||
"deepmerge": "^4.2.2", | ||
"dotenv": "^8.2.0", | ||
"docs": "^0.3.2-canary.0", | ||
"eslint": "^7.11.0", | ||
@@ -34,0 +32,0 @@ "eslint-config-airbnb-base": "^14.2.0", |
@@ -29,6 +29,29 @@ # @goodware/mysql: A mysql2-based connection helper | ||
- If usePool is true, "await stop()" must be called on the MySqlConnector object if you wish to release the connections | ||
in the pool. **Letting these objects merely go out of scope without stopping them will not close the connections.** | ||
in the pool. **Letting these objects go out of scope without stopping them will not close the connections.** | ||
# Example | ||
# Usage | ||
1. Create an instance of the class that is exported by this module | ||
2. Call exectue() or transaction(). These accept a function that accepts a mysql2 connection object. The provided functions usually call query() on the connection object. | ||
3. If you're using connection pooling, call stop() to close all connections. This is necessary if: | ||
a) The app instantiates multiple instances to access the same database server. It is recommended to use a single global instance to avoid this issue. | ||
b) The app hangs when exiting | ||
# Logger | ||
The options provided by the constructor and all other methods accept an optional 'logger' function or object. If an object is provided, it must have the method log(): | ||
```js | ||
interface Logger { | ||
/** | ||
* @param tags Typically includes a logging level name, like info or debug. | ||
* @param message An object with at least a message property | ||
*/ | ||
log(tags: string[] | string, message: Record<string, unknown>): void; | ||
} | ||
``` | ||
# Usage | ||
The following program outputs 'success' to the console. | ||
@@ -48,12 +71,12 @@ | ||
const connector = new mysql(config); | ||
const connector = new mysql(config, console.log); // The second parameter is a logger function | ||
async () => { | ||
await connector.execute( async () => { | ||
const [results] = await connection.query(`select 'success' AS column`); | ||
console.log(results[0]).column; | ||
const result = await connector.execute( async (connection) => { | ||
const [results] = await connection.query(`select 'success' AS status`); | ||
return results[0].status; | ||
}); | ||
console.log(result); | ||
await connector.stop(); | ||
}().then(console.info, console.error); | ||
``` |
@@ -1,50 +0,10 @@ | ||
const deepmerge = require('deepmerge'); | ||
const dotenv = require('dotenv'); | ||
const pack = require('../package.json'); | ||
// Read .env files into process.env | ||
const env = process.env.NODE_ENV || 'dev'; | ||
dotenv.config({ path: '.env' }); | ||
dotenv.config({ path: `.env-${env}` }); | ||
// Default configuration | ||
let config = { | ||
env, | ||
name: pack.name, | ||
version: pack.version, | ||
service: pack.name, | ||
module.exports = { | ||
host: process.env.DB_HOST || '0.0.0.0', | ||
port: process.env.DB_PORT || 3306, | ||
user: process.env.DB_USER || 'root', | ||
password: process.env.DB_PASSWORD || '', | ||
database: process.env.DB_DATABASE || 'mysql', | ||
region: process.env.DB_AWS_REGION || process.env.AWS_DEFAULT_REGION, | ||
ssl: process.env.DB_SSL, | ||
useIAM: process.env.DB_USE_IAM, | ||
}; | ||
// Extra configurations are named after NODE_ENV and merged into config | ||
// Providing .env-{NODE_ENV} file is preferable to editing this file | ||
const configs = {}; | ||
// Unit testing | ||
configs.dev = { | ||
db: { | ||
host: process.env.DB_HOST || '0.0.0.0', | ||
port: process.env.DB_PORT || 3306, | ||
user: process.env.DB_USER, | ||
password: process.env.DB_PASSWORD, | ||
database: process.env.DB_DATABASE, | ||
region: process.env.DB_AWS_REGION || process.env.AWS_DEFAULT_REGION, | ||
ssl: process.env.DB_SSL, | ||
useIAM: process.env.DB_USE_IAM, | ||
}, | ||
logging: { | ||
categories: { | ||
default: { | ||
console: 'silly', | ||
file: 'silly', | ||
errorFile: 'on', | ||
cloudWatch: 'off', | ||
}, | ||
}, | ||
}, | ||
}; | ||
if (env in configs) { | ||
config = deepmerge(config, configs[env], { arrayMerge: (destination, source) => [...destination, ...source] }); | ||
} | ||
module.exports = config; |
@@ -5,6 +5,24 @@ /* eslint-disable no-console */ | ||
function test() { | ||
return new MySqlConnector(config).connect(); | ||
class Logger { | ||
// eslint-disable-next-line class-methods-use-this | ||
log(tags, message, extra) { | ||
console.log({ tags, message, extra }); | ||
} | ||
} | ||
const logger = new Logger(); | ||
const test = async () => { | ||
config.host = '17.1.1.1'; | ||
const connector = new MySqlConnector(config, logger.log); | ||
// Test the connection | ||
await connector.connect(); | ||
const result = await connector.execute(async (connection) => { | ||
const [results] = await connection.query(`select 'success' AS status`); | ||
return results[0].status; | ||
}); | ||
if (result !== 'success') throw new Error('failed'); | ||
}; | ||
test().then(() => console.log('Successful'), console.error); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 10 instances in 1 package
1490294
4
8
81
0
1325
- Removeddocs@^0.3.2-canary.0
- Removeddocs@0.3.2-canary.0(transitive)
Updatedaws-sdk@^2.0.0
Updatedhumanize-duration@^3.0.0
Updatedjoi@^17.0.0
Updatedmysql2@^2.0.0