From e27dc9c515176a3edde780c6d81e402c532585e3 Mon Sep 17 00:00:00 2001 From: Murali Rath Date: Fri, 29 Jul 2016 13:01:05 +1000 Subject: [PATCH] Support proxy to backend This commit adds support to proxy backend. Proxy confguration would support all the configuration mentioned at https://webpack.github.io/docs/webpack-dev-server.html#proxy except transforamtions done using functions. Configuration should be added to a json file and file name(relative to project root) should be passed as an argument to ng serve commnad e.g `ng serve --proxy-config proxy.config.json` --- addon/ng2/commands/serve.ts | 4 +- addon/ng2/tasks/serve-webpack.ts | 16 +++++- tests/e2e/e2e_workflow.spec.js | 86 ++++++++++++++++++++++++++++++-- 3 files changed, 100 insertions(+), 6 deletions(-) diff --git a/addon/ng2/commands/serve.ts b/addon/ng2/commands/serve.ts index 7e048aff2a39..08154b323686 100644 --- a/addon/ng2/commands/serve.ts +++ b/addon/ng2/commands/serve.ts @@ -14,6 +14,7 @@ const defaultPort = process.env.PORT || 4200; export interface ServeTaskOptions { port?: number; host?: string; + proxyConfig?: string; proxy?: string; insecureProxy?: boolean; watcher?: string; @@ -38,7 +39,8 @@ module.exports = Command.extend({ availableOptions: [ { name: 'port', type: Number, default: defaultPort, aliases: ['p'] }, { name: 'host', type: String, default: 'localhost', aliases: ['H'], description: 'Listens on all interfaces by default' }, - { name: 'proxy', type: String, aliases: ['pr', 'pxy'] }, + { name: 'proxy-config', type: 'Path', aliases: ['pc'] }, + { name: 'proxy', type: String, aliases: ['pr', 'pxy'] }, { name: 'insecure-proxy', type: Boolean, default: false, aliases: ['inspr'], description: 'Set false to proxy self-signed SSL certificates' }, { name: 'watcher', type: String, default: 'events', aliases: ['w'] }, { name: 'live-reload', type: Boolean, default: true, aliases: ['lr'] }, diff --git a/addon/ng2/tasks/serve-webpack.ts b/addon/ng2/tasks/serve-webpack.ts index 60589e48c011..553a68f68352 100644 --- a/addon/ng2/tasks/serve-webpack.ts +++ b/addon/ng2/tasks/serve-webpack.ts @@ -1,5 +1,7 @@ +import * as fs from 'fs'; import * as path from 'path'; import * as chalk from 'chalk'; +import * as SilentError from 'silent-error'; import * as Task from 'ember-cli/lib/models/task'; import * as webpack from 'webpack'; import * as WebpackDevServer from 'webpack-dev-server'; @@ -26,11 +28,23 @@ module.exports = Task.extend({ colors: true })); + let proxyConfig = {}; + if (commandOptions.proxyConfig) { + const proxyPath = path.resolve(this.project.root, commandOptions.proxyConfig); + if (fs.existsSync(proxyPath)) { + proxyConfig = require(proxyPath); + } else { + var message = 'Proxy config file ' + proxyPath + ' does not exist.'; + return Promise.reject(new SilentError(message)); + } + } + const webpackDevServerConfiguration: IWebpackDevServerConfigurationOptions = { contentBase: path.resolve(this.project.root, `./${CliConfig.fromProject().defaults.sourceDir}`), historyApiFallback: true, stats: webpackDevServerOutputOptions, - inline: true + inline: true, + proxy: proxyConfig }; const serveMessage:string = chalk.green(`\n*\n*\n NG Live Development Server is running on http://${commandOptions.host}:${commandOptions.port}.\n*\n*`); diff --git a/tests/e2e/e2e_workflow.spec.js b/tests/e2e/e2e_workflow.spec.js index 1aaa5159f3b7..87a0fbc81cd5 100644 --- a/tests/e2e/e2e_workflow.spec.js +++ b/tests/e2e/e2e_workflow.spec.js @@ -11,6 +11,9 @@ var treeKill = require('tree-kill'); var child_process = require('child_process'); var ng = require('../helpers/ng'); var root = path.join(process.cwd(), 'tmp'); +var express = require('express'); +var http = require('http'); +var request = require('request'); function existsSync(path) { try { @@ -369,8 +372,8 @@ describe('Basic end-to-end Workflow', function () { let lessFile = path.join(componentPath, lessFilename); let lessExample = '.outer {\n .inner { background: #fff; }\n }'; let componentContents = fs.readFileSync(componentFile, 'utf8'); - - sh.mv(cssFile, lessFile); + + sh.mv(cssFile, lessFile); fs.writeFileSync(lessFile, lessExample, 'utf8'); fs.writeFileSync(componentFile, componentContents.replace(new RegExp(cssFilename, 'g'), lessFilename), 'utf8'); @@ -396,8 +399,8 @@ describe('Basic end-to-end Workflow', function () { let stylusFile = path.join(componentPath, stylusFilename); let stylusExample = '.outer {\n .inner { background: #fff; }\n }'; let componentContents = fs.readFileSync(componentFile, 'utf8'); - - sh.mv(cssFile, stylusFile); + + sh.mv(cssFile, stylusFile); fs.writeFileSync(stylusFile, stylusExample, 'utf8'); fs.writeFileSync(componentFile, componentContents.replace(new RegExp(cssFilename, 'g'), stylusFilename), 'utf8'); @@ -540,6 +543,81 @@ describe('Basic end-to-end Workflow', function () { throw new Error(msg); }); }); + + it('Serve with proxy config', function () { + this.timeout(240000); + var ngServePid; + var server; + + function executor(resolve, reject) { + var startedProtractor = false; + var app = express(); + server = http.createServer(app); + server.listen(); + app.set('port', server.address().port); + + app.get('/api/test', function (req, res) { + res.send('TEST_API_RETURN'); + }); + var backendHost = 'localhost'; + var backendPort = server.address().port + + var proxyServerUrl = `http://${backendHost}:${backendPort}`; + const proxyConfigFile = path.join(process.cwd(), 'proxy.config.json'); + const proxyConfig = { + '/api/*': { + target: proxyServerUrl + } + }; + fs.writeFileSync(proxyConfigFile, JSON.stringify(proxyConfig, null, 2), 'utf8'); + var serveProcess = child_process.exec(`${ngBin} serve --proxy-config proxy.config.json`, { maxBuffer: 500 * 1024 }); + ngServePid = serveProcess.pid; + + serveProcess.stdout.on('data', (data) => { + if (/webpack: bundle is now VALID/.test(data.toString('utf-8')) && !startedProtractor) { + + // How to get the url with out hardcoding here? + request( 'http://localhost:4200/api/test', function(err, response, body) { + expect(response.statusCode).to.be.equal(200); + expect(body).to.be.equal('TEST_API_RETURN'); + resolve(); + }); + } + }); + + serveProcess.stderr.on('data', (data) => { + reject(data); + }); + serveProcess.on('close', (code) => { + code === 0 ? resolve() : reject('ng serve command closed with error') + }); + } + + // Need a way to close the express server + return new Promise(executor) + .then(() => { + if (ngServePid) treeKill(ngServePid); + if(server){ + server.close(); + } + }) + .catch((msg) => { + if (ngServePid) treeKill(ngServePid); + if(server){ + server.close(); + } + throw new Error(msg); + }); + }); + + it('Serve fails on invalid proxy config file', function (done) { + this.timeout(420000); + sh.exec(`${ngBin} serve --proxy-config proxy.config.does_not_exist.json`, (code) => { + expect(code).to.not.equal(0); + done(); + }); + }); + }); function isMobileTest() {