Archived
2

adjusted sample

This commit is contained in:
VLE2FE 2020-05-13 12:06:28 +02:00
parent f77d39af34
commit 478660573d
7 changed files with 103 additions and 34 deletions

View File

@ -4,6 +4,7 @@
get: get:
summary: condition by id summary: condition by id
description: 'Auth: all, levels: read, write, maintain, dev, admin' description: 'Auth: all, levels: read, write, maintain, dev, admin'
x-doc: status handling (accessible (only for maintain/admin))? # TODO
tags: tags:
- /condition - /condition
responses: responses:
@ -24,6 +25,7 @@
put: put:
summary: change condition summary: change condition
description: 'Auth: basic, levels: write, maintain, dev, admin <br>Only maintain and admin are allowed to reference samples created by another user' description: 'Auth: basic, levels: write, maintain, dev, admin <br>Only maintain and admin are allowed to reference samples created by another user'
x-doc: status is reset to 0 on any changes
tags: tags:
- /condition - /condition
security: security:
@ -61,6 +63,7 @@
delete: delete:
summary: delete condition summary: delete condition
description: 'Auth: basic, levels: write, maintain, dev, admin' description: 'Auth: basic, levels: write, maintain, dev, admin'
x-doc: sets status to -1
tags: tags:
- /condition - /condition
security: security:
@ -83,6 +86,7 @@
post: post:
summary: add condition summary: add condition
description: 'Auth: basic, levels: write, maintain, dev, admin <br>Only maintain and admin are allowed to reference samples created by another user' description: 'Auth: basic, levels: write, maintain, dev, admin <br>Only maintain and admin are allowed to reference samples created by another user'
x-doc: 'Adds status: 0 automatically'
tags: tags:
- /condition - /condition
security: security:

View File

@ -4,6 +4,7 @@
get: get:
summary: measurement values by id summary: measurement values by id
description: 'Auth: all, levels: read, write, maintain, dev, admin' description: 'Auth: all, levels: read, write, maintain, dev, admin'
x-doc: status handling (accessible (only for maintain/admin))? # TODO
tags: tags:
- /measurement - /measurement
responses: responses:
@ -57,6 +58,7 @@
delete: delete:
summary: delete measurement summary: delete measurement
description: 'Auth: basic, levels: write, maintain, dev, admin' description: 'Auth: basic, levels: write, maintain, dev, admin'
x-doc: sets status to -1
tags: tags:
- /measurement - /measurement
security: security:

View File

@ -2,6 +2,7 @@
get: get:
summary: all samples in overview summary: all samples in overview
description: 'Auth: all, levels: read, write, maintain, dev, admin' description: 'Auth: all, levels: read, write, maintain, dev, admin'
x-doc: returns only samples with status 10 # TODO: methods /samples/new|deleted
tags: tags:
- /sample - /sample
responses: responses:
@ -23,6 +24,7 @@
get: get:
summary: TODO sample details summary: TODO sample details
description: 'Auth: all, levels: read, write, maintain, dev, admin' description: 'Auth: all, levels: read, write, maintain, dev, admin'
x-doc: status handling (accessible (only for maintain/admin))? # TODO
tags: tags:
- /sample - /sample
responses: responses:
@ -43,6 +45,7 @@
put: put:
summary: change sample summary: change sample
description: 'Auth: basic, levels: write, maintain, dev, admin <br>Only maintain and admin are allowed to edit samples created by another user' description: 'Auth: basic, levels: write, maintain, dev, admin <br>Only maintain and admin are allowed to edit samples created by another user'
x-doc: status is reset to 0 on any changes
tags: tags:
- /sample - /sample
security: security:
@ -73,6 +76,7 @@
delete: delete:
summary: delete sample summary: delete sample
description: 'Auth: basic, levels: write, maintain, dev, admin <br>Only maintain and admin are allowed to edit samples created by another user' description: 'Auth: basic, levels: write, maintain, dev, admin <br>Only maintain and admin are allowed to edit samples created by another user'
x-doc: sets status to -1, notes and references to this sample are also kept, only note_fields are updated accordingly
tags: tags:
- /sample - /sample
security: security:
@ -95,6 +99,7 @@
post: post:
summary: add sample summary: add sample
description: 'Auth: basic, levels: write, maintain, dev, admin' description: 'Auth: basic, levels: write, maintain, dev, admin'
x-doc: 'Adds status: 0 automatically'
tags: tags:
- /sample - /sample
security: security:
@ -125,6 +130,7 @@
get: get:
summary: list all existing field names for custom notes fields summary: list all existing field names for custom notes fields
description: 'Auth: all, levels: read, write, maintain, dev, admin' description: 'Auth: all, levels: read, write, maintain, dev, admin'
x-doc: integrity has to be ensured # TODO: implement mechanism to regularly check note_fields
tags: tags:
- /sample - /sample
responses: responses:

View File

@ -9,10 +9,10 @@ const SampleSchema = new mongoose.Schema({
type: String, type: String,
color: String, color: String,
batch: String, batch: String,
validated: Boolean,
material_id: {type: mongoose.Schema.Types.ObjectId, ref: MaterialModel}, material_id: {type: mongoose.Schema.Types.ObjectId, ref: MaterialModel},
note_id: {type: mongoose.Schema.Types.ObjectId, ref: NoteModel}, note_id: {type: mongoose.Schema.Types.ObjectId, ref: NoteModel},
user_id: {type: mongoose.Schema.Types.ObjectId, ref: UserModel} user_id: {type: mongoose.Schema.Types.ObjectId, ref: UserModel},
status: Number
}); });
export default mongoose.model('sample', SampleSchema); export default mongoose.model('sample', SampleSchema);

View File

@ -5,7 +5,6 @@ import NoteFieldModel from '../models/note_field';
import TestHelper from "../test/helper"; import TestHelper from "../test/helper";
// TODO: generate sample number // TODO: generate sample number
// TODO: think again which parameters are required at POST // TODO: think again which parameters are required at POST
// TODO: status
describe('/sample', () => { describe('/sample', () => {
let server; let server;
@ -23,7 +22,7 @@ describe('/sample', () => {
}).end((err, res) => { }).end((err, res) => {
if (err) return done(err); if (err) return done(err);
const json = require('../test/db.json'); const json = require('../test/db.json');
should(res.body).have.lengthOf(json.collections.samples.length); should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status === 10).length);
should(res.body).matchEach(material => { should(res.body).matchEach(material => {
should(material).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'material_id', 'note_id', 'user_id'); 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('_id').be.type('string');
@ -47,7 +46,7 @@ describe('/sample', () => {
}).end((err, res) => { }).end((err, res) => {
if (err) return done(err); if (err) return done(err);
const json = require('../test/db.json'); const json = require('../test/db.json');
should(res.body).have.lengthOf(json.collections.samples.length); should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status === 10).length);
should(res.body).matchEach(material => { should(res.body).matchEach(material => {
should(material).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'material_id', 'note_id', 'user_id'); 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('_id').be.type('string');
@ -88,8 +87,41 @@ describe('/sample', () => {
url: '/sample/400000000000000000000001', url: '/sample/400000000000000000000001',
auth: {basic: 'janedoe'}, auth: {basic: 'janedoe'},
httpStatus: 200, httpStatus: 200,
req: {number: '1', type: 'granulate', color: 'black', batch: '', material_id: '100000000000000000000004', notes: {}}, req: {number: '1', type: 'granulate', color: 'black', batch: '', material_id: '100000000000000000000004', notes: {}}
res: {_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'} }).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'});
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', '1');
should(data).have.property('color', 'black');
should(data).have.property('type', 'granulate');
should(data).have.property('batch', '');
should(data.material_id.toString()).be.eql('100000000000000000000004');
should(data.user_id.toString()).be.eql('000000000000000000000002');
should(data).have.property('status', 10);
should(data).have.property('note_id', null);
done();
});
});
});
it('keeps only one unchanged parameter', done => {
TestHelper.request(server, done, {
method: 'put',
url: '/sample/400000000000000000000001',
auth: {basic: 'janedoe'},
httpStatus: 200,
req: {type: 'granulate'}
}).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'});
SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
if (err) return done (err);
should(data).have.property('status', 10);
done();
});
}); });
}); });
it('changes the given properties', done => { it('changes the given properties', done => {
@ -103,15 +135,15 @@ describe('/sample', () => {
if (err) return done (err); if (err) return done (err);
SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => { SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
if (err) return done (err); if (err) return done (err);
should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'validated', 'material_id', 'note_id', 'user_id', '__v'); 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('_id');
should(data).have.property('number', '10'); should(data).have.property('number', '10');
should(data).have.property('color', 'signalviolet'); should(data).have.property('color', 'signalviolet');
should(data).have.property('type', 'part'); should(data).have.property('type', 'part');
should(data).have.property('batch', '114531'); should(data).have.property('batch', '114531');
should(data).have.property('validated').be.type('boolean');
should(data.material_id.toString()).be.eql('100000000000000000000002'); should(data.material_id.toString()).be.eql('100000000000000000000002');
should(data.user_id.toString()).be.eql('000000000000000000000002'); should(data.user_id.toString()).be.eql('000000000000000000000002');
should(data).have.property('status', 0);
should(data).have.property('note_id'); should(data).have.property('note_id');
NoteModel.findById(data.note_id).lean().exec((err, data: any) => { NoteModel.findById(data.note_id).lean().exec((err, data: any) => {
if (err) return done (err); if (err) return done (err);
@ -123,7 +155,7 @@ describe('/sample', () => {
should(data.sample_references[0]).have.property('relation', 'part to this sample'); should(data.sample_references[0]).have.property('relation', 'part to this sample');
done(); done();
}); });
}) });
}); });
}); });
it('adjusts the note_fields correctly', done => { it('adjusts the note_fields correctly', done => {
@ -315,7 +347,7 @@ describe('/sample', () => {
}); });
describe('DELETE /sample/{id}', () => { describe('DELETE /sample/{id}', () => {
it('deletes the sample', done => { it('sets the status to deleted', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'delete', method: 'delete',
url: '/sample/400000000000000000000001', url: '/sample/400000000000000000000001',
@ -324,14 +356,23 @@ describe('/sample', () => {
}).end((err, res) => { }).end((err, res) => {
if (err) return done(err); if (err) return done(err);
should(res.body).be.eql({status: 'OK'}); should(res.body).be.eql({status: 'OK'});
SampleModel.findById('400000000000000000000001').lean().exec((err, data) => { SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
if (err) return done(err); if (err) return done(err);
should(data).be.null(); 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', '1');
should(data).have.property('color', 'black');
should(data).have.property('type', 'granulate');
should(data).have.property('batch', '');
should(data.material_id.toString()).be.eql('100000000000000000000004');
should(data.user_id.toString()).be.eql('000000000000000000000002');
should(data).have.property('status', -1);
should(data).have.property('note_id', null);
done(); done();
}); });
}); });
}); });
it('deletes the notes of the sample', done => { it('keeps the notes of the sample', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'delete', method: 'delete',
url: '/sample/400000000000000000000002', url: '/sample/400000000000000000000002',
@ -342,7 +383,9 @@ describe('/sample', () => {
should(res.body).be.eql({status: 'OK'}); should(res.body).be.eql({status: 'OK'});
NoteModel.findById('500000000000000000000001').lean().exec((err, data) => { NoteModel.findById('500000000000000000000001').lean().exec((err, data) => {
if (err) return done(err); if (err) return done(err);
should(data).be.null(); should(data).have.only.keys('_id', 'comment', 'sample_references', '__v');
should(data).have.property('comment', 'Stoff gesperrt');
should(data).have.property('sample_references').with.lengthOf(0);
done(); done();
}); });
}); });
@ -367,7 +410,7 @@ describe('/sample', () => {
}); });
}); });
}); });
it('resets references to this sample', done => { it('keeps references to this sample', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'delete', method: 'delete',
url: '/sample/400000000000000000000003', url: '/sample/400000000000000000000003',
@ -377,10 +420,12 @@ describe('/sample', () => {
if (err) return done(err); if (err) return done(err);
should(res.body).be.eql({status: 'OK'}); should(res.body).be.eql({status: 'OK'});
setTimeout(() => { // background action takes some time before we can check setTimeout(() => { // background action takes some time before we can check
NoteModel.findById('500000000000000000000003').lean().exec((err, data) => { NoteModel.findById('500000000000000000000003').lean().exec((err, data: any) => {
if (err) return done(err); if (err) return done(err);
console.log(data); console.log(data);
should(data).have.property('sample_references').with.lengthOf(0); 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');
done(); done();
}); });
}, 100); }, 100);
@ -398,7 +443,7 @@ describe('/sample', () => {
should(res.body).be.eql({status: 'OK'}); should(res.body).be.eql({status: 'OK'});
SampleModel.findById('400000000000000000000001').lean().exec((err, data) => { SampleModel.findById('400000000000000000000001').lean().exec((err, data) => {
if (err) return done(err); if (err) return done(err);
should(data).be.null(); should(data).have.property('status', -1);
done(); done();
}); });
}); });
@ -486,7 +531,7 @@ describe('/sample', () => {
SampleModel.find({number: 'Rng172'}).lean().exec((err, data: any) => { SampleModel.find({number: 'Rng172'}).lean().exec((err, data: any) => {
if (err) return done (err); if (err) return done (err);
should(data).have.lengthOf(1); should(data).have.lengthOf(1);
should(data[0]).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'material_id', 'note_id', 'user_id', '__v'); 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('_id');
should(data[0]).have.property('number', 'Rng172'); should(data[0]).have.property('number', 'Rng172');
should(data[0]).have.property('color', 'black'); should(data[0]).have.property('color', 'black');
@ -494,6 +539,7 @@ describe('/sample', () => {
should(data[0]).have.property('batch', '1560237365'); should(data[0]).have.property('batch', '1560237365');
should(data[0].material_id.toString()).be.eql('100000000000000000000001'); should(data[0].material_id.toString()).be.eql('100000000000000000000001');
should(data[0].user_id.toString()).be.eql('000000000000000000000002'); should(data[0].user_id.toString()).be.eql('000000000000000000000002');
should(data[0]).have.property('status', 0);
should(data[0]).have.property('note_id'); should(data[0]).have.property('note_id');
NoteModel.findById(data[0].note_id).lean().exec((err, data: any) => { NoteModel.findById(data[0].note_id).lean().exec((err, data: any) => {
if (err) return done (err); if (err) return done (err);

View File

@ -16,7 +16,7 @@ const router = express.Router();
router.get('/samples', (req, res, next) => { router.get('/samples', (req, res, next) => {
if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'all')) return; if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'all')) return;
SampleModel.find({}).lean().exec((err, data) => { SampleModel.find({status: 10}).lean().exec((err, data) => {
if (err) return next(err); if (err) return next(err);
res.json(_.compact(data.map(e => SampleValidate.output(e)))); // validate all and filter null values from validation errors res.json(_.compact(data.map(e => SampleValidate.output(e)))); // validate all and filter null values from validation errors
}) })
@ -66,6 +66,11 @@ router.put('/sample/' + IdValidate.parameter(), (req, res, next) => {
delete sample.notes; delete sample.notes;
sample.note_id = data._id; sample.note_id = data._id;
} }
// check for changes
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) => { SampleModel.findByIdAndUpdate(req.params.id, sample, {new: true}).lean().exec((err, data) => {
if (err) return next(err); if (err) return next(err);
res.json(SampleValidate.output(data)); res.json(SampleValidate.output(data));
@ -85,22 +90,15 @@ router.delete('/sample/' + IdValidate.parameter(), (req, res, next) => {
// only maintain and admin are allowed to edit other user's data // 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 (sampleData.user_id.toString() !== req.authDetails.id && !req.auth(res, ['maintain', 'admin'], 'basic')) return;
SampleModel.findByIdAndDelete(req.params.id).lean().exec(err => { // delete sample SampleModel.findByIdAndUpdate(req.params.id, {status: -1}).lean().exec(err => { // set sample status
if (err) return next(err); if (err) return next(err);
if (sampleData.note_id !== null) { if (sampleData.note_id !== null) {
NoteModel.findByIdAndDelete(sampleData.note_id).lean().exec((err, data: any) => { // delete notes NoteModel.findById(sampleData.note_id).lean().exec((err, data: any) => { // find notes to update note_fields
if (err) return next(err); if (err) return next(err);
console.log(data);
if (data.hasOwnProperty('custom_fields')) { // update note_fields if (data.hasOwnProperty('custom_fields')) { // update note_fields
customFieldsChange(Object.keys(data.custom_fields), -1); customFieldsChange(Object.keys(data.custom_fields), -1);
} }
res.json({status: 'OK'}); res.json({status: 'OK'});
NoteModel.updateMany({'sample_references.id': req.params.id}, {$unset: {'sample_references.$': null}}).lean().exec(err => { // remove sample_references
if (err) console.error(err);
NoteModel.collection.updateMany({sample_references: null}, {$pull: {sample_references: null}}, err => { // only works with native MongoDB driver somehow
if (err) console.error(err);
});
});
}); });
} }
else { else {
@ -124,6 +122,7 @@ router.post('/sample/new', async (req, res, next) => {
customFieldsChange(Object.keys(sample.notes.custom_fields), 1); customFieldsChange(Object.keys(sample.notes.custom_fields), 1);
} }
sample.status = 0;
new NoteModel(sample.notes).save((err, data) => { new NoteModel(sample.notes).save((err, data) => {
if (err) return next(err); if (err) return next(err);
delete sample.notes; delete sample.notes;

View File

@ -7,10 +7,10 @@
"type": "granulate", "type": "granulate",
"color": "black", "color": "black",
"batch": "", "batch": "",
"validated": true,
"material_id": {"$oid":"100000000000000000000004"}, "material_id": {"$oid":"100000000000000000000004"},
"note_id": null, "note_id": null,
"user_id": {"$oid":"000000000000000000000002"}, "user_id": {"$oid":"000000000000000000000002"},
"status": 10,
"__v": 0 "__v": 0
}, },
{ {
@ -19,10 +19,10 @@
"type": "granulate", "type": "granulate",
"color": "natural", "color": "natural",
"batch": "1560237365", "batch": "1560237365",
"validated": true,
"material_id": {"$oid":"100000000000000000000001"}, "material_id": {"$oid":"100000000000000000000001"},
"note_id": {"$oid":"500000000000000000000001"}, "note_id": {"$oid":"500000000000000000000001"},
"user_id": {"$oid":"000000000000000000000002"}, "user_id": {"$oid":"000000000000000000000002"},
"status": 10,
"__v": 0 "__v": 0
}, },
{ {
@ -31,10 +31,10 @@
"type": "part", "type": "part",
"color": "black", "color": "black",
"batch": "1704-005", "batch": "1704-005",
"validated": false,
"material_id": {"$oid":"100000000000000000000005"}, "material_id": {"$oid":"100000000000000000000005"},
"note_id": {"$oid":"500000000000000000000002"}, "note_id": {"$oid":"500000000000000000000002"},
"user_id": {"$oid":"000000000000000000000003"}, "user_id": {"$oid":"000000000000000000000003"},
"status": 0,
"__v": 0 "__v": 0
}, },
{ {
@ -43,10 +43,22 @@
"type": "granulate", "type": "granulate",
"color": "black", "color": "black",
"batch": "1653000308", "batch": "1653000308",
"validated": false,
"material_id": {"$oid":"100000000000000000000005"}, "material_id": {"$oid":"100000000000000000000005"},
"note_id": {"$oid":"500000000000000000000003"}, "note_id": {"$oid":"500000000000000000000003"},
"user_id": {"$oid":"000000000000000000000003"}, "user_id": {"$oid":"000000000000000000000003"},
"status": 0,
"__v": 0
},
{
"_id": {"$oid":"400000000000000000000005"},
"number": "33",
"type": "granulate",
"color": "black",
"batch": "1653000308",
"material_id": {"$oid":"100000000000000000000005"},
"note_id": {"$oid":"500000000000000000000003"},
"user_id": {"$oid":"000000000000000000000003"},
"status": -1,
"__v": 0 "__v": 0
} }
], ],