Archived
2

added /materials route

This commit is contained in:
VLE2FE
2020-04-29 12:10:27 +02:00
parent 8b355d1d65
commit 600385cede
26 changed files with 1081 additions and 339 deletions

View File

@ -5,8 +5,9 @@ import db from "../db";
export default class TestHelper {
public static auth = {
admin: {pass: 'Abc123!#', key: '5ea131671feb9c2ee0aafc9a'},
janedoe: {pass: 'Xyz890*)', key: '5ea0450ed851c30a90e70899'}
admin: {pass: 'Abc123!#', key: '000000000000000000001003'},
janedoe: {pass: 'Xyz890*)', key: '000000000000000000001002'},
user: {pass: 'Xyz890*)', key: '000000000000000000001001'}
}
public static res = {
400: {status: 'Bad request'},

View File

@ -46,16 +46,17 @@ app.use(require('./helpers/authorize')); // handle authentication
// require routes
app.use('/', require('./routes/root'));
app.use('/', require('./routes/user'));
app.use('/', require('./routes/material'));
// Swagger UI
let oasDoc: JSONSchema = {};
jsonRefParser.bundle('oas/oas.yaml', (err, doc) => {
let apiDoc: JSONSchema = {};
jsonRefParser.bundle('api/api.yaml', (err, doc) => {
if(err) throw err;
oasDoc = doc;
oasDoc.paths = oasDoc.paths.allOf.reduce((s, e) => Object.assign(s, e));
swagger.setup(oasDoc, {defaultModelsExpandDepth: -1, customCss: '.swagger-ui .topbar { display: none }'});
apiDoc = doc;
apiDoc.paths = apiDoc.paths.allOf.reduce((s, e) => Object.assign(s, e));
swagger.setup(apiDoc, {defaultModelsExpandDepth: -1, customCss: '.swagger-ui .topbar { display: none }'});
});
app.use('/api', swagger.serve, swagger.setup(oasDoc, {defaultModelsExpandDepth: -1, customCss: '.swagger-ui .topbar { display: none }'}));
app.use('/api', swagger.serve, swagger.setup(apiDoc, {defaultModelsExpandDepth: -1, customCss: '.swagger-ui .topbar { display: none }'}));
app.use((req, res) => { // 404 error handling
res.status(404).json({status: 'Not found'});

16
src/models/material.ts Normal file
View File

@ -0,0 +1,16 @@
import mongoose from 'mongoose';
const MaterialSchema = new mongoose.Schema({
name: {type: String, index: {unique: true}},
supplier: String,
group: String,
mineral: String,
glass_fiber: String,
carbon_fiber: String,
numbers: [{
color: String,
number: Number
}]
});
export default mongoose.model('material', MaterialSchema);

387
src/routes/material.spec.ts Normal file
View File

@ -0,0 +1,387 @@
import should from 'should/as-function';
import MaterialModel from '../models/material';
import TestHelper from "../helpers/test";
describe('/material', () => {
let server;
before(done => TestHelper.before(done));
beforeEach(done => server = TestHelper.beforeEach(server, done));
afterEach(done => TestHelper.afterEach(server, done));
describe('GET /materials', () => {
it('returns all materials', done => {
TestHelper.request(server, done, {
method: 'get',
url: '/materials',
auth: {basic: 'janedoe'},
httpStatus: 200
}).end((err, res) => {
if (err) return done(err);
const json = require('../test/db.json');
should(res.body).have.lengthOf(json.collections.users.length);
should(res.body).matchEach(material => {
should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'mineral', 'glass_fiber', 'carbon_fiber', 'numbers');
should(material).have.property('_id').be.type('string');
should(material).have.property('name').be.type('string');
should(material).have.property('supplier').be.type('string');
should(material).have.property('group').be.type('string');
should(material).have.property('mineral').be.type('number');
should(material).have.property('glass_fiber').be.type('number');
should(material).have.property('carbon_fiber').be.type('number');
should(material.numbers).matchEach(number => {
should(number).have.only.keys('color', 'number');
should(number).have.property('color').be.type('string');
should(number).have.property('number').be.type('number');
});
});
done();
});
});
it('works with an API key', done => {
TestHelper.request(server, done, {
method: 'get',
url: '/materials',
auth: {key: 'janedoe'},
httpStatus: 200
}).end((err, res) => {
if (err) return done(err);
const json = require('../test/db.json');
should(res.body).have.lengthOf(json.collections.users.length);
should(res.body).matchEach(material => {
should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'mineral', 'glass_fiber', 'carbon_fiber', 'numbers');
should(material).have.property('_id').be.type('string');
should(material).have.property('name').be.type('string');
should(material).have.property('supplier').be.type('string');
should(material).have.property('group').be.type('string');
should(material).have.property('mineral').be.type('number');
should(material).have.property('glass_fiber').be.type('number');
should(material).have.property('carbon_fiber').be.type('number');
should(material.numbers).matchEach(number => {
should(number).have.only.keys('color', 'number');
should(number).have.property('color').be.type('string');
should(number).have.property('number').be.type('number');
});
});
done();
});
});
it('rejects unauthorized requests', done => {
TestHelper.request(server, done, {
method: 'get',
url: '/materials',
httpStatus: 401
});
});
});
describe('GET /material/{id}', () => {
it('returns the right material', done => {
TestHelper.request(server, done, {
method: 'get',
url: '/material/100000000000000000000001',
auth: {basic: 'janedoe'},
httpStatus: 200,
res: {_id: '100000000000000000000001', name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', mineral: 0, glass_fiber: 40, carbon_fiber: 0, numbers: [{color: 'black', number: 5514263423}]}
});
});
it('returns the right material for an API key', done => {
TestHelper.request(server, done, {
method: 'get',
url: '/material/100000000000000000000003',
auth: {key: 'admin'},
httpStatus: 200,
res: {_id: '100000000000000000000003', name: 'PA GF 50 black (2706)', supplier: 'Akro-Plastic', group: 'PA66+PA6I/6T', mineral: 0, glass_fiber: 0, carbon_fiber: 0, numbers: []}
});
});
it('rejects an invalid id', done => {
TestHelper.request(server, done, {
method: 'get',
url: '/material/10000000000000000000000x',
auth: {key: 'admin'},
httpStatus: 404
});
});
it('rejects an unknown id', done => {
TestHelper.request(server, done, {
method: 'get',
url: '/material/100000000000000000000111',
auth: {key: 'janedoe'},
httpStatus: 404
});
});
it('rejects unauthorized requests', done => {
TestHelper.request(server, done, {
method: 'get',
url: '/material/100000000000000000000001',
httpStatus: 401
});
});
});
describe('PUT /material/{id}', () => {
it('returns the right material', done => {
TestHelper.request(server, done, {
method: 'put',
url: '/material/100000000000000000000001',
auth: {basic: 'janedoe'},
httpStatus: 200,
req: {},
res: {_id: '100000000000000000000001', name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', mineral: 0, glass_fiber: 40, carbon_fiber: 0, numbers: [{color: 'black', number: 5514263423}]}
});
});
it('changes the given properties', done => {
TestHelper.request(server, done, {
method: 'put',
url: '/material/100000000000000000000001',
auth: {basic: 'janedoe'},
httpStatus: 200,
req: {name: 'UltramidTKR4355G7_2', supplier: 'BASF', group: 'PA6/6T', mineral: 0, glass_fiber: 35, carbon_fiber: 0, numbers: [{color: 'black', number: 5514212901}, {color: 'signalviolet', number: 5514612901}]}
,
}).end(err => {
if (err) return done(err);
MaterialModel.findById('100000000000000000000001').lean().exec((err, data) => {
if (err) return done(err);
should(data).be.eql({_id: '100000000000000000000002', name: 'UltramidTKR4355G7', supplier: 'BASF', group: 'PA6/6T', mineral: 0, glass_fiber: 35, carbon_fiber: 0, numbers: [{color: 'black', number: 5514212901}, {color: 'signalviolet', number: 5514612901}], __v: 0}
);
done();
});
});
});
it('rejects already existing material names', done => {
TestHelper.request(server, done, {
method: 'put',
url: '/material/100000000000000000000001',
auth: {basic: 'janedoe'},
httpStatus: 400,
req: {name: 'UltramidTKR4355G7'},
res: {status: 'Material name already taken'}
});
});
it('rejects wrong material properties', done => {
TestHelper.request(server, done, {
method: 'put',
url: '/material/100000000000000000000001',
auth: {basic: 'janedoe'},
httpStatus: 400,
req: {mineral: 'x', glass_fiber: 'x', carbon_fiber: 'x', numbers: [{colorxx: 'black', number: 'xxx'}]},
res: {status: 'Invalid body format'}
});
});
it('rejects an invalid id', done => {
TestHelper.request(server, done, {
method: 'put',
url: '/material/10000000000000000000000x',
auth: {basic: 'admin'},
httpStatus: 400,
req: {},
res: {status: 'Invalid id'}
});
});
it('rejects an API key', done => {
TestHelper.request(server, done, {
method: 'put',
url: '/material/100000000000000000000002',
auth: {key: 'admin'},
httpStatus: 401,
req: {}
});
});
it('rejects requests from a read user', done => {
TestHelper.request(server, done, {
method: 'put',
url: '/material/100000000000000000000002',
auth: {basic: 'user'},
httpStatus: 403,
req: {}
});
});
it('returns 404 for an unknown material', done => {
TestHelper.request(server, done, {
method: 'put',
url: '/material/100000000000000000000111',
auth: {basic: 'janedoe'},
httpStatus: 404,
req: {}
});
});
it('rejects unauthorized requests', done => {
TestHelper.request(server, done, {
method: 'put',
url: '/material/100000000000000000000001',
httpStatus: 401,
req: {}
});
});
});
describe('DELETE /material/{id}', () => {
it('deletes the material', done => {
TestHelper.request(server, done, {
method: 'delete',
url: '/material/100000000000000000000001',
auth: {basic: 'janedoe'},
httpStatus: 200
}).end((err, res) => {
if (err) return done(err);
should(res.body).be.eql({status: 'OK'});
MaterialModel.findById('100000000000000000000001').lean().exec((err, data) => {
if (err) return done(err);
should(data).have.lengthOf(0);
done();
});
});
});
it('rejects deleting a material referenced by samples');
it('rejects an invalid id', done => {
TestHelper.request(server, done, {
method: 'delete',
url: '/material/10000000000000000000000x',
auth: {basic: 'admin'},
httpStatus: 400,
res: {status: 'Invalid id'}
});
});
it('rejects an API key', done => {
TestHelper.request(server, done, {
method: 'delete',
url: '/material/100000000000000000000002',
auth: {key: 'admin'},
httpStatus: 401
});
});
it('rejects requests from a read user', done => {
TestHelper.request(server, done, {
method: 'delete',
url: '/material/100000000000000000000002',
auth: {basic: 'user'},
httpStatus: 403
});
});
it('returns 404 for an unknown id', done => {
TestHelper.request(server, done, {
method: 'delete',
url: '/material/100000000000000000000111',
auth: {basic: 'janedoe'},
httpStatus: 404
});
});
it('rejects unauthorized requests', done => {
TestHelper.request(server, done, {
method: 'delete',
url: '/material/100000000000000000000001',
httpStatus: 401
});
});
});
describe('POST /material/new', () => {
it('returns the right material', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/material/new',
auth: {basic: 'janedoe'},
httpStatus: 200,
req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', mineral: 0, glass_fiber: 30, carbon_fiber: 0, numbers: [{color: 'black', number: 5515798402}]}
}).end((err, res) => {
if (err) return done (err);
console.log(res.body);
should(res.body).have.only.keys('_id', 'name', 'supplier', 'group', 'mineral', 'glass_fiber', 'carbon_fiber', 'numbers');
should(res.body).have.property('_id').be.type('string');
should(res.body).have.property('name', 'Crastin CE 2510');
should(res.body).have.property('supplier', 'Du Pont');
should(res.body).have.property('group', 'PBT');
should(res.body).have.property('mineral', 0);
should(res.body).have.property('glass_fiber', 30);
should(res.body).have.property('carbon_fiber', 0);
should(res.body.numbers).matchEach(number => {
should(number).have.only.keys('color', 'number');
should(number).have.property('color', 'black');
should(number).have.property('number', 5515798402);
});
done();
});
});
it('stores the material', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/material/new',
auth: {basic: 'janedoe'},
httpStatus: 200,
req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', mineral: 0, glass_fiber: 30, carbon_fiber: 0, numbers: []}
}).end(err => {
if (err) return done (err);
MaterialModel.find({name: 'Crastin CE 2510'}).lean().exec((err, data: any) => {
if (err) return done (err);
console.log(data[0]);
should(data[0]).have.only.keys('_id', 'name', 'supplier', 'group', 'mineral', 'glass_fiber', 'carbon_fiber', 'numbers', '__v');
should(data[0]).have.property('_id');
should(data[0]).have.property('name', 'Crastin CE 2510');
should(data[0]).have.property('supplier', 'Du Pont');
should(data[0]).have.property('group', 'PBT');
should(data[0]).have.property('mineral', '0');
should(data[0]).have.property('glass_fiber', '30');
should(data[0]).have.property('carbon_fiber', '0');
should(data[0].numbers).have.lengthOf(0);
done();
});
});
});
it('rejects already existing material names', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/material/new',
auth: {basic: 'janedoe'},
httpStatus: 400,
req: {name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', mineral: 0, glass_fiber: 40, carbon_fiber: 0, numbers: [{color: 'black', number: 5514263423}]},
res: {status: 'Material name already taken'}
});
});
it('rejects wrong material properties', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/material/new',
auth: {basic: 'janedoe'},
httpStatus: 400,
req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', mineral: 'x', glass_fiber: 'x', carbon_fiber: 'x', numbers: [{colorxx: 'black', number: 'xxx'}]},
res: {status: 'Invalid body format'}
});
});
it('rejects incomplete material properties', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/material/new',
auth: {basic: 'janedoe'},
httpStatus: 400,
req: {name: 'Crastin CE 2510'},
res: {status: 'Invalid body format'}
});
});
it('rejects an API key', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/material/new',
auth: {key: 'janedoe'},
httpStatus: 401,
req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', mineral: 0, glass_fiber: 30, carbon_fiber: 0, numbers: []}
});
});
it('rejects requests from a read user', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/material/new',
auth: {basic: 'user'},
httpStatus: 403,
req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', mineral: 0, glass_fiber: 30, carbon_fiber: 0, numbers: []}
});
});
it('rejects unauthorized requests', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/material/new',
httpStatus: 401,
req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', mineral: 0, glass_fiber: 30, carbon_fiber: 0, numbers: []}
});
});
});
});

59
src/routes/material.ts Normal file
View File

@ -0,0 +1,59 @@
import express from 'express';
import MaterialValidate from './validate/material';
import MaterialModel from '../models/material'
import IdValidate from './validate/id';
const router = express.Router();
router.get('/materials', (req, res, next) => {
if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'all')) return;
MaterialModel.find({}).lean().exec((err, data) => {
if(err) next(err);
res.json(data.map(e => MaterialValidate.output(e)).filter(e => e !== null)); // validate all and filter null values from validation errors
});
});
router.get('/material/' + IdValidate.parameter(), (req, res, next) => {
if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'all')) return;
MaterialModel.findById(req.params.id).lean().exec((err, data) => {
if(err) next(err);
console.log(data);
if (data) {
res.json(MaterialValidate.output(data));
}
else {
res.status(404).json({status: 'Not found'});
}
});
});
router.post('/material/new', (req, res, next) => {
if (!req.auth(res, ['write', 'maintain', 'dev', 'admin'], 'basic')) return;
// validate input
const {error, value: material} = MaterialValidate.input(req.body, 'new');
if(error !== undefined) {
res.status(400).json({status: 'Invalid body format'});
return;
}
MaterialModel.find({name: material.name}).lean().exec((err, data) => {
if(err) next(err);
if (data.length > 0) {
res.status(400).json({status: 'Material name already taken'});
return;
}
new MaterialModel(material).save((err, data) => {
if(err) next(err);
res.json(MaterialValidate.output(data.toObject()));
});
})
})
module.exports = router;

View File

@ -48,6 +48,13 @@ describe('/user', () => {
httpStatus: 401
});
});
it('rejects unauthorized requests', done => {
TestHelper.request(server, done, {
method: 'get',
url: '/users',
httpStatus: 401
});
});
});
describe('GET /user/{name}', () => {
@ -119,6 +126,13 @@ describe('/user', () => {
httpStatus: 404
});
});
it('rejects requests from an admin API key', done => {
TestHelper.request(server, done, {
method: 'get',
url: '/user/janedoe',
httpStatus: 401
});
});
});
describe('PUT /user/{name}', () => {
@ -169,7 +183,7 @@ describe('/user', () => {
req: {name: 'adminnew', email: 'adminnew@bosch.com', pass: 'Abc123##', location: 'Abt', device_name: 'test'}
}).end(err => {
if (err) return done (err);
UserModel.find({name: 'adminnew'}).lean().exec( 'find', (err, data) => {
UserModel.find({name: 'adminnew'}).lean().exec( (err, data) => {
if (err) return done(err);
should(data).have.lengthOf(1);
should(data[0]).have.only.keys('_id', 'name', 'pass', 'email', 'level', 'location', 'device_name', 'key', '__v');
@ -193,7 +207,7 @@ describe('/user', () => {
req: {level: 'read'}
}).end(err => {
if (err) return done (err);
UserModel.find({name: 'janedoe'}).lean().exec( 'find', (err, data) => {
UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => {
if (err) return done(err);
should(data).have.lengthOf(1);
should(data[0]).have.property('level', 'read');
@ -211,7 +225,7 @@ describe('/user', () => {
}).end((err, res) => {
if (err) return done (err);
should(res.body).be.eql({status: 'Invalid body format'});
UserModel.find({name: 'janedoe'}).lean().exec( 'find', (err, data) => {
UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => {
if (err) return done(err);
should(data).have.lengthOf(1);
should(data[0]).have.property('level', 'write');
@ -229,7 +243,7 @@ describe('/user', () => {
}).end((err, res) => {
if (err) return done (err);
should(res.body).be.eql({status: 'Username already taken'});
UserModel.find({name: 'janedoe'}).lean().exec( 'find', (err, data) => {
UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => {
if (err) return done(err);
should(data).have.lengthOf(1);
done();
@ -312,6 +326,14 @@ describe('/user', () => {
req: {}
});
});
it('rejects unauthorized requests', done => {
TestHelper.request(server, done, {
method: 'put',
url: '/user/janedoe',
httpStatus: 401,
req: {}
});
});
});
describe('DELETE /user/{name}', () => {
@ -324,7 +346,7 @@ describe('/user', () => {
}).end((err, res) => {
if (err) return done (err);
should(res.body).be.eql({status: 'OK'});
UserModel.find({name: 'janedoe'}).lean().exec( 'find', (err, data) => {
UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => {
if (err) return done(err);
should(data).have.lengthOf(0);
done();
@ -340,7 +362,7 @@ describe('/user', () => {
}).end((err, res) => {
if (err) return done (err);
should(res.body).be.eql({status: 'OK'});
UserModel.find({name: 'janedoe'}).lean().exec( 'find', (err, data) => {
UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => {
if (err) return done(err);
should(data).have.lengthOf(0);
done();
@ -379,6 +401,40 @@ describe('/user', () => {
httpStatus: 404
});
});
it('rejects unauthorized requests', done => {
TestHelper.request(server, done, {
method: 'delete',
url: '/user/janedoe',
httpStatus: 401
});
});
});
describe('GET /user/key', () => {
it('returns the right API key', done => {
TestHelper.request(server, done, {
method: 'get',
url: '/user/key',
auth: {basic: 'janedoe'},
httpStatus: 200,
res: {key: TestHelper.auth.janedoe.key}
});
});
it('rejects requests from an API key', done => {
TestHelper.request(server, done, {
method: 'get',
url: '/user/key',
auth: {key: 'janedoe'},
httpStatus: 401
});
});
it('rejects requests from an API key', done => {
TestHelper.request(server, done, {
method: 'get',
url: '/user/key',
httpStatus: 401
});
});
});
describe('POST /user/new', () => {
@ -410,7 +466,7 @@ describe('/user', () => {
req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', device_name: 'Alpha II'}
}).end(err => {
if (err) return done (err);
UserModel.find({name: 'johndoe'}).lean().exec( 'find', (err, data) => {
UserModel.find({name: 'johndoe'}).lean().exec( (err, data) => {
if (err) return done(err);
should(data).have.lengthOf(1);
should(data[0]).have.only.keys('_id', 'name', 'pass', 'email', 'level', 'location', 'device_name', 'key', '__v');
@ -435,7 +491,7 @@ describe('/user', () => {
}).end((err, res) => {
if (err) return done (err);
should(res.body).be.eql({status: 'Username already taken'});
UserModel.find({name: 'janedoe'}).lean().exec( 'find', (err, data) => {
UserModel.find({name: 'janedoe'}).lean().exec( (err, data) => {
if (err) return done(err);
should(data).have.lengthOf(1);
done();
@ -510,6 +566,14 @@ describe('/user', () => {
req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', device_name: 'Alpha II'}
});
});
it('rejects unauthorized requests', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/user/new',
httpStatus: 401,
req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'Abc123!#', level: 'read', location: 'Rng', device_name: 'Alpha II'}
});
});
});
describe('POST /user/passreset', () => {
@ -539,7 +603,7 @@ describe('/user', () => {
});
});
it('changes the user password', done => {
UserModel.find({name: 'janedoe'}).lean().exec( 'find', (err, data: any) => {
UserModel.find({name: 'janedoe'}).lean().exec( (err, data: any) => {
if (err) return done(err);
const oldpass = data[0].pass;
TestHelper.request(server, done, {
@ -559,24 +623,4 @@ describe('/user', () => {
});
});
});
describe('GET /user/key', () => {
it('returns the right API key', done => {
TestHelper.request(server, done, {
method: 'get',
url: '/user/key',
auth: {basic: 'janedoe'},
httpStatus: 200,
res: {key: TestHelper.auth.janedoe.key}
});
});
it('rejects requests from an API key', done => {
TestHelper.request(server, done, {
method: 'get',
url: '/user/key',
auth: {key: 'janedoe'},
httpStatus: 401
});
});
})
});

View File

@ -1,12 +1,14 @@
import express from 'express';
import mongoose from 'mongoose';
import bcrypt from 'bcryptjs';
import UserValidate from './validate/user';
import UserModel from '../models/user';
import mail from '../helpers/mail';
const router = express.Router();
router.get('/users', (req, res) => {
if (!req.auth(res, ['admin'], 'basic')) return;
@ -168,4 +170,5 @@ router.post('/user/passreset', (req, res, next) => {
});
});
module.exports = router;

17
src/routes/validate/id.ts Normal file
View File

@ -0,0 +1,17 @@
import joi from '@hapi/joi';
export default class IdValidate {
private static id = joi.string().pattern(new RegExp('[0-9a-f]{24}')).length(24);
static get () {
return this.id;
}
static valid (id) {
return this.id.validate(id).error === undefined;
}
static parameter() { // :id url parameter
return ':id([0-9a-f]{24})';
}
}

View File

@ -0,0 +1,82 @@
import joi from '@hapi/joi';
import IdValidate from './id';
export default class MaterialValidate { // validate input for material
private static material = {
name: joi.string()
.max(128),
supplier: joi.string()
.max(128),
group: joi.string()
.max(128),
mineral: joi.number()
.integer()
.min(0)
.max(100),
glass_fiber: joi.number()
.integer()
.min(0)
.max(100),
carbon_fiber: joi.number()
.integer()
.min(0)
.max(100),
numbers: joi.array()
.items(joi.object({
color: joi.string()
.max(128),
number: joi.number()
.min(0)
}))
};
static input (data, param) { // validate data, param: new(everything required)/change(available attributes are validated)
if (param === 'new') {
return joi.object({
name: this.material.name.required(),
supplier: this.material.supplier.required(),
group: this.material.group.required(),
mineral: this.material.mineral.required(),
glass_fiber: this.material.glass_fiber.required(),
carbon_fiber: this.material.carbon_fiber.required(),
numbers: this.material.numbers
}).validate(data);
}
else if (param === 'change') {
return joi.object({
name: this.material.name,
supplier: this.material.supplier,
group: this.material.group,
mineral: this.material.mineral,
glass_fiber: this.material.glass_fiber,
carbon_fiber: this.material.carbon_fiber,
numbers: this.material.numbers
}).validate(data);
}
else {
return{error: 'No parameter specified!', value: {}};
}
}
static output (data) { // validate output from database for needed properties, strip everything else
data._id = data._id.toString();
const {value, error} = joi.object({
_id: IdValidate.get(),
name: this.material.name,
supplier: this.material.supplier,
group: this.material.group,
mineral: this.material.mineral,
glass_fiber: this.material.glass_fiber,
carbon_fiber: this.material.carbon_fiber,
numbers: this.material.numbers
}).validate(data, {stripUnknown: true});
return error !== undefined? null : value;
}
}

View File

@ -1,28 +1,34 @@
import joi from '@hapi/joi';
import globals from '../../globals';
import IdValidate from './id';
export default class UserValidate { // validate input for user
private static user = {
_id: joi.any(),
name: joi.string()
.alphanum()
.lowercase(),
.lowercase()
.max(128),
email: joi.string()
.email({minDomainSegments: 2})
.lowercase(),
.lowercase()
.max(128),
pass: joi.string()
.pattern(new RegExp('^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[!"#%&\'()*+,-.\\/:;<=>?@[\\]^_`{|}~])(?=\\S+$).{8,}$')),
.pattern(new RegExp('^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[!"#%&\'()*+,-.\\/:;<=>?@[\\]^_`{|}~])(?=\\S+$).{8,}$'))
.max(128),
level: joi.string()
.valid(...globals.levels),
location: joi.string()
.alphanum(),
.alphanum()
.max(128),
device_name: joi.string()
.allow('')
.max(128),
};
private static specialUsernames = ['admin', 'user', 'key', 'new', 'passreset']; // names a user cannot take
@ -63,14 +69,15 @@ export default class UserValidate { // validate input for user
}
static output (data) { // validate output from database for needed properties, strip everything else
data._id = data._id.toString();
const {value, error} = joi.object({
_id: joi.any(),
name: joi.string(),
email: joi.string(),
level: joi.string(),
location: joi.string(),
device_name: joi.string().allow('')
}).validate(data, {stripUnknown: true})
_id: IdValidate.get(),
name: this.user.name,
email: this.user.email,
level: this.user.level,
location: this.user.location,
device_name: this.user.device_name
}).validate(data, {stripUnknown: true});
return error !== undefined? null : value;
}

View File

@ -2,27 +2,88 @@
"collections": {
"users": [
{
"_id": {"$oid":"5ea0450ed851c30a90e70894"},
"_id": {"$oid":"000000000000000000000001"},
"email": "user@bosch.com",
"name": "user",
"pass": "$2a$10$di26XKF63OG0V00PL1kSK.ceCcTxDExBMOg.jkHiCnXcY7cN7DlPi",
"level": "read",
"location": "Rng",
"device_name": "Alpha I",
"key": "000000000000000000001001",
"__v": 0
},
{
"_id": {"$oid":"000000000000000000000002"},
"email": "jane.doe@bosch.com",
"name": "janedoe",
"pass": "$2a$10$di26XKF63OG0V00PL1kSK.ceCcTxDExBMOg.jkHiCnXcY7cN7DlPi",
"level": "write",
"location": "Rng",
"device_name": "Alpha I",
"key": "5ea0450ed851c30a90e70899",
"key": "000000000000000000001002",
"__v": 0
},
{
"_id": {"$oid":"5ea131671feb9c2ee0aafc9b"},
"_id": {"$oid":"000000000000000000000003"},
"email": "a.d.m.i.n@bosch.com",
"name": "admin",
"pass": "$2a$10$i872o3qR5V3JnbDArD8Z.eDo.BNPDBaR7dUX9KSEtl9pUjLyucy2K",
"level": "admin",
"location": "Rng",
"device_name": "",
"key": "5ea131671feb9c2ee0aafc9a",
"key": "000000000000000000001003",
"__v": "0"
}
],
"materials": [
{
"_id": {"$oid":"100000000000000000000001"},
"name": "Stanyl TW 200 F8",
"supplier": "DSM",
"group": "PA46",
"mineral": 0,
"glass_fiber": 40,
"carbon_fiber": 0,
"numbers": [
{
"color": "black",
"number": 5514263423
}
],
"__v": 0
},
{
"_id": {"$oid":"100000000000000000000002"},
"name": "Ultramid T KR 4355 G7",
"supplier": "BASF",
"group": "PA6/6T",
"mineral": 0,
"glass_fiber": 35,
"carbon_fiber": 0,
"numbers": [
{
"color": "black",
"number": 5514212901
},
{
"color": "signalviolet",
"number": 5514612901
}
],
"__v": 0
},
{
"_id": {"$oid":"100000000000000000000003"},
"name": "PA GF 50 black (2706)",
"supplier": "Akro-Plastic",
"group": "PA66+PA6I/6T",
"mineral": 0,
"glass_fiber": 0,
"carbon_fiber": 0,
"numbers": [
],
"__v": 0
}
]
}
}