-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: expose date(time) types per database. Also enhance the datasour…
…ces capabilities
- Loading branch information
Showing
33 changed files
with
251 additions
and
105 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,79 +1,182 @@ | ||
import Debug from 'debug' | ||
import { DataSource } from 'typeorm' | ||
import { BaseDataSourceOptions } from 'typeorm/data-source/BaseDataSourceOptions' | ||
import { DataSourceOptions } from 'typeorm/data-source/DataSourceOptions' | ||
import {DataSource} from 'typeorm' | ||
import {BaseDataSourceOptions} from 'typeorm/data-source/BaseDataSourceOptions' | ||
|
||
const debug = Debug(`demo:databaseService`) | ||
import {DataSourceOptions} from 'typeorm/data-source/DataSourceOptions' | ||
import {DatabaseType} from "typeorm/driver/types/DatabaseType"; | ||
|
||
|
||
const debug = Debug(`sphereon:ssi-sdk:database`) | ||
|
||
export class DataSources { | ||
private dataSources = new Map<string, DataSource>() | ||
private configs | ||
get defaultDbType(): SupportedDatabaseType { | ||
return this._defaultDbType; | ||
} | ||
|
||
set defaultDbType(value: SupportedDatabaseType) { | ||
this._defaultDbType = value; | ||
} | ||
private dataSources = new Map<string, DataSource>() | ||
private configs: Map<string, DataSourceOptions> | ||
private _defaultDbType: SupportedDatabaseType = 'postgres' | ||
|
||
private static singleton: DataSources | ||
private static singleton: DataSources | ||
|
||
public static singleInstance() { | ||
if (!DataSources.singleton) { | ||
DataSources.singleton = new DataSources() | ||
public static singleInstance() { | ||
if (!DataSources.singleton) { | ||
DataSources.singleton = new DataSources() | ||
} | ||
return DataSources.singleton | ||
} | ||
return DataSources.singleton | ||
} | ||
|
||
public static newInstance(configs?: Map<string, DataSourceOptions>) { | ||
return new DataSources(configs) | ||
} | ||
public static newInstance(configs?: Map<string, DataSourceOptions>) { | ||
return new DataSources(configs) | ||
} | ||
|
||
private constructor(configs?: Map<string, DataSourceOptions>) { | ||
this.configs = configs ?? new Map<string, BaseDataSourceOptions>() | ||
} | ||
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) | ||
return this | ||
} | ||
} | ||
|
||
deleteConfig(dbName: string): this { | ||
this.configs.delete(dbName) | ||
return this | ||
} | ||
addConfig(dbName: string, config: DataSourceOptions): this { | ||
this.configs.set(dbName, config) | ||
// yes we are aware last one wins | ||
this._defaultDbType = config.type | ||
return this | ||
} | ||
|
||
getConfig(dbName: string): BaseDataSourceOptions { | ||
const config = this.configs.get(dbName) | ||
if (!config) { | ||
throw Error(`No DB config found for ${dbName}`) | ||
deleteConfig(dbName: string): this { | ||
this.configs.delete(dbName) | ||
return this | ||
} | ||
has(dbName: string) { | ||
return this.configs.has(dbName) && this.dataSources.has(dbName) | ||
} | ||
return config | ||
} | ||
|
||
public getDbNames(): string[] { | ||
return [...this.configs.keys()] | ||
} | ||
delete(dbName: string): this { | ||
this.deleteConfig(dbName) | ||
this.dataSources.delete(dbName) | ||
return this | ||
} | ||
|
||
async getDbConnection(dbName: string): Promise<DataSource> { | ||
const config = this.getConfig(dbName) | ||
/*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` | ||
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 | ||
} | ||
/*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 | ||
} | ||
} | ||
|
||
let dataSource = this.dataSources.get(dbName) | ||
if (dataSource) { | ||
return dataSource | ||
export type SupportedDatabaseType = Pick<DatabaseType, 'postgres' & 'sqlite'> | ||
export type DateTimeType = 'timestamp' | 'datetime' | ||
|
||
export type DateType = 'date' | ||
|
||
|
||
export const getDbType = (opts?: { defaultType: SupportedDatabaseType }): SupportedDatabaseType => { | ||
const type = (typeof process === 'object' ? process?.env?.DB_TYPE : undefined) ?? DataSources.singleInstance().defaultDbType ?? opts?.defaultType | ||
if (!type) { | ||
throw Error(`Could not determine DB type. Please set the DB_TYPE global variable or env var to one of 'postgres' or 'sqlite'`) | ||
} | ||
return type as SupportedDatabaseType | ||
} | ||
|
||
|
||
const typeOrmDateTime = (opts?: { defaultType: SupportedDatabaseType }): DateTimeType => { | ||
switch (getDbType(opts)) { | ||
case "postgres": | ||
return 'timestamp' | ||
case "sqlite": | ||
return 'datetime' | ||
default: | ||
throw Error(`DB type ${getDbType(opts)} not supported`) | ||
} | ||
} | ||
|
||
const typeormDate = (opts?: { defaultType: SupportedDatabaseType }): DateType => { | ||
// The same for both DBs | ||
return 'date' | ||
} | ||
export const TYPEORM_DATE_TIME_TYPE = typeOrmDateTime() | ||
|
||
|
||
export const TYPEORM_DATE_TYPE = typeormDate() | ||
|
||
|
||
/** | ||
* 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> => { | ||
return DataSources.singleInstance().addConfig(connectionName, opts?.config).getDbConnection(connectionName) | ||
} | ||
|
||
export const dropDatabase = async (dbName: string): Promise<void> => { | ||
if (!DataSources.singleInstance().has(dbName)) { | ||
return Promise.reject(Error(`No database present with name: ${dbName}`)); | ||
} | ||
|
||
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`, | ||
) | ||
const connection: DataSource = await getDbConnection(dbName); | ||
await connection.dropDatabase(); | ||
DataSources.singleInstance().delete(dbName); | ||
}; | ||
|
||
/** | ||
* Runs a migration down (drops DB schema) | ||
* @param dataSource | ||
*/ | ||
export const revertMigration = async (dataSource: DataSource): Promise<void> => { | ||
if (dataSource.isInitialized) { | ||
await dataSource.undoLastMigration() | ||
} 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`) | ||
console.error('DataSource is not initialized') | ||
} | ||
return dataSource | ||
} | ||
} | ||
export const resetDatabase = async (dbName: string): Promise<void> => { | ||
await dropDatabase(dbName); | ||
await getDbConnection(dbName); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.