From 98e880aba1242196aee9333b1a469dc4a2acbb57 Mon Sep 17 00:00:00 2001 From: Zak Henry Date: Tue, 2 Aug 2016 16:21:57 +0100 Subject: [PATCH] feat(app): Implement server side static file delivery with express.static --- package.json | 2 +- src/server/servers/abstract.server.spec.ts | 6 +- src/server/servers/abstract.server.ts | 18 +++++- src/server/servers/express.server.spec.ts | 29 ++++++++- src/server/servers/express.server.ts | 75 ++++++++++++++++++++-- src/server/servers/hapi.server.spec.ts | 13 ++++ src/server/servers/hapi.server.ts | 13 +++- 7 files changed, 144 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index c1ecf0c..380b122 100644 --- a/package.json +++ b/package.json @@ -77,4 +77,4 @@ "directories": { "doc": "docs" } -} \ No newline at end of file +} diff --git a/src/server/servers/abstract.server.spec.ts b/src/server/servers/abstract.server.spec.ts index 4f2bd3b..9442728 100644 --- a/src/server/servers/abstract.server.spec.ts +++ b/src/server/servers/abstract.server.spec.ts @@ -29,10 +29,14 @@ export class ServerMock extends Server { return this; } - public start(): Promise { + public startEngine(): Promise { return Promise.resolve(this); } + public registerStaticLoader(webroot?: string): this { + return this; + } + } describe('Server', () => { diff --git a/src/server/servers/abstract.server.ts b/src/server/servers/abstract.server.ts index 88e7b5c..3fd7756 100644 --- a/src/server/servers/abstract.server.ts +++ b/src/server/servers/abstract.server.ts @@ -76,10 +76,26 @@ export abstract class Server { */ protected abstract initialize(): this; + /** + * Kicks off the server using the specific underlying engine + */ + public abstract startEngine(): Promise; + + /** + * Register loader with engine to handle static loading of frontend assets + * @param webroot + */ + public abstract registerStaticLoader(webroot:string):this; + /** * Kicks off the server */ - public abstract start(): Promise; + public start(): Promise { + + this.registerStaticLoader(process.env.WEB_ROOT); + + return this.startEngine(); + }; /** * Retrieves the underlying engine for custom calls diff --git a/src/server/servers/express.server.spec.ts b/src/server/servers/express.server.spec.ts index 89a6eca..12d3de5 100644 --- a/src/server/servers/express.server.spec.ts +++ b/src/server/servers/express.server.spec.ts @@ -13,7 +13,7 @@ import { AuthService } from '../services/auth.service'; describe('Express Server', () => { const expressConstructorSpy = jasmine.createSpy('expressConstructor'); - const expressSpy = jasmine.createSpyObj('express', ['get']); + const expressSpy = jasmine.createSpyObj('express', ['get', 'use']); expressConstructorSpy.and.returnValue(expressSpy); @@ -47,6 +47,10 @@ describe('Express Server', () => { addProviders(providers); }); + afterEach(() => { + delete process.env.WEB_ROOT; + }); + it('initialized http server with new express instance', inject([Server], (server: Server) => { expect(expressConstructorSpy) @@ -175,6 +179,29 @@ describe('Express Server', () => { }))); + it('registers static file loader when started', async(inject([Server], (server: Server) => { + + process.env.WEB_ROOT = '/tmp/example'; + + expressConstructorSpy['static'] = jasmine.createSpy('express_static'); + + const startPromise = server.start() + .then((res) => { + + expect(res) + .toEqual(server); + + expect(expressConstructorSpy['static']) + .toHaveBeenCalledWith('/tmp/example', {index: ['index.html']}); + + }); + + const startedCallback = httpServerSpy.listen.calls.mostRecent().args[2]; + startedCallback(); //resolve the promise + return startPromise; + + }))); + }); diff --git a/src/server/servers/express.server.ts b/src/server/servers/express.server.ts index 5a8b8c3..616c6e9 100644 --- a/src/server/servers/express.server.ts +++ b/src/server/servers/express.server.ts @@ -12,10 +12,20 @@ import * as express from 'express'; import { Application, Request as ExpressRequest, Response as ExpressResponse } from 'express'; import * as http from 'http'; +// import { +// expressEngine, +// BASE_URL, +// REQUEST_URL, +// ORIGIN_URL, +// NODE_LOCATION_PROVIDERS, +// NODE_HTTP_PROVIDERS, +// ExpressEngineConfig +// } from 'angular2-universal'; + @Injectable() export class ExpressServer extends Server { - protected engine: Application; + protected app: Application; constructor(logger: Logger, remoteCli: RemoteCli) { super(logger, remoteCli); @@ -26,7 +36,7 @@ export class ExpressServer extends Server { * @returns {Application} */ public getEngine(): Application { - return this.engine; + return this.app; } /** @@ -34,8 +44,8 @@ export class ExpressServer extends Server { * @returns {Express} */ protected initialize() { - this.engine = express(); - this.httpServer = http.createServer((this.engine)); + this.app = express(); + this.httpServer = http.createServer((this.app)); return this; } @@ -47,7 +57,7 @@ export class ExpressServer extends Server { */ protected registerRouteWithEngine(routeConfig: RouteConfig): this { - this.engine[routeConfig.method.toLowerCase()](routeConfig.path, (req: ExpressRequest, res: ExpressResponse) => { + this.app[routeConfig.method.toLowerCase()](routeConfig.path, (req: ExpressRequest, res: ExpressResponse) => { let request = new Request(req, Request.extractMapFromDictionary(req.params), @@ -59,18 +69,69 @@ export class ExpressServer extends Server { .then((response: Response) => { return this.send(response, res); }) - .catch((err:Error) => this.sendErr(err, res)); + .catch((err: Error) => this.sendErr(err, res)); }); return this; } + /** + * @inheritDoc + */ + public registerStaticLoader(webroot?: string): this { + if (webroot) { + this.app.use(express.static(webroot, {index: ['index.html']})); + } + + //@todo resolve how to load webpacked modules with angular/universal + + // const ngApp = (req:ExpressRequest, res:ExpressResponse) => { + // let baseUrl = '/'; + // let url = req.originalUrl || '/'; + // + // let config: ExpressEngineConfig = { + // directives: frontendComponents,//[ App ], + // + // // dependencies shared among all requests to server + // platformProviders: [ + // {provide: ORIGIN_URL, useValue: this.getHost()}, + // {provide: BASE_URL, useValue: baseUrl}, + // ], + // + // // dependencies re-created for each request + // providers: [ + // {provide: REQUEST_URL, useValue: url}, + // // provideRouter(routes), + // NODE_LOCATION_PROVIDERS, + // NODE_HTTP_PROVIDERS, + // ], + // + // // if true, server will wait for all async to resolve before returning response + // async: true, + // + // // if you want preboot, you need to set selector for the app root + // // you can also include various preboot options here (explained in separate document) + // preboot: false // { appRoot: 'app' } + // }; + // + // res.render('index', config); + // }; + // + // this.app.engine('.html', expressEngine); + // this.app.set('views', process.env.WEB_ROOT); + // this.app.set('view engine', 'html'); + // this.app.use(express.static(process.env.WEB_ROOT, {index: false})); + // this.app.get('/', ngApp); + + return this; + } + /** * @inheritdoc * @returns {Promise} */ - public start(): Promise { + public startEngine(): Promise { return new Promise((resolve, reject) => { this.httpServer.listen(this.port, this.host, resolve); }) diff --git a/src/server/servers/hapi.server.spec.ts b/src/server/servers/hapi.server.spec.ts index 1abdef6..8fffb8a 100644 --- a/src/server/servers/hapi.server.spec.ts +++ b/src/server/servers/hapi.server.spec.ts @@ -38,6 +38,10 @@ describe('Hapi Server', () => { addProviders(providers); }); + afterEach(() => { + delete process.env.WEB_ROOT; + }); + it('initialized http server with new hapi instance', inject([Server], (server: Server) => { expect(hapiConstructorSpy) @@ -50,6 +54,15 @@ describe('Hapi Server', () => { })); + it('throws error when webroot is defined as static file listing is not yet implemented', inject([Server], (server: Server) => { + + process.env.WEB_ROOT = '/tmp/example'; + + const fixture = () => server.start(); + + expect(fixture).toThrowError('Static file listing is not implemented for hapi'); + })); + it('kicks off an http server when started', async(inject([Server], (server: Server) => { const startPromise = server.start() diff --git a/src/server/servers/hapi.server.ts b/src/server/servers/hapi.server.ts index 697c906..ddf4d68 100644 --- a/src/server/servers/hapi.server.ts +++ b/src/server/servers/hapi.server.ts @@ -9,6 +9,7 @@ import { RemoteCli } from '../services/remoteCli.service'; import { Logger } from '../../common/services/logger.service'; import { Response } from '../controllers/response'; import { Request } from '../controllers/request'; +import { NotImplementedException } from '../../common/exeptions/exceptions'; @Injectable() export class HapiServer extends Server { @@ -110,11 +111,21 @@ export class HapiServer extends Server { return res; } + /** + * @inheritdoc + */ + public registerStaticLoader(webroot?: string): this { + if (webroot) { + throw new NotImplementedException('Static file listing is not implemented for hapi'); + } + return this; + } + /** * @inheritdoc * @returns {Promise} */ - public start(): Promise { + public startEngine(): Promise { return new Promise((resolve, reject) => { this.engine.start((err) => {