From bdff2c96d371df8d9708853ff5b459af1cc8a354 Mon Sep 17 00:00:00 2001 From: VLE2FE Date: Wed, 2 Sep 2020 13:18:33 +0200 Subject: [PATCH] Implemented new template change behaviour --- src/db.ts | 2 +- src/routes/sample.ts | 6 ++- src/routes/template.spec.ts | 91 +++++++++++++++++++++++++++++++++++-- src/routes/template.ts | 72 ++++++++++++++++++++++++++--- 4 files changed, 158 insertions(+), 13 deletions(-) diff --git a/src/db.ts b/src/db.ts index 2e8592d..f41cee3 100644 --- a/src/db.ts +++ b/src/db.ts @@ -85,7 +85,7 @@ export default class db { cron.schedule('0 0 * * *', () => { ChangelogModel.deleteMany({_id: {$lt: // id from time Math.floor(new Date().getTime() / 1000 - changelogKeepDays * 24 * 60 * 60).toString(16) + '0000000000000000' - }}).log({method: 'scheduled changelog delete', url: '', authDetails: {}}).lean().exec(err => { + }}).lean().exec(err => { if (err) console.error(err); }); }); diff --git a/src/routes/sample.ts b/src/routes/sample.ts index 4726bd3..06c2381 100644 --- a/src/routes/sample.ts +++ b/src/routes/sample.ts @@ -235,7 +235,7 @@ router.get('/samples', async (req, res, next) => { ]; if (measurementFilterFields.indexOf(globals.spectrum.spectrum) >= 0) { // filter out dpts pipeline.push( - {$project: {'values.device': true, measurement_template: true}}, + {$project: {['values.' + globals.spectrum.dpt]: false}}, {$addFields: {'values._id': '$_id'}} ); } @@ -682,7 +682,8 @@ async function numberGenerate (sample, req, res, next) { // [{$split: ['$number', 'Rng']}, 1]}, '_']}, 0]}}}}, // not working with MongoDb 3.6 {$addFields: {sortNumber: {$let: { vars: {tmp: {$concat: ['000000000000000000000000000000', - {$arrayElemAt: [{$split: [{$arrayElemAt: [{$split: ['$number', 'Rng']}, 1]}, '_']}, 0]}]}}, + {$arrayElemAt: [{$split: + [{$arrayElemAt: [{$split: ['$number', req.authDetails.location]}, 1]}, '_']}, 0]}]}}, in: {$substrCP: ['$$tmp', {$subtract: [{$strLenCP: '$$tmp'}, 30]}, {$strLenCP: '$$tmp'}]} }}}}, {$sort: {sortNumber: -1}}, @@ -691,6 +692,7 @@ async function numberGenerate (sample, req, res, next) { .exec() .catch(err => next(err)); if (sampleData instanceof Error) return false; + console.log(sampleData); let number = (sampleData[0] ? Number(sampleData[0].number.replace(/[^0-9]+/g, '')) : 0); if (numberBuffer[req.authDetails.location] && numberBuffer[req.authDetails.location] >= number) { number = numberBuffer[req.authDetails.location]; diff --git a/src/routes/template.spec.ts b/src/routes/template.spec.ts index 16da61d..d7e4b79 100644 --- a/src/routes/template.spec.ts +++ b/src/routes/template.spec.ts @@ -1,7 +1,11 @@ import should from 'should/as-function'; import _ from 'lodash'; import TemplateConditionModel from '../models/condition_template'; +import SampleModel from '../models/sample'; +import MeasurementModel from '../models/measurement'; +import MaterialModel from '../models/material'; import TestHelper from "../test/helper"; +import mongoose from 'mongoose'; describe('/template', () => { @@ -90,7 +94,7 @@ describe('/template', () => { }); }); - describe('PUT /template/condition/{name}', () => { + describe('PUT /template/condition/{id}', () => { it('returns the right condition template', done => { TestHelper.request(server, done, { method: 'put', @@ -145,6 +149,24 @@ describe('/template', () => { }); }); }); + it('renames all occurrences instead of creating a new version when only the parameter name is changed', done => { + TestHelper.request(server, done, { + method: 'put', + url: '/template/condition/200000000000000000000001', + auth: {basic: 'admin'}, + httpStatus: 200, req: {name: 'heat treatment', parameters: [{name: 'treatmentMaterial', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10, required: true}}]} + }).end((err, res) => { + if (err) return done(err); + should(res.body).be.eql({_id: '200000000000000000000001', name: 'heat treatment', version: 1, first_id: '200000000000000000000001', parameters: [{name: 'treatmentMaterial', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10, required: true}}]}); + SampleModel.find({'condition.condition_template': mongoose.Types.ObjectId('200000000000000000000001')}).lean().exec((err, data:any) => { + if (err) return done(err); + should(data).matchEach(sample => { + should(sample.condition).have.only.keys('treatmentMaterial', 'weeks', 'condition_template'); + }); + done(); + }); + }); + }); it('creates a changelog', done => { TestHelper.request(server, done, { method: 'put', @@ -161,7 +183,7 @@ describe('/template', () => { } }); }); - it('allows changing only one property', done => { + it('does not increase the version on name change', done => { TestHelper.request(server, done, { method: 'put', url: '/template/condition/200000000000000000000001', @@ -175,7 +197,7 @@ describe('/template', () => { should(data).have.only.keys('_id', 'first_id', 'name', 'version', 'parameters', '__v'); should(data.first_id.toString()).be.eql('200000000000000000000001'); should(data).have.property('name', 'heat aging'); - should(data).have.property('version', 2); + should(data).have.property('version', 1); should(data).have.property('parameters').have.lengthOf(2); should(data.parameters[0]).have.property('name', 'material'); should(data.parameters[1]).have.property('name', 'weeks'); @@ -183,6 +205,28 @@ describe('/template', () => { }); }); }); + it('does not increase the version on name change when property ranges stayed the same', done => { + TestHelper.request(server, done, { + method: 'put', + url: '/template/condition/200000000000000000000001', + auth: {basic: 'admin'}, + httpStatus: 200, + req: {name: 'heat aging', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'duration', range: {min: 1, max: 10, required: true}}]} + }).end((err, res) => { + if (err) return done(err); + TemplateConditionModel.findById(res.body._id).lean().exec((err, data:any) => { + if (err) return done(err); + should(data).have.only.keys('_id', 'first_id', 'name', 'version', 'parameters', '__v'); + should(data.first_id.toString()).be.eql('200000000000000000000001'); + should(data).have.property('name', 'heat aging'); + should(data).have.property('version', 1); + should(data).have.property('parameters').have.lengthOf(2); + should(data.parameters[0]).have.property('name', 'material'); + should(data.parameters[1]).have.property('name', 'duration'); + done(); + }); + }); + }); it('supports values ranges', done => { TestHelper.request(server, done, { method: 'put', @@ -526,6 +570,26 @@ describe('/template', () => { }); }); }); + describe('PUT /template/measurement/{id}', () => { + it('renames all occurrences instead of creating a new version when only the parameter name is changed', done => { + TestHelper.request(server, done, { + method: 'put', + url: '/template/measurement/300000000000000000000001', + auth: {basic: 'admin'}, + httpStatus: 200, req: {name: 'spectrum', parameters: [{name: 'spectrumValues', range: {type: 'array'}}, {name: 'device', range: {}}, {name: 'filename', range: {}}]} + }).end((err, res) => { + if (err) return done(err); + should(res.body).be.eql({_id: '300000000000000000000001', name: 'spectrum', version: 1, first_id: '300000000000000000000001', parameters: [{name: 'spectrumValues', range: {type: 'array'}}, {name: 'device', range: {}}, {name: 'filename', range: {}}]}); + MeasurementModel.find({'measurement_template': mongoose.Types.ObjectId('300000000000000000000001')}).lean().exec((err, data:any) => { + if (err) return done(err); + should(data).matchEach(measurement => { + should(measurement.values).have.only.keys('spectrumValues', 'device', 'filename'); + }); + done(); + }); + }); + }); + }); // other methods should be covered by condition tests }); @@ -571,6 +635,27 @@ describe('/template', () => { }); }); }); + describe('PUT /template/material/{id}', () => { + it('renames all occurrences instead of creating a new version when only the parameter name is changed', done => { + TestHelper.request(server, done, { + method: 'put', + url: '/template/material/130000000000000000000003', + auth: {basic: 'admin'}, + httpStatus: 200, + req: {name: 'plastic', parameters: [ {name: 'glassfiber', range: {min: 0, max: 100, required: true}}, {name: 'carbonfiber', range: {min: 0, max: 100, required: true}}, {name: 'mineral', range: {min: 0, max: 100, required: true}}]} + }).end((err, res) => { + if (err) return done(err); + should(res.body).be.eql({_id: '130000000000000000000003', name: 'plastic', version: 2, first_id: '130000000000000000000001', parameters: [ {name: 'glassfiber', range: {min: 0, max: 100, required: true}}, {name: 'carbonfiber', range: {min: 0, max: 100, required: true}}, {name: 'mineral', range: {min: 0, max: 100, required: true}}]}); + MaterialModel.find({'properties': mongoose.Types.ObjectId('130000000000000000000003')}).lean().exec((err, data:any) => { + if (err) return done(err); + should(data).matchEach(material => { + should(material.parameters).have.only.keys('glassfiber', 'carbonfiber', 'mineral', 'material_template'); + }); + done(); + }); + }); + }); + }); // other methods should be covered by condition tests }); }); \ No newline at end of file diff --git a/src/routes/template.ts b/src/routes/template.ts index ad73a8c..d569bd2 100644 --- a/src/routes/template.ts +++ b/src/routes/template.ts @@ -5,6 +5,9 @@ import TemplateValidate from './validate/template'; import ConditionTemplateModel from '../models/condition_template'; import MeasurementTemplateModel from '../models/measurement_template'; import MaterialTemplateModel from '../models/material_template'; +import SampleModel from '../models/sample'; +import MaterialModel from '../models/material'; +import MeasurementModel from '../models/measurement'; import res400 from './validate/res400'; import IdValidate from './validate/id'; import mongoose from "mongoose"; @@ -61,13 +64,68 @@ router.put('/template/:collection(measurement|condition|material)/' + IdValidate } if (!_.isEqual(_.pick(templateData, _.keys(template)), template)) { // data was changed - template.version = templateData.version + 1; // increase version - // save new template, fill with old properties - await new (model(req))(_.assign({}, _.omit(templateData, ['_id', '__v']), template)).save((err, data) => { - if (err) next (err); - db.log(req, req.params.collection + '_templates', {_id: data._id}, data.toObject()); - res.json(TemplateValidate.output(data.toObject())); - }); + console.log(template); + console.log(templateData); + if (!template.parameters || _.isEqual(templateData.parameters, template.parameters)) { // only name was changed + model(req).findByIdAndUpdate(req.params.id, {name: template.name}, {new: true}) + .log(req).lean().exec((err, data) => { + if (err) next (err); + res.json(TemplateValidate.output(data)); + }); + } + else if (template.parameters.filter((e, i) => _.isEqual(e.range, templateData.parameters[i].range)).length + === templateData.parameters.length) { // only names changed + const changedParameterNames = template.parameters.map((e, i) => ( // list of new names + {name: e.name, index: i, oldName: templateData.parameters[i].name} + )).filter(e => e.name !== e.oldName); + + // custom mappings for different collections + let targetModel; // model of the collection where the template is used + let pathPrefix; // path to the parameters in use + let templatePath; // complete path of the template property + switch (req.params.collection) { + case 'condition': + targetModel = SampleModel; + pathPrefix = 'condition.'; + templatePath = 'condition.condition_template'; + break; + case 'measurement': + targetModel = MeasurementModel; + pathPrefix = 'values.'; + templatePath = 'measurement_template'; + break; + case 'material': + targetModel = MaterialModel; + pathPrefix = 'properties.'; + templatePath = 'properties.material_template'; + break; + } + + targetModel.updateMany({[templatePath]: mongoose.Types.ObjectId(templateData._id)}, + {$rename: + changedParameterNames.reduce((s, e) => {s[pathPrefix + e.oldName] = pathPrefix + e.name; return s;}, {}) + }) .log(req).lean().exec(err => { + if (err) return next(err); + model(req).findByIdAndUpdate(req.params.id, + {$set: + changedParameterNames.reduce( + (s, e) => {s[`parameters.${e.index}.name`] = e.name; return s;}, {name: template.name} + ), + },{new: true}).log(req).lean().exec((err, data) => { + if (err) next (err); + res.json(TemplateValidate.output(data)); + }); + }); + } + else { + template.version = templateData.version + 1; // increase version + // save new template, fill with old properties + await new (model(req))(_.assign({}, _.omit(templateData, ['_id', '__v']), template)).save((err, data) => { + if (err) next (err); + db.log(req, req.params.collection + '_templates', {_id: data._id}, data.toObject()); + res.json(TemplateValidate.output(data.toObject())); + }); + } } else { res.json(TemplateValidate.output(templateData));