Do not subclass Bootstrap.ts

This commit is contained in:
Jerko Steiner 2019-08-31 15:29:37 +07:00
parent 5688a65cb0
commit dcc5b52725
5 changed files with 70 additions and 23 deletions

View File

@ -5,23 +5,60 @@ import { AddressInfo } from 'net'
import { Database } from '../database/Database'
import { IDatabase } from '../database/IDatabase'
import { loggerFactory, SqlLogger } from '../logger'
import { configureServer } from './configureServer'
import { ServerConfigurator } from './configureServer'
import { createServer } from './createServer'
import { IApplication } from './IApplication'
import { IBootstrap } from './IBootstrap'
import { IConfig } from './IConfig'
import { IServerConfig } from './IServerConfig'
export interface IBootstrapParams {
readonly config: IConfig
readonly configureServer: ServerConfigurator
readonly namespace?: Namespace
readonly exit?: (code: number) => void
readonly entities?: object
readonly migrations?: object
}
// tslint:disable-next-line
function getFunctions(obj: object): Function[] {
return Object.keys(obj)
.map(k => (obj as any)[k])
.filter(f => typeof f === 'function')
}
export class Bootstrap implements IBootstrap {
protected config: IConfig
protected configureServer: ServerConfigurator
protected namespace: Namespace
protected exit: (code: number) => void
protected server?: Server
protected inUse: boolean = false
readonly application: IApplication
readonly database: IDatabase
constructor(
private readonly config: IConfig,
protected readonly namespace: Namespace = createNamespace('application'),
protected readonly exit: (code: number) => void = process.exit,
) {
constructor(params: IBootstrapParams) {
this.config = {
...params.config,
app: {
...params.config.app,
db: {
...params.config.app.db,
entities: params.entities
? getFunctions(params.entities)
: params.config.app.db.entities,
migrations: params.migrations
? getFunctions(params.migrations)
: params.config.app.db.migrations,
},
},
}
this.configureServer = params.configureServer
this.namespace = params.namespace || createNamespace('application')
this.exit = params.exit || process.exit
this.database = this.createDatabase()
this.application = this.createApplication(this.database)
}
@ -31,12 +68,13 @@ export class Bootstrap implements IBootstrap {
}
protected createDatabase(): IDatabase {
const sqlLogger = new SqlLogger(
loggerFactory.getLogger('sql'), this.namespace)
return new Database(this.namespace, sqlLogger, this.getConfig().app.db)
const {namespace} = this
const sqlLogger = new SqlLogger(loggerFactory.getLogger('sql'), namespace)
return new Database(namespace, sqlLogger, this.getConfig().app.db)
}
protected createApplication(database: IDatabase): IApplication {
const {configureServer} = this
return createServer(configureServer(this.getConfig(), database))
}

View File

@ -14,14 +14,14 @@ import { TransactionalRouter } from '../router'
import { IRoutes, IContext } from '@rondo.dev/common'
import { Express } from 'express-serve-static-core'
export type AppConfigurator<
export type ServerConfigurator<
T extends IServerConfig = IServerConfig
> = (
config: IConfig,
database: IDatabase,
) => T
export const configureServer: AppConfigurator = (config, database) => {
export const configureServer: ServerConfigurator = (config, database) => {
const logger = loggerFactory.getLogger('api')

View File

@ -1,12 +1,8 @@
if (!process.env.LOG) {
process.env.LOG = 'api,sql:warn'
}
import {config} from './config'
import {Bootstrap} from './application/Bootstrap'
import {configureServer} from './application/configureServer'
export const bootstrap = new Bootstrap(config)
// FIXME determine a port by parsing app url from config
const port: string | number = process.env.PORT || 3000
bootstrap.listen(port)
export default new Bootstrap({
config,
configureServer,
})

View File

@ -1,3 +1,8 @@
if (require.main === module) {
if (!process.env.LOG) {
process.env.LOG = 'api,sql:warn'
}
}
export * from './application'
export * from './database'
export * from './entities'
@ -14,3 +19,9 @@ export * from './validator'
import * as rpc from './rpc'
export {rpc}
import bootstrap from './bootstrap'
if (require.main === module) {
bootstrap.exec(process.argv[2])
}

View File

@ -1,4 +1,5 @@
import {Bootstrap} from './application/Bootstrap'
import {configureServer} from './application/configureServer'
import {IAPIDef} from '@rondo.dev/common'
import {TestUtils} from './test-utils'
import {config} from './config'
@ -6,11 +7,12 @@ import {createNamespace} from 'cls-hooked'
export const exit = jest.fn()
export const bootstrap = new Bootstrap(
export const bootstrap = new Bootstrap({
config,
createNamespace('test'),
configureServer,
namespace: createNamespace('test'),
exit,
)
})
// TODO separate IAPIDef between projects
export const test = new TestUtils<IAPIDef>(bootstrap)