improved globals and added status and spectrum
This commit is contained in:
		@@ -146,7 +146,7 @@ export default class db {
 | 
			
		||||
      });
 | 
			
		||||
      new ChangelogModel({
 | 
			
		||||
        action: req.method + ' ' + req.url,
 | 
			
		||||
        collectionName: thisOrCollection._collection.collectionName,
 | 
			
		||||
        collection_name: thisOrCollection._collection.collectionName,
 | 
			
		||||
        conditions: thisOrCollection._conditions,
 | 
			
		||||
        data: data,
 | 
			
		||||
        user_id: req.authDetails.id ? req.authDetails.id : null
 | 
			
		||||
@@ -157,7 +157,7 @@ export default class db {
 | 
			
		||||
    else {  // (req, collection, conditions, data)
 | 
			
		||||
      new ChangelogModel({
 | 
			
		||||
        action: req.method + ' ' + req.url,
 | 
			
		||||
        collectionName: thisOrCollection,
 | 
			
		||||
        collection_name: thisOrCollection,
 | 
			
		||||
        conditions: conditions,
 | 
			
		||||
        data: data,
 | 
			
		||||
        user_id: req.authDetails.id ? req.authDetails.id : null
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,24 @@
 | 
			
		||||
const globals = {
 | 
			
		||||
  levels: [  // access levels, sorted asc by rights
 | 
			
		||||
    'read',
 | 
			
		||||
    'write',
 | 
			
		||||
    'dev',
 | 
			
		||||
    'admin'
 | 
			
		||||
  ],
 | 
			
		||||
// globals for required names in the database. change values here to rename these properties
 | 
			
		||||
// the keys are the terms used internally, the values can be changed to other terms used in database and output
 | 
			
		||||
 | 
			
		||||
  status: [  // document statuses
 | 
			
		||||
    'deleted',
 | 
			
		||||
    'new',
 | 
			
		||||
    'validated',
 | 
			
		||||
  ]
 | 
			
		||||
const globals = {
 | 
			
		||||
  levels: {  // access levels, sorted asc by rights
 | 
			
		||||
    read: 'read',
 | 
			
		||||
    write: 'write',
 | 
			
		||||
    dev: 'dev',
 | 
			
		||||
    admin: 'admin'
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  status: {  // names of the document statuses
 | 
			
		||||
    del: 'deleted',
 | 
			
		||||
    new: 'new',
 | 
			
		||||
    val: 'validated',
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  spectrum: {  // names of required spectrum fields
 | 
			
		||||
    spectrum: 'spectrum',
 | 
			
		||||
    dpt: 'dpt'
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default globals;
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import basicAuth from 'basic-auth';
 | 
			
		||||
import bcrypt from 'bcryptjs';
 | 
			
		||||
import UserModel from '../models/user';
 | 
			
		||||
import globals from '../globals';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// appends req.auth(res, ['levels'], method = 'all')
 | 
			
		||||
@@ -64,7 +65,12 @@ function basic (req, next): any {  // checks basic auth and returns changed user
 | 
			
		||||
          bcrypt.compare(auth.pass, data[0].pass, (err, res) => {  // check password
 | 
			
		||||
            if (err) return next(err);
 | 
			
		||||
            if (res === true) {  // password correct
 | 
			
		||||
              resolve({level: data[0].level, name: data[0].name, id: data[0]._id.toString(), location: data[0].location});
 | 
			
		||||
              resolve({
 | 
			
		||||
                level: Object.entries(globals.levels).find(e => e[1] === data[0].level)[0],
 | 
			
		||||
                name: data[0].name,
 | 
			
		||||
                id: data[0]._id.toString(),
 | 
			
		||||
                location: data[0].location
 | 
			
		||||
              });
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
              resolve(null);
 | 
			
		||||
@@ -88,7 +94,12 @@ function key (req, next): any {  // checks API key and returns changed user obje
 | 
			
		||||
      UserModel.find({key: req.query.key}).lean().exec( (err, data: any) => {  // find user
 | 
			
		||||
        if (err) return next(err);
 | 
			
		||||
        if (data.length === 1) {  // one user found
 | 
			
		||||
          resolve({level: data[0].level, name: data[0].name, id: data[0]._id.toString(), location: data[0].location});
 | 
			
		||||
          resolve({
 | 
			
		||||
            level: Object.entries(globals.levels).find(e => e[1] === data[0].level)[0],
 | 
			
		||||
            name: data[0].name,
 | 
			
		||||
            id: data[0]._id.toString(),
 | 
			
		||||
            location: data[0].location
 | 
			
		||||
          });
 | 
			
		||||
          if (!/^\/api/m.test(req.url)){
 | 
			
		||||
            delete req.query.key;  // delete query parameter to avoid interference with later validation
 | 
			
		||||
          }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,12 @@
 | 
			
		||||
import globals from '../globals';
 | 
			
		||||
 | 
			
		||||
export default function flatten (data, keepArray = false) {  // flatten object: {a: {b: true}} -> {a.b: true}
 | 
			
		||||
  const result = {};
 | 
			
		||||
  function recurse (cur, prop) {
 | 
			
		||||
    if (Object(cur) !== cur || Object.keys(cur).length === 0) {
 | 
			
		||||
      result[prop] = cur;
 | 
			
		||||
    }
 | 
			
		||||
    else if (prop === 'spectrum.dpt') {
 | 
			
		||||
      console.log('dpt');
 | 
			
		||||
    else if (prop === `${globals.spectrum.spectrum}.${globals.spectrum.dpt}`) {
 | 
			
		||||
      result[prop + '.labels'] = cur.map(e => e[0]);
 | 
			
		||||
      result[prop + '.values'] = cur.map(e => e[1]);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -78,10 +78,10 @@ app.use(compression());  // compress responses
 | 
			
		||||
app.use(express.json({ limit: '5mb'}));
 | 
			
		||||
app.use(express.urlencoded({ extended: false, limit: '5mb' }));
 | 
			
		||||
app.use(bodyParser.json());
 | 
			
		||||
const injectionBlackList = ['$', '{', '&&', '||'];
 | 
			
		||||
app.use(contentFilter({
 | 
			
		||||
  urlBlackList: injectionBlackList,
 | 
			
		||||
  bodyBlackList: injectionBlackList
 | 
			
		||||
  urlBlackList: ['$', '&&', '||'],
 | 
			
		||||
  bodyBlackList: ['$', '{', '&&', '||'],
 | 
			
		||||
  appendFound: true
 | 
			
		||||
}));  // filter URL query attacks
 | 
			
		||||
app.use((err, req, res, ignore) => {  // bodyParser error handling
 | 
			
		||||
  res.status(400).send({status: 'Invalid JSON body'});
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ import mongoose from 'mongoose';
 | 
			
		||||
 | 
			
		||||
const ChangelogSchema = new mongoose.Schema({
 | 
			
		||||
  action: String,
 | 
			
		||||
  collectionName: String,
 | 
			
		||||
  collection_name: String,
 | 
			
		||||
  conditions: Object,
 | 
			
		||||
  data: Object,
 | 
			
		||||
  user_id: mongoose.Schema.Types.ObjectId
 | 
			
		||||
 
 | 
			
		||||
@@ -9,10 +9,10 @@ import MaterialSupplierModel from '../models/material_suppliers';
 | 
			
		||||
import IdValidate from './validate/id';
 | 
			
		||||
import res400 from './validate/res400';
 | 
			
		||||
import mongoose from 'mongoose';
 | 
			
		||||
import globals from '../globals';
 | 
			
		||||
import db from '../db';
 | 
			
		||||
import MaterialTemplateModel from '../models/material_template';
 | 
			
		||||
import ParametersValidate from './validate/parameters';
 | 
			
		||||
import globals from '../globals';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -28,14 +28,14 @@ router.get('/materials', (req, res, next) => {
 | 
			
		||||
 | 
			
		||||
  if (filters.hasOwnProperty('status')) {
 | 
			
		||||
    if(filters.status === 'all') {
 | 
			
		||||
      conditions = {$or: [{status: 'validated'}, {status: 'new'}]}
 | 
			
		||||
      conditions = {$or: [{status: globals.status.val}, {status: globals.status.new}]}
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      conditions = {status: filters.status};
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  else {  // default
 | 
			
		||||
    conditions = {status: 'validated'};
 | 
			
		||||
    conditions = {status: globals.status.val};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  MaterialModel.find(conditions).populate('group_id').populate('supplier_id').lean().exec((err, data) => {
 | 
			
		||||
@@ -46,7 +46,7 @@ router.get('/materials', (req, res, next) => {
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
router.get('/materials/:state(new|deleted)', (req, res, next) => {
 | 
			
		||||
router.get(`/materials/:state(${globals.status.new}|${globals.status.del})`, (req, res, next) => {
 | 
			
		||||
  if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
 | 
			
		||||
 | 
			
		||||
  MaterialModel.find({status: req.params.state}).populate('group_id').populate('supplier_id')
 | 
			
		||||
@@ -69,7 +69,7 @@ router.get('/material/' + IdValidate.parameter(), (req, res, next) => {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // deleted materials only available for dev/admin
 | 
			
		||||
    if (data.status === 'deleted' && !req.auth(res, ['dev', 'admin'], 'all')) return;
 | 
			
		||||
    if (data.status === globals.status.del && !req.auth(res, ['dev', 'admin'], 'all')) return;
 | 
			
		||||
    res.json(MaterialValidate.output(data));
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
@@ -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 = 'new';  // set status to new
 | 
			
		||||
      material.status = globals.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:'deleted'})
 | 
			
		||||
    MaterialModel.findByIdAndUpdate(req.params.id, {status: globals.status.del})
 | 
			
		||||
      .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('new', req, res, next);
 | 
			
		||||
  setStatus(globals.status.new, req, res, next);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
router.put('/material/validate/' + IdValidate.parameter(), (req, res, next) => {
 | 
			
		||||
  if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
 | 
			
		||||
 | 
			
		||||
  setStatus('validated', req, res, next);
 | 
			
		||||
  setStatus(globals.status.val, 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 = 'new';  // set status to new
 | 
			
		||||
  material.status = globals.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());
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
import should from 'should/as-function';
 | 
			
		||||
import MeasurementModel from '../models/measurement';
 | 
			
		||||
import TestHelper from "../test/helper";
 | 
			
		||||
import globals from '../globals';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
describe('/measurement', () => {
 | 
			
		||||
 
 | 
			
		||||
@@ -8,8 +8,8 @@ import MeasurementValidate from './validate/measurement';
 | 
			
		||||
import IdValidate from './validate/id';
 | 
			
		||||
import res400 from './validate/res400';
 | 
			
		||||
import ParametersValidate from './validate/parameters';
 | 
			
		||||
import globals from '../globals';
 | 
			
		||||
import db from '../db';
 | 
			
		||||
import globals from '../globals';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const router = express.Router();
 | 
			
		||||
@@ -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 === 'deleted' && !req.auth(res, ['dev', 'admin'], 'all')) return;
 | 
			
		||||
    if (data.status === globals.status.del && !req.auth(res, ['dev', 'admin'], 'all')) return;
 | 
			
		||||
 | 
			
		||||
    res.json(MeasurementValidate.output(data, req));
 | 
			
		||||
  });
 | 
			
		||||
@@ -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 = 'new';  // set status to new
 | 
			
		||||
      measurement.status = globals.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:'deleted'})
 | 
			
		||||
    await MeasurementModel.findByIdAndUpdate(req.params.id, {status: globals.status.del})
 | 
			
		||||
      .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('new', req, res, next);
 | 
			
		||||
  setStatus(globals.status.new, req, res, next);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
router.put('/measurement/validate/' + IdValidate.parameter(), (req, res, next) => {
 | 
			
		||||
  if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
 | 
			
		||||
 | 
			
		||||
  setStatus('validated', req, res, next);
 | 
			
		||||
  setStatus(globals.status.val, 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 = 'new';
 | 
			
		||||
  measurement.status = globals.status.new;
 | 
			
		||||
  await new MeasurementModel(measurement).save((err, data) => {
 | 
			
		||||
    if (err) return next(err);
 | 
			
		||||
    db.log(req, 'measurements', {_id: data._id}, data.toObject());
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@ router.get('/', (req, res) => {
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
router.get('/authorized', (req, res) => {
 | 
			
		||||
  if (!req.auth(res, globals.levels)) return;
 | 
			
		||||
  if (!req.auth(res, Object.values(globals.levels))) return;
 | 
			
		||||
  res.json({
 | 
			
		||||
    status: 'Authorization successful',
 | 
			
		||||
    method: req.authDetails.method,
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@ import ParametersValidate from './validate/parameters';
 | 
			
		||||
import db from '../db';
 | 
			
		||||
import csv from '../helpers/csv';
 | 
			
		||||
import flatten from '../helpers/flatten';
 | 
			
		||||
import globals from '../globals';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const router = express.Router();
 | 
			
		||||
@@ -36,7 +37,7 @@ router.get('/samples', async (req, res, next) => {
 | 
			
		||||
  if (error) return res400(error, res);
 | 
			
		||||
 | 
			
		||||
  // spectral data and csv not allowed for read/write users
 | 
			
		||||
  if ((filters.fields.find(e => /\.dpt$/.test(e)) || filters.output !== 'json') &&
 | 
			
		||||
  if ((filters.fields.find(e => e.indexOf('.' + globals.spectrum.dpt) >= 0) || filters.output !== 'json') &&
 | 
			
		||||
    !req.auth(res, ['dev', 'admin'], 'all')) return;
 | 
			
		||||
 | 
			
		||||
  // TODO: find a better place for these
 | 
			
		||||
@@ -260,7 +261,7 @@ router.get('/samples', async (req, res, next) => {
 | 
			
		||||
 | 
			
		||||
  // count total number of items before $skip and $limit, only works when from-id is not specified and spectra are not
 | 
			
		||||
  // included
 | 
			
		||||
  if (!filters.fields.find(e => /spectrum\./.test(e)) && !filters['from-id']) {
 | 
			
		||||
  if (!filters.fields.find(e => e.indexOf(globals.spectrum.spectrum + '.') >= 0) && !filters['from-id']) {
 | 
			
		||||
    queryPtr.push({$facet: {count: [{$count: 'count'}], samples: []}});
 | 
			
		||||
    queryPtr = queryPtr[queryPtr.length - 1].$facet.samples;  // add rest of aggregation pipeline into $facet
 | 
			
		||||
  }
 | 
			
		||||
@@ -328,7 +329,7 @@ router.get('/samples', async (req, res, next) => {
 | 
			
		||||
      return res.status(400).json({status: 'Invalid body format', details: 'Measurement key not found'});
 | 
			
		||||
    }
 | 
			
		||||
    // use different lookup methods with and without spectrum for the best performance
 | 
			
		||||
    if (fieldsToAdd.find(e => /spectrum\./.test(e))) {
 | 
			
		||||
    if (fieldsToAdd.find(e => e.indexOf(globals.spectrum.spectrum + '.') >= 0)) {
 | 
			
		||||
      queryPtr.push(
 | 
			
		||||
        {$lookup: {from: 'measurements', localField: '_id', foreignField: 'sample_id', as: 'measurements'}}
 | 
			
		||||
      );
 | 
			
		||||
@@ -343,22 +344,12 @@ router.get('/samples', async (req, res, next) => {
 | 
			
		||||
          as: 'measurements'
 | 
			
		||||
        }});
 | 
			
		||||
    }
 | 
			
		||||
    measurementTemplates.forEach(template => {  // TODO: hard coded dpt for special treatment, change later
 | 
			
		||||
    measurementTemplates.forEach(template => {
 | 
			
		||||
      addMeasurements(queryPtr, template);
 | 
			
		||||
      if (measurementFieldsFields.find(e => e === 'spectrum')) {
 | 
			
		||||
        queryPtr.push({$unwind: '$spectrum'});
 | 
			
		||||
      if (measurementFieldsFields.find(e => e === globals.spectrum.spectrum)) {
 | 
			
		||||
        queryPtr.push({$unwind: '$' + globals.spectrum.spectrum});
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    // if (measurementFieldsFields.find(e => e === 'spectrum')) {  // TODO: remove hardcoded as well
 | 
			
		||||
    //   queryPtr.push(
 | 
			
		||||
    //     {$addFields: {spectrum: {$filter: {input: '$measurements', cond: {
 | 
			
		||||
    //       $eq: ['$$this.measurement_template', measurementTemplates.filter(e => e.name === 'spectrum')[0]._id]
 | 
			
		||||
    //     }}}}},
 | 
			
		||||
    //     {$addFields: {spectrum: '$spectrum.values'}},
 | 
			
		||||
    //     {$unwind: '$spectrum'}
 | 
			
		||||
    //   );
 | 
			
		||||
    // }
 | 
			
		||||
    // queryPtr.push({$unset: 'measurements'});
 | 
			
		||||
    queryPtr.push({$project: {measurements: 0}});
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -372,7 +363,8 @@ router.get('/samples', async (req, res, next) => {
 | 
			
		||||
    projection._id = false;
 | 
			
		||||
  }
 | 
			
		||||
  queryPtr.push({$project: projection});
 | 
			
		||||
  if (!fieldsToAdd.find(e => /spectrum\./.test(e))) {  // use streaming when including spectrum files
 | 
			
		||||
  // use streaming when including spectrum files
 | 
			
		||||
  if (!fieldsToAdd.find(e => e.indexOf(globals.spectrum.spectrum + '.') >= 0)) {
 | 
			
		||||
    collection.aggregate(query).allowDiskUse(true).exec((err, data) => {
 | 
			
		||||
      if (err) return next(err);
 | 
			
		||||
      if (data[0] && data[0].count) {
 | 
			
		||||
@@ -439,7 +431,7 @@ router.get('/samples', async (req, res, next) => {
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
router.get('/samples/:state(new|deleted)', (req, res, next) => {
 | 
			
		||||
router.get(`/samples/:state(${globals.status.new}|${globals.status.del})`, (req, res, next) => {
 | 
			
		||||
  if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
 | 
			
		||||
 | 
			
		||||
  SampleModel.find({status: req.params.state}).lean().exec((err, data) => {
 | 
			
		||||
@@ -479,7 +471,7 @@ router.put('/sample/' + IdValidate.parameter(), (req, res, next) => {
 | 
			
		||||
    if (!sampleData) {
 | 
			
		||||
      return res.status(404).json({status: 'Not found'});
 | 
			
		||||
    }
 | 
			
		||||
    if (sampleData.status === 'deleted') {
 | 
			
		||||
    if (sampleData.status === globals.status.del) {
 | 
			
		||||
      return res.status(403).json({status: 'Forbidden'});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -527,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 = 'new';
 | 
			
		||||
      sample.status = globals.status.new;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    await SampleModel.findByIdAndUpdate(req.params.id, sample, {new: true}).log(req).lean().exec((err, data: any) => {
 | 
			
		||||
@@ -555,7 +547,7 @@ router.delete('/sample/' + IdValidate.parameter(), (req, res, next) => {
 | 
			
		||||
      if (err) return next(err);
 | 
			
		||||
 | 
			
		||||
      // set status of associated measurements also to deleted
 | 
			
		||||
      MeasurementModel.updateMany({sample_id: mongoose.Types.ObjectId(req.params.id)}, {status: 'deleted'})
 | 
			
		||||
      MeasurementModel.updateMany({sample_id: mongoose.Types.ObjectId(req.params.id)}, {status: globals.status.del})
 | 
			
		||||
        .log(req).lean().exec(err => {
 | 
			
		||||
        if (err) return next(err);
 | 
			
		||||
 | 
			
		||||
@@ -589,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: 'new'}).log(req).lean().exec((err, data) => {
 | 
			
		||||
  SampleModel.findByIdAndUpdate(req.params.id, {status: globals.status.new}).log(req).lean().exec((err, data) => {
 | 
			
		||||
    if (err) return next(err);
 | 
			
		||||
 | 
			
		||||
    if (!data) {
 | 
			
		||||
@@ -602,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: 'validated'}).log(req).lean().exec((err, data) => {
 | 
			
		||||
  SampleModel.findByIdAndUpdate(req.params.id, {status: globals.status.val}).log(req).lean().exec((err, data) => {
 | 
			
		||||
    if (err) return next(err);
 | 
			
		||||
    if (!data) {
 | 
			
		||||
      return res.status(404).json({status: 'Not found'});
 | 
			
		||||
@@ -635,7 +627,7 @@ router.post('/sample/new', async (req, res, next) => {
 | 
			
		||||
    if (!await conditionCheck(sample.condition, 'change', res, next)) return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  sample.status = 'new';  // set status to new
 | 
			
		||||
  sample.status = globals.status.new;  // set status to new
 | 
			
		||||
  if (sample.hasOwnProperty('number')) {
 | 
			
		||||
    if (!await numberCheck(sample, res, next)) return;
 | 
			
		||||
  }
 | 
			
		||||
@@ -885,7 +877,7 @@ async function sampleReturn (sampleData, req, res, next) {
 | 
			
		||||
    sampleData = sampleData.toObject();
 | 
			
		||||
 | 
			
		||||
    // deleted samples only available for dev/admin
 | 
			
		||||
    if (sampleData.status === 'deleted' && !req.auth(res, ['dev', 'admin'], 'all')) return;
 | 
			
		||||
    if (sampleData.status === globals.status.del && !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;
 | 
			
		||||
@@ -896,8 +888,8 @@ async function sampleReturn (sampleData, req, res, next) {
 | 
			
		||||
      sampleData.measurements = data;
 | 
			
		||||
      if (['dev', 'admin'].indexOf(req.authDetails.level) < 0) {  // strip dpt values if not dev or admin
 | 
			
		||||
        sampleData.measurements.forEach(measurement => {
 | 
			
		||||
          if (measurement.values.dpt) {
 | 
			
		||||
            delete measurement.values.dpt;
 | 
			
		||||
          if (measurement.values[globals.spectrum.dpt]) {
 | 
			
		||||
            delete measurement.values[globals.spectrum.dpt];
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
 
 | 
			
		||||
@@ -60,13 +60,13 @@ router.put('/user:username([/](?!key|new).?*|/?)', async (req, res, next) => {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // get current mail address to compare to given address
 | 
			
		||||
  const {email: oldMail} = await UserModel.findOne({name: username}).lean().exec().catch(err => next(err));
 | 
			
		||||
  const oldUserData = await UserModel.findOne({name: username}).lean().exec().catch(err => next(err));
 | 
			
		||||
 | 
			
		||||
  await UserModel.findOneAndUpdate({name: username}, user, {new: true}).log(req).lean().exec(  (err, data:any) => {
 | 
			
		||||
    if (err) return next(err);
 | 
			
		||||
    if (data) {
 | 
			
		||||
      if (data.mail !== oldMail) {  // mail address was changed, send notice to old address
 | 
			
		||||
        Mail.send(oldMail, 'Email change in your DeFinMa database account',
 | 
			
		||||
      if (data.mail !== oldUserData.email) {  // mail address was changed, send notice to old address
 | 
			
		||||
        Mail.send(oldUserData.email, 'Email change in your DeFinMa database account',
 | 
			
		||||
          'Hi, <br><br> Your email address of your DeFinMa account was changed to ' + data.mail +
 | 
			
		||||
          '<br><br>If you actually did this, just delete this email.' +
 | 
			
		||||
          '<br><br>If you did not change your email, someone might be messing around with your account, ' +
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import Joi from 'joi';
 | 
			
		||||
 | 
			
		||||
import IdValidate from './id';
 | 
			
		||||
import globals from '../../globals';
 | 
			
		||||
 | 
			
		||||
export default class MaterialValidate {  // validate input for material
 | 
			
		||||
  private static material = {
 | 
			
		||||
@@ -84,7 +85,7 @@ export default class MaterialValidate {  // validate input for material
 | 
			
		||||
 | 
			
		||||
  static query (data) {
 | 
			
		||||
    return Joi.object({
 | 
			
		||||
      status: Joi.string().valid('validated', 'new', 'all')
 | 
			
		||||
      status: Joi.string().valid(globals.status.val, globals.status.new, 'all')
 | 
			
		||||
    }).validate(data);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import Joi from 'joi';
 | 
			
		||||
 | 
			
		||||
import IdValidate from './id';
 | 
			
		||||
import globals from '../../globals';
 | 
			
		||||
 | 
			
		||||
export default class MeasurementValidate {
 | 
			
		||||
  private static measurement = {
 | 
			
		||||
@@ -37,8 +38,8 @@ export default class MeasurementValidate {
 | 
			
		||||
  static output (data, req) {  // validate output and strip unwanted properties, returns null if not valid
 | 
			
		||||
    data = IdValidate.stringify(data);
 | 
			
		||||
    // spectral data not allowed for read/write users
 | 
			
		||||
    if (['dev', 'admin'].indexOf(req.authDetails.level) < 0 && data.values.dpt) {
 | 
			
		||||
      delete data.values.dpt;
 | 
			
		||||
    if (['dev', 'admin'].indexOf(req.authDetails.level) < 0 && data.values[globals.spectrum.dpt]) {
 | 
			
		||||
      delete data.values[globals.spectrum.dpt];
 | 
			
		||||
    }
 | 
			
		||||
    const {value, error} = Joi.object({
 | 
			
		||||
      _id: IdValidate.get(),
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,7 @@ export default class RootValidate {  // validate input for root methods
 | 
			
		||||
 | 
			
		||||
  static changelogOutput (data) {
 | 
			
		||||
    data.date = data._id.getTimestamp();
 | 
			
		||||
    data.collection = data.collectionName;
 | 
			
		||||
    data.collection = data.collection_name;
 | 
			
		||||
    data = IdValidate.stringify(data);
 | 
			
		||||
    const {value, error} = Joi.object({
 | 
			
		||||
      date: this.changelog.timestamp,
 | 
			
		||||
 
 | 
			
		||||
@@ -53,7 +53,7 @@ export default class SampleValidate {
 | 
			
		||||
      .min('1970-01-01T00:00:00.000Z'),
 | 
			
		||||
 | 
			
		||||
    status: Joi.string()
 | 
			
		||||
      .valid(...globals.status)
 | 
			
		||||
      .valid(...Object.values(globals.status))
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  private static sortKeys = [
 | 
			
		||||
@@ -68,7 +68,7 @@ export default class SampleValidate {
 | 
			
		||||
    'material.supplier',
 | 
			
		||||
    'material.group',
 | 
			
		||||
    'material.properties.*',
 | 
			
		||||
    'measurements.(?!spectrum\.dpt)*'
 | 
			
		||||
    `measurements.(?!${globals.spectrum.spectrum}.${globals.spectrum.dpt})*`
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  private static fieldKeys = [
 | 
			
		||||
@@ -81,7 +81,7 @@ export default class SampleValidate {
 | 
			
		||||
    'user_id',
 | 
			
		||||
    'material._id',
 | 
			
		||||
    'material.numbers',
 | 
			
		||||
    'measurements.spectrum.dpt',
 | 
			
		||||
    `measurements.${globals.spectrum.spectrum}.${globals.spectrum.dpt}`,
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  static input (data, param) {  // validate input, set param to 'new' to make all attributes required
 | 
			
		||||
@@ -215,12 +215,12 @@ export default class SampleValidate {
 | 
			
		||||
        return {error: {details: [{message: 'Invalid JSON string for filter parameter'}]}, value: null}
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    const acceptedStatuses = ['validated', 'new'];
 | 
			
		||||
    const acceptedStatuses = [globals.status.val, globals.status.new];
 | 
			
		||||
    if (dev) {  // dev and admin can also access deleted samples
 | 
			
		||||
      acceptedStatuses.push('deleted')
 | 
			
		||||
      acceptedStatuses.push(globals.status.del)
 | 
			
		||||
    }
 | 
			
		||||
    return Joi.object({
 | 
			
		||||
      status: Joi.array().items(Joi.string().valid(...acceptedStatuses)).default(['validated']),
 | 
			
		||||
      status: Joi.array().items(Joi.string().valid(...acceptedStatuses)).default([globals.status.val]),
 | 
			
		||||
      'from-id': IdValidate.get(),
 | 
			
		||||
      'to-page': Joi.number().integer(),
 | 
			
		||||
      'page-size': Joi.number().integer().min(1),
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ export default class UserValidate {  // validate input for user
 | 
			
		||||
      .max(128),
 | 
			
		||||
 | 
			
		||||
    level: Joi.string()
 | 
			
		||||
      .valid(...globals.levels),
 | 
			
		||||
      .valid(...Object.values(globals.levels)),
 | 
			
		||||
 | 
			
		||||
    location: Joi.string()
 | 
			
		||||
      .alphanum()
 | 
			
		||||
 
 | 
			
		||||
@@ -728,7 +728,7 @@
 | 
			
		||||
      {
 | 
			
		||||
        "_id" : {"$oid": "120000010000000000000000"},
 | 
			
		||||
        "action" : "PUT /sample/400000000000000000000001",
 | 
			
		||||
        "collectionName" : "samples",
 | 
			
		||||
        "collection_name" : "samples",
 | 
			
		||||
        "conditions" : {
 | 
			
		||||
          "_id" : {"$oid": "400000000000000000000001"}
 | 
			
		||||
        },
 | 
			
		||||
@@ -742,7 +742,7 @@
 | 
			
		||||
      {
 | 
			
		||||
        "_id" : {"$oid": "120000020000000000000000"},
 | 
			
		||||
        "action" : "PUT /sample/400000000000000000000001",
 | 
			
		||||
        "collectionName" : "samples",
 | 
			
		||||
        "collection_name" : "samples",
 | 
			
		||||
        "conditions" : {
 | 
			
		||||
          "_id" : {"$oid": "400000000000000000000001"}
 | 
			
		||||
        },
 | 
			
		||||
@@ -756,7 +756,7 @@
 | 
			
		||||
      {
 | 
			
		||||
        "_id" : {"$oid": "120000030000000000000000"},
 | 
			
		||||
        "action" : "PUT /sample/400000000000000000000001",
 | 
			
		||||
        "collectionName" : "samples",
 | 
			
		||||
        "collection_name" : "samples",
 | 
			
		||||
        "conditions" : {
 | 
			
		||||
          "_id" : {"$oid": "400000000000000000000001"}
 | 
			
		||||
        },
 | 
			
		||||
@@ -770,7 +770,7 @@
 | 
			
		||||
      {
 | 
			
		||||
        "_id" : {"$oid": "120000040000000000000000"},
 | 
			
		||||
        "action" : "PUT /sample/400000000000000000000001",
 | 
			
		||||
        "collectionName" : "samples",
 | 
			
		||||
        "collection_name" : "samples",
 | 
			
		||||
        "conditions" : {
 | 
			
		||||
          "_id" : {"$oid": "400000000000000000000001"}
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -104,9 +104,9 @@ export default class TestHelper {
 | 
			
		||||
        ChangelogModel.findOne({}).sort({_id: -1}).skip(options.log.skip? options.log.skip : 0)
 | 
			
		||||
          .lean().exec((err, data) => {  // latest entry
 | 
			
		||||
          if (err) return done(err);
 | 
			
		||||
          should(data).have.only.keys('_id', 'action', 'collectionName', 'conditions', 'data', 'user_id', '__v');
 | 
			
		||||
          should(data).have.only.keys('_id', 'action', 'collection_name', 'conditions', 'data', 'user_id', '__v');
 | 
			
		||||
          should(data).have.property('action', options.method.toUpperCase() + ' ' + options.url);
 | 
			
		||||
          should(data).have.property('collectionName', options.log.collection);
 | 
			
		||||
          should(data).have.property('collection_name', options.log.collection);
 | 
			
		||||
          if (options.log.hasOwnProperty('data')) {
 | 
			
		||||
            should(data).have.property('data', options.log.data);
 | 
			
		||||
          }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user