Ottomata has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/339706 )
Change subject: Merge from upstream template/master at a354b0e8bbf88123a6c2fe17693435fb1107b794 ...................................................................... Merge from upstream template/master at a354b0e8bbf88123a6c2fe17693435fb1107b794 Conflicts: lib/api-util.js package.json routes/ex.js routes/root.js routes/v1.js Change-Id: Ia0cc779e5292bde0ffe287b8bc9c901462c5852a --- A lib/swagger-ui.js M package.json M routes/root.js M spec.template.yaml 4 files changed, 101 insertions(+), 5 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/services/eventstreams refs/changes/06/339706/1 diff --git a/lib/swagger-ui.js b/lib/swagger-ui.js new file mode 100644 index 0000000..a4e41c2 --- /dev/null +++ b/lib/swagger-ui.js @@ -0,0 +1,79 @@ +'use strict'; + + +const BBPromise = require('bluebird'); +const fs = BBPromise.promisifyAll(require('fs')); +const path = require('path'); +const HTTPError = require('../lib/util.js').HTTPError; + + +// Swagger-ui helpfully exporting the absolute path of its dist directory +const docRoot = `${require('swagger-ui').dist}/`; + +function processRequest(app, req, res) { + + const reqPath = req.query.path || '/index.html'; + const filePath = path.join(docRoot, reqPath); + + // Disallow relative paths. + // Test relies on docRoot ending on a slash. + if (filePath.substring(0, docRoot.length) !== docRoot) { + throw new HTTPError({ + status: 404, + type: 'not_found', + title: 'File not found', + detail: `${reqPath} could not be found.` + }); + } + + return fs.readFileAsync(filePath) + .then((body) => { + if (reqPath === '/index.html') { + body = body.toString() + .replace(/((?:src|href)=['"])/g, '$1?doc&path=') + // Some self-promotion + .replace(/<a id="logo".*?<\/a>/, + `<a id="logo" href="${app.info.homepage}">${app.info.name}</a>`) + .replace(/<title>[^<]*<\/title>/, `<title>${app.info.name}</title>`) + // Replace the default url with ours, switch off validation & + // limit the size of documents to apply syntax highlighting to + .replace(/Sorter: "alpha"/, 'Sorter: "alpha", validatorUrl: null, ' + + 'highlightSizeThreshold: 10000') + .replace(/docExpansion: "none"/, 'docExpansion: "list"') + .replace(/ url: url,/, 'url: "/?spec",'); + } + + let contentType = 'text/html'; + if (/\.js$/.test(reqPath)) { + contentType = 'text/javascript'; + body = body.toString() + .replace(/underscore-min\.map/, '?doc&path=lib/underscore-min.map'); + } else if (/\.png$/.test(reqPath)) { + contentType = 'image/png'; + } else if (/\.map$/.test(reqPath)) { + contentType = 'application/json'; + } else if (/\.ttf$/.test(reqPath)) { + contentType = 'application/x-font-ttf'; + } else if (/\.css$/.test(reqPath)) { + contentType = 'text/css'; + body = body.toString().replace(/\.\.\/(images|fonts)\//g, '?doc&path=$1/'); + } + + res.setHeader('Content-Type', contentType); + res.setHeader('content-security-policy', "default-src 'none'; " + + "script-src 'self' 'unsafe-inline'; connect-src *; " + + "style-src 'self' 'unsafe-inline'; img-src 'self'; font-src 'self';"); + res.send(body.toString()); + }) + .catch({ code: 'ENOENT' }, () => { + res.status(404) + .type('not_found') + .send('not found'); + }); + +} + +module.exports = { + processRequest +}; + diff --git a/package.json b/package.json index 4e1bd94..bf74eab 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,20 @@ { +<<<<<<< HEAD "name": "eventstreams", "version": "0.0.2", "description": "Streaming Wikimedia Events via HTTP SSE", +======= + "name": "service-template-node", + "version": "0.5.0", + "description": "A blueprint for MediaWiki REST API services", +>>>>>>> tmpl/master "main": "./app.js", "scripts": { "start": "service-runner", - "test": "mocha && nsp check", + "test": "PREQ_CONNECT_TIMEOUT=15 mocha && nsp check", "docker-start": "service-runner docker-start", "docker-test": "service-runner docker-test", + "test-build": "service-runner docker-test && service-runner build --deploy-repo --force", "coverage": "istanbul cover _mocha -- -R spec" }, "repository": { @@ -42,6 +49,7 @@ "js-yaml": "^3.7.0", "service-runner": "^2.1.11", "swagger-router": "^0.5.5", + "swagger-ui": "git+https://github.com/wikimedia/swagger-ui#master", "node-rdkafka-statsd": "^0.1.0", "kafka-sse": "git+https://phabricator.wikimedia.org/diffusion/WKSE/kafkasse.git#v0.0.6" }, diff --git a/routes/root.js b/routes/root.js index 28930cb..42686eb 100644 --- a/routes/root.js +++ b/routes/root.js @@ -2,6 +2,7 @@ const sUtil = require('../lib/util'); +const swaggerUi = require('../lib/swagger-ui'); /** @@ -31,15 +32,17 @@ /** * GET / - * Main entry point. Currently it only responds if the spec query + * Main entry point. Currently it only responds if the spec or doc query * parameter is given, otherwise lets the next middleware handle it */ router.get('/', (req, res, next) => { - if (!{}.hasOwnProperty.call(req.query || {}, 'spec')) { - next(); - } else { + if ({}.hasOwnProperty.call(req.query || {}, 'spec')) { res.json(app.conf.spec); + } else if ({}.hasOwnProperty.call(req.query || {}, 'doc')) { + return swaggerUi.processRequest(app, req, res); + } else { + next(); } }); diff --git a/spec.template.yaml b/spec.template.yaml index 75b0db1..608f92f 100644 --- a/spec.template.yaml +++ b/spec.template.yaml @@ -46,6 +46,12 @@ spec: true response: status: 200 + - title: doc from root + request: + query: + doc: true + response: + status: 200 - title: root with wrong query param request: query: -- To view, visit https://gerrit.wikimedia.org/r/339706 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Ia0cc779e5292bde0ffe287b8bc9c901462c5852a Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/services/eventstreams Gerrit-Branch: master Gerrit-Owner: Ottomata <ao...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits