only allowed latest template version and allowed admin to set sample number
This commit is contained in:
		@@ -586,14 +586,24 @@ describe('/measurement', () => {
 | 
			
		||||
        done();
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('rejects no values', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'post',
 | 
			
		||||
        url: '/measurement/new',
 | 
			
		||||
        auth: {basic: 'janedoe'},
 | 
			
		||||
        httpStatus: 400,
 | 
			
		||||
        req: {sample_id: '400000000000000000000001', values: {}, measurement_template: '300000000000000000000002'},
 | 
			
		||||
        res: {status: 'At least one value is required'}
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('rejects a value not in the value range', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'post',
 | 
			
		||||
        url: '/measurement/new',
 | 
			
		||||
        auth: {basic: 'janedoe'},
 | 
			
		||||
        httpStatus: 400,
 | 
			
		||||
        req: {sample_id: '400000000000000000000001', values: {val1: 4}, measurement_template: '300000000000000000000003'},
 | 
			
		||||
        res: {status: 'Invalid body format', details: '"val1" must be one of [1, 2, 3, null]'}
 | 
			
		||||
        req: {sample_id: '400000000000000000000001', values: {val2: 5}, measurement_template: '300000000000000000000004'},
 | 
			
		||||
        res: {status: 'Invalid body format', details: '"val2" must be one of [1, 2, 3, 4, null]'}
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('rejects a value below minimum range', done => {
 | 
			
		||||
@@ -664,6 +674,16 @@ describe('/measurement', () => {
 | 
			
		||||
        done();
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('rejects an old version of a measurement template', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'post',
 | 
			
		||||
        url: '/measurement/new',
 | 
			
		||||
        auth: {basic: 'janedoe'},
 | 
			
		||||
        httpStatus: 400,
 | 
			
		||||
        req: {sample_id: '400000000000000000000001', values: {val1: 2}, measurement_template: '300000000000000000000003'},
 | 
			
		||||
        res: {status: 'Old template version not allowed'}
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('rejects an API key', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'post',
 | 
			
		||||
 
 | 
			
		||||
@@ -130,6 +130,14 @@ async function templateCheck (measurement, param, res, next) {  // validate meas
 | 
			
		||||
 | 
			
		||||
  // fill not given values for new measurements
 | 
			
		||||
  if (param === 'new') {
 | 
			
		||||
    // get all template versions and check if given is latest
 | 
			
		||||
    const templateVersions = await MeasurementTemplateModel.find({first_id: templateData.first_id}).sort({version: -1}).lean().exec().catch(err => next(err)) as any;
 | 
			
		||||
    if (templateVersions instanceof Error) return false;
 | 
			
		||||
    if (measurement.measurement_template !== templateVersions[0]._id.toString()) {  // template not latest
 | 
			
		||||
      res.status(400).json({status: 'Old template version not allowed'});
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (Object.keys(measurement.values).length === 0) {
 | 
			
		||||
      res.status(400).json({status: 'At least one value is required'});
 | 
			
		||||
      return false
 | 
			
		||||
 
 | 
			
		||||
@@ -454,6 +454,16 @@ describe('/sample', () => {
 | 
			
		||||
        res: {status: 'Color not available for material'}
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('rejects an undefined color for the same material', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'put',
 | 
			
		||||
        url: '/sample/400000000000000000000001',
 | 
			
		||||
        auth: {basic: 'janedoe'},
 | 
			
		||||
        httpStatus: 400,
 | 
			
		||||
        req: {type: 'part', color: 'signalviolet', batch: '114531', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
			
		||||
        res: {status: 'Color not available for material'}
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('rejects an unknown material id', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'put',
 | 
			
		||||
@@ -573,6 +583,26 @@ describe('/sample', () => {
 | 
			
		||||
        res: {_id: '400000000000000000000006', number: 'Rng36', type: 'granulate', color: 'black', batch: '', condition: {}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'}
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('rejects an old version of a condition template', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'put',
 | 
			
		||||
        url: '/sample/400000000000000000000001',
 | 
			
		||||
        auth: {basic: 'janedoe'},
 | 
			
		||||
        httpStatus: 400,
 | 
			
		||||
        req: {condition: {p1: 36, condition_template: '200000000000000000000004'}},
 | 
			
		||||
        res: {status: 'Old template version not allowed'}
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('allows keeping an old version of a condition template', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'put',
 | 
			
		||||
        url: '/sample/400000000000000000000004',
 | 
			
		||||
        auth: {basic: 'admin'},
 | 
			
		||||
        httpStatus: 200,
 | 
			
		||||
        req: {condition: {p1: 36, condition_template: '200000000000000000000004'}},
 | 
			
		||||
        res: {_id: '400000000000000000000004', number: '32', type: 'granulate', color: 'black', batch: '1653000308', condition: {p1: 36, condition_template: '200000000000000000000004'}, material_id: '100000000000000000000005', note_id: '500000000000000000000003', user_id: '000000000000000000000003'}
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('rejects an changing back to an empty condition', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'put',
 | 
			
		||||
@@ -1108,7 +1138,7 @@ describe('/sample', () => {
 | 
			
		||||
        res: {status: 'Material not available'}
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('rejects a sample number', done => {
 | 
			
		||||
    it('rejects a sample number for a write user', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'post',
 | 
			
		||||
        url: '/sample/new',
 | 
			
		||||
@@ -1118,6 +1148,38 @@ describe('/sample', () => {
 | 
			
		||||
        res: {status: 'Invalid body format', details: '"number" is not allowed'}
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('allows a sample number for an admin user', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'post',
 | 
			
		||||
        url: '/sample/new',
 | 
			
		||||
        auth: {basic: 'admin'},
 | 
			
		||||
        httpStatus: 200,
 | 
			
		||||
        req: {number: 'Rng34', color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
			
		||||
      }).end((err, res) => {
 | 
			
		||||
        if (err) return done (err);
 | 
			
		||||
        should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id');
 | 
			
		||||
        should(res.body).have.property('_id').be.type('string');
 | 
			
		||||
        should(res.body).have.property('number', 'Rng34');
 | 
			
		||||
        should(res.body).have.property('color', 'black');
 | 
			
		||||
        should(res.body).have.property('type', 'granulate');
 | 
			
		||||
        should(res.body).have.property('batch', '1560237365');
 | 
			
		||||
        should(res.body).have.property('condition', {});
 | 
			
		||||
        should(res.body).have.property('material_id', '100000000000000000000001');
 | 
			
		||||
        should(res.body).have.property('note_id').be.type('string');
 | 
			
		||||
        should(res.body).have.property('user_id', '000000000000000000000003');
 | 
			
		||||
        done();
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('rejects an existing sample number for an admin user', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'post',
 | 
			
		||||
        url: '/sample/new',
 | 
			
		||||
        auth: {basic: 'admin'},
 | 
			
		||||
        httpStatus: 400,
 | 
			
		||||
        req: {number: 'Rng33', color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
			
		||||
        res: {status: 'Sample number already taken'}
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('rejects an invalid sample reference', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'post',
 | 
			
		||||
@@ -1208,6 +1270,16 @@ describe('/sample', () => {
 | 
			
		||||
        res: {status: 'Condition template not available'}
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('rejects an old version of a condition template', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'post',
 | 
			
		||||
        url: '/sample/new',
 | 
			
		||||
        auth: {basic: 'janedoe'},
 | 
			
		||||
        httpStatus: 400,
 | 
			
		||||
        req: {color: 'black', type: 'granulate', batch: '1560237365', condition: {p1: 36, condition_template: '200000000000000000000004'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
 | 
			
		||||
        res: {status: 'Old template version not allowed'}
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('rejects a missing color', done => {
 | 
			
		||||
      TestHelper.request(server, done, {
 | 
			
		||||
        method: 'post',
 | 
			
		||||
 
 | 
			
		||||
@@ -90,7 +90,7 @@ router.put('/sample/' + IdValidate.parameter(), (req, res, next) => {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (sample.hasOwnProperty('condition') && !(_.isEmpty(sample.condition) && _.isEmpty(sampleData.condition))) {  // do not execute check if condition is and was empty
 | 
			
		||||
      if (!await conditionCheck(sample.condition, 'change', res, next)) return;
 | 
			
		||||
      if (!await conditionCheck(sample.condition, 'change', res, next, sampleData.condition.condition_template.toString() !== sample.condition.condition_template)) return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (sample.hasOwnProperty('notes')) {
 | 
			
		||||
@@ -217,7 +217,7 @@ router.post('/sample/new', async (req, res, next) => {
 | 
			
		||||
    req.body.condition = {};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const {error, value: sample} = SampleValidate.input(req.body, 'new');
 | 
			
		||||
  const {error, value: sample} = SampleValidate.input(req.body, 'new' + (req.authDetails.level === 'admin' ? '-admin' : ''));
 | 
			
		||||
  if (error) return res400(error, res);
 | 
			
		||||
 | 
			
		||||
  if (!await materialCheck(sample, res, next)) return;
 | 
			
		||||
@@ -232,7 +232,12 @@ router.post('/sample/new', async (req, res, next) => {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  sample.status = globals.status.new;  // set status to new
 | 
			
		||||
  sample.number = await numberGenerate(sample, req, res, next);
 | 
			
		||||
  if (sample.hasOwnProperty('number')) {
 | 
			
		||||
    if (!await numberCheck(sample, res, next)) return;
 | 
			
		||||
  }
 | 
			
		||||
  else {
 | 
			
		||||
    sample.number = await numberGenerate(sample, req, res, next);
 | 
			
		||||
  }
 | 
			
		||||
  if (!sample.number) return;
 | 
			
		||||
 | 
			
		||||
  await new NoteModel(sample.notes).save((err, data) => {  // save notes
 | 
			
		||||
@@ -272,6 +277,15 @@ async function numberGenerate (sample, req, res, next) {  // generate number in
 | 
			
		||||
  return req.authDetails.location + (sampleData ? Number(sampleData.number.replace(/[^0-9]+/g, '')) + 1 : 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function numberCheck(sample, res, next) {
 | 
			
		||||
  const sampleData = await SampleModel.findOne({number: sample.number}).lean().exec().catch(err => {next(err); return false;});
 | 
			
		||||
  if (sampleData) {  // found entry with sample number
 | 
			
		||||
    res.status(400).json({status: 'Sample number already taken'});
 | 
			
		||||
    return false
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function materialCheck (sample, res, next, id = sample.material_id) {  // validate material_id and color, returns false if invalid
 | 
			
		||||
  const materialData = await MaterialModel.findById(id).lean().exec().catch(err => next(err)) as any;
 | 
			
		||||
  if (materialData instanceof Error) return false;
 | 
			
		||||
@@ -286,7 +300,7 @@ async function materialCheck (sample, res, next, id = sample.material_id) {  //
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function conditionCheck (condition, param, res, next) {  // validate treatment template, returns false if invalid, otherwise template data
 | 
			
		||||
async function conditionCheck (condition, param, res, next, checkVersion = true) {  // validate treatment template, returns false if invalid, otherwise template data
 | 
			
		||||
  if (!condition.condition_template || !IdValidate.valid(condition.condition_template)) {  // template id not found
 | 
			
		||||
    res.status(400).json({status: 'Condition template not available'});
 | 
			
		||||
    return false;
 | 
			
		||||
@@ -298,6 +312,16 @@ async function conditionCheck (condition, param, res, next) {  // validate treat
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (checkVersion) {
 | 
			
		||||
    // get all template versions and check if given is latest
 | 
			
		||||
    const conditionVersions = await ConditionTemplateModel.find({first_id: conditionData.first_id}).sort({version: -1}).lean().exec().catch(err => next(err)) as any;
 | 
			
		||||
    if (conditionVersions instanceof Error) return false;
 | 
			
		||||
    if (condition.condition_template !== conditionVersions[0]._id.toString()) {  // template not latest
 | 
			
		||||
      res.status(400).json({status: 'Old template version not allowed'});
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // validate parameters
 | 
			
		||||
  const {error, value: ignore} = ParametersValidate.input(_.omit(condition, 'condition_template'), conditionData.parameters, param);
 | 
			
		||||
  if (error) {res400(error, res); return false;}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,6 @@ import TemplateConditionModel from '../models/condition_template';
 | 
			
		||||
import TemplateMeasurementModel from '../models/measurement_template';
 | 
			
		||||
import TestHelper from "../test/helper";
 | 
			
		||||
 | 
			
		||||
// TODO: do not allow usage of old templates for new samples
 | 
			
		||||
 | 
			
		||||
describe('/template', () => {
 | 
			
		||||
  let server;
 | 
			
		||||
@@ -644,7 +643,7 @@ describe('/template', () => {
 | 
			
		||||
          req: {parameters: [{name: 'weight %', range: {}}]}
 | 
			
		||||
        }).end((err, res) => {
 | 
			
		||||
          if (err) return done(err);
 | 
			
		||||
          should(_.omit(res.body, '_id')).be.eql({name: 'kf', version: 3, parameters: [{name: 'weight %', range: {}}]});
 | 
			
		||||
          should(_.omit(res.body, '_id')).be.eql({name: 'kf', version: 2, parameters: [{name: 'weight %', range: {}}]});
 | 
			
		||||
          done();
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
 
 | 
			
		||||
@@ -67,6 +67,17 @@ export default class SampleValidate {
 | 
			
		||||
        notes: this.sample.notes,
 | 
			
		||||
      }).validate(data);
 | 
			
		||||
    }
 | 
			
		||||
    else if (param === 'new-admin') {
 | 
			
		||||
      return Joi.object({
 | 
			
		||||
        number: this.sample.number.required(),
 | 
			
		||||
        color: this.sample.color.required(),
 | 
			
		||||
        type: this.sample.type.required(),
 | 
			
		||||
        batch: this.sample.batch.required(),
 | 
			
		||||
        condition: this.sample.condition.required(),
 | 
			
		||||
        material_id: IdValidate.get().required(),
 | 
			
		||||
        notes: this.sample.notes.required()
 | 
			
		||||
      }).validate(data);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      return{error: 'No parameter specified!', value: {}};
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user