Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for a connection string #140

Merged
merged 2 commits into from
Apr 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
4 changes: 2 additions & 2 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ declare namespace serverlessMysql {

export type ServerlessMysql = {
connect(wait?: number): Promise<void>
config(config?: MySQL.ConnectionConfig): MySQL.ConnectionConfig
config(config?: string | MySQL.ConnectionConfig): MySQL.ConnectionConfig
query<T>(...args): Promise<T>
end(): Promise<void>
escape(str: string): MySQL.EscapeFunctions
Expand All @@ -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
48 changes: 44 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
@@ -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:
Expand Down Expand Up @@ -50,11 +52,43 @@ 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))
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)
}


/********************************************************************/
Expand Down Expand Up @@ -348,8 +382,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
Expand Down Expand Up @@ -377,7 +410,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 = params
}

let escape = MYSQL.escape
// Set MySQL configs
config(connCfg)
Expand Down
55 changes: 55 additions & 0 deletions test/connection-config.spec.js
Original file line number Diff line number Diff line change
@@ -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());
});
});