@sphereon/ssi-sdk.agent-config
Version:
160 lines (137 loc) • 5.12 kB
text/typescript
import Debug from 'debug'
import { DataSource } from 'typeorm'
import { BaseDataSourceOptions } from 'typeorm/data-source/BaseDataSourceOptions'
import { DataSourceOptions } from 'typeorm/data-source/DataSourceOptions'
const debug = Debug(`sphereon:ssi-sdk:database`)
export class DataSources {
get defaultDbType(): SupportedDatabaseType {
return this._defaultDbType
}
set defaultDbType(value: SupportedDatabaseType) {
this._defaultDbType = value
}
private dataSources = new Map<string, DataSource>()
private configs = new Map<string, DataSourceOptions>()
private _defaultDbType: SupportedDatabaseType = 'sqlite'
private static singleton: DataSources
public static singleInstance() {
if (!DataSources.singleton) {
DataSources.singleton = new DataSources()
}
return DataSources.singleton
}
public static newInstance(configs?: Map<string, DataSourceOptions>) {
return new DataSources(configs)
}
private constructor(configs?: Map<string, DataSourceOptions>) {
;(configs ?? new Map<string, DataSourceOptions>()).forEach((config, name) => this.addConfig(name, config))
}
addConfig(dbName: string, config: DataSourceOptions): this {
this.configs.set(dbName, config)
// yes we are aware last one wins
this._defaultDbType = config.type as SupportedDatabaseType
return this
}
deleteConfig(dbName: string): this {
this.configs.delete(dbName)
return this
}
has(dbName: string) {
return this.configs.has(dbName) && this.dataSources.has(dbName)
}
delete(dbName: string): this {
this.deleteConfig(dbName)
this.dataSources.delete(dbName)
return this
}
getConfig(dbName: string): BaseDataSourceOptions {
const config = this.configs.get(dbName)
if (!config) {
throw Error(`No DB config found for ${dbName}`)
}
return config
}
public getDbNames(): string[] {
return [...this.configs.keys()]
}
async getDbConnection(dbName: string): Promise<DataSource> {
const config = this.getConfig(dbName)
if (!this._defaultDbType) {
this._defaultDbType = config.type as SupportedDatabaseType
}
/*if (config.synchronize) {
return Promise.reject(
`WARNING: Automatic migrations need to be disabled in this app! Adjust the database configuration and set synchronize to false`
)
}*/
let dataSource = this.dataSources.get(dbName)
if (dataSource) {
return dataSource
}
dataSource = await new DataSource({ ...(config as DataSourceOptions), name: dbName }).initialize()
this.dataSources.set(dbName, dataSource)
if (config.synchronize) {
debug(`WARNING: Automatic migrations need to be disabled in this app! Adjust the database configuration and set synchronize to false`)
} else if (config.migrationsRun) {
debug(
`Migrations are currently managed from config. Please set migrationsRun and synchronize to false to get consistent behaviour. We run migrations from code explicitly`,
)
} else {
debug(`Running ${dataSource.migrations.length} migration(s) from code if needed...`)
await dataSource.runMigrations()
debug(`${dataSource.migrations.length} migration(s) from code were inspected and applied`)
}
return dataSource
}
}
export type SupportedDatabaseType = 'postgres' | 'sqlite' | 'react-native'
export type DateTimeType = 'timestamp' | 'datetime'
export type DateType = 'date'
/**
* Gets the database connection.
*
* Also makes sure that migrations are run (versioning for DB schema's), so we can properly update over time
*
* @param connectionName The database name
* @param opts
*/
export const getDbConnection = async (
connectionName: string,
opts?: {
config: BaseDataSourceOptions | any
},
): Promise<DataSource> => {
if (!DataSources.singleInstance().has(connectionName) && opts?.config) {
DataSources.singleInstance().addConfig(connectionName, opts?.config)
}
return DataSources.singleInstance().getDbConnection(connectionName)
}
export const dropDatabase = async (dbName: string, opts?: { removeDataSource?: boolean }): Promise<void> => {
const { removeDataSource = false } = { ...opts }
if (!DataSources.singleInstance().has(dbName)) {
return Promise.reject(Error(`No database present with name: ${dbName}`))
}
const connection: DataSource = await getDbConnection(dbName)
await connection.dropDatabase()
if (removeDataSource) {
DataSources.singleInstance().delete(dbName)
} else if (!connection.isInitialized) {
await connection.initialize()
}
}
/**
* Runs a migration down (drops DB schema)
* @param dataSource
*/
export const revertMigration = async (dataSource: DataSource): Promise<void> => {
if (dataSource.isInitialized) {
await dataSource.undoLastMigration()
} else {
console.error('DataSource is not initialized')
}
}
export const resetDatabase = async (dbName: string): Promise<void> => {
await dropDatabase(dbName)
const connection = await getDbConnection(dbName)
await connection.runMigrations()
}