From efa00ff1dca24f4b63181b042089d8a9410d1ef3 Mon Sep 17 00:00:00 2001 From: Zak Henry Date: Mon, 4 Jul 2016 12:38:19 +0100 Subject: [PATCH] fix(bootstrap): Major overhaul of bootstrapping behavior to allow external registered components to have correct injector providers --- src/server/bootstrap/bootstrap.spec.ts | 2 +- src/server/bootstrap/bootstrap.ts | 27 +++++++------ .../bootstrap/controllers.bootstrapper.ts | 8 ++-- src/server/bootstrap/entity.bootstrapper.ts | 38 ++++++++++++++++--- .../bootstrap/migrations.bootstrapper.ts | 8 ++-- src/server/bootstrap/models.bootstrapper.ts | 2 +- src/server/bootstrap/seeders.bootstrapper.ts | 10 ++--- src/server/bootstrap/services.bootstrapper.ts | 10 ++--- src/server/main.ts | 27 ++++++++++--- src/server/services/remoteCli.service.ts | 2 + 10 files changed, 91 insertions(+), 43 deletions(-) diff --git a/src/server/bootstrap/bootstrap.spec.ts b/src/server/bootstrap/bootstrap.spec.ts index 0b7e359..e6617e4 100644 --- a/src/server/bootstrap/bootstrap.spec.ts +++ b/src/server/bootstrap/bootstrap.spec.ts @@ -26,7 +26,7 @@ const providers: any[] = [ {provide: RemoteCli, useClass: RemoteCliMock}, ]; -describe('Bootstrap', () => { +fdescribe('Bootstrap', () => { beforeEachProviders(() => providers); diff --git a/src/server/bootstrap/bootstrap.ts b/src/server/bootstrap/bootstrap.ts index 6e13e0e..577fa13 100644 --- a/src/server/bootstrap/bootstrap.ts +++ b/src/server/bootstrap/bootstrap.ts @@ -4,7 +4,7 @@ import { ReflectiveInjector, Provider, Type, ResolvedReflectiveProvider } from ' import { Server } from '../servers/abstract.server'; import { AbstractController } from '../controllers/abstract.controller'; import { Logger, LogLevel } from '../../common/services/logger.service'; -import { coreInjector } from '../main'; +import { mainProviders, mainLoadClasses } from '../main'; import { registry } from '../../common/registry/entityRegistry'; import { ControllerBootstrapper } from './controllers.bootstrapper'; import { ModelBootstrapper } from './models.bootstrapper'; @@ -12,6 +12,7 @@ import { SeederBootstrapper } from './seeders.bootstrapper'; import { EntityBootstrapper } from './entity.bootstrapper'; import { MigrationBootstrapper } from './migrations.bootstrapper'; import { ServiceBootstrapper } from './services.bootstrapper'; +import { Database } from '../services/database.service'; export type ProviderType = Type | Provider | { [k: string]: any; @@ -62,6 +63,8 @@ function handleBootstrapError(e: Error, logger: Logger) { export function bootstrap(loadClasses: ClassDictionary[], providers: ProviderDefinition[] = [], afterBootstrap?: (bootstrap: BootstrapResponse)=>void): () => Promise { + mainLoadClasses.concat(loadClasses); + let logger: Logger; deferredLog('debug', registry); @@ -69,9 +72,11 @@ export function bootstrap(loadClasses: ClassDictionary[], providers: Provid deferredLog('info', 'Bootstrapping server'); - return Promise.all(providers) + return Promise.all(providers.concat(mainProviders)) .then((providers: ProviderType[]) => { + const resolvedProviders = ReflectiveInjector.resolve(providers); + //initialize all bootstrappers (in order they need to be created) const resolvedBootstrappers: EntityBootstrapper[] = [ new ModelBootstrapper, @@ -81,22 +86,15 @@ export function bootstrap(loadClasses: ClassDictionary[], providers: Provid new ControllerBootstrapper, ]; - // extract all of the resolved entities from the bootstrappers for registration with the - // injector const bootrapperProviders = resolvedBootstrappers.reduce((result: ResolvedReflectiveProvider[], bootstrapper: EntityBootstrapper) => { - return result.concat(bootstrapper.getResolvedEntities()); + return result.concat(bootstrapper.getResolvedProviders()); }, []); - // resolve all other user classes - const resolvedProviders: ResolvedReflectiveProvider[] = ReflectiveInjector - .resolve(providers) - .concat(bootrapperProviders) - ; + const mergedProviders = resolvedProviders.concat(bootrapperProviders); - // get an injector from the resolutions, using the core injector as parent - const injector = ReflectiveInjector.fromResolvedProviders(resolvedProviders, coreInjector); + // const registryInjector = ReflectiveInjector.fromResolvedProviders(bootrapperProviders); + const injector = ReflectiveInjector.fromResolvedProviders(mergedProviders); - // assign logger instance as soon as possible so the error handler might use it logger = injector.get(Logger) .source('bootstrap'); @@ -112,7 +110,8 @@ export function bootstrap(loadClasses: ClassDictionary[], providers: Provid return resolvedBootstrappers.reduce((current: Promise, next: EntityBootstrapper): Promise => { return current.then((): Promise => { - return Promise.resolve(next.invokeBootstrap(injector)); + + return Promise.resolve(next.setInjector(injector).invokeBootstrap()); }); }, Promise.resolve()) //initial value diff --git a/src/server/bootstrap/controllers.bootstrapper.ts b/src/server/bootstrap/controllers.bootstrapper.ts index 6f436d6..92203fa 100644 --- a/src/server/bootstrap/controllers.bootstrapper.ts +++ b/src/server/bootstrap/controllers.bootstrapper.ts @@ -1,9 +1,10 @@ import { ResolvedReflectiveProvider } from '@angular/core'; import { EntityBootstrapper } from './entity.bootstrapper'; +import { AbstractController } from '../controllers/abstract.controller'; export class ControllerBootstrapper extends EntityBootstrapper { - public getResolvedEntities(): ResolvedReflectiveProvider[] { + public getResolvedProviders(): ResolvedReflectiveProvider[] { return this.getResolvedFromRegistry('controller'); } @@ -11,8 +12,9 @@ export class ControllerBootstrapper extends EntityBootstrapper { this.resolvedEntityProviders.forEach((resolvedControllerProvider: ResolvedReflectiveProvider) => { this.logger.info(`initializing ${resolvedControllerProvider.key.displayName}`); - this.injector.instantiateResolved(resolvedControllerProvider) - .registerInjector(this.injector) + let controller = this.getInstance(resolvedControllerProvider); + + controller.registerInjector(this.injector) .registerRoutes(); }); diff --git a/src/server/bootstrap/entity.bootstrapper.ts b/src/server/bootstrap/entity.bootstrapper.ts index 68837dc..7e13089 100644 --- a/src/server/bootstrap/entity.bootstrapper.ts +++ b/src/server/bootstrap/entity.bootstrapper.ts @@ -1,6 +1,6 @@ import 'core-js'; import 'reflect-metadata'; -import { ReflectiveInjector, ResolvedReflectiveProvider } from '@angular/core'; +import { ReflectiveInjector, ResolvedReflectiveProvider, NoProviderError } from '@angular/core'; import { Logger } from '../../common/services/logger.service'; import { registry, EntityType, RegistryEntityStatic } from '../../common/registry/entityRegistry'; @@ -10,14 +10,42 @@ export abstract class EntityBootstrapper { protected injector: ReflectiveInjector; protected logger: Logger; - public abstract getResolvedEntities(): ResolvedReflectiveProvider[]; + public abstract getResolvedProviders(): ResolvedReflectiveProvider[]; - public invokeBootstrap(injector: ReflectiveInjector): void | Promise { - this.logger = injector.get(Logger).source(this.constructor.name); - this.injector = injector; + public invokeBootstrap(): void | Promise { + this.logger = this.injector.get(Logger) + .source(this.constructor.name); return this.bootstrap(); } + public setInjector(injector: ReflectiveInjector):this { + this.injector = injector; + return this; + } + + protected getInstance(resolvedInstanceProvider: ResolvedReflectiveProvider): T { + let instance: T; + try { + instance = this.injector.get(resolvedInstanceProvider.key.token); + } catch (e) { + if (!(e instanceof NoProviderError)) { + console.log('ERROR!', resolvedInstanceProvider.key.displayName, e); + throw e; + } + instance = this.injector.instantiateResolved(resolvedInstanceProvider); + } + + let logMessage = `Resolved ${instance.constructor.name}`; + + if (instance.constructor.name !== resolvedInstanceProvider.key.displayName) { + logMessage += ` as ${resolvedInstanceProvider.key.displayName}`; + } + + this.logger.info(logMessage); + + return instance; + } + protected abstract bootstrap(): void | Promise; protected getFromRegistry(type: EntityType): RegistryEntityStatic[] { diff --git a/src/server/bootstrap/migrations.bootstrapper.ts b/src/server/bootstrap/migrations.bootstrapper.ts index bca422f..125b2b7 100644 --- a/src/server/bootstrap/migrations.bootstrapper.ts +++ b/src/server/bootstrap/migrations.bootstrapper.ts @@ -4,7 +4,7 @@ import { AbstractMigration } from '../migrations/index'; export class MigrationBootstrapper extends EntityBootstrapper { - public getResolvedEntities(): ResolvedReflectiveProvider[] { + public getResolvedProviders(): ResolvedReflectiveProvider[] { return this.getResolvedFromRegistry('migration'); } @@ -12,10 +12,10 @@ export class MigrationBootstrapper extends EntityBootstrapper { this.logger.debug(`Running [${this.resolvedEntityProviders.length}] migrations`); - const allMigrationPromises = this.resolvedEntityProviders.map((resolvedControllerProvider: ResolvedReflectiveProvider) => { + const allMigrationPromises = this.resolvedEntityProviders.map((resolvedMigrationProvider: ResolvedReflectiveProvider) => { - this.logger.info(`migrating ${resolvedControllerProvider.key.displayName}`); - return (this.injector.instantiateResolved(resolvedControllerProvider) as AbstractMigration) + this.logger.info(`migrating ${resolvedMigrationProvider.key.displayName}`); + return this.getInstance(resolvedMigrationProvider) .migrate() .catch((error) => { if (error.code === 'ECONNREFUSED'){ diff --git a/src/server/bootstrap/models.bootstrapper.ts b/src/server/bootstrap/models.bootstrapper.ts index 723655b..7458caf 100644 --- a/src/server/bootstrap/models.bootstrapper.ts +++ b/src/server/bootstrap/models.bootstrapper.ts @@ -7,7 +7,7 @@ import { ModelMetadata } from '../../common/metadata/metadata'; export class ModelBootstrapper extends EntityBootstrapper { - public getResolvedEntities(): ResolvedReflectiveProvider[] { + public getResolvedProviders(): ResolvedReflectiveProvider[] { return []; } diff --git a/src/server/bootstrap/seeders.bootstrapper.ts b/src/server/bootstrap/seeders.bootstrapper.ts index 32a5a07..895e127 100644 --- a/src/server/bootstrap/seeders.bootstrapper.ts +++ b/src/server/bootstrap/seeders.bootstrapper.ts @@ -4,20 +4,20 @@ import { AbstractSeeder } from '../seeders/index'; export class SeederBootstrapper extends EntityBootstrapper { - public getResolvedEntities(): ResolvedReflectiveProvider[] { + public getResolvedProviders(): ResolvedReflectiveProvider[] { return this.getResolvedFromRegistry('seeder'); } public bootstrap(): Promise { // iterate seeders, to fill the db @todo change to register "unseeded" with the remote cli so // they can be executed on demand - const allSeederPromises = this.resolvedEntityProviders.map((resolvedControllerProvider: ResolvedReflectiveProvider) => { + const allSeederPromises = this.resolvedEntityProviders.map((resolvedSeederProvider: ResolvedReflectiveProvider) => { - this.logger.info(`seeding ${resolvedControllerProvider.key.displayName}`); - return (this.injector.instantiateResolved(resolvedControllerProvider) as AbstractSeeder).seed(); + this.logger.info(`seeding ${resolvedSeederProvider.key.displayName}`); + return this.getInstance(resolvedSeederProvider).seed(); }, []); - + return Promise.all(allSeederPromises); } diff --git a/src/server/bootstrap/services.bootstrapper.ts b/src/server/bootstrap/services.bootstrapper.ts index d6d82f9..130c292 100644 --- a/src/server/bootstrap/services.bootstrapper.ts +++ b/src/server/bootstrap/services.bootstrapper.ts @@ -4,7 +4,7 @@ import { AbstractService } from '../../common/services/service'; export class ServiceBootstrapper extends EntityBootstrapper { - public getResolvedEntities(): ResolvedReflectiveProvider[] { + public getResolvedProviders(): ResolvedReflectiveProvider[] { return this.getResolvedFromRegistry('service'); } @@ -12,11 +12,11 @@ export class ServiceBootstrapper extends EntityBootstrapper { this.logger.debug(`Initializing [${this.resolvedEntityProviders.length}] services`); - const allServicePromises = this.resolvedEntityProviders.map((resolvedControllerProvider: ResolvedReflectiveProvider) => { + const allServicePromises = this.resolvedEntityProviders.map((resolvedServiceProvider: ResolvedReflectiveProvider) => { - this.logger.info(`Initializing ${resolvedControllerProvider.key.displayName}`); - const service = (this.injector.instantiateResolved(resolvedControllerProvider) as AbstractService).initialize(); - return Promise.resolve(service); + let service = this.getInstance(resolvedServiceProvider); + + return Promise.resolve(service.initialize()); }, []); return Promise.all(allServicePromises); diff --git a/src/server/main.ts b/src/server/main.ts index d6cbb61..365ec80 100644 --- a/src/server/main.ts +++ b/src/server/main.ts @@ -2,7 +2,6 @@ import 'core-js'; import 'reflect-metadata'; import { ReflectiveInjector } from '@angular/core'; import { Server } from './servers/abstract.server'; -import { AbstractController } from './controllers/abstract.controller'; import { Database } from './services/database.service'; import { RemoteCli } from './services/remoteCli.service'; import { Logger } from '../common/services/logger.service'; @@ -12,6 +11,7 @@ import { ExpressServer } from './servers/express.server'; import * as dotenv from 'dotenv'; import * as path from 'path'; import * as _ from 'lodash'; +import { ProviderDefinition } from './bootstrap/bootstrap'; /** * Load .env variables into process.env.* @@ -22,17 +22,34 @@ dotenv.config({ process.env = _.mapKeys(process.env, (value: any, key: string) => key.replace(/^PUBLIC_/, '')); +export const mainLoadClasses:any[] = [ + Database, + RemoteCli +]; +// /** +// * The core injector is exported so implementations can pick up already registered injectables +// * without having to register them themselves. +// * @type {ReflectiveInjector} +// */ +// export const coreInjector: ReflectiveInjector = ReflectiveInjector.resolveAndCreate([ +// // Database, +// RemoteCli, +// DebugLogMiddleware, +// // {provide: Server, useClass: HapiServer}, +// {provide: Server, useClass: ExpressServer}, +// {provide: Logger, useClass: ConsoleLogger}, +// ]); + /** * The core injector is exported so implementations can pick up already registered injectables * without having to register them themselves. * @type {ReflectiveInjector} */ -export const coreInjector: ReflectiveInjector = ReflectiveInjector.resolveAndCreate([ - AbstractController, - Database, +export const mainProviders: ProviderDefinition[] = [ + // Database, RemoteCli, DebugLogMiddleware, // {provide: Server, useClass: HapiServer}, {provide: Server, useClass: ExpressServer}, {provide: Logger, useClass: ConsoleLogger}, -]); +]; diff --git a/src/server/services/remoteCli.service.ts b/src/server/services/remoteCli.service.ts index 881d0e4..35ee6b8 100644 --- a/src/server/services/remoteCli.service.ts +++ b/src/server/services/remoteCli.service.ts @@ -108,6 +108,8 @@ export class RemoteCli extends AbstractService { this.vantage.banner(displayBanner); + this.logger.debug('Remote cli initialized'); + return this.registerCommands(); }