config-file-provider.ts•7.08 kB
import * as vscode from 'vscode';
import { existsSync } from 'fs';
import { SqliteEngine } from '../database-engines/sqlite-engine';
import { getConfigFileContent } from '../services/config-service';
import { brief } from '../services/string';
import { DatabaseEngine, DatabaseEngineProvider, EngineProviderCache, EngineProviderOption, MysqlConfig, PostgresConfig, SqliteConfig, MssqlConfig, ConfigFileConnectionTypes } from '../types';
import { MysqlEngine } from '../database-engines/mysql-engine';
import { getConnectionFor } from '../services/connector';
import { PostgresEngine } from '../database-engines/postgres-engine';
import { MssqlEngine } from '../database-engines/mssql-engine';
import { showErrorWithConfigFileButton } from '../services/config-error-service';
import { reportError } from '../services/initialization-error-service';
export const ConfigFileProvider: DatabaseEngineProvider = {
	name: 'Config File',
	type: 'sqlite',
	id: 'config-file-provider',
	description: 'Databases defined in your config file',
	engine: undefined,
	cache: undefined,
	async boot(): Promise<void> {
		this.cache = undefined
		this.engine = undefined
	},
	async canBeUsedInCurrentWorkspace(): Promise<boolean> {
		const configContent: (SqliteConfig | MysqlConfig | PostgresConfig | MssqlConfig)[] | undefined = await getConfigFileContent()
		if (!configContent) return false
		if (!configContent.length) return false
		if (!this.cache) this.cache = []
		for (const config of configContent) {
			try {
				await this.resolveConfiguration!(config)
			} catch (error) {
				reportError(String(error))
			}
		}
		return (this.cache ?? []).length > 0
	},
	async resolveConfiguration(config: SqliteConfig | MysqlConfig | PostgresConfig | MssqlConfig): Promise<boolean> {
		if (!this.cache) this.cache = []
		if (config.type === 'sqlite') {
			const connection = await sqliteConfigResolver(config)
			if (connection) this.cache.push(connection)
		}
		const requiresName = config.type === 'mysql' || config.type === 'mariadb' || config.type === 'postgres' || config.type === 'mssql'
		if (requiresName && !config.name) {
			return await reportNameError(config);
		}
		if (config.type === 'mysql') {
			const connection: EngineProviderCache | undefined = await mysqlConfigResolver(config)
			if (connection) this.cache?.push(connection)
		}
		if (config.type === 'postgres') {
			const connection: EngineProviderCache | undefined = await postgresConfigResolver(config)
			if (connection) this.cache?.push(connection)
		}
		if (config.type === 'mssql') {
			const connection: EngineProviderCache | undefined = await mssqlConfigResolver(config)
			if (connection) this.cache?.push(connection)
		}
		return true
	},
	reconnect(): Promise<boolean> {
		return this.canBeUsedInCurrentWorkspace()
	},
	async getDatabaseEngine(option: EngineProviderOption): Promise<DatabaseEngine | undefined> {
		if (option) {
			const matchedOption = Object.values(this.cache ?? {}).find((cache: { id: unknown }) => cache.id === option.option.id)
			if (!matchedOption) {
				await vscode.window.showErrorMessage(`Could not find option with id ${option.option.id}`)
				return
			}
			this.engine = matchedOption.engine
		}
		return this.engine
	}
}
async function reportNameError(config: MysqlConfig | PostgresConfig | MssqlConfig) {
	let typeName;
	switch (config.type) {
		case 'mysql':
			typeName = 'MySQL';
			break;
		case 'mariadb':
			typeName = 'MariaDB';
			break;
		case 'postgres':
			typeName = 'Postgres';
			break;
		case 'mssql':
			typeName = 'MSSQL';
			break;
	}
	await vscode.window.showErrorMessage(`The ${typeName} config file entry ${config.name || ''} does not have a name.`);
	return false;
}
async function mssqlConfigResolver(mssqlConfig: MssqlConfig): Promise<EngineProviderCache | undefined> {
	const connection = await getConnectionFor('Config file provider', 'mssql', mssqlConfig.host, mssqlConfig.port, mssqlConfig.username, mssqlConfig.password, mssqlConfig.database, false, mssqlConfig.options)
	if (!connection) return
	const engine: MssqlEngine = new MssqlEngine(connection)
	const isOkay = (await engine.isOkay())
	if (!isOkay || !engine.connection) {
		await showErrorWithConfigFileButton(
			`The MSSQL connection ${mssqlConfig.name || ''} specified in your config file is not valid.`,
			mssqlConfig
		);
		return
	}
	return {
		id: mssqlConfig.name,
		description: mssqlConfig.name,
		type: 'mssql',
		engine: engine
	}
}
async function sqliteConfigResolver(sqliteConnection: SqliteConfig): Promise<EngineProviderCache | undefined> {
	if (!existsSync(sqliteConnection.path)) {
		await showErrorWithConfigFileButton(
			`A path to an SQLite database file specified in your config file is not valid: ${sqliteConnection.path}`,
			sqliteConnection
		);
		return Promise.resolve(undefined);
	}
	const engine: SqliteEngine = new SqliteEngine(sqliteConnection.path)
	const isOkay = (await engine.isOkay())
	if (!isOkay) {
		await showErrorWithConfigFileButton(
			'The SQLite database specified in your config file is not valid.',
			sqliteConnection
		);
		return
	} else {
		return {
			id: sqliteConnection.path,
			details: sqliteConnection.path,
			description: brief(sqliteConnection.path),
			type: 'sqlite',
			engine: engine
		}
	}
}
async function mysqlConfigResolver(mysqlConfig: MysqlConfig): Promise<EngineProviderCache | undefined> {
	const connection = await getConnectionFor('Config file provider', 'mysql2', mysqlConfig.host, mysqlConfig.port, mysqlConfig.username, mysqlConfig.password, mysqlConfig.database, false)
	if (!connection) {
		await showErrorWithConfigFileButton(`The MySQL connection ${mysqlConfig.name || ''} specified in your config file is not valid.`, mysqlConfig);
		return
	}
	const engine: MysqlEngine = new MysqlEngine(connection)
	const isOkay = (await engine.isOkay())
	if (!isOkay || !engine.connection) {
		await showErrorWithConfigFileButton(`The MySQL connection ${mysqlConfig.name || ''} specified in your config file is not valid.`, mysqlConfig);
		return
	}
	return {
		id: mysqlConfig.name,
		description: mysqlConfig.name,
		type: 'mysql',
		engine: engine
	}
}
async function postgresConfigResolver(postgresConfig: PostgresConfig): Promise<EngineProviderCache | undefined> {
	const connection = await getConnectionFor('Config file provider', 'postgres', postgresConfig.host, postgresConfig.port, postgresConfig.username, postgresConfig.password, postgresConfig.database, false)
	if (!connection) {
		await showErrorWithConfigFileButton(`The Postgres connection ${postgresConfig.name || ''} specified in your config file is not valid.`, postgresConfig);
		return
	}
	const engine: PostgresEngine = new PostgresEngine(connection)
	const isOkay = (await engine.isOkay())
	if (!isOkay || !engine.connection) {
		await showErrorWithConfigFileButton(`The Postgres connection ${postgresConfig.name || ''} specified in your config file is not valid.`, postgresConfig);
		return
	}
	return {
		id: postgresConfig.name,
		description: postgresConfig.name,
		type: 'postgres',
		engine: engine
	}
}