refactored user.ts
This commit is contained in:
@ -37,13 +37,14 @@ router.put('/condition/' + IdValidate.parameter(), async (req, res, next) => {
|
||||
if (!data) {
|
||||
res.status(404).json({status: 'Not found'});
|
||||
}
|
||||
|
||||
// add properties needed for sampleIdCheck
|
||||
condition.treatment_template = data.treatment_template;
|
||||
condition.sample_id = data.sample_id;
|
||||
if (!await sampleIdCheck(condition, req, res, next)) return;
|
||||
if (condition.parameters) {
|
||||
condition.parameters = _.assign({}, data.parameters, condition.parameters);
|
||||
if (!_.isEqual(condition.parameters, data.parameters)) {
|
||||
if (!_.isEqual(condition.parameters, data.parameters)) { // parameters did not change
|
||||
condition.status = 0;
|
||||
}
|
||||
}
|
||||
@ -83,7 +84,7 @@ router.post('/condition/new', async (req, res, next) => {
|
||||
|
||||
condition.number = await numberGenerate(condition, treatmentData, next);
|
||||
if (!condition.number) return;
|
||||
condition.status = 0;
|
||||
condition.status = 0; // set status to new
|
||||
await new ConditionModel(condition).save((err, data) => {
|
||||
if (err) return next(err);
|
||||
res.json(ConditionValidate.output(data.toObject()));
|
||||
@ -105,8 +106,8 @@ async function sampleIdCheck (condition, req, res, next) { // validate sample_i
|
||||
return true;
|
||||
}
|
||||
|
||||
async function numberGenerate (condition, treatmentData, next) { // validate number, returns false if invalid
|
||||
const conditionData = await ConditionModel
|
||||
async function numberGenerate (condition, treatmentData, next) { // generate number, returns false on error
|
||||
const conditionData = await ConditionModel // find condition with highest number belonging to the same sample
|
||||
.find({sample_id: condition.sample_id, number: new RegExp('^' + treatmentData.number_prefix + '[0-9]+$', 'm')})
|
||||
.sort({number: -1})
|
||||
.limit(1)
|
||||
@ -114,7 +115,7 @@ async function numberGenerate (condition, treatmentData, next) { // validate nu
|
||||
.exec()
|
||||
.catch(err => next(err)) as any;
|
||||
if (conditionData instanceof Error) return false;
|
||||
return treatmentData.number_prefix + (conditionData.length > 0 ? Number(conditionData[0].number.replace(/[^0-9]+/g, '')) + 1 : 1);
|
||||
return treatmentData.number_prefix + (conditionData.length > 0 ? Number(conditionData[0].number.replace(/[^0-9]+/g, '')) + 1 : 1); // return new number
|
||||
}
|
||||
|
||||
async function treatmentCheck (condition, param, res, next) { // validate treatment template, returns false if invalid, otherwise template data
|
||||
|
@ -33,7 +33,6 @@ router.get('/materials/:group(new|deleted)', (req, res, next) => {
|
||||
}
|
||||
MaterialModel.find({status: status}).lean().exec((err, data) => {
|
||||
if (err) return next(err);
|
||||
console.log(data);
|
||||
res.json(_.compact(data.map(e => MaterialValidate.output(e)))); // validate all and filter null values from validation errors
|
||||
});
|
||||
});
|
||||
@ -68,7 +67,7 @@ router.put('/material/' + IdValidate.parameter(), (req, res, next) => {
|
||||
|
||||
// check for changes
|
||||
if (!_.isEqual(_.pick(IdValidate.stringify(materialData), _.keys(material)), material)) {
|
||||
material.status = 0;
|
||||
material.status = 0; // set status to new
|
||||
}
|
||||
|
||||
await MaterialModel.findByIdAndUpdate(req.params.id, material, {new: true}).lean().exec((err, data) => {
|
||||
@ -102,13 +101,12 @@ router.delete('/material/' + IdValidate.parameter(), (req, res, next) => {
|
||||
router.post('/material/new', async (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) return res400(error, res);
|
||||
|
||||
if (!await nameCheck(material, res, next)) return;
|
||||
|
||||
material.status = 0;
|
||||
material.status = 0; // set status to new
|
||||
await new MaterialModel(material).save((err, data) => {
|
||||
if (err) return next(err);
|
||||
res.json(MaterialValidate.output(data.toObject()));
|
||||
@ -120,7 +118,7 @@ module.exports = router;
|
||||
|
||||
|
||||
async function nameCheck (material, res, next) { // check if name was already taken
|
||||
const materialData = await MaterialModel.findOne({name: material.name}).lean().exec().catch(err => {next(err); return false;}) as any;
|
||||
const materialData = await MaterialModel.findOne({name: material.name}).lean().exec().catch(err => next(err)) as any;
|
||||
if (materialData instanceof Error) return false;
|
||||
if (materialData) { // could not find material_id
|
||||
res.status(400).json({status: 'Material name already taken'});
|
||||
|
@ -36,16 +36,20 @@ router.put('/measurement/' + IdValidate.parameter(), async (req, res, next) => {
|
||||
if (!data) {
|
||||
res.status(404).json({status: 'Not found'});
|
||||
}
|
||||
|
||||
// add properties needed for conditionIdCheck
|
||||
measurement.measurement_template = data.measurement_template;
|
||||
measurement.condition_id = data.condition_id;
|
||||
if (!await conditionIdCheck(measurement, req, res, next)) return;
|
||||
|
||||
// check for changes
|
||||
if (measurement.values) {
|
||||
measurement.values = _.assign({}, data.values, measurement.values);
|
||||
if (!_.isEqual(measurement.values, data.values)) {
|
||||
measurement.status = 0;
|
||||
measurement.status = 0; // set status to new
|
||||
}
|
||||
}
|
||||
|
||||
if (!await templateCheck(measurement, 'change', res, next)) return;
|
||||
await MeasurementModel.findByIdAndUpdate(req.params.id, measurement, {new: true}).lean().exec((err, data) => {
|
||||
if (err) return next(err);
|
||||
@ -99,7 +103,7 @@ async function conditionIdCheck (measurement, req, res, next) { // validate con
|
||||
return true;
|
||||
}
|
||||
|
||||
async function templateCheck (measurement, param, res, next) { // validate measurement_template and values
|
||||
async function templateCheck (measurement, param, res, next) { // validate measurement_template and values, param for new/change
|
||||
const templateData = await MeasurementTemplateModel.findById(measurement.measurement_template).lean().exec().catch(err => {next(err); return false;}) as any;
|
||||
if (!templateData) { // template not found
|
||||
res.status(400).json({status: 'Measurement template not available'});
|
||||
@ -108,7 +112,6 @@ async function templateCheck (measurement, param, res, next) { // validate meas
|
||||
|
||||
// validate values
|
||||
const {error, value: ignore} = ParametersValidate.input(measurement.values, templateData.parameters, param);
|
||||
console.log(error);
|
||||
if (error) {res400(error, res); return false;}
|
||||
return true;
|
||||
}
|
@ -4,7 +4,6 @@ import NoteModel from '../models/note';
|
||||
import NoteFieldModel from '../models/note_field';
|
||||
import TestHelper from "../test/helper";
|
||||
|
||||
// TODO: think again which parameters are required at POST
|
||||
|
||||
describe('/sample', () => {
|
||||
let server;
|
||||
@ -23,16 +22,16 @@ describe('/sample', () => {
|
||||
if (err) return done(err);
|
||||
const json = require('../test/db.json');
|
||||
should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status === 10).length);
|
||||
should(res.body).matchEach(material => {
|
||||
should(material).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'material_id', 'note_id', 'user_id');
|
||||
should(material).have.property('_id').be.type('string');
|
||||
should(material).have.property('number').be.type('string');
|
||||
should(material).have.property('type').be.type('string');
|
||||
should(material).have.property('color').be.type('string');
|
||||
should(material).have.property('batch').be.type('string');
|
||||
should(material).have.property('material_id').be.type('string');
|
||||
should(material).have.property('note_id');
|
||||
should(material).have.property('user_id').be.type('string');
|
||||
should(res.body).matchEach(sample => {
|
||||
should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'material_id', 'note_id', 'user_id');
|
||||
should(sample).have.property('_id').be.type('string');
|
||||
should(sample).have.property('number').be.type('string');
|
||||
should(sample).have.property('type').be.type('string');
|
||||
should(sample).have.property('color').be.type('string');
|
||||
should(sample).have.property('batch').be.type('string');
|
||||
should(sample).have.property('material_id').be.type('string');
|
||||
should(sample).have.property('note_id');
|
||||
should(sample).have.property('user_id').be.type('string');
|
||||
});
|
||||
done();
|
||||
});
|
||||
@ -70,6 +69,94 @@ describe('/sample', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /samples/{group}', () => {
|
||||
it('returns all new samples', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'get',
|
||||
url: '/samples/new',
|
||||
auth: {basic: 'admin'},
|
||||
httpStatus: 200
|
||||
}).end((err, res) => {
|
||||
if (err) return done(err);
|
||||
const json = require('../test/db.json');
|
||||
let asyncCounter = res.body.length;
|
||||
should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status === 0).length);
|
||||
should(res.body).matchEach(sample => {
|
||||
should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'material_id', 'note_id', 'user_id');
|
||||
should(sample).have.property('_id').be.type('string');
|
||||
should(sample).have.property('number').be.type('string');
|
||||
should(sample).have.property('type').be.type('string');
|
||||
should(sample).have.property('color').be.type('string');
|
||||
should(sample).have.property('batch').be.type('string');
|
||||
should(sample).have.property('material_id').be.type('string');
|
||||
should(sample).have.property('note_id');
|
||||
should(sample).have.property('user_id').be.type('string');
|
||||
SampleModel.findById(sample._id).lean().exec((err, data) => {
|
||||
should(data).have.property('status', 0);
|
||||
if (--asyncCounter === 0) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('returns all deleted samples', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'get',
|
||||
url: '/samples/deleted',
|
||||
auth: {basic: 'admin'},
|
||||
httpStatus: 200
|
||||
}).end((err, res) => {
|
||||
if (err) return done(err);
|
||||
const json = require('../test/db.json');
|
||||
let asyncCounter = res.body.length;
|
||||
should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status === -1).length);
|
||||
should(res.body).matchEach(sample => {
|
||||
should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'material_id', 'note_id', 'user_id');
|
||||
should(sample).have.property('_id').be.type('string');
|
||||
should(sample).have.property('number').be.type('string');
|
||||
should(sample).have.property('type').be.type('string');
|
||||
should(sample).have.property('color').be.type('string');
|
||||
should(sample).have.property('batch').be.type('string');
|
||||
should(sample).have.property('material_id').be.type('string');
|
||||
should(sample).have.property('note_id');
|
||||
should(sample).have.property('user_id').be.type('string');
|
||||
SampleModel.findById(sample._id).lean().exec((err, data) => {
|
||||
should(data).have.property('status', -1);
|
||||
if (--asyncCounter === 0) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('rejects requests from a write user', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'get',
|
||||
url: '/samples/new',
|
||||
auth: {basic: 'janedoe'},
|
||||
httpStatus: 403
|
||||
});
|
||||
});
|
||||
it('rejects an API key', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'get',
|
||||
url: '/samples/new',
|
||||
auth: {key: 'admin'},
|
||||
httpStatus: 401
|
||||
});
|
||||
});
|
||||
it('rejects unauthorized requests', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'get',
|
||||
url: '/samples/new',
|
||||
httpStatus: 401
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('PUT /sample/{id}', () => {
|
||||
it('returns the right sample', done => {
|
||||
TestHelper.request(server, done, {
|
||||
@ -194,12 +281,10 @@ describe('/sample', () => {
|
||||
}).end(err => {
|
||||
if (err) return done(err);
|
||||
NoteFieldModel.findOne({name: 'not allowed for new applications'}).lean().exec((err, data) => {
|
||||
console.log(data);
|
||||
if (err) return done(err);
|
||||
should(data).have.property('qty', 1);
|
||||
NoteFieldModel.findOne({name: 'field1'}).lean().exec((err, data) => {
|
||||
if (err) return done(err);
|
||||
console.log(data);
|
||||
should(data).have.property('qty', 1);
|
||||
done();
|
||||
});
|
||||
@ -233,7 +318,6 @@ describe('/sample', () => {
|
||||
if (err) return done (err);
|
||||
NoteModel.findById(res.body.note_id).lean().exec((err, data) => {
|
||||
if (err) return done (err);
|
||||
console.log(data);
|
||||
should(data).not.be.null();
|
||||
should(data).have.property('comment', 'Stoff gesperrt');
|
||||
should(data).have.property('sample_references').have.lengthOf(0);
|
||||
@ -448,7 +532,6 @@ describe('/sample', () => {
|
||||
setTimeout(() => { // background action takes some time before we can check
|
||||
NoteModel.findById('500000000000000000000003').lean().exec((err, data: any) => {
|
||||
if (err) return done(err);
|
||||
console.log(data);
|
||||
should(data).have.property('sample_references').with.lengthOf(1);
|
||||
should(data.sample_references[0].id.toString()).be.eql('400000000000000000000003');
|
||||
should(data.sample_references[0]).have.property('relation', 'part to sample');
|
||||
|
@ -22,6 +22,22 @@ router.get('/samples', (req, res, next) => {
|
||||
})
|
||||
});
|
||||
|
||||
router.get('/samples/:group(new|deleted)', (req, res, next) => {
|
||||
if (!req.auth(res, ['maintain', 'admin'], 'basic')) return;
|
||||
|
||||
let status;
|
||||
switch (req.params.group) {
|
||||
case 'new': status = 0;
|
||||
break;
|
||||
case 'deleted': status = -1;
|
||||
break;
|
||||
}
|
||||
SampleModel.find({status: status}).lean().exec((err, data) => {
|
||||
if (err) return next(err);
|
||||
res.json(_.compact(data.map(e => SampleValidate.output(e)))); // validate all and filter null values from validation errors
|
||||
})
|
||||
});
|
||||
|
||||
router.put('/sample/' + IdValidate.parameter(), (req, res, next) => {
|
||||
if (!req.auth(res, ['write', 'maintain', 'dev', 'admin'], 'basic')) return;
|
||||
|
||||
@ -33,6 +49,7 @@ router.put('/sample/' + IdValidate.parameter(), (req, res, next) => {
|
||||
if (!sampleData) {
|
||||
return res.status(404).json({status: 'Not found'});
|
||||
}
|
||||
|
||||
// only maintain and admin are allowed to edit other user's data
|
||||
if (sampleData.user_id.toString() !== req.authDetails.id && !req.auth(res, ['maintain', 'admin'], 'basic')) return;
|
||||
|
||||
@ -48,12 +65,12 @@ router.put('/sample/' + IdValidate.parameter(), (req, res, next) => {
|
||||
if (sampleData.note_id !== null) { // old notes data exists
|
||||
const data = await NoteModel.findById(sampleData.note_id).lean().exec().catch(err => {next(err);}) as any;
|
||||
if (data instanceof Error) return;
|
||||
newNotes = !_.isEqual(_.pick(IdValidate.stringify(data), _.keys(sample.notes)), sample.notes);
|
||||
newNotes = !_.isEqual(_.pick(IdValidate.stringify(data), _.keys(sample.notes)), sample.notes); // check if notes were changed
|
||||
if (newNotes) {
|
||||
if (data.hasOwnProperty('custom_fields')) { // update note_fields
|
||||
customFieldsChange(Object.keys(data.custom_fields), -1);
|
||||
}
|
||||
NoteModel.findByIdAndDelete(sampleData.note_id).lean().exec(err => { // delete old notes
|
||||
await NoteModel.findByIdAndDelete(sampleData.note_id).lean().exec(err => { // delete old notes
|
||||
if (err) return console.error(err);
|
||||
});
|
||||
}
|
||||
@ -74,7 +91,8 @@ router.put('/sample/' + IdValidate.parameter(), (req, res, next) => {
|
||||
if (!_.isEqual(_.pick(IdValidate.stringify(sampleData), _.keys(sample)), _.omit(sample, ['notes']))) {
|
||||
sample.status = 0;
|
||||
}
|
||||
SampleModel.findByIdAndUpdate(req.params.id, sample, {new: true}).lean().exec((err, data) => {
|
||||
|
||||
await SampleModel.findByIdAndUpdate(req.params.id, sample, {new: true}).lean().exec((err, data) => {
|
||||
if (err) return next(err);
|
||||
res.json(SampleValidate.output(data));
|
||||
});
|
||||
@ -90,12 +108,13 @@ router.delete('/sample/' + IdValidate.parameter(), (req, res, next) => {
|
||||
if (!sampleData) {
|
||||
return res.status(404).json({status: 'Not found'});
|
||||
}
|
||||
|
||||
// only maintain and admin are allowed to edit other user's data
|
||||
if (sampleData.user_id.toString() !== req.authDetails.id && !req.auth(res, ['maintain', 'admin'], 'basic')) return;
|
||||
|
||||
SampleModel.findByIdAndUpdate(req.params.id, {status: -1}).lean().exec(err => { // set sample status
|
||||
await SampleModel.findByIdAndUpdate(req.params.id, {status: -1}).lean().exec(err => { // set sample status
|
||||
if (err) return next(err);
|
||||
if (sampleData.note_id !== null) {
|
||||
if (sampleData.note_id !== null) { // handle notes
|
||||
NoteModel.findById(sampleData.note_id).lean().exec((err, data: any) => { // find notes to update note_fields
|
||||
if (err) return next(err);
|
||||
if (data.hasOwnProperty('custom_fields')) { // update note_fields
|
||||
@ -124,15 +143,15 @@ router.post('/sample/new', async (req, res, next) => {
|
||||
customFieldsChange(Object.keys(sample.notes.custom_fields), 1);
|
||||
}
|
||||
|
||||
sample.status = 0;
|
||||
sample.status = 0; // set status to new
|
||||
sample.number = await numberGenerate(sample, req, res, next);
|
||||
if (!sample.number) return;
|
||||
new NoteModel(sample.notes).save((err, data) => {
|
||||
|
||||
await new NoteModel(sample.notes).save((err, data) => { // save notes
|
||||
if (err) return next(err);
|
||||
delete sample.notes;
|
||||
sample.note_id = data._id;
|
||||
sample.user_id = req.authDetails.id;
|
||||
console.log(sample);
|
||||
new SampleModel(sample).save((err, data) => {
|
||||
if (err) return next(err);
|
||||
res.json(SampleValidate.output(data.toObject()));
|
||||
@ -153,7 +172,7 @@ router.get('/sample/notes/fields', (req, res, next) => {
|
||||
module.exports = router;
|
||||
|
||||
|
||||
async function numberGenerate (sample, req, res, next) { // validate number, returns false if invalid
|
||||
async function numberGenerate (sample, req, res, next) { // generate number, returns false on error
|
||||
const sampleData = await SampleModel
|
||||
.find({number: new RegExp('^' + req.authDetails.location + '[0-9]+$', 'm')})
|
||||
.lean()
|
||||
@ -180,7 +199,8 @@ async function materialCheck (sample, res, next, id = sample.material_id) { //
|
||||
function sampleRefCheck (sample, res, next) { // validate sample_references, resolves false for invalid reference
|
||||
return new Promise(resolve => {
|
||||
if (sample.notes.sample_references.length > 0) { // there are sample_references
|
||||
let referencesCount = sample.notes.sample_references.length;
|
||||
let referencesCount = sample.notes.sample_references.length; // count to keep track of running async operations
|
||||
|
||||
sample.notes.sample_references.forEach(reference => {
|
||||
SampleModel.findById(reference.id).lean().exec((err, data) => {
|
||||
if (err) {next(err); resolve(false)}
|
||||
@ -189,7 +209,7 @@ function sampleRefCheck (sample, res, next) { // validate sample_references, re
|
||||
return resolve(false);
|
||||
}
|
||||
referencesCount --;
|
||||
if (referencesCount <= 0) {
|
||||
if (referencesCount <= 0) { // all async requests done
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
@ -201,7 +221,7 @@ function sampleRefCheck (sample, res, next) { // validate sample_references, re
|
||||
});
|
||||
}
|
||||
|
||||
function customFieldsChange (fields, amount) {
|
||||
function customFieldsChange (fields, amount) { // update custom_fields and respective quantities
|
||||
fields.forEach(field => {
|
||||
NoteFieldModel.findOneAndUpdate({name: field}, {$inc: {qty: amount}}, {new: true}).lean().exec((err, data: any) => { // check if field exists
|
||||
if (err) return console.error(err);
|
||||
|
@ -200,7 +200,6 @@ describe('/template', () => {
|
||||
httpStatus: 200,
|
||||
req: {parameters: [{name: 'time', range: {type: 'array'}}]}
|
||||
}).end((err, res) => {
|
||||
console.log(res.body);
|
||||
if (err) return done(err);
|
||||
should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, number_prefix: 'A', parameters: [{name: 'time', range: {type: 'array'}}]});
|
||||
done();
|
||||
|
@ -14,7 +14,7 @@ const router = express.Router();
|
||||
router.get('/template/:collection(measurements|treatments)', (req, res, next) => {
|
||||
if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'basic')) return;
|
||||
|
||||
req.params.collection = req.params.collection.replace(/s$/g, '');
|
||||
req.params.collection = req.params.collection.replace(/s$/g, ''); // remove trailing s
|
||||
model(req).find({}).lean().exec((err, data) => {
|
||||
if (err) next (err);
|
||||
res.json(_.compact(data.map(e => TemplateValidate.output(e, req.params.collection)))); // validate all and filter null values from validation errors
|
||||
@ -52,8 +52,8 @@ router.put('/template/:collection(measurement|treatment)/' + IdValidate.paramete
|
||||
}
|
||||
|
||||
if (!_.isEqual(_.pick(templateData, _.keys(template)), template)) { // data was changed
|
||||
template.version = templateData.version + 1;
|
||||
await new (model(req))(_.assign({}, _.omit(templateData, ['_id', '__v']), template)).save((err, data) => {
|
||||
template.version = templateData.version + 1; // increase version
|
||||
await new (model(req))(_.assign({}, _.omit(templateData, ['_id', '__v']), template)).save((err, data) => { // save new template, fill with old properties
|
||||
if (err) next (err);
|
||||
res.json(TemplateValidate.output(data.toObject(), req.params.collection));
|
||||
});
|
||||
@ -73,7 +73,7 @@ router.post('/template/:collection(measurement|treatment)/new', async (req, res,
|
||||
if (!await numberPrefixCheck(template, req, res, next)) return;
|
||||
}
|
||||
|
||||
template.version = 1;
|
||||
template.version = 1; // set template version
|
||||
await new (model(req))(template).save((err, data) => {
|
||||
if (err) next (err);
|
||||
res.json(TemplateValidate.output(data.toObject(), req.params.collection));
|
||||
@ -84,7 +84,7 @@ router.post('/template/:collection(measurement|treatment)/new', async (req, res,
|
||||
module.exports = router;
|
||||
|
||||
|
||||
async function numberPrefixCheck (template, req, res, next) {
|
||||
async function numberPrefixCheck (template, req, res, next) { // check if number_prefix is available
|
||||
const data = await model(req).findOne({number_prefix: template.number_prefix}).lean().exec().catch(err => {next(err); return false;}) as any;
|
||||
if (data) {
|
||||
res.status(400).json({status: 'Number prefix already taken'});
|
||||
@ -93,6 +93,6 @@ async function numberPrefixCheck (template, req, res, next) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function model (req) {
|
||||
function model (req) { // return right template model
|
||||
return req.params.collection === 'treatment' ? TemplateTreatmentModel : TemplateMeasurementModel;
|
||||
}
|
@ -20,14 +20,10 @@ router.get('/users', (req, res) => {
|
||||
});
|
||||
|
||||
router.get('/user:username([/](?!key|new).?*|/?)', (req, res, next) => { // this path matches /user, /user/ and /user/xxx, but not /user/key or user/new. See https://forbeslindesay.github.io/express-route-tester/ for the generated regex
|
||||
req.params.username = req.params[0];
|
||||
if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'basic')) return;
|
||||
let username = req.authDetails.username;
|
||||
if (req.params.username !== undefined) {
|
||||
if (!req.auth(res, ['admin'], 'basic')) return;
|
||||
username = req.params.username;
|
||||
}
|
||||
|
||||
const username = getUsername(req, res);
|
||||
if (!username) return;
|
||||
UserModel.findOne({name: username}).lean().exec( (err, data:any) => {
|
||||
if (err) return next(err);
|
||||
if (data) {
|
||||
@ -39,14 +35,13 @@ router.get('/user:username([/](?!key|new).?*|/?)', (req, res, next) => { // thi
|
||||
});
|
||||
});
|
||||
|
||||
router.put('/user:username([/](?!key|new).?*|/?)', (req, res, next) => { // this path matches /user, /user/ and /user/xxx, but not /user/key or user/new
|
||||
req.params.username = req.params[0];
|
||||
router.put('/user:username([/](?!key|new).?*|/?)', async (req, res, next) => { // this path matches /user, /user/ and /user/xxx, but not /user/key or user/new
|
||||
if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'basic')) return;
|
||||
let username = req.authDetails.username;
|
||||
if (req.params.username !== undefined) {
|
||||
if (!req.auth(res, ['admin'], 'basic')) return;
|
||||
username = req.params.username;
|
||||
}
|
||||
|
||||
const username = getUsername(req, res);
|
||||
if (!username) return;
|
||||
console.log(username);
|
||||
|
||||
const {error, value: user} = UserValidate.input(req.body, 'change' + (req.authDetails.level === 'admin'? 'admin' : ''));
|
||||
if (error) return res400(error, res);
|
||||
|
||||
@ -56,45 +51,25 @@ router.put('/user:username([/](?!key|new).?*|/?)', (req, res, next) => { // thi
|
||||
|
||||
// check that user does not already exist if new name was specified
|
||||
if (user.hasOwnProperty('name') && user.name !== username) {
|
||||
UserModel.find({name: user.name}).lean().exec( (err, data:any) => {
|
||||
if (err) return next(err);
|
||||
if (data.length > 0 || UserValidate.isSpecialName(user.name)) {
|
||||
res.status(400).json({status: 'Username already taken'});
|
||||
return;
|
||||
}
|
||||
if (!await usernameCheck(user.name, res, next)) return;
|
||||
}
|
||||
|
||||
UserModel.findOneAndUpdate({name: username}, user, {new: true}).lean().exec( (err, data:any) => {
|
||||
if (err) return next(err);
|
||||
if (data) {
|
||||
res.json(UserValidate.output(data));
|
||||
}
|
||||
else {
|
||||
res.status(404).json({status: 'Not found'});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
UserModel.findOneAndUpdate({name: username}, user, {new: true}).lean().exec( (err, data:any) => {
|
||||
if (err) return next(err);
|
||||
if (data) {
|
||||
res.json(UserValidate.output(data)); // validate all and filter null values from validation errors
|
||||
}
|
||||
else {
|
||||
res.status(404).json({status: 'Not found'});
|
||||
}
|
||||
});
|
||||
}
|
||||
await UserModel.findOneAndUpdate({name: username}, user, {new: true}).lean().exec( (err, data:any) => {
|
||||
if (err) return next(err);
|
||||
if (data) {
|
||||
res.json(UserValidate.output(data));
|
||||
}
|
||||
else {
|
||||
res.status(404).json({status: 'Not found'});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
router.delete('/user:username([/](?!key|new).?*|/?)', (req, res, next) => { // this path matches /user, /user/ and /user/xxx, but not /user/key or user/new. See https://forbeslindesay.github.io/express-route-tester/ for the generated regex
|
||||
req.params.username = req.params[0];
|
||||
if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'basic')) return;
|
||||
let username = req.authDetails.username;
|
||||
if (req.params.username !== undefined) {
|
||||
if (!req.auth(res, ['admin'], 'basic')) return;
|
||||
username = req.params.username;
|
||||
}
|
||||
|
||||
const username = getUsername(req, res);
|
||||
if (!username) return;
|
||||
|
||||
UserModel.findOneAndDelete({name: username}).lean().exec( (err, data:any) => {
|
||||
if (err) return next(err);
|
||||
@ -116,7 +91,7 @@ router.get('/user/key', (req, res, next) => {
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/user/new', (req, res, next) => {
|
||||
router.post('/user/new', async (req, res, next) => {
|
||||
if (!req.auth(res, ['admin'], 'basic')) return;
|
||||
|
||||
// validate input
|
||||
@ -124,20 +99,14 @@ router.post('/user/new', (req, res, next) => {
|
||||
if (error) return res400(error, res);
|
||||
|
||||
// check that user does not already exist
|
||||
UserModel.find({name: user.name}).lean().exec( (err, data:any) => {
|
||||
if (err) return next(err);
|
||||
if (data.length > 0 || UserValidate.isSpecialName(user.name)) {
|
||||
res.status(400).json({status: 'Username already taken'});
|
||||
return;
|
||||
}
|
||||
if (!await usernameCheck(user.name, res, next)) return;
|
||||
|
||||
user.key = mongoose.Types.ObjectId(); // use object id as unique API key
|
||||
bcrypt.hash(user.pass, 10, (err, hash) => { // password hashing
|
||||
user.pass = hash;
|
||||
new UserModel(user).save((err, data) => { // store user
|
||||
if (err) return next(err);
|
||||
res.json(UserValidate.output(data.toObject()));
|
||||
});
|
||||
user.key = mongoose.Types.ObjectId(); // use object id as unique API key
|
||||
bcrypt.hash(user.pass, 10, (err, hash) => { // password hashing
|
||||
user.pass = hash;
|
||||
new UserModel(user).save((err, data) => { // store user
|
||||
if (err) return next(err);
|
||||
res.json(UserValidate.output(data.toObject()));
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -147,11 +116,14 @@ router.post('/user/passreset', (req, res, next) => {
|
||||
UserModel.find({name: req.body.name, email: req.body.email}).lean().exec( (err, data: any) => {
|
||||
if (err) return next(err);
|
||||
if (data.length === 1) { // it exists
|
||||
const newPass = Math.random().toString(36).substring(2);
|
||||
const newPass = Math.random().toString(36).substring(2); // generate temporary password
|
||||
bcrypt.hash(newPass, 10, (err, hash) => { // password hashing
|
||||
if (err) return next(err);
|
||||
|
||||
UserModel.findByIdAndUpdate(data[0]._id, {pass: hash}, err => { // write new password
|
||||
if (err) return next(err);
|
||||
|
||||
// send email
|
||||
mail(data[0].email, 'Your new password for the DFOP database', 'Hi, <br><br> You requested to reset your password.<br>Your new password is:<br><br>' + newPass + '<br><br>If you did not request a password reset, talk to the sysadmin quickly!<br><br>Have a nice day.<br><br>The DFOP team', err => {
|
||||
if (err) return next(err);
|
||||
res.json({status: 'OK'});
|
||||
@ -166,4 +138,27 @@ router.post('/user/passreset', (req, res, next) => {
|
||||
});
|
||||
|
||||
|
||||
module.exports = router;
|
||||
module.exports = router;
|
||||
|
||||
function getUsername (req, res) { // returns username or false if action is not allowed
|
||||
req.params.username = req.params[0]; // because of path regex
|
||||
if (req.params.username !== undefined) { // different username than request user
|
||||
if (!req.auth(res, ['admin'], 'basic')) return false;
|
||||
return req.params.username;
|
||||
}
|
||||
else {
|
||||
return req.authDetails.username;
|
||||
}
|
||||
}
|
||||
|
||||
async function usernameCheck (name, res, next) { // check if username is already taken
|
||||
const userData = await UserModel.findOne({name: name}).lean().exec().catch(err => next(err)) as any;
|
||||
if (userData instanceof Error) return false;
|
||||
console.log(userData);
|
||||
console.log(UserValidate.isSpecialName(name));
|
||||
if (userData || UserValidate.isSpecialName(name)) {
|
||||
res.status(400).json({status: 'Username already taken'});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
@ -18,7 +18,7 @@ export default class ConditionValidate {
|
||||
)
|
||||
}
|
||||
|
||||
static input (data, param) {
|
||||
static input (data, param) { // validate input, set param to 'new' to make all attributes required
|
||||
if (param === 'new') {
|
||||
return Joi.object({
|
||||
sample_id: IdValidate.get().required(),
|
||||
@ -36,7 +36,7 @@ export default class ConditionValidate {
|
||||
}
|
||||
}
|
||||
|
||||
static output (data) {
|
||||
static output (data) { // validate output and strip unwanted properties, returns null if not valid
|
||||
data = IdValidate.stringify(data);
|
||||
const {value, error} = Joi.object({
|
||||
_id: IdValidate.get(),
|
||||
|
@ -3,11 +3,11 @@ 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 () {
|
||||
static get () { // return joi validation
|
||||
return this.id;
|
||||
}
|
||||
|
||||
static valid (id) {
|
||||
static valid (id) { // validate id
|
||||
return this.id.validate(id).error === undefined;
|
||||
}
|
||||
|
||||
@ -15,11 +15,14 @@ export default class IdValidate {
|
||||
return ':id([0-9a-f]{24})';
|
||||
}
|
||||
|
||||
static stringify (data) {
|
||||
static stringify (data) { // convert all ObjectID objects to plain strings
|
||||
Object.keys(data).forEach(key => {
|
||||
if (data[key] !== null && data[key].hasOwnProperty('_bsontype') && data[key]._bsontype === 'ObjectID') {
|
||||
if (data[key] !== null && data[key].hasOwnProperty('_bsontype') && data[key]._bsontype === 'ObjectID') { // stringify id
|
||||
data[key] = data[key].toString();
|
||||
}
|
||||
else if (typeof data[key] === 'object' && data[key] !== null) { // deeper into recursion
|
||||
data[key] = this.stringify(data[key]);
|
||||
}
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ export default class MaterialValidate { // validate input for material
|
||||
}))
|
||||
};
|
||||
|
||||
static input (data, param) { // validate data, param: new(everything required)/change(available attributes are validated)
|
||||
static input (data, param) { // validate input, set param to 'new' to make all attributes required
|
||||
if (param === 'new') {
|
||||
return joi.object({
|
||||
name: this.material.name.required(),
|
||||
@ -68,7 +68,7 @@ export default class MaterialValidate { // validate input for material
|
||||
}
|
||||
}
|
||||
|
||||
static output (data) { // validate output from database for needed properties, strip everything else
|
||||
static output (data) { // validate output and strip unwanted properties, returns null if not valid
|
||||
data = IdValidate.stringify(data);
|
||||
const {value, error} = joi.object({
|
||||
_id: IdValidate.get(),
|
||||
|
@ -15,7 +15,7 @@ export default class MeasurementValidate {
|
||||
)
|
||||
};
|
||||
|
||||
static input (data, param) {
|
||||
static input (data, param) { // validate input, set param to 'new' to make all attributes required
|
||||
if (param === 'new') {
|
||||
return Joi.object({
|
||||
condition_id: IdValidate.get().required(),
|
||||
@ -33,7 +33,7 @@ export default class MeasurementValidate {
|
||||
}
|
||||
}
|
||||
|
||||
static output (data) {
|
||||
static output (data) { // validate output and strip unwanted properties, returns null if not valid
|
||||
data = IdValidate.stringify(data);
|
||||
const {value, error} = Joi.object({
|
||||
_id: IdValidate.get(),
|
||||
|
@ -8,7 +8,7 @@ export default class NoteFieldValidate {
|
||||
qty: Joi.number()
|
||||
};
|
||||
|
||||
static output (data) {
|
||||
static output (data) { // validate output and strip unwanted properties, returns null if not valid
|
||||
const {value, error} = Joi.object({
|
||||
name: this.note_field.name,
|
||||
qty: this.note_field.qty
|
||||
|
@ -4,7 +4,7 @@ export default class ParametersValidate {
|
||||
static input (data, parameters, param) { // data to validate, parameters from template, param: 'new', 'change'
|
||||
let joiObject = {};
|
||||
parameters.forEach(parameter => {
|
||||
if (parameter.range.hasOwnProperty('values')) {
|
||||
if (parameter.range.hasOwnProperty('values')) { // append right validation method according to parameter
|
||||
joiObject[parameter.name] = Joi.alternatives()
|
||||
.try(Joi.string().max(128), Joi.number(), Joi.boolean())
|
||||
.valid(...parameter.range.values);
|
||||
|
@ -1,3 +1,5 @@
|
||||
// respond with 400 and include error details from the joi validation
|
||||
|
||||
export default function res400 (error, res) {
|
||||
res.status(400).json({status: 'Invalid body format', details: error.details[0].message});
|
||||
}
|
@ -41,7 +41,7 @@ export default class SampleValidate {
|
||||
})
|
||||
};
|
||||
|
||||
static input (data, param) { // validate data, param: new(everything required)/change(available attributes are validated)
|
||||
static input (data, param) { // validate input, set param to 'new' to make all attributes required
|
||||
if (param === 'new') {
|
||||
return Joi.object({
|
||||
color: this.sample.color.required(),
|
||||
@ -65,7 +65,7 @@ export default class SampleValidate {
|
||||
}
|
||||
}
|
||||
|
||||
static output (data) {
|
||||
static output (data) { // validate output and strip unwanted properties, returns null if not valid
|
||||
data = IdValidate.stringify(data);
|
||||
const {value, error} = Joi.object({
|
||||
_id: IdValidate.get(),
|
||||
|
@ -43,7 +43,7 @@ export default class TemplateValidate {
|
||||
)
|
||||
};
|
||||
|
||||
static input (data, param, template) { // validate data, param: new(everything required)/change(available attributes are validated)
|
||||
static input (data, param, template) { // validate input, set param to 'new' to make all attributes required
|
||||
if (param === 'new') {
|
||||
if (template === 'treatment') {
|
||||
return Joi.object({
|
||||
@ -79,10 +79,10 @@ export default class TemplateValidate {
|
||||
}
|
||||
}
|
||||
|
||||
static output (data, template) { // validate output from database for needed properties, strip everything else
|
||||
static output (data, template) { // validate output and strip unwanted properties, returns null if not valid
|
||||
data = IdValidate.stringify(data);
|
||||
let joiObject;
|
||||
if (template === 'treatment') {
|
||||
if (template === 'treatment') { // differentiate between measurement and treatment (has number_prefix) template
|
||||
joiObject = {
|
||||
_id: IdValidate.get(),
|
||||
name: this.template.name,
|
||||
|
@ -33,7 +33,7 @@ export default class UserValidate { // validate input for user
|
||||
|
||||
private static specialUsernames = ['admin', 'user', 'key', 'new', 'passreset']; // names a user cannot take
|
||||
|
||||
static input (data, param) {
|
||||
static input (data, param) { // validate input, set param to 'new' to make all attributes required
|
||||
if (param === 'new') {
|
||||
return Joi.object({
|
||||
name: this.user.name.required(),
|
||||
@ -68,7 +68,7 @@ export default class UserValidate { // validate input for user
|
||||
}
|
||||
}
|
||||
|
||||
static output (data) { // validate output from database for needed properties, strip everything else
|
||||
static output (data) { // validate output and strip unwanted properties, returns null if not valid
|
||||
data = IdValidate.stringify(data);
|
||||
const {value, error} = Joi.object({
|
||||
_id: IdValidate.get(),
|
||||
|
Reference in New Issue
Block a user