UNPKG

@kitmi/data

Version:

Jacaranda Framework Data Access Model

219 lines (181 loc) 5.76 kB
import { URL } from 'node:url'; import { _, isEmpty } from '@kitmi/utils'; import { Types } from '@kitmi/validators/allSync'; export const FIELDS_OMIT_FROM_LOG = ['$assoc', '$data', '$ctx', '$where']; export function connectionObjectToString(obj, withoutCredential = false) { const { driver, user, password, host, database, ...options } = obj; let base = withoutCredential ? `${driver}://${host}/${database}` : `${driver}://${user}:${password}@${host}/${database}`; if (!isEmpty(options)) { base += '?' + new URLSearchParams(options).toString(); } return base; } export function connectionStringToObject(str, driver) { const url = new URL(str); const obj = { driver: driver ?? url.protocol.replace(':', ''), user: url.username, password: url.password, host: url.hostname, database: url.pathname.substring(1), }; url.searchParams.forEach((value, key) => { obj[key] = value; }); return obj; } /** * A database storage connector object. * @class */ class Connector { /** * Create a connector. * @param {App} app - The Jacaranda app object * @param {string} driver - Data storage type * @param {string} connectionString - The connection string or object * @param {object} [options] - Connector options */ constructor(app, driver, _connectionString, options) { const { connection: connectionString } = Types.OBJECT.sanitize( { connection: _connectionString }, { schema: { connection: [ { type: 'text' }, { type: 'object', schema: { driver: { type: 'text', optional: true }, user: { type: 'text' }, password: { type: 'text' }, host: { type: 'text' }, database: { type: 'text', optional: true }, port: { type: 'integer', optional: true }, }, }, ], }, } ); /** * The Jacaranda app object * @member {App} */ this.app = app; /** * The database storage type, e.g. mysql, mongodb * @member {string} */ this.driver = driver; /** * The default URL style connection string, e.g. mysql://username:password@host:port/dbname * @member {string} */ this.connectionString = connectionString; /** * Connector options * @member {object} */ this.options = options || {}; /** * Is the database a relational database * @member {boolean} */ this.relational = false; } /** * Make a new connection components from current connection string and given components. * @param {object} components * @property {string} [components.user] * @property {string} [components.password] * @property {string} [components.database] * @property {object} [components.options] */ makeNewConnectionString(components) { const url = new URL( typeof this.connectionString === 'string' ? this.connectionString : connectionObjectToString(this.connectionString) ); if ('user' in components) { url.username = components.user; } else if ('username' in components) { url.username = components.username; } if ('password' in components) { url.password = components.password; } if ('database' in components) { url.pathname = '/' + components.database; } else { url.pathname = '/' + this.database; } if ('options' in components) { const options = components.options; _.forOwn(options, (value, key) => { url.searchParams.set(key, typeof value === 'boolean' ? (value ? 1 : 0) : value); }); } return url.href; } /** * Get the connection without credential information, usually used for displaying. * @returns {string} */ getConnectionStringWithoutCredential(connStr) { const strOrObj = connStr || this.connectionString; if (typeof strOrObj === 'string') { const url = new URL(strOrObj); url.username = ''; url.password = ''; return url.href; } return connectionObjectToString(strOrObj, true); } /** * Database name. * @member {string} */ get database() { if (!this._database) { this._database = new URL(this.connectionString).pathname.substring(1); } return this._database; } /** * Database sub-collection. * @member {string} */ get collection() { // default as database for those 1 layer database, like mysql return this.database; } /** * Get the db name to be used in cross-db join. * @member {string} */ get crossJoinDbName() { return this.collection; } /** * Client library. * @member {object} */ get driverLib() { return this.constructor.driverLib; } /** * Log query. */ /* async connect_() {} async disconnect_() {} async ping_() {} async execute_() {} async end_() {} */ } export default Connector;