From 3ff29845d44de2aa5f37c3f2e598e6e513cb1e51 Mon Sep 17 00:00:00 2001 From: VLE2FE Date: Thu, 6 Aug 2020 18:50:50 +0200 Subject: [PATCH 1/4] changed status and returned status for /samples --- api/sample.yaml | 5 +- api/schemas.yaml | 4 ++ src/db.ts | 2 +- src/globals.ts | 10 +-- src/models/material.ts | 2 +- src/models/measurement.ts | 2 +- src/models/sample.ts | 2 +- src/routes/material.spec.ts | 40 +++++------ src/routes/material.ts | 22 +++--- src/routes/measurement.spec.ts | 24 +++---- src/routes/measurement.ts | 14 ++-- src/routes/sample.spec.ts | 118 +++++++++++++++++++-------------- src/routes/sample.ts | 27 ++++---- src/routes/validate/sample.ts | 14 +++- src/test/db.json | 48 +++++++------- 15 files changed, 182 insertions(+), 152 deletions(-) diff --git a/api/sample.yaml b/api/sample.yaml index b6689d9..52b5705 100644 --- a/api/sample.yaml +++ b/api/sample.yaml @@ -66,7 +66,8 @@ %5D%7D"]' responses: 200: - description: samples overview (if the csv parameter is set, this is in CSV instead of JSON format) + description: samples overview (output depends on the fields specified)
+ if the csv parameter is set, this is in CSV instead of JSON format headers: x-total-items: description: Total number of available items when from-id is not specified and spectrum field is not @@ -79,7 +80,7 @@ schema: type: array items: - $ref: 'api.yaml#/components/schemas/SampleRefs' + $ref: 'api.yaml#/components/schemas/SampleDetail' 400: $ref: 'api.yaml#/components/responses/400' 401: diff --git a/api/schemas.yaml b/api/schemas.yaml index c1645da..16c11a3 100644 --- a/api/schemas.yaml +++ b/api/schemas.yaml @@ -106,6 +106,10 @@ SampleDetail: user: type: string example: admin + status: + type: string + description: can be deleted/new/validated + example: new Material: allOf: diff --git a/src/db.ts b/src/db.ts index 8f72f81..bba4cb2 100644 --- a/src/db.ts +++ b/src/db.ts @@ -7,7 +7,7 @@ import ChangelogModel from './models/changelog'; // database urls, prod db url is retrieved automatically const TESTING_URL = 'mongodb://localhost/dfopdb_test'; const DEV_URL = 'mongodb://localhost/dfopdb'; -const debugging = false; +const debugging = true; if (process.env.NODE_ENV !== 'production' && debugging) { mongoose.set('debug', true); // enable mongoose debug diff --git a/src/globals.ts b/src/globals.ts index f51bf57..2928a89 100644 --- a/src/globals.ts +++ b/src/globals.ts @@ -6,11 +6,11 @@ const globals = { 'admin' ], - status: { // document statuses - deleted: -1, - new: 0, - validated: 10, - } + status: [ // document statuses + 'deleted', + 'new', + 'validated', + ] }; export default globals; \ No newline at end of file diff --git a/src/models/material.ts b/src/models/material.ts index 0c1629a..4790a44 100644 --- a/src/models/material.ts +++ b/src/models/material.ts @@ -9,7 +9,7 @@ const MaterialSchema = new mongoose.Schema({ group_id: {type: mongoose.Schema.Types.ObjectId, ref: MaterialGroupsModel}, properties: mongoose.Schema.Types.Mixed, numbers: [String], - status: Number + status: String }, {minimize: false}); // changelog query helper diff --git a/src/models/measurement.ts b/src/models/measurement.ts index 55267ec..d83c466 100644 --- a/src/models/measurement.ts +++ b/src/models/measurement.ts @@ -9,7 +9,7 @@ const MeasurementSchema = new mongoose.Schema({ sample_id: {type: mongoose.Schema.Types.ObjectId, ref: SampleModel}, values: mongoose.Schema.Types.Mixed, measurement_template: {type: mongoose.Schema.Types.ObjectId, ref: MeasurementTemplateModel}, - status: Number + status: String }, {minimize: false}); // changelog query helper diff --git a/src/models/sample.ts b/src/models/sample.ts index 8eec7bd..fd8f53e 100644 --- a/src/models/sample.ts +++ b/src/models/sample.ts @@ -14,7 +14,7 @@ const SampleSchema = new mongoose.Schema({ material_id: {type: mongoose.Schema.Types.ObjectId, ref: MaterialModel}, note_id: {type: mongoose.Schema.Types.ObjectId, ref: NoteModel}, user_id: {type: mongoose.Schema.Types.ObjectId, ref: UserModel}, - status: Number + status: String }, {minimize: false}); // changelog query helper diff --git a/src/routes/material.spec.ts b/src/routes/material.spec.ts index fa97483..0f362ae 100644 --- a/src/routes/material.spec.ts +++ b/src/routes/material.spec.ts @@ -23,7 +23,7 @@ describe('/material', () => { }).end((err, res) => { if (err) return done(err); const json = require('../test/db.json'); - should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === globals.status.validated).length); + should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === 'validated').length); should(res.body).matchEach(material => { should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers'); should(material).have.property('_id').be.type('string'); @@ -45,7 +45,7 @@ describe('/material', () => { }).end((err, res) => { if (err) return done(err); const json = require('../test/db.json'); - should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === globals.status.validated).length); + should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === 'validated').length); should(res.body).matchEach(material => { should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers'); should(material).have.property('_id').be.type('string'); @@ -67,7 +67,7 @@ describe('/material', () => { }).end((err, res) => { if (err) return done(err); const json = require('../test/db.json'); - should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === globals.status.new).length); + should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === 'new').length); should(res.body).matchEach(material => { should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers'); should(material).have.property('_id').be.type('string'); @@ -109,7 +109,7 @@ describe('/material', () => { if (err) return done(err); const json = require('../test/db.json'); let asyncCounter = res.body.length; - should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status ===globals.status.new).length); + should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status ==='new').length); should(res.body).matchEach(material => { should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers'); should(material).have.property('_id').be.type('string'); @@ -119,7 +119,7 @@ describe('/material', () => { should(material.properties).have.property('material_template').be.type('string'); should(material.numbers).be.instanceof(Array); MaterialModel.findById(material._id).lean().exec((err, data) => { - should(data).have.property('status',globals.status.new); + should(data).have.property('status','new'); if (--asyncCounter === 0) { done(); } @@ -137,7 +137,7 @@ describe('/material', () => { if (err) return done(err); const json = require('../test/db.json'); let asyncCounter = res.body.length; - should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status ===globals.status.deleted).length); + should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status ==='deleted').length); should(res.body).matchEach(material => { should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers'); should(material).have.property('_id').be.type('string'); @@ -147,7 +147,7 @@ describe('/material', () => { should(material.properties).have.property('material_template').be.type('string'); should(material.numbers).be.instanceof(Array); MaterialModel.findById(material._id).lean().exec((err, data) => { - should(data).have.property('status',globals.status.deleted); + should(data).have.property('status','deleted'); if (--asyncCounter === 0) { done(); } @@ -273,7 +273,7 @@ describe('/material', () => { should(res.body).be.eql({_id: '100000000000000000000001', name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 40, carbon_fiber: 0}, numbers: ['5514263423', '5514263422']}); MaterialModel.findById('100000000000000000000001').lean().exec((err, data) => { if (err) return done(err); - should(data).have.property('status',globals.status.validated); + should(data).have.property('status','validated'); MaterialGroupModel.find({name: 'PA46'}).lean().exec((err, data) => { if (err) return done(err); should(data).have.lengthOf(1); @@ -300,7 +300,7 @@ describe('/material', () => { should(res.body).be.eql({_id: '100000000000000000000001', name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 40, carbon_fiber: 0}, numbers: ['5514263423', '5514263422']}); MaterialModel.findById('100000000000000000000001').lean().exec((err, data) => { if (err) return done(err); - should(data).have.property('status',globals.status.validated); + should(data).have.property('status','validated'); done(); }); }); @@ -317,7 +317,7 @@ describe('/material', () => { should(res.body).be.eql({_id: '100000000000000000000001', name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 40, carbon_fiber: 0}, numbers: ['5514263423', '5514263422']}); MaterialModel.findById('100000000000000000000001').lean().exec((err, data) => { if (err) return done(err); - should(data).have.property('status',globals.status.validated); + should(data).have.property('status','validated'); done(); }); }); @@ -337,7 +337,7 @@ describe('/material', () => { data._id = data._id.toString(); data.group_id = data.group_id.toString(); data.supplier_id = data.supplier_id.toString(); - should(data).be.eql({_id: '100000000000000000000001', name: 'UltramidTKR4355G7_2', supplier_id: '110000000000000000000002', group_id: '900000000000000000000002', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 35, carbon_fiber: 0}, numbers: ['5514212901', '5514612901'], status: 0, __v: 0}); + should(data).be.eql({_id: '100000000000000000000001', name: 'UltramidTKR4355G7_2', supplier_id: '110000000000000000000002', group_id: '900000000000000000000002', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 35, carbon_fiber: 0}, numbers: ['5514212901', '5514612901'], status: 'new', __v: 0}); MaterialGroupModel.find({name: 'PA6/6T'}).lean().exec((err, data) => { if (err) return done(err); should(data).have.lengthOf(1); @@ -364,7 +364,7 @@ describe('/material', () => { dataAdd: { group_id: '900000000000000000000002', supplier_id: '110000000000000000000002', - status: 0 + status: 'new' }, dataIgn: ['supplier', 'group'] } @@ -541,7 +541,7 @@ describe('/material', () => { data.group_id = data.group_id.toString(); data.supplier_id = data.supplier_id.toString(); data.properties.material_template = data.properties.material_template.toString(); - should(data).be.eql({_id: '100000000000000000000002', name: 'Ultramid T KR 4355 G7', supplier_id: '110000000000000000000002', group_id: '900000000000000000000002', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 35, carbon_fiber: 0}, numbers: ['5514212901', '5514612901'], status: -1, __v: 0} + should(data).be.eql({_id: '100000000000000000000002', name: 'Ultramid T KR 4355 G7', supplier_id: '110000000000000000000002', group_id: '900000000000000000000002', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 35, carbon_fiber: 0}, numbers: ['5514212901', '5514612901'], status: 'deleted', __v: 0} ); done(); }); @@ -555,7 +555,7 @@ describe('/material', () => { httpStatus: 200, log: { collection: 'materials', - dataAdd: { status: -1} + dataAdd: { status: 'deleted'} } }); }); @@ -622,7 +622,7 @@ describe('/material', () => { should(res.body).be.eql({status: 'OK'}); MaterialModel.findById('100000000000000000000008').lean().exec((err, data: any) => { if (err) return done(err); - should(data).have.property('status',globals.status.new); + should(data).have.property('status','new'); done(); }); }); @@ -637,7 +637,7 @@ describe('/material', () => { log: { collection: 'materials', dataAdd: { - status: 0 + status: 'new' } } }); @@ -692,7 +692,7 @@ describe('/material', () => { should(res.body).be.eql({status: 'OK'}); MaterialModel.findById('100000000000000000000007').lean().exec((err, data: any) => { if (err) return done(err); - should(data).have.property('status',globals.status.validated); + should(data).have.property('status','validated'); done(); }); }); @@ -707,7 +707,7 @@ describe('/material', () => { log: { collection: 'materials', dataAdd: { - status: 10 + status: 'validated' } } }); @@ -790,7 +790,7 @@ describe('/material', () => { should(materialData[0].properties).have.property('mineral', 0); should(materialData[0].properties).have.property('glass_fiber', 30); should(materialData[0].properties).have.property('carbon_fiber', 0); - should(materialData[0]).have.property('status',globals.status.new); + should(materialData[0]).have.property('status','new'); should(materialData[0].numbers).have.lengthOf(0); MaterialGroupModel.findById(materialData[0].group_id).lean().exec((err, data) => { if (err) return done(err); @@ -813,7 +813,7 @@ describe('/material', () => { req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 30, carbon_fiber: 0}, numbers: []}, log: { collection: 'materials', - dataAdd: {status: 0}, + dataAdd: {status: 'new'}, dataIgn: ['group_id', 'supplier_id', 'group', 'supplier'] } }); diff --git a/src/routes/material.ts b/src/routes/material.ts index dfd7cbf..1d9f135 100644 --- a/src/routes/material.ts +++ b/src/routes/material.ts @@ -28,14 +28,14 @@ router.get('/materials', (req, res, next) => { if (filters.hasOwnProperty('status')) { if(filters.status === 'all') { - conditions = {$or: [{status: globals.status.validated}, {status: globals.status.new}]} + conditions = {$or: [{status: 'validated'}, {status: 'new'}]} } else { - conditions = {status: globals.status[filters.status]}; + conditions = {status: filters.status}; } } else { // default - conditions = {status: globals.status.validated}; + conditions = {status: 'validated'}; } MaterialModel.find(conditions).populate('group_id').populate('supplier_id').lean().exec((err, data) => { @@ -49,7 +49,7 @@ router.get('/materials', (req, res, next) => { router.get('/materials/:state(new|deleted)', (req, res, next) => { if (!req.auth(res, ['dev', 'admin'], 'basic')) return; - MaterialModel.find({status: globals.status[req.params.state]}).populate('group_id').populate('supplier_id') + MaterialModel.find({status: req.params.state}).populate('group_id').populate('supplier_id') .lean().exec((err, data) => { if (err) return next(err); @@ -69,7 +69,7 @@ router.get('/material/' + IdValidate.parameter(), (req, res, next) => { } // deleted materials only available for dev/admin - if (data.status === globals.status.deleted && !req.auth(res, ['dev', 'admin'], 'all')) return; + if (data.status === 'deleted' && !req.auth(res, ['dev', 'admin'], 'all')) return; res.json(MaterialValidate.output(data)); }); }); @@ -84,7 +84,7 @@ router.put('/material/' + IdValidate.parameter(), (req, res, next) => { if (!materialData) { return res.status(404).json({status: 'Not found'}); } - if (materialData.status === globals.status.deleted) { + if (materialData.status === 'deleted') { return res.status(403).json({status: 'Forbidden'}); } if (material.hasOwnProperty('name') && material.name !== materialData.name) { @@ -105,7 +105,7 @@ router.put('/material/' + IdValidate.parameter(), (req, res, next) => { // check for changes if (!_.isEqual(_.pick(IdValidate.stringify(materialData), _.keys(material)), IdValidate.stringify(material))) { - material.status = globals.status.new; // set status to new + material.status = 'new'; // set status to new } await MaterialModel.findByIdAndUpdate(req.params.id, material, {new: true}) @@ -125,7 +125,7 @@ router.delete('/material/' + IdValidate.parameter(), (req, res, next) => { if (data.length) { return res.status(400).json({status: 'Material still in use'}); } - MaterialModel.findByIdAndUpdate(req.params.id, {status:globals.status.deleted}) + MaterialModel.findByIdAndUpdate(req.params.id, {status:'deleted'}) .log(req).populate('group_id').populate('supplier_id').lean().exec((err, data) => { if (err) return next(err); if (data) { @@ -141,13 +141,13 @@ router.delete('/material/' + IdValidate.parameter(), (req, res, next) => { router.put('/material/restore/' + IdValidate.parameter(), (req, res, next) => { if (!req.auth(res, ['dev', 'admin'], 'basic')) return; - setStatus(globals.status.new, req, res, next); + setStatus('new', req, res, next); }); router.put('/material/validate/' + IdValidate.parameter(), (req, res, next) => { if (!req.auth(res, ['dev', 'admin'], 'basic')) return; - setStatus(globals.status.validated, req, res, next); + setStatus('validated', req, res, next); }); router.post('/material/new', async (req, res, next) => { @@ -163,7 +163,7 @@ router.post('/material/new', async (req, res, next) => { if (!material) return; if (!await propertiesCheck(material.properties, 'new', res, next)) return; - material.status = globals.status.new; // set status to new + material.status = 'new'; // set status to new await new MaterialModel(material).save(async (err, data) => { if (err) return next(err); db.log(req, 'materials', {_id: data._id}, data.toObject()); diff --git a/src/routes/measurement.spec.ts b/src/routes/measurement.spec.ts index e3d0d68..3b1c5ee 100644 --- a/src/routes/measurement.spec.ts +++ b/src/routes/measurement.spec.ts @@ -104,7 +104,7 @@ describe('/measurement', () => { should(res.body).be.eql({_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]], device: 'Alpha I'}, measurement_template: '300000000000000000000001'}); MeasurementModel.findById('800000000000000000000001').lean().exec((err, data: any) => { if (err) return done(err); - should(data).have.property('status',globals.status.validated); + should(data).have.property('status','validated'); done(); }); }); @@ -121,7 +121,7 @@ describe('/measurement', () => { should(res.body).be.eql({_id: '800000000000000000000002', sample_id: '400000000000000000000002', values: {'weight %': 0.5, 'standard deviation': 0.2}, measurement_template: '300000000000000000000002'}); MeasurementModel.findById('800000000000000000000002').lean().exec((err, data: any) => { if (err) return done(err); - should(data).have.property('status',globals.status.validated); + should(data).have.property('status','validated'); done(); }); }); @@ -140,7 +140,7 @@ describe('/measurement', () => { should(data).have.only.keys('_id', 'sample_id', 'values', 'measurement_template', 'status', '__v'); should(data.sample_id.toString()).be.eql('400000000000000000000001'); should(data.measurement_template.toString()).be.eql('300000000000000000000001'); - should(data).have.property('status',globals.status.new); + should(data).have.property('status','new'); should(data).have.property('values'); should(data.values).have.property('dpt', [[1,2],[3,4],[5,6]]); done(); @@ -159,7 +159,7 @@ describe('/measurement', () => { dataAdd: { measurement_template: '300000000000000000000001', sample_id: '400000000000000000000001', - status: 0 + status: 'new' } } }); @@ -328,7 +328,7 @@ describe('/measurement', () => { should(res.body).be.eql({status: 'OK'}); MeasurementModel.findById('800000000000000000000001').lean().exec((err, data) => { if (err) return done(err); - should(data).have.property('status',globals.status.deleted); + should(data).have.property('status','deleted'); done(); }); }); @@ -342,7 +342,7 @@ describe('/measurement', () => { log: { collection: 'measurements', dataAdd: { - status: -1 + status: 'deleted' } } }); @@ -418,7 +418,7 @@ describe('/measurement', () => { should(res.body).be.eql({status: 'OK'}); MeasurementModel.findById('800000000000000000000004').lean().exec((err, data: any) => { if (err) return done(err); - should(data).have.property('status',globals.status.new); + should(data).have.property('status','new'); done(); }); }); @@ -433,7 +433,7 @@ describe('/measurement', () => { log: { collection: 'measurements', dataAdd: { - status: 0 + status: 'new' } } }); @@ -488,7 +488,7 @@ describe('/measurement', () => { should(res.body).be.eql({status: 'OK'}); MeasurementModel.findById('800000000000000000000003').lean().exec((err, data: any) => { if (err) return done(err); - should(data).have.property('status',globals.status.validated); + should(data).have.property('status','validated'); done(); }); }); @@ -503,7 +503,7 @@ describe('/measurement', () => { log: { collection: 'measurements', dataAdd: { - status: 10 + status: 'validated' } } }); @@ -579,7 +579,7 @@ describe('/measurement', () => { should(data).have.only.keys('_id', 'sample_id', 'values', 'measurement_template', 'status', '__v'); should(data.sample_id.toString()).be.eql('400000000000000000000001'); should(data.measurement_template.toString()).be.eql('300000000000000000000002'); - should(data).have.property('status', 0); + should(data).have.property('status', 'new'); should(data).have.property('values'); should(data.values).have.property('weight %', 0.8); should(data.values).have.property('standard deviation', 0.1); @@ -597,7 +597,7 @@ describe('/measurement', () => { log: { collection: 'measurements', dataAdd: { - status: 0 + status: 'new' } } }); diff --git a/src/routes/measurement.ts b/src/routes/measurement.ts index 5078379..352a98d 100644 --- a/src/routes/measurement.ts +++ b/src/routes/measurement.ts @@ -23,7 +23,7 @@ router.get('/measurement/' + IdValidate.parameter(), (req, res, next) => { return res.status(404).json({status: 'Not found'}); } // deleted measurements only available for dev/admin - if (data.status === globals.status.deleted && !req.auth(res, ['dev', 'admin'], 'all')) return; + if (data.status === 'deleted' && !req.auth(res, ['dev', 'admin'], 'all')) return; res.json(MeasurementValidate.output(data, req)); }); @@ -40,7 +40,7 @@ router.put('/measurement/' + IdValidate.parameter(), async (req, res, next) => { if (!data) { return res.status(404).json({status: 'Not found'}); } - if (data.status === globals.status.deleted) { + if (data.status === 'deleted') { return res.status(403).json({status: 'Forbidden'}); } @@ -53,7 +53,7 @@ router.put('/measurement/' + IdValidate.parameter(), async (req, res, next) => { if (measurement.values) { // fill not changed values from database measurement.values = _.assign({}, data.values, measurement.values); if (!_.isEqual(measurement.values, data.values)) { - measurement.status = globals.status.new; // set status to new + measurement.status = 'new'; // set status to new } } @@ -74,7 +74,7 @@ router.delete('/measurement/' + IdValidate.parameter(), (req, res, next) => { return res.status(404).json({status: 'Not found'}); } if (!await sampleIdCheck(data, req, res, next)) return; - await MeasurementModel.findByIdAndUpdate(req.params.id, {status:globals.status.deleted}) + await MeasurementModel.findByIdAndUpdate(req.params.id, {status:'deleted'}) .log(req).lean().exec(err => { if (err) return next(err); return res.json({status: 'OK'}); @@ -85,13 +85,13 @@ router.delete('/measurement/' + IdValidate.parameter(), (req, res, next) => { router.put('/measurement/restore/' + IdValidate.parameter(), (req, res, next) => { if (!req.auth(res, ['dev', 'admin'], 'basic')) return; - setStatus(globals.status.new, req, res, next); + setStatus('new', req, res, next); }); router.put('/measurement/validate/' + IdValidate.parameter(), (req, res, next) => { if (!req.auth(res, ['dev', 'admin'], 'basic')) return; - setStatus(globals.status.validated, req, res, next); + setStatus('validated', req, res, next); }); router.post('/measurement/new', async (req, res, next) => { @@ -104,7 +104,7 @@ router.post('/measurement/new', async (req, res, next) => { measurement.values = await templateCheck(measurement, 'new', res, next); if (!measurement.values) return; - measurement.status = 0; + measurement.status = 'new'; await new MeasurementModel(measurement).save((err, data) => { if (err) return next(err); db.log(req, 'measurements', {_id: data._id}, data.toObject()); diff --git a/src/routes/sample.spec.ts b/src/routes/sample.spec.ts index 9c6b2ec..c32146a 100644 --- a/src/routes/sample.spec.ts +++ b/src/routes/sample.spec.ts @@ -4,7 +4,6 @@ import NoteModel from '../models/note'; import NoteFieldModel from '../models/note_field'; import MeasurementModel from '../models/measurement'; import TestHelper from "../test/helper"; -import globals from '../globals'; import mongoose from 'mongoose'; @@ -31,7 +30,7 @@ describe('/sample', () => { }).end((err, res) => { if (err) return done(err); const json = require('../test/db.json'); - should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ===globals.status.validated).length); + should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ==='validated').length); should(res.body).matchEach(sample => { should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'added'); should(sample).have.property('_id').be.type('string'); @@ -58,7 +57,7 @@ describe('/sample', () => { }).end((err, res) => { if (err) return done(err); const json = require('../test/db.json'); - should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ===globals.status.validated).length); + should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ==='validated').length); should(res.body).matchEach(sample => { should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'added'); should(sample).have.property('_id').be.type('string'); @@ -85,7 +84,7 @@ describe('/sample', () => { }).end((err, res) => { if (err) return done(err); const json = require('../test/db.json'); - should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ===globals.status.new).length); + should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ==='new').length); should(res.body).matchEach(sample => { should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'added'); should(sample).have.property('_id').be.type('string'); @@ -245,6 +244,19 @@ describe('/sample', () => { done(); }); }); + it('adds the status if specified', done => { + TestHelper.request(server, done, { + method: 'get', + url: '/samples?status=all&fields[]=number&fields[]=status', + auth: {basic: 'janedoe'}, + httpStatus: 200 + }).end((err, res) => { + if (err) return done(err); + should(res.body.find(e => e.number === '1')).have.property('status', 'validated'); + should(res.body.find(e => e.number === 'Rng36')).have.property('status', 'new'); + done(); + }); + }); it('adds the specified measurements', done => { TestHelper.request(server, done, { method: 'get', @@ -549,9 +561,9 @@ describe('/sample', () => { 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 ===globals.status.new).length); + should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ==='new').length); should(res.body).matchEach(sample => { - should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'added'); + should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', 'added'); should(sample).have.property('_id').be.type('string'); should(sample).have.property('number').be.type('string'); should(sample).have.property('type').be.type('string'); @@ -565,8 +577,9 @@ describe('/sample', () => { should(sample).have.property('note_id'); should(sample).have.property('user_id').be.type('string'); should(sample).have.property('added').be.type('string'); + should(sample).have.property('status').be.type('string'); SampleModel.findById(sample._id).lean().exec((err, data) => { - should(data).have.property('status',globals.status.new); + should(data).have.property('status','new'); if (--asyncCounter === 0) { done(); } @@ -584,9 +597,9 @@ describe('/sample', () => { 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).have.lengthOf(json.collections.samples.filter(e => e.status === 'deleted').length); should(res.body).matchEach(sample => { - should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'added'); + should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', 'added'); should(sample).have.property('_id').be.type('string'); should(sample).have.property('number').be.type('string'); should(sample).have.property('type').be.type('string'); @@ -600,8 +613,9 @@ describe('/sample', () => { should(sample).have.property('note_id'); should(sample).have.property('user_id').be.type('string'); should(sample).have.property('added').be.type('string'); + should(sample).have.property('status').be.type('string'); SampleModel.findById(sample._id).lean().exec((err, data) => { - should(data).have.property('status',globals.status.deleted); + should(data).have.property('status','deleted'); if (--asyncCounter === 0) { done(); } @@ -677,7 +691,7 @@ describe('/sample', () => { url: '/sample/400000000000000000000003', auth: {basic: 'janedoe'}, httpStatus: 200, - res: {_id: '400000000000000000000003', number: '33', type: 'part', color: 'black', batch: '1704-005', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'granulate to sample'}], custom_fields: {'not allowed for new applications': true}}, measurements: [{_id: '800000000000000000000003', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003'}], user: 'admin'} + res: {_id: '400000000000000000000003', number: '33', type: 'part', color: 'black', batch: '1704-005', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'granulate to sample'}], custom_fields: {'not allowed for new applications': true}}, measurements: [{_id: '800000000000000000000003', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003'}], status: 'new', user: 'admin'} }); }); it('works with an API key', done => { @@ -686,7 +700,7 @@ describe('/sample', () => { url: '/sample/400000000000000000000003', auth: {key: 'janedoe'}, httpStatus: 200, - res: {_id: '400000000000000000000003', number: '33', type: 'part', color: 'black', batch: '1704-005', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'granulate to sample'}], custom_fields: {'not allowed for new applications': true}}, measurements: [{_id: '800000000000000000000003', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003'}], user: 'admin'} + res: {_id: '400000000000000000000003', number: '33', type: 'part', color: 'black', batch: '1704-005', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'granulate to sample'}], custom_fields: {'not allowed for new applications': true}}, measurements: [{_id: '800000000000000000000003', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003'}], status: 'new', user: 'admin'} }); }); it ('filters out spectral data for a write user', done => { @@ -695,7 +709,7 @@ describe('/sample', () => { url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 200, - res: {_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {numbers: ['5513933405'], _id: '100000000000000000000004', name: 'Schulamid 66 GF 25 H', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 25, carbon_fiber: 0}, group: 'PA66', supplier: 'Schulmann'}, user: 'janedoe', notes: {}, measurements: [{_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {device: 'Alpha I'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000007', sample_id: '400000000000000000000001', values: {device: 'Alpha II'}, measurement_template: '300000000000000000000001'}]} + res: {_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {numbers: ['5513933405'], _id: '100000000000000000000004', name: 'Schulamid 66 GF 25 H', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 25, carbon_fiber: 0}, group: 'PA66', supplier: 'Schulmann'}, user: 'janedoe', notes: {}, measurements: [{_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {device: 'Alpha I'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000007', sample_id: '400000000000000000000001', values: {device: 'Alpha II'}, measurement_template: '300000000000000000000001'}], status: 'validated'} }); }); it ('returns spectral data for an admin user', done => { @@ -704,7 +718,7 @@ describe('/sample', () => { url: '/sample/400000000000000000000001', auth: {basic: 'admin'}, httpStatus: 200, - res: {_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {numbers: ['5513933405'], _id: '100000000000000000000004', name: 'Schulamid 66 GF 25 H', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 25, carbon_fiber: 0}, group: 'PA66', supplier: 'Schulmann'}, user: 'janedoe', notes: {}, measurements: [{_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[ 3997.12558, 98.00555 ], [ 3995.08519, 98.03253 ], [ 3993.0448, 98.02657 ]],device: 'Alpha I'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000007', sample_id: '400000000000000000000001', values: {dpt: [[ 3996.12558, 98.00555 ], [ 3995.08519, 98.03253 ], [ 3993.0448, 98.02657 ]], device: 'Alpha II'}, measurement_template: '300000000000000000000001'}]} + res: {_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {numbers: ['5513933405'], _id: '100000000000000000000004', name: 'Schulamid 66 GF 25 H', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 25, carbon_fiber: 0}, group: 'PA66', supplier: 'Schulmann'}, user: 'janedoe', notes: {}, measurements: [{_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[ 3997.12558, 98.00555 ], [ 3995.08519, 98.03253 ], [ 3993.0448, 98.02657 ]],device: 'Alpha I'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000007', sample_id: '400000000000000000000001', values: {dpt: [[ 3996.12558, 98.00555 ], [ 3995.08519, 98.03253 ], [ 3993.0448, 98.02657 ]], device: 'Alpha II'}, measurement_template: '300000000000000000000001'}], status: 'validated'} }); }); it('returns a deleted sample for a dev/admin user', done => { @@ -713,7 +727,7 @@ describe('/sample', () => { url: '/sample/400000000000000000000005', auth: {basic: 'admin'}, httpStatus: 200, - res: {_id: '400000000000000000000005', number: 'Rng33', type: 'granulate', color: 'black', batch: '1653000308', condition: {condition_template: '200000000000000000000003'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {}, measurements: [], user: 'admin'} + res: {_id: '400000000000000000000005', number: 'Rng33', type: 'granulate', color: 'black', batch: '1653000308', condition: {condition_template: '200000000000000000000003'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {}, measurements: [], status: 'deleted', user: 'admin'} }); }); it('returns 403 for a write user when requesting a deleted sample', done => { @@ -757,7 +771,7 @@ describe('/sample', () => { auth: {basic: 'janedoe'}, httpStatus: 200, req: {}, - res: {_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', added: '2004-01-10T13:37:04.000Z'} + res: {_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', status: 'validated', added: '2004-01-10T13:37:04.000Z'} }); }); it('keeps unchanged properties', done => { @@ -769,7 +783,7 @@ describe('/sample', () => { req: {type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, 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: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', added: '2004-01-10T13:37:04.000Z'}); + should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', status: 'validated', added: '2004-01-10T13:37:04.000Z'}); SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => { if (err) return done (err); should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', '__v'); @@ -781,7 +795,7 @@ describe('/sample', () => { should(data).have.property('condition', {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}); should(data.material_id.toString()).be.eql('100000000000000000000004'); should(data.user_id.toString()).be.eql('000000000000000000000002'); - should(data).have.property('status',globals.status.validated); + should(data).have.property('status','validated'); should(data).have.property('note_id', null); done(); }); @@ -796,10 +810,10 @@ describe('/sample', () => { 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: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', added: '2004-01-10T13:37:04.000Z'}); + should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', status: 'validated', added: '2004-01-10T13:37:04.000Z'}); SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => { if (err) return done (err); - should(data).have.property('status',globals.status.validated); + should(data).have.property('status','validated'); done(); }); }); @@ -813,10 +827,10 @@ describe('/sample', () => { req: {condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}} }).end((err, res) => { if (err) return done(err); - should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', added: '2004-01-10T13:37:04.000Z'}); + should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', status: 'validated', added: '2004-01-10T13:37:04.000Z'}); SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => { if (err) return done (err); - should(data).have.property('status',globals.status.validated); + should(data).have.property('status','validated'); done(); }); }); @@ -830,7 +844,7 @@ describe('/sample', () => { req: {notes: {comment: 'Stoff gesperrt', sample_references: []}} }).end((err, res) => { if (err) return done(err); - should(res.body).be.eql({_id: '400000000000000000000002', number: '21', type: 'granulate', color: 'natural', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', note_id: '500000000000000000000001', user_id: '000000000000000000000002', added: '2004-01-10T13:37:04.000Z'}); + should(res.body).be.eql({_id: '400000000000000000000002', number: '21', type: 'granulate', color: 'natural', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', note_id: '500000000000000000000001', user_id: '000000000000000000000002', status: 'validated', added: '2004-01-10T13:37:04.000Z'}); SampleModel.findById('400000000000000000000002').lean().exec((err, data: any) => { if (err) return done (err); should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', '__v'); @@ -844,7 +858,7 @@ describe('/sample', () => { should(data.condition.condition_template.toString()).be.eql('200000000000000000000001'); should(data.material_id.toString()).be.eql('100000000000000000000001'); should(data.user_id.toString()).be.eql('000000000000000000000002'); - should(data).have.property('status',globals.status.validated); + should(data).have.property('status','validated'); should(data.note_id.toString()).be.eql('500000000000000000000001'); done(); }); @@ -870,7 +884,7 @@ describe('/sample', () => { should(data).have.property('condition', {condition_template: '200000000000000000000003'}); should(data.material_id.toString()).be.eql('100000000000000000000002'); should(data.user_id.toString()).be.eql('000000000000000000000002'); - should(data).have.property('status',globals.status.new); + should(data).have.property('status','new'); should(data).have.property('note_id'); NoteModel.findById(data.note_id).lean().exec((err, data: any) => { if (err) return done (err); @@ -895,7 +909,7 @@ describe('/sample', () => { log: { collection: 'samples', dataAdd: { - status: 0 + status: 'new' }, dataIgn: ['notes', 'note_id'] } @@ -1097,7 +1111,7 @@ describe('/sample', () => { auth: {basic: 'janedoe'}, httpStatus: 200, req: {condition: {}}, - res: {_id: '400000000000000000000006', number: 'Rng36', type: 'granulate', color: 'black', batch: '', condition: {}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', added: '2004-01-10T13:37:04.000Z'} + res: {_id: '400000000000000000000006', number: 'Rng36', type: 'granulate', color: 'black', batch: '', condition: {}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', status: 'new', added: '2004-01-10T13:37:04.000Z'} }); }); it('rejects an old version of a condition template', done => { @@ -1117,7 +1131,7 @@ describe('/sample', () => { 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', added: '2004-01-10T13:37:04.000Z'} + 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', status: 'new', added: '2004-01-10T13:37:04.000Z'} }); }); it('rejects changing back to an empty condition', done => { @@ -1164,7 +1178,7 @@ describe('/sample', () => { auth: {basic: 'admin'}, httpStatus: 200, req: {}, - res: {_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {condition_template: '200000000000000000000001', material: 'copper', weeks: 3}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', added: '2004-01-10T13:37:04.000Z'} + res: {_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {condition_template: '200000000000000000000001', material: 'copper', weeks: 3}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', status: 'validated', added: '2004-01-10T13:37:04.000Z'} }); }); it('rejects requests from a read user', done => { @@ -1218,7 +1232,7 @@ describe('/sample', () => { should(data.condition.condition_template.toString()).be.eql('200000000000000000000001'); should(data.material_id.toString()).be.eql('100000000000000000000004'); should(data.user_id.toString()).be.eql('000000000000000000000002'); - should(data).have.property('status',globals.status.deleted); + should(data).have.property('status','deleted'); should(data).have.property('note_id', null); done(); }); @@ -1233,7 +1247,7 @@ describe('/sample', () => { log: { collection: 'samples', skip: 1, - dataAdd: {status: -1} + dataAdd: {status: 'deleted'} } }); }); @@ -1307,7 +1321,7 @@ describe('/sample', () => { should(res.body).be.eql({status: 'OK'}); SampleModel.findById('400000000000000000000001').lean().exec((err, data) => { if (err) return done(err); - should(data).have.property('status',globals.status.deleted); + should(data).have.property('status','deleted'); done(); }); }); @@ -1324,7 +1338,7 @@ describe('/sample', () => { MeasurementModel.find({sample_id: mongoose.Types.ObjectId('400000000000000000000001')}).lean().exec((err, data: any) => { if (err) return done(err); should(data).matchEach(sample => { - should(sample).have.property('status', -1); + should(sample).have.property('status', 'deleted'); }); done(); }); @@ -1386,7 +1400,7 @@ describe('/sample', () => { url: '/sample/number/33', auth: {basic: 'janedoe'}, httpStatus: 200, - res: {_id: '400000000000000000000003', number: '33', type: 'part', color: 'black', batch: '1704-005', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'granulate to sample'}], custom_fields: {'not allowed for new applications': true}}, measurements: [{_id: '800000000000000000000003', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003'}], user: 'admin'} + res: {_id: '400000000000000000000003', number: '33', type: 'part', color: 'black', batch: '1704-005', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'granulate to sample'}], custom_fields: {'not allowed for new applications': true}}, measurements: [{_id: '800000000000000000000003', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003'}], status: 'new', user: 'admin'} }); }); it('works with an API key', done => { @@ -1395,7 +1409,7 @@ describe('/sample', () => { url: '/sample/number/33', auth: {key: 'janedoe'}, httpStatus: 200, - res: {_id: '400000000000000000000003', number: '33', type: 'part', color: 'black', batch: '1704-005', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'granulate to sample'}], custom_fields: {'not allowed for new applications': true}}, measurements: [{_id: '800000000000000000000003', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003'}], user: 'admin'} + res: {_id: '400000000000000000000003', number: '33', type: 'part', color: 'black', batch: '1704-005', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'granulate to sample'}], custom_fields: {'not allowed for new applications': true}}, measurements: [{_id: '800000000000000000000003', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003'}], status: 'new', user: 'admin'} }); }); it('returns a deleted sample for a dev/admin user', done => { @@ -1404,7 +1418,7 @@ describe('/sample', () => { url: '/sample/number/Rng33', auth: {basic: 'admin'}, httpStatus: 200, - res: {_id: '400000000000000000000005', number: 'Rng33', type: 'granulate', color: 'black', batch: '1653000308', condition: {condition_template: '200000000000000000000003'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {}, measurements: [], user: 'admin'} + res: {_id: '400000000000000000000005', number: 'Rng33', type: 'granulate', color: 'black', batch: '1653000308', condition: {condition_template: '200000000000000000000003'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {}, measurements: [], status: 'deleted', user: 'admin'} }); }); it ('filters out spectral data for a write user', done => { @@ -1413,7 +1427,7 @@ describe('/sample', () => { url: '/sample/number/1', auth: {basic: 'janedoe'}, httpStatus: 200, - res: {_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {numbers: ['5513933405'], _id: '100000000000000000000004', name: 'Schulamid 66 GF 25 H', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 25, carbon_fiber: 0}, group: 'PA66', supplier: 'Schulmann'}, user: 'janedoe', notes: {}, measurements: [{_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {device: 'Alpha I'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000007', sample_id: '400000000000000000000001', values: {device: 'Alpha II'}, measurement_template: '300000000000000000000001'}]} + res: {_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {numbers: ['5513933405'], _id: '100000000000000000000004', name: 'Schulamid 66 GF 25 H', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 25, carbon_fiber: 0}, group: 'PA66', supplier: 'Schulmann'}, user: 'janedoe', notes: {}, measurements: [{_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {device: 'Alpha I'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000007', sample_id: '400000000000000000000001', values: {device: 'Alpha II'}, measurement_template: '300000000000000000000001'}], status: 'validated'} }); }); it ('returns spectral data for an admin user', done => { @@ -1422,7 +1436,7 @@ describe('/sample', () => { url: '/sample/number/1', auth: {basic: 'admin'}, httpStatus: 200, - res: {_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {numbers: ['5513933405'], _id: '100000000000000000000004', name: 'Schulamid 66 GF 25 H', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 25, carbon_fiber: 0}, group: 'PA66', supplier: 'Schulmann'}, user: 'janedoe', notes: {}, measurements: [{_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[ 3997.12558, 98.00555 ], [ 3995.08519, 98.03253 ], [ 3993.0448, 98.02657 ]],device: 'Alpha I'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000007', sample_id: '400000000000000000000001', values: {dpt: [[ 3996.12558, 98.00555 ], [ 3995.08519, 98.03253 ], [ 3993.0448, 98.02657 ]], device: 'Alpha II'}, measurement_template: '300000000000000000000001'}]} + res: {_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {numbers: ['5513933405'], _id: '100000000000000000000004', name: 'Schulamid 66 GF 25 H', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 25, carbon_fiber: 0}, group: 'PA66', supplier: 'Schulmann'}, user: 'janedoe', notes: {}, measurements: [{_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[ 3997.12558, 98.00555 ], [ 3995.08519, 98.03253 ], [ 3993.0448, 98.02657 ]],device: 'Alpha I'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000007', sample_id: '400000000000000000000001', values: {dpt: [[ 3996.12558, 98.00555 ], [ 3995.08519, 98.03253 ], [ 3993.0448, 98.02657 ]], device: 'Alpha II'}, measurement_template: '300000000000000000000001'}], status: 'validated'} }); }); it('returns 403 for a write user when requesting a deleted sample', done => { @@ -1471,7 +1485,7 @@ describe('/sample', () => { should(res.body).be.eql({status: 'OK'}); SampleModel.findById('400000000000000000000005').lean().exec((err, data: any) => { if (err) return done(err); - should(data).have.property('status',globals.status.new); + should(data).have.property('status','new'); done(); }); }); @@ -1488,7 +1502,7 @@ describe('/sample', () => { dataAdd: { group_id: '900000000000000000000002', supplier_id: '110000000000000000000002', - status: 0 + status: 'new' }, dataIgn: ['group_id', 'supplier_id'] } @@ -1544,7 +1558,7 @@ describe('/sample', () => { should(res.body).be.eql({status: 'OK'}); SampleModel.findById('400000000000000000000003').lean().exec((err, data: any) => { if (err) return done(err); - should(data).have.property('status',globals.status.validated); + should(data).have.property('status','validated'); done(); }); }); @@ -1561,7 +1575,7 @@ describe('/sample', () => { dataAdd: { group_id: '900000000000000000000002', supplier_id: '110000000000000000000002', - status: 10 + status: 'validated' }, dataIgn: ['group_id', 'supplier_id'] } @@ -1579,7 +1593,7 @@ describe('/sample', () => { should(res.body).be.eql({status: 'OK'}); SampleModel.findById('400000000000000000000006').lean().exec((err, data: any) => { if (err) return done(err); - should(data).have.property('status',globals.status.validated); + should(data).have.property('status','validated'); done(); }); }); @@ -1596,7 +1610,7 @@ describe('/sample', () => { should(res.body).be.eql({status: 'OK'}); SampleModel.findById('400000000000000000000004').lean().exec((err, data: any) => { if (err) return done(err); - should(data).have.property('status',globals.status.validated); + should(data).have.property('status','validated'); done(); }); }); @@ -1648,7 +1662,7 @@ describe('/sample', () => { req: {color: 'black', type: 'granulate', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, 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', 'added'); + should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', 'added'); should(res.body).have.property('_id').be.type('string'); should(res.body).have.property('number', 'Rng37'); should(res.body).have.property('color', 'black'); @@ -1658,6 +1672,7 @@ describe('/sample', () => { 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', '000000000000000000000002'); + should(res.body).have.property('status', 'new'); should(res.body).have.property('added').be.type('string'); should(new Date().getTime() - new Date(res.body.added).getTime()).be.below(1000); done(); @@ -1684,7 +1699,7 @@ describe('/sample', () => { should(data[0]).have.property('condition', {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}); should(data[0].material_id.toString()).be.eql('100000000000000000000001'); should(data[0].user_id.toString()).be.eql('000000000000000000000002'); - should(data[0]).have.property('status',globals.status.new); + should(data[0]).have.property('status','new'); should(data[0]).have.property('note_id'); NoteModel.findById(data[0].note_id).lean().exec((err, data: any) => { if (err) return done (err); @@ -1711,7 +1726,7 @@ describe('/sample', () => { dataAdd: { number: 'Rng37', user_id: '000000000000000000000002', - status: 0 + status: 'new' }, dataIgn: ['notes', 'note_id'] } @@ -1763,7 +1778,7 @@ describe('/sample', () => { req: {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', 'added'); + should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', 'added'); should(res.body).have.property('_id').be.type('string'); should(res.body).have.property('number', 'Fe1'); should(res.body).have.property('color', 'black'); @@ -1772,6 +1787,7 @@ describe('/sample', () => { 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'); + should(res.body).have.property('status', 'new'); should(res.body).have.property('added').be.type('string'); should(new Date().getTime() - new Date(res.body.added).getTime()).be.below(1500); done(); @@ -1786,7 +1802,7 @@ describe('/sample', () => { req: {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', 'added'); + should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', 'added'); should(res.body).have.property('_id').be.type('string'); should(res.body).have.property('number', 'Rng37'); should(res.body).have.property('color', 'black'); @@ -1796,6 +1812,7 @@ describe('/sample', () => { 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', '000000000000000000000002'); + should(res.body).have.property('status', 'new'); should(res.body).have.property('added').be.type('string'); should(new Date().getTime() - new Date(res.body.added).getTime()).be.below(1000); done(); @@ -1830,7 +1847,7 @@ describe('/sample', () => { 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', 'added'); + should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', 'added'); should(res.body).have.property('_id').be.type('string'); should(res.body).have.property('number', 'Rng34'); should(res.body).have.property('color', 'black'); @@ -1840,6 +1857,7 @@ describe('/sample', () => { 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'); + should(res.body).have.property('status', 'new'); should(res.body).have.property('added').be.type('string'); should(new Date().getTime() - new Date(res.body.added).getTime()).be.below(1000); done(); diff --git a/src/routes/sample.ts b/src/routes/sample.ts index 9b93aaa..3bbfe9c 100644 --- a/src/routes/sample.ts +++ b/src/routes/sample.ts @@ -14,7 +14,6 @@ import IdValidate from './validate/id'; import mongoose from 'mongoose'; import ConditionTemplateModel from '../models/condition_template'; import ParametersValidate from './validate/parameters'; -import globals from '../globals'; import db from '../db'; import csv from '../helpers/csv'; @@ -431,7 +430,7 @@ router.get('/samples', async (req, res, next) => { router.get('/samples/:state(new|deleted)', (req, res, next) => { if (!req.auth(res, ['dev', 'admin'], 'basic')) return; - SampleModel.find({status: globals.status[req.params.state]}).lean().exec((err, data) => { + SampleModel.find({status: req.params.state}).lean().exec((err, data) => { if (err) return next(err); // validate all and filter null values from validation errors res.json(_.compact(data.map(e => SampleValidate.output(e)))); @@ -469,7 +468,7 @@ router.put('/sample/' + IdValidate.parameter(), (req, res, next) => { if (!sampleData) { return res.status(404).json({status: 'Not found'}); } - if (sampleData.status === globals.status.deleted) { + if (sampleData.status === 'deleted') { return res.status(403).json({status: 'Forbidden'}); } @@ -520,7 +519,7 @@ router.put('/sample/' + IdValidate.parameter(), (req, res, next) => { // check for changes if (!_.isEqual(_.pick(IdValidate.stringify(sampleData), _.keys(sample)), _.omit(sample, ['notes']))) { - sample.status = globals.status.new; + sample.status = 'new'; } await SampleModel.findByIdAndUpdate(req.params.id, sample, {new: true}).log(req).lean().exec((err, data: any) => { @@ -544,11 +543,11 @@ router.delete('/sample/' + IdValidate.parameter(), (req, res, next) => { if (sampleData.user_id.toString() !== req.authDetails.id && !req.auth(res, ['dev', 'admin'], 'basic')) return; // set sample status - await SampleModel.findByIdAndUpdate(req.params.id, {status:globals.status.deleted}).log(req).lean().exec(err => { + await SampleModel.findByIdAndUpdate(req.params.id, {status:'deleted'}).log(req).lean().exec(err => { if (err) return next(err); // set status of associated measurements also to deleted - MeasurementModel.updateMany({sample_id: mongoose.Types.ObjectId(req.params.id)}, {status: -1}) + MeasurementModel.updateMany({sample_id: mongoose.Types.ObjectId(req.params.id)}, {status: 'deleted'}) .log(req).lean().exec(err => { if (err) return next(err); @@ -582,7 +581,7 @@ router.get('/sample/number/:number', (req, res, next) => { router.put('/sample/restore/' + IdValidate.parameter(), (req, res, next) => { if (!req.auth(res, ['dev', 'admin'], 'basic')) return; - SampleModel.findByIdAndUpdate(req.params.id, {status: globals.status.new}).log(req).lean().exec((err, data) => { + SampleModel.findByIdAndUpdate(req.params.id, {status: 'new'}).log(req).lean().exec((err, data) => { if (err) return next(err); if (!data) { @@ -595,7 +594,7 @@ router.put('/sample/restore/' + IdValidate.parameter(), (req, res, next) => { router.put('/sample/validate/' + IdValidate.parameter(), (req, res, next) => { if (!req.auth(res, ['dev', 'admin'], 'basic')) return; - SampleModel.findByIdAndUpdate(req.params.id, {status: globals.status.validated}).log(req).lean().exec((err, data) => { + SampleModel.findByIdAndUpdate(req.params.id, {status: 'validated'}).log(req).lean().exec((err, data) => { if (err) return next(err); if (!data) { return res.status(404).json({status: 'Not found'}); @@ -628,7 +627,7 @@ router.post('/sample/new', async (req, res, next) => { if (!await conditionCheck(sample.condition, 'change', res, next)) return; } - sample.status = globals.status.new; // set status to new + sample.status = 'new'; // set status to new if (sample.hasOwnProperty('number')) { if (!await numberCheck(sample, res, next)) return; } @@ -827,14 +826,14 @@ function sortQuery(filters, sortKeys, sortStartValue) { // sortKeys = ['primary function statusQuery(filters, field) { if (filters.hasOwnProperty('status')) { if(filters.status === 'all') { - return {$or: [{[field]: globals.status.validated}, {[field]: globals.status.new}]}; + return {$or: [{[field]: 'validated'}, {[field]: 'new'}]}; } else { - return {[field]: globals.status[filters.status]}; + return {[field]: filters.status}; } } else { // default - return {[field]: globals.status.validated}; + return {[field]: 'validated'}; } } @@ -888,13 +887,13 @@ async function sampleReturn (sampleData, req, res, next) { sampleData = sampleData.toObject(); // deleted samples only available for dev/admin - if (sampleData.status === globals.status.deleted && !req.auth(res, ['dev', 'admin'], 'all')) return; + if (sampleData.status === 'deleted' && !req.auth(res, ['dev', 'admin'], 'all')) return; sampleData.material = sampleData.material_id; // map data to right keys sampleData.material.group = sampleData.material.group_id.name; sampleData.material.supplier = sampleData.material.supplier_id.name; sampleData.user = sampleData.user_id.name; sampleData.notes = sampleData.note_id ? sampleData.note_id : {}; - MeasurementModel.find({sample_id: sampleData._id, status: {$ne: globals.status.deleted}}) + MeasurementModel.find({sample_id: sampleData._id, status: {$ne: 'deleted'}}) .lean().exec((err, data) => { sampleData.measurements = data; if (['dev', 'admin'].indexOf(req.authDetails.level) < 0) { // strip dpt values if not dev or admin diff --git a/src/routes/validate/sample.ts b/src/routes/validate/sample.ts index 5431429..e831fc1 100644 --- a/src/routes/validate/sample.ts +++ b/src/routes/validate/sample.ts @@ -4,6 +4,7 @@ import IdValidate from './id'; import UserValidate from './user'; import MaterialValidate from './material'; import MeasurementValidate from './measurement'; +import globals from '../../globals'; export default class SampleValidate { private static sample = { @@ -49,7 +50,10 @@ export default class SampleValidate { added: Joi.date() .iso() - .min('1970-01-01T00:00:00.000Z') + .min('1970-01-01T00:00:00.000Z'), + + status: Joi.string() + .valid(...globals.status) }; private static sortKeys = [ @@ -59,6 +63,7 @@ export default class SampleValidate { 'type', 'batch', 'added', + 'status', 'material.name', 'material.supplier', 'material.group', @@ -137,7 +142,8 @@ export default class SampleValidate { note_id: IdValidate.get().allow(null), notes: this.sample.notes, user_id: IdValidate.get(), - added: this.sample.added + added: this.sample.added, + status: this.sample.status }; } else if(param === 'details') { @@ -151,7 +157,8 @@ export default class SampleValidate { material: MaterialValidate.outputV(), measurements: Joi.array().items(MeasurementValidate.outputV()), notes: this.sample.notes, - user: UserValidate.username() + user: UserValidate.username(), + status: this.sample.status } } else { @@ -161,6 +168,7 @@ export default class SampleValidate { joiObject[param] = Joi.any(); }); const {value, error} = Joi.object(joiObject).validate(data, {stripUnknown: true}); + console.log(error); return error !== undefined? null : value; } diff --git a/src/test/db.json b/src/test/db.json index 82f3a14..ea037b1 100644 --- a/src/test/db.json +++ b/src/test/db.json @@ -15,7 +15,7 @@ "material_id": {"$oid":"100000000000000000000004"}, "note_id": null, "user_id": {"$oid":"000000000000000000000002"}, - "status": 10, + "status": "validated", "__v": 0 }, { @@ -32,7 +32,7 @@ "material_id": {"$oid":"100000000000000000000001"}, "note_id": {"$oid":"500000000000000000000001"}, "user_id": {"$oid":"000000000000000000000002"}, - "status": 10, + "status": "validated", "__v": 0 }, { @@ -49,7 +49,7 @@ "material_id": {"$oid":"100000000000000000000005"}, "note_id": {"$oid":"500000000000000000000002"}, "user_id": {"$oid":"000000000000000000000003"}, - "status": 0, + "status": "new", "__v": 0 }, { @@ -65,7 +65,7 @@ "material_id": {"$oid":"100000000000000000000005"}, "note_id": {"$oid":"500000000000000000000003"}, "user_id": {"$oid":"000000000000000000000003"}, - "status": 0, + "status": "new", "__v": 0 }, { @@ -80,7 +80,7 @@ "material_id": {"$oid":"100000000000000000000005"}, "note_id": null, "user_id": {"$oid":"000000000000000000000003"}, - "status": -1, + "status": "deleted", "__v": 0 }, { @@ -93,7 +93,7 @@ "material_id": {"$oid":"100000000000000000000004"}, "note_id": null, "user_id": {"$oid":"000000000000000000000002"}, - "status": 0, + "status": "new", "__v": 0 }, { @@ -106,7 +106,7 @@ "material_id": {"$oid":"100000000000000000000009"}, "note_id": null, "user_id": {"$oid":"000000000000000000000002"}, - "status": 0, + "status": "new", "__v": 0 } ], @@ -173,7 +173,7 @@ "5514263423", "5514263422" ], - "status": 10, + "status": "validated", "__v": 0 }, { @@ -191,7 +191,7 @@ "5514212901", "5514612901" ], - "status": 10, + "status": "validated", "__v": 0 }, { @@ -207,7 +207,7 @@ }, "numbers": [ ], - "status": 10, + "status": "validated", "__v": 0 }, { @@ -224,7 +224,7 @@ "numbers": [ "5513933405" ], - "status": 10, + "status": "validated", "__v": 0 }, { @@ -241,7 +241,7 @@ "numbers": [ "5514262406" ], - "status": 10, + "status": "validated", "__v": 0 }, { @@ -258,7 +258,7 @@ "numbers": [ "1000000000" ], - "status": -1, + "status": "deleted", "__v": 0 }, { @@ -274,7 +274,7 @@ }, "numbers": [ ], - "status": 0, + "status": "new", "__v": 0 }, { @@ -291,7 +291,7 @@ "numbers": [ "5513943509" ], - "status": -1, + "status": "deleted", "__v": 0 }, { @@ -306,7 +306,7 @@ "numbers": [ "5513943509" ], - "status": 0, + "status": "new", "__v": 0 }, { @@ -321,7 +321,7 @@ "numbers": [ "5513943509" ], - "status": 0, + "status": "new", "__v": 0 } ], @@ -406,7 +406,7 @@ ], "device": "Alpha I" }, - "status": 10, + "status": "validated", "measurement_template": {"$oid":"300000000000000000000001"}, "__v": 0 }, @@ -417,7 +417,7 @@ "weight %": 0.5, "standard deviation": 0.2 }, - "status": 10, + "status": "validated", "measurement_template": {"$oid":"300000000000000000000002"}, "__v": 0 }, @@ -427,7 +427,7 @@ "values": { "val1": 1 }, - "status": 0, + "status": "new", "measurement_template": {"$oid":"300000000000000000000003"}, "__v": 0 }, @@ -437,7 +437,7 @@ "values": { "val1": 1 }, - "status": -1, + "status": "deleted", "measurement_template": {"$oid":"300000000000000000000003"}, "__v": 0 }, @@ -448,7 +448,7 @@ "weight %": 0.5, "standard deviation":null }, - "status": 10, + "status": "validated", "measurement_template": {"$oid":"300000000000000000000002"}, "__v": 0 }, @@ -459,7 +459,7 @@ "weight %": 0.6, "standard deviation":null }, - "status": 0, + "status": "new", "measurement_template": {"$oid":"300000000000000000000002"}, "__v": 0 }, @@ -474,7 +474,7 @@ ], "device": "Alpha II" }, - "status": 10, + "status": "validated", "measurement_template": {"$oid":"300000000000000000000001"}, "__v": 0 } From 63d14acc3c5e15b870174ce1895eb791e4fde03c Mon Sep 17 00:00:00 2001 From: VLE2FE Date: Fri, 7 Aug 2020 08:37:25 +0200 Subject: [PATCH 2/4] fixed mail service --- src/db.ts | 44 ++++++++++++++++++++++++++++++++------------ src/helpers/mail.ts | 3 ++- src/index.ts | 5 ++++- 3 files changed, 38 insertions(+), 14 deletions(-) diff --git a/src/db.ts b/src/db.ts index bba4cb2..6873ade 100644 --- a/src/db.ts +++ b/src/db.ts @@ -7,7 +7,7 @@ import ChangelogModel from './models/changelog'; // database urls, prod db url is retrieved automatically const TESTING_URL = 'mongodb://localhost/dfopdb_test'; const DEV_URL = 'mongodb://localhost/dfopdb'; -const debugging = true; +const debugging = false; if (process.env.NODE_ENV !== 'production' && debugging) { mongoose.set('debug', true); // enable mongoose debug @@ -19,7 +19,8 @@ export default class db { mode: null, }; - static connect (mode = '', done: Function = () => {}) { // set mode to test for unit/integration tests, otherwise skip parameters. done is also only needed for testing + // set mode to test for unit/integration tests, otherwise skip parameters. done is also only needed for testing + static connect (mode = '', done: Function = () => {}) { if (this.state.db) return done(); // db is already connected // find right connection url @@ -43,9 +44,14 @@ export default class db { } // connect to db - mongoose.connect(connectionString, {useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true, connectTimeoutMS: 10000}, err => { + mongoose.connect(connectionString, { + useNewUrlParser: true, + useUnifiedTopology: true, + useCreateIndex: true, + connectTimeoutMS: 10000 + }, err => { if (err) done(err); - }); + }).then(() => {}); mongoose.connection.on('error', console.error.bind(console, 'connection error:')); mongoose.connection.on('connected', () => { // evaluation connection behaviour on prod if (process.env.NODE_ENV !== 'test') { // Do not interfere with testing @@ -63,7 +69,7 @@ export default class db { mongoose.connection.close(() => { console.info('Mongoose default connection disconnected through app termination'); process.exit(0); - }); + }).then(() => {}); } }); mongoose.connection.once('open', () => { @@ -79,14 +85,15 @@ export default class db { console.info(process.env.NODE_ENV === 'test' ? '' : `Disconnected from database`); this.state.db = 0; done(); - }); + }).then(() => {}); } static getState () { return this.state; } - static drop (done: Function = () => {}) { // drop all collections of connected db (only dev and test for safety reasons ;) + // drop all collections of connected db (only dev and test for safety reasons) + static drop (done: Function = () => {}) { if (!this.state.db || this.state.mode === 'prod') return done(); // no db connection or prod db this.state.db.db.listCollections().toArray((err, collections) => { // get list of all collections if (collections.length === 0) { // there are no collections to drop @@ -106,7 +113,8 @@ export default class db { } static loadJson (json, done: Function = () => {}) { // insert given JSON data into db, uses core mongodb methods - if (!this.state.db || !json.hasOwnProperty('collections') || json.collections.length === 0) { // no db connection or nothing to load + // no db connection or nothing to load + if (!this.state.db || !json.hasOwnProperty('collections') || json.collections.length === 0) { return done(); } @@ -126,8 +134,8 @@ export default class db { }); } - // changelog entry - static log(req, thisOrCollection, conditions = null, data = null) { // expects (req, this (from query helper)) or (req, collection, conditions, data) + // changelog entry, expects (req, this (from query helper)) or (req, collection, conditions, data) + static log(req, thisOrCollection, conditions = null, data = null) { if (! (conditions || data)) { // (req, this) data = thisOrCollection._update ? _.cloneDeep(thisOrCollection._update) : {}; // replace undefined with {} Object.keys(data).forEach(key => { @@ -136,12 +144,24 @@ export default class db { delete data[key]; } }); - new ChangelogModel({action: req.method + ' ' + req.url, collectionName: thisOrCollection._collection.collectionName, conditions: thisOrCollection._conditions, data: data, user_id: req.authDetails.id ? req.authDetails.id : null}).save(err => { + new ChangelogModel({ + action: req.method + ' ' + req.url, + collectionName: thisOrCollection._collection.collectionName, + conditions: thisOrCollection._conditions, + data: data, + user_id: req.authDetails.id ? req.authDetails.id : null + }).save(err => { if (err) console.error(err); }); } else { // (req, collection, conditions, data) - new ChangelogModel({action: req.method + ' ' + req.url, collectionName: thisOrCollection, conditions: conditions, data: data, user_id: req.authDetails.id ? req.authDetails.id : null}).save(err => { + new ChangelogModel({ + action: req.method + ' ' + req.url, + collectionName: thisOrCollection, + conditions: conditions, + data: data, + user_id: req.authDetails.id ? req.authDetails.id : null + }).save(err => { if (err) console.error(err); }); } diff --git a/src/helpers/mail.ts b/src/helpers/mail.ts index c8291d1..7f7e64d 100644 --- a/src/helpers/mail.ts +++ b/src/helpers/mail.ts @@ -10,7 +10,7 @@ export default class Mail{ static mailPass: string; static init() { - this.mailPass = Array(64).map(() => Math.floor(Math.random() * 10)).join(''); + this.mailPass = Array(64).fill(0).map(() => Math.floor(Math.random() * 10)).join(''); this.uri = JSON.parse(process.env.VCAP_SERVICES).Mail[0].credentials.uri; this.auth.username = JSON.parse(process.env.VCAP_SERVICES).Mail[0].credentials.username; this.auth.password = JSON.parse(process.env.VCAP_SERVICES).Mail[0].credentials.password; @@ -48,6 +48,7 @@ export default class Mail{ auth: this.auth }); }).then(() => { // init done successfully + console.info('Mail service established successfully'); this.send('lukas.veit@bosch.com', 'Mail Service started', new Date().toString()); }).catch(err => { // anywhere an error occurred console.error(`Mail init error: ${err.request.method} ${err.request.path}: ${err.response.status}`, diff --git a/src/index.ts b/src/index.ts index 54f3c68..7f3834f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,7 @@ import helmet from 'helmet'; import cors from 'cors'; import api from './api'; import db from './db'; +import Mail from './helpers/mail'; // TODO: check header, also in UI @@ -17,9 +18,11 @@ console.info(process.env.NODE_ENV === 'production' ? // mongodb connection db.connect(); +// mail service +Mail.init(); + // create Express app const app = express(); -app.disable('x-powered-by'); // get port from environment, defaults to 3000 const port = process.env.PORT || 3000; From 4ce65ad7cc8dbf1f0ea87ae305332b216832ad46 Mon Sep 17 00:00:00 2001 From: VLE2FE Date: Fri, 7 Aug 2020 09:37:02 +0200 Subject: [PATCH 3/4] added deleted status for /samples --- api/sample.yaml | 8 ++-- src/db.ts | 6 +-- src/helpers/mail.ts | 80 +++++++++++++++++---------------- src/routes/sample.spec.ts | 84 +++++++++++++++++++++-------------- src/routes/sample.ts | 15 +------ src/routes/validate/sample.ts | 9 ++-- src/routes/validate/user.ts | 1 - 7 files changed, 107 insertions(+), 96 deletions(-) diff --git a/api/sample.yaml b/api/sample.yaml index 52b5705..193e9de 100644 --- a/api/sample.yaml +++ b/api/sample.yaml @@ -8,11 +8,13 @@ - /sample parameters: - name: status - description: 'values: validated|new|all, defaults to validated' + description: 'values: validated|new, for dev/admin also deleted, defaults to validated' in: query schema: - type: string - example: all + type: array + items: + type: string + example: ['validated'] - name: from-id description: first id of the requested page, if not given the results are displayed from start in: query diff --git a/src/db.ts b/src/db.ts index 6873ade..933cdc1 100644 --- a/src/db.ts +++ b/src/db.ts @@ -51,7 +51,7 @@ export default class db { connectTimeoutMS: 10000 }, err => { if (err) done(err); - }).then(() => {}); + }); mongoose.connection.on('error', console.error.bind(console, 'connection error:')); mongoose.connection.on('connected', () => { // evaluation connection behaviour on prod if (process.env.NODE_ENV !== 'test') { // Do not interfere with testing @@ -69,7 +69,7 @@ export default class db { mongoose.connection.close(() => { console.info('Mongoose default connection disconnected through app termination'); process.exit(0); - }).then(() => {}); + }); } }); mongoose.connection.once('open', () => { @@ -85,7 +85,7 @@ export default class db { console.info(process.env.NODE_ENV === 'test' ? '' : `Disconnected from database`); this.state.db = 0; done(); - }).then(() => {}); + }); } static getState () { diff --git a/src/helpers/mail.ts b/src/helpers/mail.ts index 7f7e64d..a76b287 100644 --- a/src/helpers/mail.ts +++ b/src/helpers/mail.ts @@ -10,50 +10,52 @@ export default class Mail{ static mailPass: string; static init() { - this.mailPass = Array(64).fill(0).map(() => Math.floor(Math.random() * 10)).join(''); - this.uri = JSON.parse(process.env.VCAP_SERVICES).Mail[0].credentials.uri; - this.auth.username = JSON.parse(process.env.VCAP_SERVICES).Mail[0].credentials.username; - this.auth.password = JSON.parse(process.env.VCAP_SERVICES).Mail[0].credentials.password; - axios({ // get registered mail addresses - method: 'get', - url: this.uri + '/management/userDomainMapping', - auth: this.auth - }).then(res => { - return new Promise(async (resolve, reject) => { - try { - if (res.data.addresses.indexOf(this.address) < 0) { // mail address not registered - if (res.data.addresses.length) { // delete wrong registered mail address - await axios({ - method: 'delete', - url: this.uri + '/management/mailAddresses/' + res.data.addresses[0], + if (process.env.NODE_ENV === 'production') { // only send mails in production + this.mailPass = Array(64).fill(0).map(() => Math.floor(Math.random() * 10)).join(''); + this.uri = JSON.parse(process.env.VCAP_SERVICES).Mail[0].credentials.uri; + this.auth.username = JSON.parse(process.env.VCAP_SERVICES).Mail[0].credentials.username; + this.auth.password = JSON.parse(process.env.VCAP_SERVICES).Mail[0].credentials.password; + axios({ // get registered mail addresses + method: 'get', + url: this.uri + '/management/userDomainMapping', + auth: this.auth + }).then(res => { + return new Promise(async (resolve, reject) => { + try { + if (res.data.addresses.indexOf(this.address) < 0) { // mail address not registered + if (res.data.addresses.length) { // delete wrong registered mail address + await axios({ + method: 'delete', + url: this.uri + '/management/mailAddresses/' + res.data.addresses[0], + auth: this.auth + }); + } + await axios({ // register right mail address + method: 'post', + url: this.uri + '/management/mailAddresses/' + this.address, auth: this.auth }); } - await axios({ // register right mail address - method: 'post', - url: this.uri + '/management/mailAddresses/' + this.address, - auth: this.auth - }); + resolve(); } - resolve(); - } - catch (e) { - reject(e); - } + catch (e) { + reject(e); + } + }); + }).then(() => { + return axios({ // set new mail password + method: 'put', + url: this.uri + '/management/mailAddresses/' + this.address + '/password/' + this.mailPass, + auth: this.auth + }); + }).then(() => { // init done successfully + console.info('Mail service established successfully'); + this.send('lukas.veit@bosch.com', 'Mail Service started', new Date().toString()); + }).catch(err => { // anywhere an error occurred + console.error(`Mail init error: ${err.request.method} ${err.request.path}: ${err.response.status}`, + err.response.data); }); - }).then(() => { - return axios({ // set new mail password - method: 'put', - url: this.uri + '/management/mailAddresses/' + this.address + '/password/' + this.mailPass, - auth: this.auth - }); - }).then(() => { // init done successfully - console.info('Mail service established successfully'); - this.send('lukas.veit@bosch.com', 'Mail Service started', new Date().toString()); - }).catch(err => { // anywhere an error occurred - console.error(`Mail init error: ${err.request.method} ${err.request.path}: ${err.response.status}`, - err.response.data); - }); + } } static send (mailAddress, subject, content, f: (x?) => void = () => {}) { // callback, executed empty or with error diff --git a/src/routes/sample.spec.ts b/src/routes/sample.spec.ts index c32146a..bd798a4 100644 --- a/src/routes/sample.spec.ts +++ b/src/routes/sample.spec.ts @@ -48,6 +48,22 @@ describe('/sample', () => { done(); }); }); + it('returns deleted samples for admin', done => { + TestHelper.request(server, done, { + method: 'get', + url: '/samples?status[]=deleted&fields[]=number&fields=status', + auth: {basic: 'admin'}, + httpStatus: 200 + }).end((err, res) => { + if (err) return done(err); + const json = require('../test/db.json'); + should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ==='deleted').length); + should(res.body).matchEach(sample => { + should(sample).have.property('status', 'deleted').be.type('string'); + }); + done(); + }); + }); it('works with an API key', done => { TestHelper.request(server, done, { method: 'get', @@ -78,7 +94,7 @@ describe('/sample', () => { it('allows filtering by state', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=new', + url: '/samples?status[]=new', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { @@ -104,7 +120,7 @@ describe('/sample', () => { it('uses the given page size', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&page-size=3', + url: '/samples?status[]=new&status[]=validated&page-size=3', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { @@ -116,7 +132,7 @@ describe('/sample', () => { it('returns results starting from first-id', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&from-id=400000000000000000000002', + url: '/samples?status[]=new&status[]=validated&from-id=400000000000000000000002', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { @@ -129,7 +145,7 @@ describe('/sample', () => { it('returns the right page number', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&to-page=2&page-size=2', + url: '/samples?status[]=new&status[]=validated&to-page=2&page-size=2', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { @@ -141,7 +157,7 @@ describe('/sample', () => { it('works with negative page numbers', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&to-page=-1&page-size=2&from-id=400000000000000000000004', + url: '/samples?status[]=new&status[]=validated&to-page=-1&page-size=2&from-id=400000000000000000000004', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { @@ -154,7 +170,7 @@ describe('/sample', () => { it('returns an empty array for a page number out of range', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&to-page=100&page-size=2', + url: '/samples?status[]=new&status[]=validated&to-page=100&page-size=2', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { @@ -167,7 +183,7 @@ describe('/sample', () => { it('returns an empty array for a page number out of negative range', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&to-page=-100&page-size=3&from-id=400000000000000000000004', + url: '/samples?status[]=new&status[]=validated&to-page=-100&page-size=3&from-id=400000000000000000000004', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { @@ -180,7 +196,7 @@ describe('/sample', () => { it('sorts the samples ascending', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&sort=color-asc', + url: '/samples?status[]=new&status[]=validated&sort=color-asc', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { @@ -193,7 +209,7 @@ describe('/sample', () => { it('sorts the samples descending', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&sort=number-desc', + url: '/samples?status[]=new&status[]=validated&sort=number-desc', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { @@ -207,7 +223,7 @@ describe('/sample', () => { it('sorts the samples correctly in combination with paging', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&sort=color-asc&page-size=2&from-id=400000000000000000000006', + url: '/samples?status[]=new&status[]=validated&sort=color-asc&page-size=2&from-id=400000000000000000000006', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { @@ -220,7 +236,7 @@ describe('/sample', () => { it('sorts the samples correctly in combination with going pages backward', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&sort=color-desc&page-size=2&from-id=400000000000000000000004&to-page=-1', + url: '/samples?status[]=new&status[]=validated&sort=color-desc&page-size=2&from-id=400000000000000000000004&to-page=-1', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { @@ -233,7 +249,7 @@ describe('/sample', () => { it('sorts the samples correctly for material keys', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&sort=material.name-desc', + url: '/samples?status[]=new&status[]=validated&sort=material.name-desc', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { @@ -247,7 +263,7 @@ describe('/sample', () => { it('adds the status if specified', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&fields[]=number&fields[]=status', + url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=status', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { @@ -260,7 +276,7 @@ describe('/sample', () => { it('adds the specified measurements', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&fields[]=number&fields[]=measurements.kf', + url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=measurements.kf', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { @@ -273,7 +289,7 @@ describe('/sample', () => { it('multiplies the sample information for each spectrum', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&fields[]=number&fields[]=measurements.spectrum.dpt', + url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=measurements.spectrum.dpt', auth: {basic: 'admin'}, httpStatus: 200 }).end((err, res) => { @@ -287,7 +303,7 @@ describe('/sample', () => { it('filters a sample property', done => { // TODO: implement filters TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&fields[]=number&fields[]=type&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22type%22%2C%22values%22%3A%5B%22part%22%5D%7D', + url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=type&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22type%22%2C%22values%22%3A%5B%22part%22%5D%7D', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { @@ -303,7 +319,7 @@ describe('/sample', () => { it('filters a material property', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&fields[]=number&fields[]=material.name&filters[]=%7B%22mode%22%3A%22in%22%2C%22field%22%3A%22material.name%22%2C%22values%22%3A%5B%22Schulamid%2066%20GF%2025%20H%22%2C%22Stanyl%20TW%20200%20F8%22%5D%7D', + url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.name&filters[]=%7B%22mode%22%3A%22in%22%2C%22field%22%3A%22material.name%22%2C%22values%22%3A%5B%22Schulamid%2066%20GF%2025%20H%22%2C%22Stanyl%20TW%20200%20F8%22%5D%7D', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { @@ -319,7 +335,7 @@ describe('/sample', () => { it('filters by measurement value', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&fields[]=number&fields[]=material.name&fields[]=measurements.kf.weight%20%25&filters[]=%7B%22mode%22%3A%22gt%22%2C%22field%22%3A%22measurements.kf.weight%20%25%22%2C%22values%22%3A%5B0.5%5D%7D', + url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.name&fields[]=measurements.kf.weight%20%25&filters[]=%7B%22mode%22%3A%22gt%22%2C%22field%22%3A%22measurements.kf.weight%20%25%22%2C%22values%22%3A%5B0.5%5D%7D', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { @@ -335,7 +351,7 @@ describe('/sample', () => { it('filters by measurement value not in the fields', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&fields[]=number&fields[]=material.name&filters[]=%7B%22mode%22%3A%22gt%22%2C%22field%22%3A%22measurements.kf.weight%20%25%22%2C%22values%22%3A%5B0.5%5D%7D', + url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.name&filters[]=%7B%22mode%22%3A%22gt%22%2C%22field%22%3A%22measurements.kf.weight%20%25%22%2C%22values%22%3A%5B0.5%5D%7D', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { @@ -349,7 +365,7 @@ describe('/sample', () => { it('filters by a measurement properties property', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&fields[]=number&fields[]=material.name&fields[]=material.properties.glass_fiber&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22material.properties.glass_fiber%22%2C%22values%22%3A%5B%2225%22%5D%7D', + url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.name&fields[]=material.properties.glass_fiber&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22material.properties.glass_fiber%22%2C%22values%22%3A%5B%2225%22%5D%7D', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { @@ -364,7 +380,7 @@ describe('/sample', () => { it('filters and sorts by a measurement properties property', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&sort=material.properties.glass_fiber-desc&fields[]=number&fields[]=material.name&fields[]=material.properties.glass_fiber&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22material.properties.glass_fiber%22%2C%22values%22%3A%5B%2225%22%5D%7D', + url: '/samples?status[]=new&status[]=validated&sort=material.properties.glass_fiber-desc&fields[]=number&fields[]=material.name&fields[]=material.properties.glass_fiber&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22material.properties.glass_fiber%22%2C%22values%22%3A%5B%2225%22%5D%7D', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { @@ -381,7 +397,7 @@ describe('/sample', () => { it('filters multiple properties', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&fields[]=number&fields[]=batch&filters[]=%7B%22mode%22%3A%22lte%22%2C%22field%22%3A%22number%22%2C%22values%22%3A%5B%22Rng33%22%5D%7D&filters[]=%7B%22mode%22%3A%22nin%22%2C%22field%22%3A%22batch%22%2C%22values%22%3A%5B%221704-005%22%5D%7D', + url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=batch&filters[]=%7B%22mode%22%3A%22lte%22%2C%22field%22%3A%22number%22%2C%22values%22%3A%5B%22Rng33%22%5D%7D&filters[]=%7B%22mode%22%3A%22nin%22%2C%22field%22%3A%22batch%22%2C%22values%22%3A%5B%221704-005%22%5D%7D', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { @@ -394,7 +410,7 @@ describe('/sample', () => { it('rejects returning spectral data for a write user', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&fields[]=number&fields[]=measurements.spectrum.dpt', + url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=measurements.spectrum.dpt', auth: {basic: 'janedoe'}, httpStatus: 403 }); @@ -402,7 +418,7 @@ describe('/sample', () => { it('rejects an invalid JSON string as a filters parameter', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&fields[]=number&fields[]=material.glass_fiber&fields[]=batch&filters[]=xx', + url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.glass_fiber&fields[]=batch&filters[]=xx', auth: {basic: 'janedoe'}, httpStatus: 400, res: {status: 'Invalid body format', details: 'Invalid JSON string for filter parameter'} @@ -411,7 +427,7 @@ describe('/sample', () => { it('rejects an invalid filter mode', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&fields[]=number&fields[]=batch&filters[]=%7B%22mode%22%3A%22xx%22%2C%22field%22%3A%22batch%22%2C%22values%22%3A%5B%221704-005%22%5D%7D', + url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=batch&filters[]=%7B%22mode%22%3A%22xx%22%2C%22field%22%3A%22batch%22%2C%22values%22%3A%5B%221704-005%22%5D%7D', auth: {basic: 'janedoe'}, httpStatus: 400, res: {status: 'Invalid body format', details: '"filters[0].mode" must be one of [eq, ne, lt, lte, gt, gte, in, nin, stringin]'} @@ -420,7 +436,7 @@ describe('/sample', () => { it('rejects an filter field not existing', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&fields[]=number&fields[]=material.glass_fiber&fields[]=batch&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22xx%22%2C%22values%22%3A%5B%221704-005%22%5D%7D', + url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.glass_fiber&fields[]=batch&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22xx%22%2C%22values%22%3A%5B%221704-005%22%5D%7D', auth: {basic: 'janedoe'}, httpStatus: 400, res: {status: 'Invalid body format', details: 'Invalid JSON string for filter parameter'} @@ -429,7 +445,7 @@ describe('/sample', () => { it('rejects unknown measurement names', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&fields[]=number&fields[]=measurements.xx', + url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=measurements.xx', auth: {basic: 'janedoe'}, httpStatus: 400, res: {status: 'Invalid body format', details: 'Measurement key not found'} @@ -438,7 +454,7 @@ describe('/sample', () => { it('returns a correct csv file if specified', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&page-size=2&csv=true', + url: '/samples?status[]=new&status[]=validated&page-size=2&csv=true', contentType: /text\/csv/, auth: {basic: 'janedoe'}, httpStatus: 200 @@ -453,7 +469,7 @@ describe('/sample', () => { it('returns only the fields specified', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&page-size=1&fields[]=number&fields[]=condition&fields[]=color&fields[]=material.name&fields[]=material.supplier', + url: '/samples?status[]=new&status[]=validated&page-size=1&fields[]=number&fields[]=condition&fields[]=color&fields[]=material.name&fields[]=material.supplier', auth: {basic: 'janedoe'}, httpStatus: 200, res: [{number: '1', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, color: 'black', material: {name: 'Schulamid 66 GF 25 H', supplier: 'Schulmann'}}] @@ -462,7 +478,7 @@ describe('/sample', () => { it('returns specified material properties fields', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&fields[]=number&fields[]=material.properties.glass_fiber&fields[]=material.name', + url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.properties.glass_fiber&fields[]=material.name', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { @@ -490,7 +506,7 @@ describe('/sample', () => { it('rejects an invalid fields parameter', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&page-size=1&fields=number', + url: '/samples?status[]=new&status[]=validated&page-size=1&fields=number', auth: {basic: 'janedoe'}, httpStatus: 400, res: {status: 'Invalid body format', details: '"fields" must be an array'} @@ -499,7 +515,7 @@ describe('/sample', () => { it('rejects an unknown field name', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=all&page-size=1&fields[]=xx', + url: '/samples?status[]=new&status[]=validated&page-size=1&fields[]=xx', auth: {basic: 'janedoe'}, httpStatus: 400, res: {status: 'Invalid body format', details: 'Invalid field name'} @@ -535,10 +551,10 @@ describe('/sample', () => { it('rejects an invalid state name', done => { TestHelper.request(server, done, { method: 'get', - url: '/samples?status=xxx', + url: '/samples?status[]=xxx', auth: {basic: 'janedoe'}, httpStatus: 400, - res: {status: 'Invalid body format', details: '"status" must be one of [validated, new, all]'} + res: {status: 'Invalid body format', details: '"status[0]" must be one of [validated, new]'} }); }); it('rejects unauthorized requests', done => { diff --git a/src/routes/sample.ts b/src/routes/sample.ts index 3bbfe9c..922b576 100644 --- a/src/routes/sample.ts +++ b/src/routes/sample.ts @@ -31,7 +31,7 @@ const router = express.Router(); router.get('/samples', async (req, res, next) => { if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'all')) return; - const {error, value: filters} = SampleValidate.query(req.query); + const {error, value: filters} = SampleValidate.query(req.query, ['dev', 'admin'].indexOf(req.authDetails.level) >= 0); if (error) return res400(error, res); // spectral data not allowed for read/write users @@ -460,7 +460,6 @@ router.put('/sample/' + IdValidate.parameter(), (req, res, next) => { if (!req.auth(res, ['write', 'dev', 'admin'], 'basic')) return; const {error, value: sample} = SampleValidate.input(req.body, 'change'); - console.log(error); if (error) return res400(error, res); SampleModel.findById(req.params.id).lean().exec(async (err, sampleData: any) => { // check if id exists @@ -824,17 +823,7 @@ function sortQuery(filters, sortKeys, sortStartValue) { // sortKeys = ['primary } function statusQuery(filters, field) { - if (filters.hasOwnProperty('status')) { - if(filters.status === 'all') { - return {$or: [{[field]: 'validated'}, {[field]: 'new'}]}; - } - else { - return {[field]: filters.status}; - } - } - else { // default - return {[field]: 'validated'}; - } + return {$or: filters.status.map(e => ({[field]: e}))}; } function addFilterQueries (queryPtr, filters) { // returns array of match queries from given filters diff --git a/src/routes/validate/sample.ts b/src/routes/validate/sample.ts index e831fc1..96cbc9c 100644 --- a/src/routes/validate/sample.ts +++ b/src/routes/validate/sample.ts @@ -168,11 +168,10 @@ export default class SampleValidate { joiObject[param] = Joi.any(); }); const {value, error} = Joi.object(joiObject).validate(data, {stripUnknown: true}); - console.log(error); return error !== undefined? null : value; } - static query (data) { + static query (data, dev = false) { if (data.filters && data.filters.length) { const filterValidation = Joi.array().items(Joi.string()).validate(data.filters); if (filterValidation.error) return filterValidation; @@ -216,8 +215,12 @@ export default class SampleValidate { return {error: {details: [{message: 'Invalid JSON string for filter parameter'}]}, value: null} } } + const acceptedStatuses = ['validated', 'new']; + if (dev) { // dev and admin can also access deleted samples + acceptedStatuses.push('deleted') + } return Joi.object({ - status: Joi.string().valid('validated', 'new', 'all'), + status: Joi.array().items(Joi.string().valid(...acceptedStatuses)).default(['validated']), 'from-id': IdValidate.get(), 'to-page': Joi.number().integer(), 'page-size': Joi.number().integer().min(1), diff --git a/src/routes/validate/user.ts b/src/routes/validate/user.ts index b7de592..f049fd4 100644 --- a/src/routes/validate/user.ts +++ b/src/routes/validate/user.ts @@ -82,7 +82,6 @@ export default class UserValidate { // validate input for user location: this.user.location, devices: this.user.devices }).validate(data, {stripUnknown: true}); - console.log(data); return error !== undefined? null : value; } From 1396fb03260c9793be019f95386996649195a0a2 Mon Sep 17 00:00:00 2001 From: VLE2FE Date: Fri, 7 Aug 2020 10:54:01 +0200 Subject: [PATCH 4/4] changed password policy --- src/routes/user.spec.ts | 8 ++++---- src/routes/validate/user.ts | 5 ++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/routes/user.spec.ts b/src/routes/user.spec.ts index 710ee0a..4a501e3 100644 --- a/src/routes/user.spec.ts +++ b/src/routes/user.spec.ts @@ -303,8 +303,8 @@ describe('/user', () => { url: '/user', auth: {basic: 'admin'}, httpStatus: 400, - req: {pass: 'password'}, - res: {status: 'Invalid body format', details: 'password must have at least 8 characters, one uppercase and one lowercase character, one number and at least one of the following characters: !\"\\#%&\'()*+,-.\\/:;<=>?@[]^_`\u0000|}~'} + req: {pass: 'pass'}, + res: {status: 'Invalid body format', details: '"pass" length must be at least 8 characters long'} }); }); it('rejects requests from non-admins for another user', done => { @@ -585,8 +585,8 @@ describe('/user', () => { url: '/user/new', auth: {basic: 'admin'}, httpStatus: 400, - req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'password', level: 'read', location: 'Rng', devices: ['Alpha II']}, - res: {status: 'Invalid body format', details: 'password must have at least 8 characters, one uppercase and one lowercase character, one number and at least one of the following characters: !\"\\#%&\'()*+,-.\\/:;<=>?@[]^_`\u0000|}~'} + req: {email: 'john.doe@bosch.com', name: 'johndoe', pass: 'pass', level: 'read', location: 'Rng', devices: ['Alpha II']}, + res: {status: 'Invalid body format', details: '"pass" length must be at least 8 characters long'} }); }); it('rejects requests from non-admins', done => { diff --git a/src/routes/validate/user.ts b/src/routes/validate/user.ts index f049fd4..f25e492 100644 --- a/src/routes/validate/user.ts +++ b/src/routes/validate/user.ts @@ -17,9 +17,8 @@ export default class UserValidate { // validate input for user .max(128), pass: Joi.string() - .pattern(/^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[!"#%&'()*+,-.\/:;<=>?@[\]^_`{|}~])(?=\S+$)[a-zA-Z0-9!"#%&'()*+,\-.\/:;<=>?@[\]^_`{|}~]{8,}$/) - .max(128) - .messages({'string.pattern.base': 'password must have at least 8 characters, one uppercase and one lowercase character, one number and at least one of the following characters: !"\\#%&\'()*+,-.\\/:;<=>?@[]^_`\\{|}~'}), + .min(8) + .max(128), level: Joi.string() .valid(...globals.levels),