Merge pull request #5563 from mozilla/jh/5550/settings-server-config

feat(settings): start injecting server config into beta settings
This commit is contained in:
Jody Heavener 2020-06-10 11:08:22 -04:00 коммит произвёл GitHub
Родитель 07ce536e17 41fb60f637
Коммит e34c7b0fa3
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
7 изменённых файлов: 221 добавлений и 6 удалений

Просмотреть файл

@ -136,6 +136,8 @@
"@babel/cli": "7.7.4",
"@testing-library/react": "^8.0.4",
"@types/backbone": "^1.4.1",
"@types/sinon-chai": "3.2.4",
"@types/sinon-express-mock": "1.3.8",
"audit-filter": "^0.5.0",
"babel-eslint": "9.0.0",
"babel-plugin-dynamic-import-webpack": "1.0.2",
@ -163,6 +165,8 @@
"request": "2.88.0",
"request-promise": "4.2.0",
"sinon": "4.5.0",
"sinon-chai": "^3.5.0",
"sinon-express-mock": "^2.2.1",
"ts-node": "^8.10.1",
"upng-js": "2.1.0",
"url-loader": "4.1.0",

Просмотреть файл

@ -27,6 +27,7 @@ const https = require('https');
const path = require('path');
const serveStatic = require('serve-static');
const { settingsMiddleware } = require('../lib/beta-settings');
const config = require('../lib/configuration');
const sentry = require('../lib/sentry');
const { cors, routing } = require('fxa-shared/express')();
@ -89,11 +90,7 @@ function makeApp() {
writeToDisk: true,
})
);
const { createProxyMiddleware } = require('http-proxy-middleware');
app.use(
'/beta/settings',
createProxyMiddleware({ target: 'http://localhost:3000', ws: true })
);
app.use('/beta/settings', settingsMiddleware);
}
app.engine('html', consolidate.handlebars);

Просмотреть файл

@ -0,0 +1,68 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const { createProxyMiddleware } = require('http-proxy-middleware');
const config = require('./configuration');
// Inject Beta Settings meta content
function swapBetaMeta(html, metaContent = {}) {
let result = html;
Object.keys(metaContent).forEach((key) => {
result = result.replace(
key,
encodeURIComponent(JSON.stringify(metaContent[key]))
);
});
return result;
}
// Conditionally modify the response
function modifyResponse(proxyRes, req, res) {
const bodyChunks = [];
proxyRes.on('data', (chunk) => {
bodyChunks.push(chunk);
});
proxyRes.on('end', () => {
const body = Buffer.concat(bodyChunks);
// forward existing response data
res.status(proxyRes.statusCode);
Object.keys(proxyRes.headers).forEach((key) => {
res.append(key, proxyRes.headers[key]);
});
// if it's an html content type, inject server config
if (
proxyRes.headers['content-type'] &&
proxyRes.headers['content-type'].includes('text/html')
) {
let html = body.toString();
html = swapBetaMeta(html, {
__SERVER_CONFIG__: config,
});
res.send(new Buffer.from(html));
} else {
res.send(body);
}
res.end();
});
}
const settingsMiddleware = createProxyMiddleware({
target: 'http://localhost:3000',
ws: true,
selfHandleResponse: true, // ensure res.end is not called early
onProxyRes: modifyResponse,
});
module.exports = {
settingsMiddleware,
swapBetaMeta,
modifyResponse,
};

Просмотреть файл

@ -0,0 +1,98 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const { registerSuite } = intern.getInterface('object');
const { expect, use } = require('chai');
const { readFileSync } = require('fs');
const { resolve } = require('path');
const sinonChai = require('sinon-chai');
const { mockRes } = require('sinon-express-mock');
const config = require('../../server/lib/configuration');
const {
swapBetaMeta,
modifyResponse,
} = require('../../server/lib/beta-settings');
use(sinonChai);
const dummyHtml = readFileSync(
resolve(__dirname, './fixtures/server-config-index.html'),
{ encoding: 'utf8' }
);
const responseOptions = {
headers: {},
};
function mockedResponse(data = '', headers = {}) {
return mockRes(
Object.assign(responseOptions, {
headers,
statusCode: 200,
on(name, callback) {
const args = [];
if (name === 'data') {
args.push(new Buffer.from(data));
}
callback(...args);
},
})
);
}
registerSuite('beta settings', {
tests: {
'replaces beta meta string with config data': function () {
const config = { kenny: 'spenny' };
const encodedConfig = encodeURIComponent(JSON.stringify(config));
const result = swapBetaMeta(dummyHtml, {
__SERVER_CONFIG__: config,
});
expect(result).to.contain(encodedConfig);
},
'proxies the response': function () {
const headers = { 'x-foo': 'bar' };
const proxyingResponse = mockedResponse('', headers);
const proxiedResponse = mockRes(responseOptions);
modifyResponse(proxyingResponse, null, proxiedResponse);
proxyingResponse.send();
expect(proxiedResponse.send).to.be.called;
expect(proxiedResponse.end).to.be.called;
expect(proxiedResponse.statusCode).to.equal(proxyingResponse.statusCode);
expect(proxiedResponse.headers).to.equal(headers);
},
'modifies the response of html files': function () {
const proxyingResponse = mockedResponse(dummyHtml, {
'content-type': 'text/html',
});
const proxiedResponse = mockRes(responseOptions);
modifyResponse(proxyingResponse, null, proxiedResponse);
proxyingResponse.send(dummyHtml);
const outputHtml = swapBetaMeta(dummyHtml, {
__SERVER_CONFIG__: config,
});
expect(proxiedResponse.send).to.be.calledWith(
new Buffer.from(outputHtml)
);
},
'does not modify the response of non-html files': function () {
const data = JSON.stringify({ foo: 'bar' });
const proxyingResponse = mockedResponse(data, {
'content-type': 'application/json',
});
const proxiedResponse = mockRes(responseOptions);
modifyResponse(proxyingResponse, null, proxiedResponse);
proxyingResponse.send(data);
expect(proxiedResponse.send).to.be.calledWith(new Buffer.from(data));
},
},
});

Просмотреть файл

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<meta charset="utf-8" />
<title>Hello, world</title>
<meta name="fxa-config" content="__SERVER_CONFIG__" />
</head>
<body>
<div id="root"></div>
</body>
</html>

Просмотреть файл

@ -8,6 +8,7 @@ module.exports = [
'tests/server/ver.json.js',
'tests/server/amplitude.js',
'tests/server/amplitude-schema-validation.js',
'tests/server/beta-settings.js',
'tests/server/csp.js',
'tests/server/flow-event.js',
'tests/server/flow-metrics.js',

Просмотреть файл

@ -5929,7 +5929,7 @@ __metadata:
languageName: node
linkType: hard
"@types/sinon-chai@npm:^3.2.4":
"@types/sinon-chai@npm:3.2.4, @types/sinon-chai@npm:^3.2.4":
version: 3.2.4
resolution: "@types/sinon-chai@npm:3.2.4"
dependencies:
@ -5939,6 +5939,16 @@ __metadata:
languageName: node
linkType: hard
"@types/sinon-express-mock@npm:1.3.8":
version: 1.3.8
resolution: "@types/sinon-express-mock@npm:1.3.8"
dependencies:
"@types/express": "*"
"@types/sinon": "*"
checksum: 3/06ef01ba0081b2d5e6995457a599872b891e44a3141a9f17b3c7f2ccd914460747379e50984780b2114b2a4a25d88c6ecf3ef7df2ab9c225c742e83f0af8e2e6
languageName: node
linkType: hard
"@types/sinon@npm:*, @types/sinon@npm:^9.0.0":
version: 9.0.0
resolution: "@types/sinon@npm:9.0.0"
@ -17168,6 +17178,8 @@ fsevents@^1.2.7:
"@sentry/node": ^5.11.0
"@testing-library/react": ^8.0.4
"@types/backbone": ^1.4.1
"@types/sinon-chai": 3.2.4
"@types/sinon-express-mock": 1.3.8
asmcrypto.js: ^0.22.0
audit-filter: ^0.5.0
autoprefixer: 9.0.1
@ -17275,6 +17287,8 @@ fsevents@^1.2.7:
sass-loader: ^8.0.2
serve-static: 1.13.1
sinon: 4.5.0
sinon-chai: ^3.5.0
sinon-express-mock: ^2.2.1
source-map: ^0.7.3
source-map-loader: ^0.2.4
speed-trap: 0.0.10
@ -33332,6 +33346,25 @@ resolve@~1.11.1:
languageName: node
linkType: hard
"sinon-chai@npm:^3.5.0":
version: 3.5.0
resolution: "sinon-chai@npm:3.5.0"
peerDependencies:
chai: ^4.0.0
sinon: ">=4.0.0 <10.0.0"
checksum: 3/7be5aba73ed36111064069c191af35cf5540540ed11be9da80cccc7c2a457e6dd0864e216dc8a78c5ab8d7769d6c697bbcee4cedc887d67239dcb2b9f740d7c7
languageName: node
linkType: hard
"sinon-express-mock@npm:^2.2.1":
version: 2.2.1
resolution: "sinon-express-mock@npm:2.2.1"
peerDependencies:
sinon: "*"
checksum: 3/d10e5f527bd7092ae9848f2ce780a2168ae7bae8026e8d2cb4fee422d58bfc3515ba5898edcfe359385c47dea25f73155f572ae6c3fd379910e890f9d77e06b0
languageName: node
linkType: hard
"sinon@npm:4.5.0":
version: 4.5.0
resolution: "sinon@npm:4.5.0"