From 1aa0ae46d5102142306f25b0df7d6b600cbffea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Soko=C5=82owski?= Date: Thu, 13 Dec 2018 00:06:13 +0100 Subject: [PATCH] add build body schema validation with Joi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jakub SokoĊ‚owski --- package.json | 4 +++- src/app.js | 29 ++++++++++++++++++----------- src/builds.js | 7 +++++++ src/schema.js | 13 +++++++++++++ src/template.js | 8 ++++---- src/validator.js | 10 ++++++++++ 6 files changed, 55 insertions(+), 16 deletions(-) create mode 100644 src/schema.js create mode 100644 src/validator.js diff --git a/package.json b/package.json index cfa9333..92a20e3 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,13 @@ "main": "index.js", "dependencies": { "@octokit/rest": "^16.2.0", + "joi": "^14.3.0", + "joy": "^0.1.1", "koa": "^2.5.3", "koa-bodyparser": "^4.2.1", + "koa-joi-router": "^5.1.0", "koa-json": "^2.0.2", "koa-logger": "^3.2.0", - "koa-router": "^7.4.0", "lokijs": "^1.5.5", "nunjucks": "^3.1.4" }, diff --git a/src/app.js b/src/app.js index eef275c..4c22a5b 100644 --- a/src/app.js +++ b/src/app.js @@ -1,17 +1,16 @@ import Koa from 'koa' -import Router from 'koa-router' import JSON from 'koa-json' +import JoiRouter from 'koa-joi-router' import BodyParser from 'koa-bodyparser' const App = (ghc) => { const app = new Koa() - const router = new Router() + const router = new JoiRouter() - app.use(BodyParser({onerror:console.error})) - .use(router.routes()) - .use(router.allowedMethods()) + app.use(router.middleware()) + .use(BodyParser({onerror:console.error})) .use(JSON({pretty: true})) - + app.on('error', (err, ctx) => { console.error('server error', err, ctx) }) @@ -20,11 +19,19 @@ const App = (ghc) => { ctx.body = 'OK' }) - router.post('/builds/:pr', async (ctx) => { - /* TODO add validation of received JSON body */ - await ghc.db.addBuild(ctx.params.pr, ctx.request.body) - await ghc.update(ctx.params.pr) - ctx.body = {status:'ok'} + router.route({ + method: 'post', + path: '/builds/:pr', + validate: { + type: 'json', + body: ghc.db.schema, + }, + handler: async (ctx) => { + /* TODO add validation of received JSON body */ + await ghc.db.addBuild(ctx.params.pr, ctx.request.body) + await ghc.update(ctx.params.pr) + ctx.body = {status:'ok'} + } }) router.get('/builds/:pr', async (ctx) => { diff --git a/src/builds.js b/src/builds.js index b18ba65..7c1e553 100644 --- a/src/builds.js +++ b/src/builds.js @@ -1,7 +1,10 @@ +import Joi from 'joi' import Loki from 'lokijs' +import schema from './schema' class Builds { constructor(path, interval) { + this.schema = schema this.db = new Loki(path, { autoload: true, autosave: true, @@ -23,6 +26,10 @@ class Builds { this.db.on('close', () => this.save()) } + validate (build) { + return Joi.validate(build, this.schema) + } + async save () { this.db.saveDatabase((err) => { if (err) { console.error('error saving', err) } diff --git a/src/schema.js b/src/schema.js new file mode 100644 index 0000000..679c6f2 --- /dev/null +++ b/src/schema.js @@ -0,0 +1,13 @@ +import Joi from 'joi' + +const schema = Joi.object().keys({ + id: Joi.number().positive().required(), + commit: Joi.string().regex(/^[a-zA-Z0-9]{6,40}$/).required(), + success: Joi.boolean().required(), + platform: Joi.string().max(20).required(), + duration: Joi.string().max(20).required(), + url: Joi.string().uri().required(), + pkg_url: Joi.string().uri().required(), +}) + +module.exports = schema diff --git a/src/template.js b/src/template.js index 449a139..72f00e2 100644 --- a/src/template.js +++ b/src/template.js @@ -1,12 +1,12 @@ module.exports = ` ### Jenkins Builds -| :grey_question: | Commit | Platform | Build | Duration | Result | -|-|-|-|-|-|-| +| :grey_question: | Commit | When | Platform | Build | Duration | Result | +|-|-|-|-|-|-|-| {% for b in builds -%} {% if b.success -%} -| :heavy_check_mark: | {{ b.commit }} | \`{{ b.platform }}\` | [{{ b.pr }}#{{ b.id }}]({{ b.url }}) | {{ b.duration }} | [:package: {{ b.platform }} package]({{ b.url }}) | +| :heavy_check_mark: | {{ b.commit }} | {{ b.when }} | \`{{ b.platform }}\` | [{{ b.pr }}#{{ b.id }}]({{ b.url }}) | {{ b.duration }} | [:package: {{ b.platform }}]({{ b.pkg_url }}) | {% else -%} -| :x: | {{ b.commit }} | \`{{ b.platform }}\` | [{{ b.pr }}#{{ b.id }}]({{ b.url }}) | {{ b.duration }} | [:page_facing_up: build log]({{ b.url }}consoleText) | +| :x: | {{ b.commit }} | {{ b.when }} | \`{{ b.platform }}\` | [{{ b.pr }}#{{ b.id }}]({{ b.url }}) | {{ b.duration }} | [:page_facing_up: build log]({{ b.pkg_url }}consoleText) | {%- endif %} {%- endfor %} ` diff --git a/src/validator.js b/src/validator.js new file mode 100644 index 0000000..295b41a --- /dev/null +++ b/src/validator.js @@ -0,0 +1,10 @@ +const Joi = require('joi'); + +const schema = Joi.object().keys({ + username: Joi.string().alphanum().min(3).max(30).required(), + password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/), + access_token: [Joi.string(), Joi.number()], + birthyear: Joi.number().integer().min(1900).max(2013), + email: Joi.string().email({ minDomainAtoms: 2 }) +}).with('username', 'birthyear').without('password', 'access_token'); +