From fe6e82f00bbae5eefd07d614f35dac8e1db8b6a8 Mon Sep 17 00:00:00 2001 From: VLE2FE Date: Mon, 18 May 2020 09:58:15 +0200 Subject: [PATCH] sample number generation --- api/schemas.yaml | 1 + src/helpers/authorize.ts | 9 +-- src/routes/condition.spec.ts | 31 ++++++++-- src/routes/condition.ts | 5 +- src/routes/sample.spec.ts | 105 +++++++++++++++++++--------------- src/routes/sample.ts | 23 ++++---- src/routes/validate/sample.ts | 2 - src/test/db.json | 19 ++++-- src/test/helper.ts | 3 +- 9 files changed, 121 insertions(+), 77 deletions(-) diff --git a/api/schemas.yaml b/api/schemas.yaml index 3be9b11..c872443 100644 --- a/api/schemas.yaml +++ b/api/schemas.yaml @@ -16,6 +16,7 @@ SampleProperties: properties: number: type: string + readOnly: true example: Rng172 type: type: string diff --git a/src/helpers/authorize.ts b/src/helpers/authorize.ts index e2f626a..c2404a4 100644 --- a/src/helpers/authorize.ts +++ b/src/helpers/authorize.ts @@ -9,7 +9,7 @@ import UserModel from '../models/user'; module.exports = async (req, res, next) => { let givenMethod = ''; // authorization method given by client, basic taken preferred - let user = {name: '', level: '', id: ''}; // user object + let user = {name: '', level: '', id: '', location: ''}; // user object // test authentications const userBasic = await basic(req, next); @@ -46,7 +46,8 @@ module.exports = async (req, res, next) => { method: givenMethod, username: user.name, level: user.level, - id: user.id + id: user.id, + location: user.location }; next(); @@ -63,7 +64,7 @@ function basic (req, next): any { // checks basic auth and returns changed user bcrypt.compare(auth.pass, data[0].pass, (err, res) => { // check password if (err) return next(err); if (res === true) { - resolve({level: data[0].level, name: data[0].name, id: data[0]._id.toString()}); + resolve({level: data[0].level, name: data[0].name, id: data[0]._id.toString(), location: data[0].location}); } else { resolve(null); @@ -87,7 +88,7 @@ function key (req, next): any { // checks API key and returns changed user obje UserModel.find({key: req.query.key}).lean().exec( (err, data: any) => { // find user if (err) return next(err); if (data.length === 1) { // one user found - resolve({level: data[0].level, name: data[0].name, id: data[0]._id.toString()}); + resolve({level: data[0].level, name: data[0].name, id: data[0]._id.toString(), location: data[0].location}); } else { resolve(null); diff --git a/src/routes/condition.spec.ts b/src/routes/condition.spec.ts index 8b4a73c..ef01c89 100644 --- a/src/routes/condition.spec.ts +++ b/src/routes/condition.spec.ts @@ -253,7 +253,7 @@ describe('/condition', () => { if (err) return done(err); should(data).have.only.keys('_id', 'sample_id', 'number', 'parameters', 'treatment_template', 'status', '__v'); should(data.sample_id.toString()).be.eql('400000000000000000000001'); - should(data).have.property('number', 'A6'); + should(data).have.property('number', 'A2'); should(data.treatment_template.toString()).be.eql('200000000000000000000001'); should(data).have.property('status', -1); should(data).have.property('parameters'); @@ -346,7 +346,7 @@ describe('/condition', () => { should(res.body).have.only.keys('_id', 'sample_id', 'number', 'parameters', 'treatment_template'); should(res.body).have.property('_id').be.type('string'); should(res.body).have.property('sample_id', '400000000000000000000002'); - should(res.body).have.property('number', 'A7'); + should(res.body).have.property('number', 'A2'); should(res.body).have.property('treatment_template', '200000000000000000000001'); should(res.body).have.property('parameters'); should(res.body.parameters).have.property('material', 'hot air'); @@ -367,7 +367,30 @@ describe('/condition', () => { if (err) return done(err); should(data).have.only.keys('_id', 'sample_id', 'number', 'parameters', 'treatment_template', 'status', '__v'); should(data.sample_id.toString()).be.eql('400000000000000000000002'); - should(data).have.property('number', 'A7'); + should(data).have.property('number', 'A2'); + should(data.treatment_template.toString()).be.eql('200000000000000000000001'); + should(data).have.property('status', 0); + should(data).have.property('parameters'); + should(data.parameters).have.property('material', 'hot air'); + should(data.parameters).have.property('weeks', 10); + done(); + }); + }); + }); + it('stores the first condition as 1', done => { + TestHelper.request(server, done, { + method: 'post', + url: '/condition/new', + auth: {basic: 'admin'}, + httpStatus: 200, + req: {sample_id: '400000000000000000000003', parameters: {material: 'hot air', weeks: 10}, treatment_template: '200000000000000000000001'} + }).end((err, res) => { + if (err) return done(err); + ConditionModel.findById(res.body._id).lean().exec((err, data: any) => { + if (err) return done(err); + should(data).have.only.keys('_id', 'sample_id', 'number', 'parameters', 'treatment_template', 'status', '__v'); + should(data.sample_id.toString()).be.eql('400000000000000000000003'); + should(data).have.property('number', 'A1'); should(data.treatment_template.toString()).be.eql('200000000000000000000001'); should(data).have.property('status', 0); should(data).have.property('parameters'); @@ -518,7 +541,7 @@ describe('/condition', () => { should(res.body).have.only.keys('_id', 'sample_id', 'number', 'parameters', 'treatment_template'); should(res.body).have.property('_id').be.type('string'); should(res.body).have.property('sample_id', '400000000000000000000002'); - should(res.body).have.property('number', 'A7'); + should(res.body).have.property('number', 'A2'); should(res.body).have.property('treatment_template', '200000000000000000000001'); should(res.body).have.property('parameters'); should(res.body.parameters).have.property('material', 'hot air'); diff --git a/src/routes/condition.ts b/src/routes/condition.ts index 1e54a00..71dc27b 100644 --- a/src/routes/condition.ts +++ b/src/routes/condition.ts @@ -107,15 +107,14 @@ async function sampleIdCheck (condition, req, res, next) { // validate sample_i async function numberGenerate (condition, treatmentData, next) { // validate number, returns false if invalid const conditionData = await ConditionModel - .find({number: new RegExp('^' + treatmentData.number_prefix + '[0-9]+$', 'm')}) + .find({sample_id: condition.sample_id, number: new RegExp('^' + treatmentData.number_prefix + '[0-9]+$', 'm')}) .sort({number: -1}) .limit(1) .lean() .exec() .catch(err => next(err)) as any; if (conditionData instanceof Error) return false; - console.log(conditionData); - return treatmentData.number_prefix + (Number(conditionData[0].number.replace(/[^0-9]+/g, '')) + 1); + return treatmentData.number_prefix + (conditionData.length > 0 ? Number(conditionData[0].number.replace(/[^0-9]+/g, '')) + 1 : 1); } async function treatmentCheck (condition, param, res, next) { // validate treatment template, returns false if invalid, otherwise template data diff --git a/src/routes/sample.spec.ts b/src/routes/sample.spec.ts index 8efce68..581ee0c 100644 --- a/src/routes/sample.spec.ts +++ b/src/routes/sample.spec.ts @@ -3,7 +3,7 @@ import SampleModel from '../models/sample'; import NoteModel from '../models/note'; import NoteFieldModel from '../models/note_field'; import TestHelper from "../test/helper"; -// TODO: generate sample number + // TODO: think again which parameters are required at POST describe('/sample', () => { @@ -87,7 +87,7 @@ describe('/sample', () => { url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 200, - req: {number: '1', type: 'granulate', color: 'black', batch: '', material_id: '100000000000000000000004', notes: {}} + req: {type: 'granulate', color: 'black', batch: '', material_id: '100000000000000000000004', notes: {}} }).end((err, res) => { if (err) return done(err); should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'}); @@ -156,14 +156,14 @@ describe('/sample', () => { url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 200, - req: {number: '10', type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}} + req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}} }).end(err => { if (err) return done (err); SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => { if (err) return done (err); should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'material_id', 'note_id', 'user_id', 'status', '__v'); should(data).have.property('_id'); - should(data).have.property('number', '10'); + should(data).have.property('number', '1'); should(data).have.property('color', 'signalviolet'); should(data).have.property('type', 'part'); should(data).have.property('batch', '114531'); @@ -228,7 +228,7 @@ describe('/sample', () => { url: '/sample/400000000000000000000002', auth: {basic: 'janedoe'}, httpStatus: 200, - req: {number: '111'} + req: {type: 'part'} }).end((err, res) => { if (err) return done (err); NoteModel.findById(res.body.note_id).lean().exec((err, data) => { @@ -263,7 +263,7 @@ describe('/sample', () => { url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 400, - req: {number: '10', type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, + req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Color not available for material'} }); }); @@ -273,18 +273,18 @@ describe('/sample', () => { url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 400, - req: {number: '10', type: 'part', color: 'signalviolet', batch: '114531', material_id: '000000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, + req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '000000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Material not available'} }); }); - it('rejects a sample number in use', done => { + it('rejects a sample number', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 400, - req: {number: '21', type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, - res: {status: 'Sample number already taken'} + req: {number: 25, type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, + res: {status: 'Invalid body format', details: '"number" is not allowed'} }); }); it('rejects an invalid sample reference', done => { @@ -293,7 +293,7 @@ describe('/sample', () => { url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 400, - req: {number: '10', type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '000000000000000000000003', relation: 'part to this sample'}]}}, + req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '000000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Sample reference not available'} }); }); @@ -303,7 +303,7 @@ describe('/sample', () => { url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 400, - req: {number: '10', type: 'part', color: 'signalviolet', batch: '114531', material_id: '10000000000h000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, + req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '10000000000h000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Invalid body format', details: '"material_id" with value "10000000000h000000000001" fails to match the required pattern: /[0-9a-f]{24}/'} }); }); @@ -313,7 +313,7 @@ describe('/sample', () => { url: '/sample/10000000000h000000000001', auth: {basic: 'janedoe'}, httpStatus: 404, - req: {number: '10', type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, + req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, }); }); it('rejects an API key', done => { @@ -322,7 +322,7 @@ describe('/sample', () => { url: '/sample/400000000000000000000001', auth: {key: 'janedoe'}, httpStatus: 401, - req: {number: '10', type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, + req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, }); }); it('rejects changes for samples from another user for a write user', done => { @@ -350,7 +350,7 @@ describe('/sample', () => { url: '/sample/400000000000000000000001', auth: {basic: 'user'}, httpStatus: 403, - req: {number: '10', type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, + req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, }); }); it('returns 404 for an unknown sample', done => { @@ -359,7 +359,7 @@ describe('/sample', () => { url: '/sample/000000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 404, - req: {number: '10', type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}} + req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}} }); }) it('rejects unauthorized requests', done => { @@ -367,7 +367,7 @@ describe('/sample', () => { method: 'put', url: '/sample/400000000000000000000001', httpStatus: 401, - req: {number: '10', type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, + req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, }); }); }); @@ -531,12 +531,12 @@ describe('/sample', () => { url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 200, - req: {number: 'Rng172', color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}} + req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{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', 'material_id', 'note_id', 'user_id'); should(res.body).have.property('_id').be.type('string'); - should(res.body).have.property('number', 'Rng172'); + 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'); @@ -552,15 +552,15 @@ describe('/sample', () => { url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 200, - req: {number: 'Rng172', color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}} + req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}} }).end(err => { if (err) return done (err); - SampleModel.find({number: 'Rng172'}).lean().exec((err, data: any) => { + SampleModel.find({number: 'Rng34'}).lean().exec((err, data: any) => { if (err) return done (err); should(data).have.lengthOf(1); should(data[0]).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'material_id', 'note_id', 'user_id', 'status', '__v'); should(data[0]).have.property('_id'); - should(data[0]).have.property('number', 'Rng172'); + should(data[0]).have.property('number', 'Rng34'); should(data[0]).have.property('color', 'black'); should(data[0]).have.property('type', 'granulate'); should(data[0]).have.property('batch', '1560237365'); @@ -587,7 +587,7 @@ describe('/sample', () => { url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 200, - req: {number: 'Rng172', color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [], custom_fields: {field1: 'a', field2: 'b', 'not allowed for new applications': true}}} + req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [], custom_fields: {field1: 'a', field2: 'b', 'not allowed for new applications': true}}} }).end((err, res) => { if (err) return done (err); NoteModel.findById(res.body.note_id).lean().exec((err, data: any) => { @@ -618,13 +618,34 @@ describe('/sample', () => { }); }); }); + it('stores a new sample location as 1', done => { + TestHelper.request(server, done, { + method: 'post', + url: '/sample/new', + auth: {basic: 'johnnydoe'}, + httpStatus: 200, + req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{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', 'material_id', 'note_id', 'user_id'); + should(res.body).have.property('_id').be.type('string'); + should(res.body).have.property('number', 'Fe1'); + 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('material_id', '100000000000000000000001'); + should(res.body).have.property('note_id').be.type('string'); + should(res.body).have.property('user_id', '000000000000000000000004'); + done(); + }); + }); it('rejects a color not defined for the material', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, - req: {number: 'Rng172', color: 'green', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, + req: {color: 'green', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Color not available for material'} }); }); @@ -634,18 +655,18 @@ describe('/sample', () => { url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, - req: {number: 'Rng172', color: 'black', type: 'granulate', batch: '1560237365', material_id: '000000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, + req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '000000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Material not available'} }); }); - it('rejects a sample number in use', done => { + it('rejects a sample number', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, - req: {number: '1', color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, - res: {status: 'Sample number already taken'} + req: {number: 'Rng34', color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, + res: {status: 'Invalid body format', details: '"number" is not allowed'} }); }); it('rejects an invalid sample reference', done => { @@ -654,7 +675,7 @@ describe('/sample', () => { url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, - req: {number: 'Rng172', color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '000000000000000000000003', relation: 'part to this sample'}]}}, + req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '000000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Sample reference not available'} }); }); @@ -664,27 +685,17 @@ describe('/sample', () => { url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, - req: {number: 'Rng172', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, + req: {type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Invalid body format', details: '"color" is required'} }); }); - it('rejects a missing sample number', done => { - TestHelper.request(server, done, { - method: 'post', - url: '/sample/new', - auth: {basic: 'janedoe'}, - httpStatus: 400, - req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, - res: {status: 'Invalid body format', details: '"number" is required'} - }); - }); it('rejects a missing type', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, - req: {number: 'Rng172', color: 'black', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, + req: {color: 'black', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Invalid body format', details: '"type" is required'} }); }); @@ -694,7 +705,7 @@ describe('/sample', () => { url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, - req: {number: 'Rng172', color: 'black', type: 'granulate', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, + req: {color: 'black', type: 'granulate', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Invalid body format', details: '"batch" is required'} }); }); @@ -704,7 +715,7 @@ describe('/sample', () => { url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, - req: {number: 'Rng172', color: 'black', type: 'granulate', batch: '1560237365', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, + req: {color: 'black', type: 'granulate', batch: '1560237365', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Invalid body format', details: '"material_id" is required'} }); }); @@ -714,7 +725,7 @@ describe('/sample', () => { url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, - req: {number: 'Rng172', color: 'black', type: 'granulate', batch: '1560237365', material_id: '10000000000h000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, + req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '10000000000h000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Invalid body format', details: '"material_id" with value "10000000000h000000000001" fails to match the required pattern: /[0-9a-f]{24}/'} }); }); @@ -724,7 +735,7 @@ describe('/sample', () => { url: '/sample/new', auth: {key: 'janedoe'}, httpStatus: 401, - req: {number: 'Rng172', color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}} + req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}} }); }); it('rejects requests from a read user', done => { @@ -733,7 +744,7 @@ describe('/sample', () => { url: '/sample/new', auth: {basic: 'user'}, httpStatus: 403, - req: {number: 'Rng172', color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}} + req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}} }); }); it('rejects unauthorized requests', done => { @@ -741,7 +752,7 @@ describe('/sample', () => { method: 'post', url: '/sample/new', httpStatus: 401, - req: {number: 'Rng172', color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}} + req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}} }); }); }); diff --git a/src/routes/sample.ts b/src/routes/sample.ts index 6acb7d2..38e2282 100644 --- a/src/routes/sample.ts +++ b/src/routes/sample.ts @@ -36,9 +36,6 @@ router.put('/sample/' + IdValidate.parameter(), (req, res, next) => { // 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; - if (sample.hasOwnProperty('number') && sample.number !== sampleData.number) { - if (!await numberCheck(sample, res, next)) return; - } if (sample.hasOwnProperty('material_id')) { if (!await materialCheck(sample, res, next)) return; } @@ -120,7 +117,6 @@ router.post('/sample/new', async (req, res, next) => { const {error, value: sample} = SampleValidate.input(req.body, 'new'); if (error) return res400(error, res); - if (!await numberCheck(sample, res, next)) return; if (!await materialCheck(sample, res, next)) return; if (!await sampleRefCheck(sample, res, next)) return; @@ -129,6 +125,8 @@ router.post('/sample/new', async (req, res, next) => { } sample.status = 0; + sample.number = await numberGenerate(sample, req, res, next); + if (!sample.number) return; new NoteModel(sample.notes).save((err, data) => { if (err) return next(err); delete sample.notes; @@ -155,17 +153,18 @@ router.get('/sample/notes/fields', (req, res, next) => { module.exports = router; -async function numberCheck (sample, res, next) { // validate number, returns false if invalid - 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 numberGenerate (sample, req, res, next) { // validate number, returns false if invalid + const sampleData = await SampleModel + .find({number: new RegExp('^' + req.authDetails.location + '[0-9]+$', 'm')}) + .lean() + .exec() + .catch(err => next(err)); + if (sampleData instanceof Error) return false; + return req.authDetails.location + (sampleData.length > 0 ? Number(sampleData[0].number.replace(/[^0-9]+/g, '')) + 1 : 1); } 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); return false;}) as any; + const materialData = await MaterialModel.findById(id).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 not available'}); diff --git a/src/routes/validate/sample.ts b/src/routes/validate/sample.ts index aa28304..9373152 100644 --- a/src/routes/validate/sample.ts +++ b/src/routes/validate/sample.ts @@ -44,7 +44,6 @@ export default class SampleValidate { static input (data, param) { // validate data, param: new(everything required)/change(available attributes are validated) if (param === 'new') { return Joi.object({ - number: this.sample.number.required(), color: this.sample.color.required(), type: this.sample.type.required(), batch: this.sample.batch.required(), @@ -54,7 +53,6 @@ export default class SampleValidate { } else if (param === 'change') { return Joi.object({ - number: this.sample.number, color: this.sample.color, type: this.sample.type, batch: this.sample.batch, diff --git a/src/test/db.json b/src/test/db.json index fcd1631..7154863 100644 --- a/src/test/db.json +++ b/src/test/db.json @@ -51,7 +51,7 @@ }, { "_id": {"$oid":"400000000000000000000005"}, - "number": "33", + "number": "Rng33", "type": "granulate", "color": "black", "batch": "1653000308", @@ -250,7 +250,7 @@ { "_id": {"$oid":"700000000000000000000002"}, "sample_id": {"$oid":"400000000000000000000002"}, - "number": "A3", + "number": "A1", "parameters": { "material": "copper", "weeks": 3 @@ -262,7 +262,7 @@ { "_id": {"$oid":"700000000000000000000003"}, "sample_id": {"$oid":"400000000000000000000004"}, - "number": "A4", + "number": "A1", "parameters": { "material": "copper", "weeks": 3 @@ -274,7 +274,7 @@ { "_id": {"$oid":"700000000000000000000004"}, "sample_id": {"$oid":"400000000000000000000001"}, - "number": "A6", + "number": "A2", "parameters": { "material": "hot air", "weeks": 5 @@ -446,6 +446,17 @@ "device_name": "", "key": "000000000000000000001003", "__v": "0" + }, + { + "_id": {"$oid":"000000000000000000000004"}, + "email": "johnny.doe@bosch.com", + "name": "johnnydoe", + "pass": "$2a$10$di26XKF63OG0V00PL1kSK.ceCcTxDExBMOg.jkHiCnXcY7cN7DlPi", + "level": "write", + "location": "Fe", + "device_name": "Alpha I", + "key": "000000000000000000001004", + "__v": 0 } ] } diff --git a/src/test/helper.ts b/src/test/helper.ts index 26cb5a5..b7ff49f 100644 --- a/src/test/helper.ts +++ b/src/test/helper.ts @@ -7,7 +7,8 @@ export default class TestHelper { public static auth = { admin: {pass: 'Abc123!#', key: '000000000000000000001003'}, janedoe: {pass: 'Xyz890*)', key: '000000000000000000001002'}, - user: {pass: 'Xyz890*)', key: '000000000000000000001001'} + user: {pass: 'Xyz890*)', key: '000000000000000000001001'}, + johnnydoe: {pass: 'Xyz890*)', key: '000000000000000000001004'} } public static res = { 400: {status: 'Bad request'},