parent
b6698ff89e
commit
1c905b1105
@ -1,5 +1,5 @@
|
|||||||
const mssql = require("mssql");
|
const mssql = require("mssql");
|
||||||
const UrlPattern = require("url-pattern");
|
const { ConnectionStringParser } = require("./utils");
|
||||||
|
|
||||||
class MSSQLConnector {
|
class MSSQLConnector {
|
||||||
#connected = false;
|
#connected = false;
|
||||||
@ -34,18 +34,17 @@ class MSSQLConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#parseDatabase() {
|
#parseDatabase() {
|
||||||
const connectionPattern = new UrlPattern(
|
const connectionParser = new ConnectionStringParser({ scheme: "mssql" });
|
||||||
"mssql\\://:username\\::password@*\\::port/:database*"
|
const parsed = connectionParser.parse(this.connectionString);
|
||||||
);
|
|
||||||
const match = connectionPattern.match(this.connectionString);
|
this.database_id = parsed?.endpoint;
|
||||||
this.database_id = match?.database;
|
|
||||||
this.connectionConfig = {
|
this.connectionConfig = {
|
||||||
...this.connectionConfig,
|
...this.connectionConfig,
|
||||||
user: match?.username,
|
user: parsed?.username,
|
||||||
password: match?.password,
|
password: parsed?.password,
|
||||||
database: match?.database,
|
database: parsed?.endpoint,
|
||||||
server: match?._[0],
|
server: parsed?.hosts[0]?.host,
|
||||||
port: match?.port ? Number(match.port) : null,
|
port: parsed?.hosts[0]?.port,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
const mysql = require("mysql2/promise");
|
const mysql = require("mysql2/promise");
|
||||||
const UrlPattern = require("url-pattern");
|
const { ConnectionStringParser } = require("./utils");
|
||||||
|
|
||||||
class MySQLConnector {
|
class MySQLConnector {
|
||||||
#connected = false;
|
#connected = false;
|
||||||
@ -15,9 +15,9 @@ class MySQLConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#parseDatabase() {
|
#parseDatabase() {
|
||||||
const connectionPattern = new UrlPattern("mysql\\://*@*/:database*");
|
const connectionParser = new ConnectionStringParser({ scheme: "mysql" });
|
||||||
const match = connectionPattern.match(this.connectionString);
|
const parsed = connectionParser.parse(this.connectionString);
|
||||||
return match?.database;
|
return parsed?.endpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
async connect() {
|
async connect() {
|
||||||
|
|||||||
@ -0,0 +1,182 @@
|
|||||||
|
// Credit: https://github.com/sindilevich/connection-string-parser
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} ConnectionStringParserOptions
|
||||||
|
* @property {'mssql' | 'mysql' | 'postgresql' | 'db'} [scheme] - The scheme of the connection string
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} ConnectionStringObject
|
||||||
|
* @property {string} scheme - The scheme of the connection string eg: mongodb, mssql, mysql, postgresql, etc.
|
||||||
|
* @property {string} username - The username of the connection string
|
||||||
|
* @property {string} password - The password of the connection string
|
||||||
|
* @property {{host: string, port: number}[]} hosts - The hosts of the connection string
|
||||||
|
* @property {string} endpoint - The endpoint (database name) of the connection string
|
||||||
|
* @property {Object} options - The options of the connection string
|
||||||
|
*/
|
||||||
|
class ConnectionStringParser {
|
||||||
|
static DEFAULT_SCHEME = "db";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ConnectionStringParserOptions} options
|
||||||
|
*/
|
||||||
|
constructor(options = {}) {
|
||||||
|
this.scheme =
|
||||||
|
(options && options.scheme) || ConnectionStringParser.DEFAULT_SCHEME;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a connection string object and returns a URI string of the form:
|
||||||
|
*
|
||||||
|
* scheme://[username[:password]@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[endpoint]][?options]
|
||||||
|
* @param {Object} connectionStringObject The object that describes connection string parameters
|
||||||
|
*/
|
||||||
|
format(connectionStringObject) {
|
||||||
|
if (!connectionStringObject) {
|
||||||
|
return this.scheme + "://localhost";
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
this.scheme &&
|
||||||
|
connectionStringObject.scheme &&
|
||||||
|
this.scheme !== connectionStringObject.scheme
|
||||||
|
) {
|
||||||
|
throw new Error(`Scheme not supported: ${connectionStringObject.scheme}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
let uri =
|
||||||
|
(this.scheme ||
|
||||||
|
connectionStringObject.scheme ||
|
||||||
|
ConnectionStringParser.DEFAULT_SCHEME) + "://";
|
||||||
|
|
||||||
|
if (connectionStringObject.username) {
|
||||||
|
uri += encodeURIComponent(connectionStringObject.username);
|
||||||
|
// Allow empty passwords
|
||||||
|
if (connectionStringObject.password) {
|
||||||
|
uri += ":" + encodeURIComponent(connectionStringObject.password);
|
||||||
|
}
|
||||||
|
uri += "@";
|
||||||
|
}
|
||||||
|
uri += this._formatAddress(connectionStringObject);
|
||||||
|
// Only put a slash when there is an endpoint
|
||||||
|
if (connectionStringObject.endpoint) {
|
||||||
|
uri += "/" + encodeURIComponent(connectionStringObject.endpoint);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
connectionStringObject.options &&
|
||||||
|
Object.keys(connectionStringObject.options).length > 0
|
||||||
|
) {
|
||||||
|
uri +=
|
||||||
|
"?" +
|
||||||
|
Object.keys(connectionStringObject.options)
|
||||||
|
.map(
|
||||||
|
(option) =>
|
||||||
|
encodeURIComponent(option) +
|
||||||
|
"=" +
|
||||||
|
encodeURIComponent(connectionStringObject.options[option])
|
||||||
|
)
|
||||||
|
.join("&");
|
||||||
|
}
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where scheme and hosts will always be present. Other fields will only be present in the result if they were
|
||||||
|
* present in the input.
|
||||||
|
* @param {string} uri The connection string URI
|
||||||
|
* @returns {ConnectionStringObject} The connection string object
|
||||||
|
*/
|
||||||
|
parse(uri) {
|
||||||
|
const connectionStringParser = new RegExp(
|
||||||
|
"^\\s*" + // Optional whitespace padding at the beginning of the line
|
||||||
|
"([^:]+)://" + // Scheme (Group 1)
|
||||||
|
"(?:([^:@,/?=&]+)(?::([^:@,/?=&]+))?@)?" + // User (Group 2) and Password (Group 3)
|
||||||
|
"([^@/?=&]+)" + // Host address(es) (Group 4)
|
||||||
|
"(?:/([^:@,/?=&]+)?)?" + // Endpoint (Group 5)
|
||||||
|
"(?:\\?([^:@,/?]+)?)?" + // Options (Group 6)
|
||||||
|
"\\s*$", // Optional whitespace padding at the end of the line
|
||||||
|
"gi"
|
||||||
|
);
|
||||||
|
const connectionStringObject = {};
|
||||||
|
|
||||||
|
if (!uri.includes("://")) {
|
||||||
|
throw new Error(`No scheme found in URI ${uri}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokens = connectionStringParser.exec(uri);
|
||||||
|
|
||||||
|
if (Array.isArray(tokens)) {
|
||||||
|
connectionStringObject.scheme = tokens[1];
|
||||||
|
if (this.scheme && this.scheme !== connectionStringObject.scheme) {
|
||||||
|
throw new Error(`URI must start with '${this.scheme}://'`);
|
||||||
|
}
|
||||||
|
connectionStringObject.username = tokens[2]
|
||||||
|
? decodeURIComponent(tokens[2])
|
||||||
|
: tokens[2];
|
||||||
|
connectionStringObject.password = tokens[3]
|
||||||
|
? decodeURIComponent(tokens[3])
|
||||||
|
: tokens[3];
|
||||||
|
connectionStringObject.hosts = this._parseAddress(tokens[4]);
|
||||||
|
connectionStringObject.endpoint = tokens[5]
|
||||||
|
? decodeURIComponent(tokens[5])
|
||||||
|
: tokens[5];
|
||||||
|
connectionStringObject.options = tokens[6]
|
||||||
|
? this._parseOptions(tokens[6])
|
||||||
|
: tokens[6];
|
||||||
|
}
|
||||||
|
return connectionStringObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats the address portion of a connection string
|
||||||
|
* @param {Object} connectionStringObject The object that describes connection string parameters
|
||||||
|
*/
|
||||||
|
_formatAddress(connectionStringObject) {
|
||||||
|
return connectionStringObject.hosts
|
||||||
|
.map(
|
||||||
|
(address) =>
|
||||||
|
encodeURIComponent(address.host) +
|
||||||
|
(address.port
|
||||||
|
? ":" + encodeURIComponent(address.port.toString(10))
|
||||||
|
: "")
|
||||||
|
)
|
||||||
|
.join(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses an address
|
||||||
|
* @param {string} addresses The address(es) to process
|
||||||
|
*/
|
||||||
|
_parseAddress(addresses) {
|
||||||
|
return addresses.split(",").map((address) => {
|
||||||
|
const i = address.indexOf(":");
|
||||||
|
|
||||||
|
return i >= 0
|
||||||
|
? {
|
||||||
|
host: decodeURIComponent(address.substring(0, i)),
|
||||||
|
port: +address.substring(i + 1),
|
||||||
|
}
|
||||||
|
: { host: decodeURIComponent(address) };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses options
|
||||||
|
* @param {string} options The options to process
|
||||||
|
*/
|
||||||
|
_parseOptions(options) {
|
||||||
|
const result = {};
|
||||||
|
|
||||||
|
options.split("&").forEach((option) => {
|
||||||
|
const i = option.indexOf("=");
|
||||||
|
|
||||||
|
if (i >= 0) {
|
||||||
|
result[decodeURIComponent(option.substring(0, i))] = decodeURIComponent(
|
||||||
|
option.substring(i + 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { ConnectionStringParser };
|
||||||
Loading…
Reference in New Issue
Block a user