зеркало из https://github.com/mozilla/fxa.git
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:
Коммит
e34c7b0fa3
|
@ -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',
|
||||
|
|
35
yarn.lock
35
yarn.lock
|
@ -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"
|
||||
|
|
Загрузка…
Ссылка в новой задаче