Mholloway has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/390878 )
Change subject: Update to service-template-node v0.5.2 ...................................................................... Update to service-template-node v0.5.2 Change-Id: Id6a2fe9c8656ce9947e2b9936275a40db704e402 --- M .eslintrc.yml M .travis.yml M lib/api-util.js A lib/swagger-ui.js M package.json M routes/root.js M test/features/app/spec.js M test/index.js 8 files changed, 116 insertions(+), 26 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/services/mobileapps refs/changes/78/390878/1 diff --git a/.eslintrc.yml b/.eslintrc.yml index bf9f475..312fdfd 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -1 +1 @@ -extends: node-services \ No newline at end of file +extends: 'eslint-config-node-services' diff --git a/.travis.yml b/.travis.yml index c417c7f..df202fa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,4 +5,3 @@ node_js: - "4" - "6" - - "node" diff --git a/lib/api-util.js b/lib/api-util.js index 9fd9959..d6059d7 100644 --- a/lib/api-util.js +++ b/lib/api-util.js @@ -4,7 +4,6 @@ const preq = require('preq'); const sUtil = require('./util'); const Template = require('swagger-router').Template; - const HTTPError = sUtil.HTTPError; diff --git a/lib/swagger-ui.js b/lib/swagger-ui.js new file mode 100644 index 0000000..9e39ff5 --- /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(/docExpansion: "none"/, 'docExpansion: "list", ' + + 'validatorUrl: null, ' + + 'highlightSizeThreshold: 10000') + .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 f1a64ef..05712d1 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "lint": "eslint --cache --max-warnings 0 --ext .js --ext .json .", "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": { @@ -40,45 +41,47 @@ }, "homepage": "https://www.mediawiki.org/wiki/RESTBase_services_for_apps", "dependencies": { - "bluebird": "^3.4.6", - "body-parser": "^1.15.2", - "bunyan": "^1.8.5", + "bluebird": "^3.5.0", + "body-parser": "^1.17.1", + "bunyan": "^1.8.9", "cassandra-uuid": "^0.0.2", "compression": "^1.6.2", "core-js": "^2.4.1", - "domino": "^1.0.27", + "domino": "^1.0.28", "escape-string-regexp": "^1.0.5", "express": "^4.16.0", - "js-yaml": "^3.7.0", + "js-yaml": "^3.8.2", "mediawiki-title": "^0.6.3", "parsoid-dom-utils": "^0.1.3", - "preq": "^0.5.1", + "preq": "^0.5.2", "service-runner": "^2.2.5", - "swagger-router": "^0.5.5", + "swagger-router": "^0.5.6", + "swagger-ui": "git+https://github.com/wikimedia/swagger-ui#master", "underscore": "^1.8.3" }, "devDependencies": { "ajv": "^4.7.7", "csv-parse": "^1.1.7", - "eslint": "^3.11.1", - "eslint-config-node-services": "^2.2.2", - "eslint-config-wikimedia": "^0.4.0", - "eslint-plugin-jsdoc": "^3.0.0", - "eslint-plugin-json": "^1.2.0", "extend": "^3.0.0", "istanbul": "^0.4.5", "js-beautify": "^1.6.8", "mkdirp": "^0.5.1", - "mocha": "^3.1.2", + "mocha": "^3.2.0", "mocha-jshint": "^2.3.1", - "mocha-lcov-reporter": "^1.2.0", - "nsp": "^2.6.2", + "mocha-lcov-reporter": "^1.3.0", + "nsp": "^2.6.3", + "mocha-eslint": "^3.0.1", + "eslint": "^3.12.0", + "eslint-config-node-services": "^2.0.2", + "eslint-config-wikimedia": "^0.4.0", + "eslint-plugin-json": "^1.2.0", + "eslint-plugin-jsdoc": "^3.0.0", "rss-parser": "^2.5.2", "sepia": "^2.0.1" }, "deploy": { "target": "debian", - "node": "6.11.1", + "node": "6.9.1", "dependencies": { "_all": [] } diff --git a/routes/root.js b/routes/root.js index cf0ffda..83ee521 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,21 +32,23 @@ /** * 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(); } }); -module.exports = function(appObj) { +module.exports = (appObj) => { app = appObj; diff --git a/test/features/app/spec.js b/test/features/app/spec.js index c5abfb0..1645149 100644 --- a/test/features/app/spec.js +++ b/test/features/app/spec.js @@ -62,8 +62,9 @@ try { uri.expand(Object.assign({}, defParams, ex.request.params || {})); } catch (e) { - const msg = `Route ${pathStr}, example ${ex.title}: missing parameter: ${e.message}`; - throw new Error(msg); + throw new Error( + `Route ${pathStr}, example ${idx} (${ex.title}): missing parameter: ${e.message}` + ); } }); diff --git a/test/index.js b/test/index.js index a8c4173..8c26dad 100644 --- a/test/index.js +++ b/test/index.js @@ -2,3 +2,9 @@ // Run jshint as part of normal testing require('mocha-jshint')(); +require('mocha-eslint')([ + 'lib', + 'routes' +], { + timeout: 10000 +}); -- To view, visit https://gerrit.wikimedia.org/r/390878 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Id6a2fe9c8656ce9947e2b9936275a40db704e402 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/services/mobileapps Gerrit-Branch: master Gerrit-Owner: Mholloway <mhollo...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits