From aa8d54f1db5eacbeae2b094966f1bf078dcebeae Mon Sep 17 00:00:00 2001 From: sromic Date: Sat, 24 Dec 2022 16:42:14 +0100 Subject: [PATCH 1/2] CHANGE - added support for a connection string --- README.md | 11 +++++++++++ index.d.ts | 2 +- index.js | 41 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 20632be..9b38ee7 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,17 @@ let value = connection.escape('Some value to be escaped') ``` ## Configuration Options + +There are two ways to provide a configuration. + +The one way is using a connection string at initialization time. + +```javascript +const mysql = require('serverless-mysql')(`mysql://${process.env.USERNAME}:${process.env.PASSWORD}@${process.env.ENDPOINT}:${process.env.PORT}/${process.env.DATABASE}`) +``` + +The other way is to pass in the options defined in the below table. + Below is a table containing all of the possible configuration options for `serverless-mysql`. Additional details are provided throughout the documentation. | Property | Type | Description | Default | diff --git a/index.d.ts b/index.d.ts index 5ce24c0..5fb0ba7 100644 --- a/index.d.ts +++ b/index.d.ts @@ -110,5 +110,5 @@ declare namespace serverlessMysql { } } -declare function serverlessMysql (cfg?: serverlessMysql.Config): serverlessMysql.ServerlessMysql +declare function serverlessMysql (cfg?: string | serverlessMysql.Config): serverlessMysql.ServerlessMysql export = serverlessMysql diff --git a/index.js b/index.js index 6bf1967..cf42398 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,7 @@ 'use strict' +const NodeURL = require('url') + /** * This module manages MySQL connections in serverless applications. * More detail regarding the MySQL module can be found here: @@ -55,6 +57,33 @@ module.exports = (params) => { const randRange = (min,max) => Math.floor(Math.random() * (max - min + 1)) + min const fullJitter = () => randRange(0, Math.min(cap, base * 2 ** retries)) const decorrelatedJitter = (sleep=0) => Math.min(cap, randRange(base, sleep * 3)) + const uriToConnectionConfig = (connectionString) => { + let uri = undefined + + try { + uri = new NodeURL.URL(connectionString) + } catch (error) { + throw new Error('Invalid data source URL provided') + } + + const extraFields = {} + + for (const [name, value] of uri.searchParams) { + extraFields[name] = value + } + + const database = uri.pathname && uri.pathname.startsWith('/') ? uri.pathname.slice(1) : undefined + + const connectionFields = { + host: uri.hostname ? uri.hostname : undefined, + user: uri.username ? uri.username : undefined, + port: uri.port ? Number(uri.port) : undefined, + password: uri.password ? uri.password : undefined, + database + } + + return Object.assign(connectionFields, extraFields) + } /********************************************************************/ @@ -348,8 +377,7 @@ module.exports = (params) => { /********************************************************************/ /** INITIALIZATION **/ /********************************************************************/ - - let cfg = typeof params === 'object' && !Array.isArray(params) ? params : {} + const cfg = typeof params === 'object' && !Array.isArray(params) ? params : {} MYSQL = cfg.library || require('mysql') PromiseLibrary = cfg.promise || Promise @@ -377,7 +405,14 @@ module.exports = (params) => { onKill = typeof cfg.onKill === 'function' ? cfg.onKill : () => {} onKillError = typeof cfg.onKillError === 'function' ? cfg.onKillError : () => {} - let connCfg = typeof cfg.config === 'object' && !Array.isArray(cfg.config) ? cfg.config : {} + let connCfg = {} + + if (typeof cfg.config === 'object' && !Array.isArray(cfg.config)) { + connCfg = cfg.config + } else if (typeof params === 'string') { + connCfg = uriToConnectionConfig(params) + } + let escape = MYSQL.escape // Set MySQL configs config(connCfg) From 57eee18f8aecf79a5e64fd25c5c9b6561c736f5d Mon Sep 17 00:00:00 2001 From: sromic Date: Wed, 18 Jan 2023 23:14:25 +0100 Subject: [PATCH 2/2] CHANGE - added tests and a little config refactored --- index.d.ts | 2 +- index.js | 9 ++++-- test/connection-config.spec.js | 55 ++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 test/connection-config.spec.js diff --git a/index.d.ts b/index.d.ts index 5fb0ba7..80f842c 100644 --- a/index.d.ts +++ b/index.d.ts @@ -97,7 +97,7 @@ declare namespace serverlessMysql { export type ServerlessMysql = { connect(wait?: number): Promise - config(config?: MySQL.ConnectionConfig): MySQL.ConnectionConfig + config(config?: string | MySQL.ConnectionConfig): MySQL.ConnectionConfig query(...args): Promise end(): Promise escape(str: string): MySQL.EscapeFunctions diff --git a/index.js b/index.js index cf42398..67341cc 100644 --- a/index.js +++ b/index.js @@ -52,7 +52,12 @@ module.exports = (params) => { const resetRetries = () => retries = 0 const getErrorCount = () => errors const getConfig = () => _cfg - const config = (args) => Object.assign(_cfg,args) + const config = (args) => { + if (typeof args === 'string') { + return Object.assign(_cfg,uriToConnectionConfig(args)) + } + return Object.assign(_cfg,args) + } const delay = ms => new PromiseLibrary(res => setTimeout(res, ms)) const randRange = (min,max) => Math.floor(Math.random() * (max - min + 1)) + min const fullJitter = () => randRange(0, Math.min(cap, base * 2 ** retries)) @@ -410,7 +415,7 @@ module.exports = (params) => { if (typeof cfg.config === 'object' && !Array.isArray(cfg.config)) { connCfg = cfg.config } else if (typeof params === 'string') { - connCfg = uriToConnectionConfig(params) + connCfg = params } let escape = MYSQL.escape diff --git a/test/connection-config.spec.js b/test/connection-config.spec.js new file mode 100644 index 0000000..e7c950b --- /dev/null +++ b/test/connection-config.spec.js @@ -0,0 +1,55 @@ +const assert = require("assert"); + +const mysql = require("../index"); + +describe("Test a connection config", () => { + it("Should get a valid connection config when a config is passed in", () => { + const configs = [ + mysql({ + config: { + host: "localhost", + database: "database", + user: "user", + password: "password", + }, + }).getConfig(), + mysql().config({ + host: "localhost", + database: "database", + user: "user", + password: "password", + }), + ]; + + configs.forEach((config) => { + assert.deepEqual(config, { + host: "localhost", + database: "database", + user: "user", + password: "password", + }); + }); + }); + + it("Should get a valid connection config when a connection string is passed in", () => { + const configs = [ + mysql("mysql://user:password@localhost:3306/database").getConfig(), + mysql().config("mysql://user:password@localhost:3306/database"), + ]; + + configs.forEach((config) => { + assert.deepEqual(config, { + host: "localhost", + database: "database", + user: "user", + password: "password", + port: "3306", + }); + }); + }); + + it("Should throw an exception when an invalid connection string is passed in", () => { + assert.throws(() => mysql("mysql://:3306/database").getConfig()); + assert.throws(() => mysql("mysql://:3306").getConfig()); + }); +});