Archived
2

implemented /help route

This commit is contained in:
VLE2FE
2020-09-03 10:06:16 +02:00
parent 364ad1964e
commit 6ae49e9f09
10 changed files with 393 additions and 2 deletions

184
src/routes/help.spec.ts Normal file
View File

@ -0,0 +1,184 @@
import should from 'should/as-function';
import TestHelper from "../test/helper";
import HelpModel from '../models/help';
describe('/help', () => {
let server;
before(done => TestHelper.before(done));
beforeEach(done => server = TestHelper.beforeEach(server, done));
afterEach(done => TestHelper.afterEach(server, done));
after(done => TestHelper.after(done));
describe('GET /help/{key}', () => {
it('returns the required text', done => {
TestHelper.request(server, done, {
method: 'get',
url: '/help/%2Fsamples',
auth: {basic: 'janedoe'},
httpStatus: 200,
res: {text: 'Samples help', level: 'read'}
});
});
it('returns the required text without authorization if allowed', done => {
TestHelper.request(server, done, {
method: 'get',
url: '/help/%2Fdocumentation',
auth: {basic: 'janedoe'},
httpStatus: 200,
res: {text: 'Documentation help', level: 'none'}
});
});
it('returns 404 for an invalid key', done => {
TestHelper.request(server, done, {
method: 'get',
url: '/help/documentation/database',
httpStatus: 404
});
});
it('returns 404 for an unknown key', done => {
TestHelper.request(server, done, {
method: 'get',
url: '/help/xxx',
auth: {basic: 'janedoe'},
httpStatus: 404
});
});
it('returns 403 for a text with a higher level than given', done => {
TestHelper.request(server, done, {
method: 'get',
url: '/help/%2Fmodels',
auth: {basic: 'janedoe'},
httpStatus: 403
});
});
it('rejects an API key', done => {
TestHelper.request(server, done, {
method: 'get',
url: '/help/%2Fsamples',
auth: {api: 'janedoe'},
httpStatus: 401,
});
});
it('rejects an unauthorized request if a level is given', done => {
TestHelper.request(server, done, {
method: 'get',
url: '/help/%2Fsamples',
httpStatus: 401
});
});
});
describe('POST /help/{key}', () => {
it('changes the required text', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/help/%2Fsamples',
auth: {basic: 'admin'},
httpStatus: 200,
req: {text: 'New samples help', level: 'write'}
}).end((err, res) => {
if (err) return done(err);
should(res.body).be.eql({status: 'OK'});
HelpModel.find({key: '/samples'}).lean().exec((err, data) => {
if (err) return done(err);
should(data).have.lengthOf(1);
should(data[0]).have.only.keys('_id', 'key', 'text', 'level');
should(data[0]).property('key', '/samples');
should(data[0]).property('text', 'New samples help');
should(data[0]).property('level', 'write');
done();
});
});
});
it('saves a new text', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/help/%2Fmaterials',
auth: {basic: 'admin'},
httpStatus: 200,
req: {text: 'Materials help', level: 'dev'}
}).end((err, res) => {
if (err) return done(err);
should(res.body).be.eql({status: 'OK'});
HelpModel.find({key: '/materials'}).lean().exec((err, data) => {
if (err) return done(err);
should(data).have.lengthOf(1);
should(data[0]).have.only.keys('_id', 'key', 'text', 'level', '__v');
should(data[0]).property('key', '/materials');
should(data[0]).property('text', 'Materials help');
should(data[0]).property('level', 'dev');
done();
});
});
});
it('rejects an API key', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/help/%2Fsamples',
auth: {key: 'admin'},
httpStatus: 401,
req: {text: 'New samples help', level: 'write'}
});
});
it('rejects a write user', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/help/%2Fsamples',
auth: {basic: 'janedoe'},
httpStatus: 403,
req: {text: 'New samples help', level: 'write'}
});
});
it('rejects an unauthorized request', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/help/%2Fsamples',
httpStatus: 401,
req: {text: 'New samples help', level: 'write'}
});
});
});
describe('DELETE /help/{key}', () => {
it('deletes the required entry', done => {
TestHelper.request(server, done, {
method: 'delete',
url: '/help/%2Fsamples',
auth: {basic: 'admin'},
httpStatus: 200
}).end((err, res) => {
if (err) return done(err);
should(res.body).be.eql({status: 'OK'});
HelpModel.find({key: '/materials'}).lean().exec((err, data) => {
if (err) return done(err);
should(data).have.lengthOf(0);
done();
});
});
});
it('rejects an API key', done => {
TestHelper.request(server, done, {
method: 'delete',
url: '/help/%2Fsamples',
auth: {key: 'admin'},
httpStatus: 401
});
});
it('rejects a write user', done => {
TestHelper.request(server, done, {
method: 'delete',
url: '/help/%2Fsamples',
auth: {basic: 'janedoe'},
httpStatus: 403
});
});
it('rejects an unauthorized request', done => {
TestHelper.request(server, done, {
method: 'delete',
url: '/help/%2Fsamples',
httpStatus: 401
});
});
});
});

55
src/routes/help.ts Normal file
View File

@ -0,0 +1,55 @@
import express from 'express';
import HelpModel from '../models/help';
import HelpValidate from './validate/help';
import res400 from './validate/res400';
import globals from '../globals';
const router = express.Router();
router.get('/help/:key', (req, res, next) => {
const {error: paramError, value: key} = HelpValidate.params(req.params);
if (paramError) return res400(paramError, res);
HelpModel.findOne(key).lean().exec((err, data) => {
if (err) return next(err);
if (!data) {
return res.status(404).json({status: 'Not found'});
}
if (data.level !== 'none') { // check level
if (!req.auth(res,
Object.values(globals.levels).slice(Object.values(globals.levels).findIndex(e => e === data.level))
, 'basic')) return;
}
res.json(HelpValidate.output(data));
})
});
router.post('/help/:key', (req, res, next) => {
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
const {error: paramError, value: key} = HelpValidate.params(req.params);
if (paramError) return res400(paramError, res);
const {error, value: help} = HelpValidate.input(req.body);
if (error) return res400(error, res);
HelpModel.findOneAndUpdate(key, help, {upsert: true}).log(req).lean().exec(err => {
if (err) return next(err);
res.json({status: 'OK'});
});
});
router.delete('/help/:key', (req, res, next) => {
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
const {error: paramError, value: key} = HelpValidate.params(req.params);
if (paramError) return res400(paramError, res);
HelpModel.findOneAndDelete(key).log(req).lean().exec((err, data) => {
if (err) return next(err);
if (!data) {
return res.status(404).json({status: 'Not found'});
}
res.json({status: 'OK'});
});
});
module.exports = router;

View File

@ -0,0 +1,34 @@
import Joi from 'joi';
import globals from '../../globals';
export default class HelpValidate {
private static help = {
text: Joi.string()
.allow('')
.max(8192),
level: Joi.string()
.valid('none', ...Object.values(globals.levels))
}
static input (data) {
return Joi.object({
text: this.help.text.required(),
level: this.help.level.required()
}).validate(data);
}
static output (data) {
const {value, error} = Joi.object({
text: this.help.text,
level: this.help.level
}).validate(data, {stripUnknown: true});
return error !== undefined? null : value;
}
static params(data) {
return Joi.object({
key: Joi.string().min(1).max(128)
}).validate(data);
}
}