@absolunet/ioc2.1.0

View on GitHub

database/services/Connector/drivers/Driver.js

//--------------------------------------------------------
//-- Node IoC - Database - Connector - Driver
//--------------------------------------------------------

import __                  from '@absolunet/private-registry';
import NotImplementedError from '../../../../foundation/exceptions/NotImplementedError';

/* istanbul ignore next */
/**
 * Abstract database connector driver.
 *
 * @memberof database.services.Connector.drivers
 * @abstract
 * @hideconstructor
 */
class Driver {

	/**
	 * Class dependencies: <code>['config', 'db.resolver', 'file']</code>.
	 *
	 * @type {Array<string>}
	 */
	static get dependencies() {
		return ['config', 'db.resolver', 'file'];
	}

	/**
	 * Client to be used, such as "mysql", "sqlite3", etc.
	 *
	 * @type {string}
	 * @abstract
	 */
	get client() {
		throw new NotImplementedError(this, 'client', 'string', 'accessor');
	}

	/**
	 * @inheritdoc
	 * @private
	 */
	init() {
		__(this).set('connections', {});
	}

	/**
	 * Get Knex connection.
	 *
	 * @param {string} [name="default"] - The connection name.
	 * @returns {Knex} A Knex connection instance.
	 */
	getConnection(name = 'default') {
		if (!this.hasConnection(name)) {
			throw new TypeError(`The [${name}] connection does not exists.`);
		}

		return __(this).get('connections')[name];
	}

	/**
	 * Get default Knex connection.
	 *
	 * @returns {Knex} A Knex connection instance.
	 */
	getDefaultConnection() {
		return this.getConnection('default');
	}

	/**
	 * Set Knex connection with name.
	 *
	 * @param {string} name - The connection name.
	 * @param {Knex} connection - The Knex connection instance.
	 * @returns {translation.services.Translator.drivers.Driver} The current driver instance.
	 */
	setConnection(name, connection) {
		__(this).get('connections')[name] = connection;

		return this;
	}

	/**
	 * Set default Knex connection instance.
	 *
	 * @param {Knex} connection - The Knex connection instance.
	 * @returns {translation.services.Translator.drivers.Driver} The current driver instance.
	 */
	setDefaultConnection(connection) {
		this.setConnection('default', connection);

		return this;
	}

	/**
	 * Check if connection exists by name.
	 *
	 * @param {string} name - The connection name.
	 * @returns {boolean} Indicates that the connection exists.
	 */
	hasConnection(name) {
		return Boolean(__(this).get('connections')[name]);
	}

	/**
	 * Get connection if it exists, otherwise create a new connection and returns it.
	 *
	 * @param {string} name - The connection name.
	 * @param {object} config - The connection configuration.
	 * @returns {Knex} A Knex connection instance.
	 */
	getOrCreateConnection(name, config) {
		if (this.hasConnection(name)) {
			return this.getConnection(name);
		}

		return this.createConnection(name, config);
	}

	/**
	 * Create new connection and store it by name.
	 *
	 * @param {string} name - The connection name.
	 * @param {object} config - The connection configuration.
	 * @returns {Knex} A Knex connection instance.
	 */
	createConnection(name, config) {
		const connection = this.makeConnection(config);

		this.setConnection(name, connection);

		return connection;
	}

	/**
	 * Make new Knex connection.
	 *
	 * @param {object} config - The connection configuration.
	 * @returns {Knex} A Knex instance.
	 */
	makeConnection(config) {
		const connection = require('knex')(this.mapConfig(config)); // eslint-disable-line global-require

		__(connection).set('driver', this);

		return connection;
	}

	/**
	 * Map configuration from the driver and given configuration into a Knex configuratioon model.
	 *
	 * @param {object} config - The connection configuration.
	 * @returns {object} The full Knex connection configuration.
	 */
	mapConfig(config) {
		return {
			client: this.client,
			connection: config,
			migrations: {
				tableName: this.migrationsTable,
				directory: this.dbResolver.resolvePath('migrations')
			},
			seeds: {
				directory: this.dbResolver.resolvePath('seeds')
			}
		};
	}

	/**
	 * Clean the database of existing data.
	 * Keeps the tables.
	 *
	 * @see dropAll()
	 * @param {Knex} [connection] - The Knex connection instance.
	 * @param {object} [options] - The knex-clean options.
	 * @returns {Promise} The async process promise.
	 */
	async clean(connection = this.defaultConnection, options = {}) {
		const { migrationsTable } = this;

		await require('knex-cleaner').clean(connection, { // eslint-disable-line global-require
			...options,
			ignoreTables: [
				...options.ignoreTables || [],
				migrationsTable,
				`${migrationsTable}_lock`
			]
		});
	}

	/**
	 * Drop all tables from the database.
	 *
	 * @param {Knex} [connection] - The Knex connection instance.
	 * @param {object} [options] - The knex-clean options.
	 * @returns {Promise} The async process promise.
	 */
	async dropAll(connection, options = {}) {
		const tables = await require('knex-cleaner/lib/knex_tables').getTableNames(connection, options); // eslint-disable-line global-require
		await Promise.all(tables.map((tableName) => {
			return connection.schema.dropTableIfExists(tableName);
		}));
	}

	/**
	 * Get migration status.
	 *
	 * @param {Knex} connection - The Knex connection instance.
	 * @returns {Promise<Array<{name: string, ran: boolean}>>} The migration status.
	 */
	async migrationStatus(connection) {
		const [migrationsPath] = connection.migrate.generator.config.migrationSource.migrationsPaths;

		const allMigrations = this.file.scandir(migrationsPath);
		const data = await connection.select().from(this.migrationsTable);
		const ranMigrations = data.map(({ name }) => {
			return name;
		});

		return allMigrations.map((name) => {
			const ran = ranMigrations.includes(name);

			return { name, ran };
		});
	}

	/**
	 * The migration table name.
	 *
	 * @type {string}
	 */
	get migrationsTable() {
		return this.config.get('database.migrations_table', 'migrations');
	}

}


export default Driver;