Archived
2

GET finished

This commit is contained in:
VLE2FE 2020-05-14 12:31:57 +02:00
parent 649a0b166e
commit 81a7663f6c
6 changed files with 432 additions and 178 deletions

View File

@ -25,7 +25,7 @@ export default class api {
console.error(err); console.error(err);
} }
else { else {
console.info('API ok, version ' + api.info.version); console.info(process.env.NODE_ENV === 'test' ? '' : 'API ok, version ' + api.info.version);
swagger.setup(apiDoc); swagger.setup(apiDoc);
} }
}); });

View File

@ -2,6 +2,7 @@ import mongoose from 'mongoose';
const MeasurementTemplateSchema = new mongoose.Schema({ const MeasurementTemplateSchema = new mongoose.Schema({
name: {type: String, index: {unique: true}}, name: {type: String, index: {unique: true}},
version: Number,
parameters: [{ parameters: [{
name: String, name: String,
range: mongoose.Schema.Types.Mixed range: mongoose.Schema.Types.Mixed

View File

@ -2,6 +2,8 @@ import mongoose from 'mongoose';
const TreatmentTemplateSchema = new mongoose.Schema({ const TreatmentTemplateSchema = new mongoose.Schema({
name: {type: String, index: {unique: true}}, name: {type: String, index: {unique: true}},
version: Number,
number_prefix: String,
parameters: [{ parameters: [{
name: String, name: String,
range: mongoose.Schema.Types.Mixed range: mongoose.Schema.Types.Mixed

View File

@ -24,7 +24,7 @@ describe('/template', () => {
const json = require('../test/db.json'); const json = require('../test/db.json');
should(res.body).have.lengthOf(json.collections.treatment_templates.length); should(res.body).have.lengthOf(json.collections.treatment_templates.length);
should(res.body).matchEach(treatment => { should(res.body).matchEach(treatment => {
should(treatment).have.only.keys('_id', 'name', 'parameters'); should(treatment).have.only.keys('_id', 'name', 'version', 'parameters', 'number_prefix');
should(treatment).have.property('_id').be.type('string'); should(treatment).have.property('_id').be.type('string');
should(treatment).have.property('name').be.type('string'); should(treatment).have.property('name').be.type('string');
should(treatment).have.property('version').be.type('number'); should(treatment).have.property('version').be.type('number');
@ -131,22 +131,44 @@ describe('/template', () => {
}).end((err, res) => { }).end((err, res) => {
if (err) return done(err); if (err) return done(err);
should(res.body).be.eql({_id: '200000000000000000000001', name: 'heat aging', version: 2, number_prefix: 'A', parameters: [{name: 'time', range: {min: 1}}]}); should(res.body).be.eql({_id: '200000000000000000000001', name: 'heat aging', version: 2, number_prefix: 'A', parameters: [{name: 'time', range: {min: 1}}]});
TemplateTreatmentModel.find({name: 'heat aging'}).lean().exec((err, data:any) => { TemplateTreatmentModel.findById('200000000000000000000001').lean().exec((err, data:any) => {
if (err) return done(err); if (err) return done(err);
should(data).have.lengthOf(1); should(data).have.only.keys('_id', 'name', 'version', 'number_prefix', 'parameters', '__v');
should(data[0]).have.only.keys('_id', 'name', 'version', 'number_prefix', 'parameters', '__v'); should(data).have.property('name', 'heat aging');
should(data[0]).have.property('name', 'heat aging'); should(data).have.property('version', 2);
should(data[0]).have.property('version', 2); should(data).have.property('number_prefix', 'A');
should(data[0]).have.property('number_prefix', 'A'); should(data).have.property('parameters').have.lengthOf(1);
should(data[0]).have.property('parameters').have.lengthOf(1); should(data.parameters[0]).have.property('name', 'time');
should(data[0].parameters[0]).have.property('name', 'time'); should(data.parameters[0]).have.property('range');
should(data[0].parameters[0]).have.property('range'); should(data.parameters[0].range).have.property('min', 1);
should(data[0].parameters[0].range).have.property('min', 1);
done(); done();
}); });
}); });
}); });
it('allows changing only one property'); // TODO: adapt PUT to other PUTs and do POST, everything for measurement too it('allows changing only one property', done => {
TestHelper.request(server, done, {
method: 'put',
url: '/template/treatment/200000000000000000000001',
auth: {basic: 'admin'},
httpStatus: 200,
req: {name: 'heat aging'}
}).end((err, res) => {
if (err) return done(err);
should(res.body).be.eql({_id: '200000000000000000000001', name: 'heat aging', version: 2, number_prefix: 'A', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]});
TemplateTreatmentModel.find({name: 'heat aging'}).lean().exec((err, data:any) => {
if (err) return done(err);
should(data).have.lengthOf(1);
should(data[0]).have.only.keys('_id', 'name', 'version', 'number_prefix', 'parameters', '__v');
should(data[0]).have.property('name', 'heat aging');
should(data[0]).have.property('version', 2);
should(data[0]).have.property('number_prefix', 'A');
should(data[0]).have.property('parameters').have.lengthOf(1);
should(data[0].parameters[0]).have.property('name', 'material');
should(data[0].parameters[1]).have.property('name', 'weeks');
done();
});
});
});
it('supports values ranges', done => { it('supports values ranges', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'put', method: 'put',
@ -167,6 +189,16 @@ describe('/template', () => {
res: {_id: '200000000000000000000001', name: 'heat treatment', version: 2, number_prefix: 'A', parameters: [{name: 'time', range: {min: 1, max: 11}}]} res: {_id: '200000000000000000000001', name: 'heat treatment', version: 2, number_prefix: 'A', parameters: [{name: 'time', range: {min: 1, max: 11}}]}
}); });
}); });
it('supports array type ranges', done => {
TestHelper.request(server, done, {
method: 'put',
url: '/template/treatment/200000000000000000000001',
auth: {basic: 'admin'},
httpStatus: 200,
req: {parameters: [{name: 'time', range: {type: 'array'}}]},
res: {_id: '200000000000000000000001', name: 'heat treatment', version: 2, number_prefix: 'A', parameters: [{name: 'time', range: {type: 'array'}}]}
});
});
it('supports empty ranges', done => { it('supports empty ranges', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'put', method: 'put',
@ -177,6 +209,16 @@ describe('/template', () => {
res: {_id: '200000000000000000000001', name: 'heat treatment', version: 2, number_prefix: 'A', parameters: [{name: 'time', range: {}}]} res: {_id: '200000000000000000000001', name: 'heat treatment', version: 2, number_prefix: 'A', parameters: [{name: 'time', range: {}}]}
}); });
}); });
it('rejects not specified parameters', done => {
TestHelper.request(server, done, {
method: 'put',
url: '/template/treatment/200000000000000000000001',
auth: {basic: 'admin'},
httpStatus: 400,
req: {name: 'heat treatment', parameters: [{name: 'material', range: {xx: 5}}]},
res: {}
});
})
it('rejects an invalid id', done => { it('rejects an invalid id', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'put', method: 'put',
@ -236,63 +278,81 @@ describe('/template', () => {
describe('POST /template/treatment/new', () => { describe('POST /template/treatment/new', () => {
it('returns the right treatment template', done => { it('returns the right treatment template', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'put', method: 'post',
url: '/template/treatment/200000000000000000000001', url: '/template/treatment/new',
auth: {basic: 'admin'}, auth: {basic: 'admin'},
httpStatus: 200, httpStatus: 200,
req: {}, req: {name: 'heat treatment3', number_prefix: 'C', parameters: [{name: 'material', range: {values: ['copper']}}]}
res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, number_prefix: 'A', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]} }).end((err, res) => {
if (err) return done(err);
should(res.body).have.only.keys('_id', 'name', 'version', 'number_prefix', 'parameters');
should(res.body).have.property('name', 'heat treatment3');
should(res.body).have.property('version', 1);
should(res.body).have.property('number_prefix', 'C');
should(res.body).have.property('parameters').have.lengthOf(1);
should(res.body.parameters[0]).have.property('name', 'material');
should(res.body.parameters[0]).have.property('range');
should(res.body.parameters[0].range).have.property('values');
should(res.body.parameters[0].range.values[0]).be.eql('copper');
}); });
}); });
it('stores the template', done => { it('stores the template', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'put', method: 'post',
url: '/template/treatment/200000000000000000000001', url: '/template/treatment/new',
auth: {basic: 'admin'}, auth: {basic: 'admin'},
httpStatus: 200, httpStatus: 200,
req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]} req: {name: 'heat aging', number_prefix: 'C', parameters: [{name: 'time', range: {min: 1}}]}
}).end((err, res) => { }).end((err, res) => {
if (err) return done(err); if (err) return done(err);
should(res.body).be.eql({_id: '200000000000000000000001', name: 'heat aging', version: 2, number_prefix: 'A', parameters: [{name: 'time', range: {min: 1}}]}); TemplateTreatmentModel.findById(res.body._id).lean().exec((err, data:any) => {
TemplateTreatmentModel.find({name: 'heat aging'}).lean().exec((err, data:any) => {
if (err) return done(err); if (err) return done(err);
should(data).have.lengthOf(1); should(data).have.only.keys('_id', 'name', 'version', 'number_prefix', 'parameters', '__v');
should(data[0]).have.only.keys('_id', 'name', 'version', 'number_prefix', 'parameters', '__v'); should(data).have.property('name', 'heat aging');
should(data[0]).have.property('name', 'heat aging'); should(data).have.property('version', 1);
should(data[0]).have.property('version', 2); should(data).have.property('number_prefix', 'C');
should(data[0]).have.property('number_prefix', 'A'); should(data).have.property('parameters').have.lengthOf(1);
should(data[0]).have.property('parameters').have.lengthOf(1); should(data.parameters[0]).have.property('name', 'time');
should(data[0].parameters[0]).have.property('name', 'time'); should(data.parameters[0]).have.property('range');
should(data[0].parameters[0]).have.property('range'); should(data.parameters[0].range).have.property('min', 1);
should(data[0].parameters[0].range).have.property('min', 1);
done(); done();
}); });
}); });
}); });
it('rejects a missing name', done => { it('rejects a missing name', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'put', method: 'post',
url: '/template/treatment/heat%20aging', url: '/template/treatment/new',
auth: {basic: 'admin'}, auth: {basic: 'admin'},
httpStatus: 400, httpStatus: 400,
req: {parameters: [{name: 'time', range: {min: 1}}]}, req: {number_prefix: 'C', parameters: [{name: 'time', range: {min: 1}}]},
res: {status: 'Invalid body format', details: '"name" is required'} res: {status: 'Invalid body format', details: '"name" is required'}
}); });
}); });
it('rejects a missing number prefix', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/template/treatment/new',
auth: {basic: 'admin'},
httpStatus: 400,
req: {name: 'heat aging', parameters: [{name: 'time', range: {min: 1}}]},
res: {status: 'Invalid body format', details: '"number_prefix" is required'}
});
});
it('rejects missing parameters', done => { it('rejects missing parameters', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'put', method: 'post',
url: '/template/treatment/heat%20aging', url: '/template/treatment/new',
auth: {basic: 'admin'}, auth: {basic: 'admin'},
httpStatus: 400, httpStatus: 400,
req: {name: 'heat aging'}, req: {name: 'heat aging', number_prefix: 'C'},
res: {status: 'Invalid body format', details: '"parameters" is required'} res: {status: 'Invalid body format', details: '"parameters" is required'}
}); });
}); });
it('rejects a missing parameter name', done => { it('rejects a missing parameter name', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'put', method: 'post',
url: '/template/treatment/heat%20aging', url: '/template/treatment/new',
auth: {basic: 'admin'}, auth: {basic: 'admin'},
httpStatus: 400, httpStatus: 400,
req: {name: 'heat aging', parameters: [{range: {min: 1}}]}, req: {name: 'heat aging', parameters: [{range: {min: 1}}]},
@ -301,18 +361,18 @@ describe('/template', () => {
}); });
it('rejects a missing parameter range', done => { it('rejects a missing parameter range', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'put', method: 'post',
url: '/template/treatment/heat%20aging', url: '/template/treatment/new',
auth: {basic: 'admin'}, auth: {basic: 'admin'},
httpStatus: 400, httpStatus: 400,
req: {name: 'heat aging', parameters: [{name: 'time'}]}, req: {name: 'heat aging', parameters: [{name: 'time'}]},
res: {status: 'Invalid body format', details: '"parameters[0].range" is required'} res: {status: 'Invalid body format', details: '"parameters[0].range" is required'}
}); });
}); });
it('rejects a an invalid parameter range property', done => { it('rejects an invalid parameter range property', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'put', method: 'post',
url: '/template/treatment/heat%20aging', url: '/template/treatment/new',
auth: {basic: 'admin'}, auth: {basic: 'admin'},
httpStatus: 400, httpStatus: 400,
req: {name: 'heat aging', parameters: [{name: 'time', range: {xx: 1}}]}, req: {name: 'heat aging', parameters: [{name: 'time', range: {xx: 1}}]},
@ -321,12 +381,48 @@ describe('/template', () => {
}); });
it('rejects wrong properties', done => { it('rejects wrong properties', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'put', method: 'post',
url: '/template/treatment/heat%20aging', url: '/template/treatment/new',
auth: {basic: 'admin'}, auth: {basic: 'admin'},
httpStatus: 400, httpStatus: 400,
req: {parameters: [{name: 'time'}], xx: 33}, req: {name: 'heat aging', parameters: [{name: 'time', range: {}}], xx: 33},
res: {status: 'Invalid body format', details: '"name" is required'} res: {}
});
});
it('rejects already existing number prefixes', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/template/treatment/new',
auth: {basic: 'admin'},
httpStatus: 400,
req: {name: 'heat aging', number_prefix: 'B', parameters: [{name: 'time', range: {min: 1}}]},
res: {status: 'Number prefix already taken'}
});
});
it('rejects an API key', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/template/treatment/new',
auth: {key: 'admin'},
httpStatus: 401,
req: {name: 'heat aging', number_prefix: 'C', parameters: [{name: 'time', range: {min: 1}}]}
});
});
it('rejects requests from a write user', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/template/treatment/new',
auth: {basic: 'janedoe'},
httpStatus: 403,
req: {name: 'heat aging', number_prefix: 'C', parameters: [{name: 'time', range: {min: 1}}]}
});
});
it('rejects unauthorized requests', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/template/treatment/new',
httpStatus: 401,
req: {name: 'heat aging', number_prefix: 'C', parameters: [{name: 'time', range: {min: 1}}]}
}); });
}); });
}); });
@ -345,9 +441,10 @@ describe('/template', () => {
const json = require('../test/db.json'); const json = require('../test/db.json');
should(res.body).have.lengthOf(json.collections.measurement_templates.length); should(res.body).have.lengthOf(json.collections.measurement_templates.length);
should(res.body).matchEach(measurement => { should(res.body).matchEach(measurement => {
should(measurement).have.only.keys('_id', 'name', 'parameters'); should(measurement).have.only.keys('_id', 'name', 'version', 'parameters');
should(measurement).have.property('_id').be.type('string'); should(measurement).have.property('_id').be.type('string');
should(measurement).have.property('name').be.type('string'); should(measurement).have.property('name').be.type('string');
should(measurement).have.property('version').be.type('number');
should(measurement.parameters).matchEach(number => { should(measurement.parameters).matchEach(number => {
should(number).have.only.keys('name', 'range'); should(number).have.only.keys('name', 'range');
should(number).have.property('name').be.type('string'); should(number).have.property('name').be.type('string');
@ -374,28 +471,28 @@ describe('/template', () => {
}); });
}); });
describe('GET /template/measurement/{name}', () => { describe('GET /template/measurement/id', () => {
it('returns the right measurement template', done => { it('returns the right measurement template', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'get', method: 'get',
url: '/template/measurement/spectrum', url: '/template/measurement/300000000000000000000001',
auth: {basic: 'janedoe'}, auth: {basic: 'janedoe'},
httpStatus: 200, httpStatus: 200,
res: {_id: '300000000000000000000001', name: 'spectrum', parameters: [{name: 'dpt', range: {}}]} res: {_id: '300000000000000000000001', name: 'spectrum', version: 1, parameters: [{name: 'dpt', range: {}}]}
}); });
}); });
it('rejects an API key', done => { it('rejects an API key', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'get', method: 'get',
url: '/template/measurement/spectrum', url: '/template/measurement/300000000000000000000001',
auth: {key: 'janedoe'}, auth: {key: 'janedoe'},
httpStatus: 401 httpStatus: 401
}); });
}); });
it('rejects an unknown name', done => { it('rejects an unknown id', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'get', method: 'get',
url: '/template/measurement/xxx', url: '/template/measurement/000000000000000000000001',
auth: {basic: 'janedoe'}, auth: {basic: 'janedoe'},
httpStatus: 404 httpStatus: 404
}); });
@ -403,7 +500,7 @@ describe('/template', () => {
it('rejects unauthorized requests', done => { it('rejects unauthorized requests', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'get', method: 'get',
url: '/template/measurement/spectrum', url: '/template/measurement/300000000000000000000001',
httpStatus: 401 httpStatus: 401
}); });
}); });
@ -413,38 +510,49 @@ describe('/template', () => {
it('returns the right measurement template', done => { it('returns the right measurement template', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'put', method: 'put',
url: '/template/measurement/spectrum', url: '/template/measurement/300000000000000000000001',
auth: {basic: 'admin'}, auth: {basic: 'admin'},
httpStatus: 200, httpStatus: 200,
req: {}, req: {},
res: {_id: '300000000000000000000001', name: 'spectrum', parameters: [{name: 'dpt', range: {}}]} res: {_id: '300000000000000000000001', name: 'spectrum', version: 1, parameters: [{name: 'dpt', range: {}}]}
}); });
}); });
it('keeps unchanged properties', done => { it('keeps unchanged properties', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'put', method: 'put',
url: '/template/measurement/spectrum', url: '/template/measurement/300000000000000000000001',
auth: {basic: 'admin'}, auth: {basic: 'admin'},
httpStatus: 200, httpStatus: 200,
req: {name: 'spectrum', parameters: [{name: 'dpt', range: {}}]}, req: {name: 'spectrum', parameters: [{name: 'dpt', range: {}}]},
res: {_id: '300000000000000000000001', name: 'spectrum', parameters: [{name: 'dpt', range: {}}]} res: {_id: '300000000000000000000001', name: 'spectrum', version: 1, parameters: [{name: 'dpt', range: {type: 'array'}}]}
});
});
it('keeps only one unchanged property', done => {
TestHelper.request(server, done, {
method: 'put',
url: '/template/measurement/300000000000000000000001',
auth: {basic: 'admin'},
httpStatus: 200,
req: {name: 'spectrum'},
res: {_id: '300000000000000000000001', name: 'spectrum', version: 1, parameters: [{name: 'dpt', range: {type: 'array'}}]}
}); });
}); });
it('changes the given properties', done => { it('changes the given properties', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'put', method: 'put',
url: '/template/measurement/spectrum', url: '/template/measurement/300000000000000000000001',
auth: {basic: 'admin'}, auth: {basic: 'admin'},
httpStatus: 200, httpStatus: 200,
req: {name: 'IR spectrum', parameters: [{name: 'data point table', range: {min: 0, max: 1000}}]}, req: {name: 'IR spectrum', parameters: [{name: 'data point table', range: {min: 0, max: 1000}}]},
}).end((err, res) => { }).end((err, res) => {
if (err) return done(err); if (err) return done(err);
should(res.body).be.eql({_id: '300000000000000000000001', name: 'IR spectrum', parameters: [{name: 'data point table', range: {min: 0, max: 1000}}]}); should(res.body).be.eql({_id: '300000000000000000000001', name: 'IR spectrum', parameters: [{name: 'data point table', range: {min: 0, max: 1000}}]});
TemplateMeasurementModel.find({name: 'IR spectrum'}).lean().exec((err, data:any) => { TemplateMeasurementModel.findById('300000000000000000000001').lean().exec((err, data:any) => {
if (err) return done(err); if (err) return done(err);
should(data).have.lengthOf(1); should(data).have.lengthOf(1);
should(data[0]).have.only.keys('_id', 'name', 'parameters', '__v'); should(data[0]).have.only.keys('_id', 'name', 'version', 'parameters', '__v');
should(data[0]).have.property('name', 'IR spectrum'); should(data[0]).have.property('name', 'IR spectrum');
should(data[0]).have.property('version', 2);
should(data[0]).have.property('parameters').have.lengthOf(1); should(data[0]).have.property('parameters').have.lengthOf(1);
should(data[0].parameters[0]).have.property('name', 'data point table'); should(data[0].parameters[0]).have.property('name', 'data point table');
should(data[0].parameters[0]).have.property('range'); should(data[0].parameters[0]).have.property('range');
@ -454,132 +562,102 @@ describe('/template', () => {
}); });
}); });
}); });
it('allows changing only one property', done => {
TestHelper.request(server, done, {
method: 'put',
url: '/template/measurement/300000000000000000000001',
auth: {basic: 'admin'},
httpStatus: 200,
req: {name: 'IR spectrum'},
}).end((err, res) => {
if (err) return done(err);
should(res.body).be.eql({_id: '300000000000000000000001', name: 'IR spectrum', parameters: [{name: 'dpt', range: {type: 'array'}}]});
TemplateMeasurementModel.findById('300000000000000000000001').lean().exec((err, data:any) => {
if (err) return done(err);
should(data).have.lengthOf(1);
should(data).have.only.keys('_id', 'name', 'version', 'parameters', '__v');
should(data).have.property('name', 'IR spectrum');
should(data).have.property('version', 2);
should(data).have.property('parameters').have.lengthOf(1);
should(data.parameters[0]).have.property('name', 'dpt');
should(data.parameters[0]).have.property('range');
should(data.parameters[0].range).have.property('type', 'array');
done();
});
});
});
it('supports values ranges', done => { it('supports values ranges', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'put', method: 'put',
url: '/template/measurement/spectrum', url: '/template/measurement/300000000000000000000001',
auth: {basic: 'admin'}, auth: {basic: 'admin'},
httpStatus: 200, httpStatus: 200,
req: {parameters: [{name: 'dpt', range: {values: [1, 2, 5]}}]}, req: {parameters: [{name: 'dpt', range: {values: [1, 2, 5]}}]},
res: {_id: '300000000000000000000001', name: 'spectrum', parameters: [{name: 'dpt', range: {values: [1, 2, 5]}}]} res: {_id: '300000000000000000000001', name: 'spectrum', version: 2, parameters: [{name: 'dpt', range: {values: [1, 2, 5]}}]}
}); });
}); });
it('supports min max ranges', done => { it('supports min max ranges', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'put', method: 'put',
url: '/template/measurement/spectrum', url: '/template/measurement/300000000000000000000001',
auth: {basic: 'admin'}, auth: {basic: 'admin'},
httpStatus: 200, httpStatus: 200,
req: {parameters: [{name: 'dpt', range: {min: 0, max: 1000}}]}, req: {parameters: [{name: 'dpt', range: {min: 0, max: 1000}}]},
res: {_id: '300000000000000000000001', name: 'spectrum', parameters: [{name: 'dpt', range: {min: 0, max: 1000}}]} res: {_id: '300000000000000000000001', name: 'spectrum', version: 2, parameters: [{name: 'dpt', range: {min: 0, max: 1000}}]}
});
});
it('supports min max ranges', done => {
TestHelper.request(server, done, {
method: 'put',
url: '/template/measurement/300000000000000000000001',
auth: {basic: 'admin'},
httpStatus: 200,
req: {parameters: [{name: 'dpt', range: {type: 'array'}}]},
res: {_id: '300000000000000000000001', name: 'spectrum', version: 2, parameters: [{name: 'dpt', range: {type: 'array'}}]}
}); });
}); });
it('supports empty ranges', done => { it('supports empty ranges', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'put', method: 'put',
url: '/template/measurement/kf', url: '/template/measurement/300000000000000000000002',
auth: {basic: 'admin'}, auth: {basic: 'admin'},
httpStatus: 200, httpStatus: 200,
req: {parameters: [{name: 'weight %', range: {}}]}, req: {parameters: [{name: 'weight %', range: {}}]},
res: {_id: '300000000000000000000002', name: 'kf', parameters: [{name: 'weight %', range: {}}]} res: {_id: '300000000000000000000002', name: 'kf', version: 2, parameters: [{name: 'weight %', range: {}}]}
}); });
}); });
it('adds a new template for an unknown name', done => { it('rejects not specified parameters', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'put', method: 'put',
url: '/template/measurement/vz', url: '/template/measurement/300000000000000000000001',
auth: {basic: 'admin'},
httpStatus: 200,
req: {name: 'vz', parameters: [{name: 'vz', range: {min: 1}}]}
}).end(err => {
if (err) return done(err);
TemplateMeasurementModel.find({name: 'vz'}).lean().exec((err, data:any) => {
if (err) return done(err);
should(data).have.lengthOf(1);
should(data[0]).have.only.keys('_id', 'name', 'parameters', '__v');
should(data[0]).have.property('name', 'vz');
should(data[0]).have.property('parameters').have.lengthOf(1);
should(data[0].parameters[0]).have.property('name', 'vz');
should(data[0].parameters[0]).have.property('range');
should(data[0].parameters[0].range).have.property('min', 1);
done();
});
});
});
it('rejects a missing name for a new name', done => {
TestHelper.request(server, done, {
method: 'put',
url: '/template/measurement/spectrum2',
auth: {basic: 'admin'}, auth: {basic: 'admin'},
httpStatus: 400, httpStatus: 400,
req: {parameters: [{name: 'data point table', range: {min: 0, max: 1000}}]}, req: {parameters: [{name: 'dpt'}], range: {xx: 33}},
res: {status: 'Invalid body format', details: '"name" is required'} res: {}
}); });
}); });
it('rejects missing parameters for a new name', done => { it('rejects an invalid id', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'put', method: 'put',
url: '/template/measurement/spectrum2', url: '/template/measurement/3000000000h0000000000001',
auth: {basic: 'admin'}, auth: {basic: 'admin'},
httpStatus: 400, httpStatus: 404,
req: {name: 'IR spectrum'}, req: {name: 'IR spectrum', parameters: [{name: 'data point table', range: {min: 0, max: 1000}}]},
res: {status: 'Invalid body format', details: '"parameters" is required'}
}); });
}); });
it('rejects a missing parameter name for a new name', done => { it('rejects an unknown id', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'put', method: 'put',
url: '/template/measurement/spectrum2', url: '/template/measurement/000000000000000000000001',
auth: {basic: 'admin'}, auth: {basic: 'admin'},
httpStatus: 400, httpStatus: 404,
req: {name: 'IR spectrum', parameters: [{range: {min: 0, max: 1000}}]}, req: {name: 'IR spectrum', parameters: [{name: 'data point table', range: {min: 0, max: 1000}}]},
res: {status: 'Invalid body format', details: '"parameters[0].name" is required'}
});
});
it('rejects a missing parameter range for a new name', done => {
TestHelper.request(server, done, {
method: 'put',
url: '/template/measurement/spectrum2',
auth: {basic: 'admin'},
httpStatus: 400,
req: {name: 'IR spectrum', parameters: [{name: 'data point table'}]},
res: {status: 'Invalid body format', details: '"parameters[0].range" is required'}
});
});
it('rejects a an invalid parameter range property for a new name', done => {
TestHelper.request(server, done, {
method: 'put',
url: '/template/measurement/spectrum2',
auth: {basic: 'admin'},
httpStatus: 400,
req: {name: 'IR spectrum', parameters: [{name: 'data point table', range: {xx: 0}}]},
res: {status: 'Invalid body format', details: '"parameters[0].range.xx" is not allowed'}
});
});
it('rejects already existing names', done => {
TestHelper.request(server, done, {
method: 'put',
url: '/template/measurement/spectrum',
auth: {basic: 'admin'},
httpStatus: 400,
req: {name: 'kf', parameters: [{name: 'dpt', range: {min: 1}}]},
res: {status: 'Template name already taken'}
});
});
it('rejects wrong properties', done => {
TestHelper.request(server, done, {
method: 'put',
url: '/template/measurement/spectrum',
auth: {basic: 'admin'},
httpStatus: 400,
req: {parameters: [{name: 'dpt'}], xx: 33},
res: {status: 'Invalid body format', details: '"parameters[0].range" is required'}
}); });
}); });
it('rejects an API key', done => { it('rejects an API key', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'put', method: 'put',
url: '/template/measurement/spectrum', url: '/template/measurement/300000000000000000000001',
auth: {key: 'admin'}, auth: {key: 'admin'},
httpStatus: 401, httpStatus: 401,
req: {} req: {}
@ -588,7 +666,7 @@ describe('/template', () => {
it('rejects requests from a write user', done => { it('rejects requests from a write user', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'put', method: 'put',
url: '/template/measurement/spectrum', url: '/template/measurement/300000000000000000000001',
auth: {basic: 'janedoe'}, auth: {basic: 'janedoe'},
httpStatus: 403, httpStatus: 403,
req: {} req: {}
@ -597,11 +675,141 @@ describe('/template', () => {
it('rejects unauthorized requests', done => { it('rejects unauthorized requests', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'put', method: 'put',
url: '/template/measurement/spectrum', url: '/template/measurement/300000000000000000000001',
httpStatus: 401, httpStatus: 401,
req: {} req: {}
}); });
}); });
}); });
describe('POST /template/measurement/new', () => {
it('returns the right measurement template', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/template/measurement/new',
auth: {basic: 'admin'},
httpStatus: 200,
req: {name: 'vz', parameters: [{name: 'vz', range: {min: 1}}]}
}).end((err, res) => {
if (err) return done(err);
should(res.body).have.only.keys('_id', 'name', 'version', 'parameters');
should(res.body).have.property('name', 'vz');
should(res.body).have.property('version', 1);
should(res.body).have.property('parameters').have.lengthOf(1);
should(res.body.parameters[0]).have.property('name', 'vz');
should(res.body.parameters[0]).have.property('range');
should(res.body.parameters[0].range).have.property('min', 1);
});
});
it('stores the template', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/template/measurement/new',
auth: {basic: 'admin'},
httpStatus: 200,
req: {name: 'vz', parameters: [{name: 'vz', range: {min: 1}}]}
}).end(err => {
if (err) return done(err);
TemplateMeasurementModel.find({name: 'vz'}).lean().exec((err, data:any) => {
if (err) return done(err);
should(data).have.lengthOf(1);
should(data[0]).have.only.keys('_id', 'name', 'version', 'parameters', '__v');
should(data[0]).have.property('name', 'vz');
should(data[0]).have.property('vaersion', 1);
should(data[0]).have.property('parameters').have.lengthOf(1);
should(data[0].parameters[0]).have.property('name', 'vz');
should(data[0].parameters[0]).have.property('range');
should(data[0].parameters[0].range).have.property('min', 1);
done();
});
});
});
it('rejects a missing name', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/template/measurement/new',
auth: {basic: 'admin'},
httpStatus: 400,
req: {parameters: [{name: 'data point table', range: {min: 0, max: 1000}}]},
res: {status: 'Invalid body format', details: '"name" is required'}
});
});
it('rejects missing parameters', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/template/measurement/new',
auth: {basic: 'admin'},
httpStatus: 400,
req: {name: 'IR spectrum'},
res: {status: 'Invalid body format', details: '"parameters" is required'}
});
});
it('rejects a missing parameter name', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/template/measurement/new',
auth: {basic: 'admin'},
httpStatus: 400,
req: {name: 'IR spectrum', parameters: [{range: {min: 0, max: 1000}}]},
res: {status: 'Invalid body format', details: '"parameters[0].name" is required'}
});
});
it('rejects a missing parameter range', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/template/measurement/new',
auth: {basic: 'admin'},
httpStatus: 400,
req: {name: 'IR spectrum', parameters: [{name: 'data point table'}]},
res: {status: 'Invalid body format', details: '"parameters[0].range" is required'}
});
});
it('rejects a an invalid parameter range property', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/template/measurement/new',
auth: {basic: 'admin'},
httpStatus: 400,
req: {name: 'IR spectrum', parameters: [{name: 'data point table', range: {xx: 0}}]},
res: {status: 'Invalid body format', details: '"parameters[0].range.xx" is not allowed'}
});
});
it('rejects wrong properties', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/template/measurement/new',
auth: {basic: 'admin'},
httpStatus: 400,
req: {name: 'IR spectrum', parameters: [{name: 'data point table', range: {}}], xx: 35},
res: {}
});
});
it('rejects an API key', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/template/measurement/new',
auth: {key: 'admin'},
httpStatus: 401,
req: {name: 'vz', parameters: [{name: 'vz', range: {min: 1}}]}
});
});
it('rejects requests from a write user', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/template/measurement/new',
auth: {basic: 'janedoe'},
httpStatus: 403,
req: {name: 'vz', parameters: [{name: 'vz', range: {min: 1}}]}
});
});
it('rejects unauthorized requests', done => {
TestHelper.request(server, done, {
method: 'post',
url: '/template/measurement/new',
httpStatus: 401,
req: {name: 'vz', parameters: [{name: 'vz', range: {min: 1}}]}
});
});
});
}); });
}); });

View File

@ -5,6 +5,7 @@ import TemplateValidate from './validate/template';
import TemplateTreatmentModel from '../models/treatment_template'; import TemplateTreatmentModel from '../models/treatment_template';
import TemplateMeasurementModel from '../models/measurement_template'; import TemplateMeasurementModel from '../models/measurement_template';
import res400 from './validate/res400'; import res400 from './validate/res400';
import IdValidate from './validate/id';
// TODO: remove f() for await // TODO: remove f() for await
@ -13,21 +14,20 @@ const router = express.Router();
router.get('/template/:collection(measurements|treatments)', (req, res, next) => { router.get('/template/:collection(measurements|treatments)', (req, res, next) => {
if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'basic')) return; if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'basic')) return;
(req.params.collection === 'treatments' ? TemplateTreatmentModel : TemplateMeasurementModel) req.params.collection = req.params.collection.replace(/s$/g, '');
.find({}).lean().exec((err, data) => { model(req).find({}).lean().exec((err, data) => {
if (err) next (err); if (err) next (err);
res.json(_.compact(data.map(e => TemplateValidate.output(e)))); // validate all and filter null values from validation errors res.json(_.compact(data.map(e => TemplateValidate.output(e, req.params.collection)))); // validate all and filter null values from validation errors
}); });
}); });
router.get('/template/:collection(measurement|treatment)/:name', (req, res, next) => { router.get('/template/:collection(measurement|treatment)/' + IdValidate.parameter(), (req, res, next) => {
if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'basic')) return; if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'basic')) return;
(req.params.collection === 'treatment' ? TemplateTreatmentModel : TemplateMeasurementModel) model(req).findById(req.params.id).lean().exec((err, data) => {
.findOne({name: req.params.name}).lean().exec((err, data) => {
if (err) next (err); if (err) next (err);
if (data) { if (data) {
res.json(TemplateValidate.output(data)); res.json(TemplateValidate.output(data, req.params.collection));
} }
else { else {
res.status(404).json({status: 'Not found'}); res.status(404).json({status: 'Not found'});
@ -35,19 +35,18 @@ router.get('/template/:collection(measurement|treatment)/:name', (req, res, next
}); });
}); });
router.put('/template/:collection(measurement|treatment)/:name', (req, res, next) => { router.put('/template/:collection(measurement|treatment)/' + IdValidate.parameter(), (req, res, next) => {
if (!req.auth(res, ['maintain', 'admin'], 'basic')) return; if (!req.auth(res, ['maintain', 'admin'], 'basic')) return;
const collectionModel = req.params.collection === 'treatment' ? TemplateTreatmentModel : TemplateMeasurementModel;
collectionModel.findOne({name: req.params.name}).lean().exec((err, data) => { model(req).findOne({name: req.params.name}).lean().exec((err, data) => {
if (err) next (err); if (err) next (err);
const templateState = data? 'change': 'new'; const templateState = data? 'change': 'new';
const {error, value: template} = TemplateValidate.input(req.body, templateState); const {error, value: template} = TemplateValidate.input(req.body, templateState, req.params.collection);
if (error) return res400(error, res); if (error) return res400(error, res);
if (template.hasOwnProperty('name') && template.name !== req.params.name) { if (template.hasOwnProperty('name') && template.name !== req.params.name) {
collectionModel.find({name: template.name}).lean().exec((err, data) => { model(req).find({name: template.name}).lean().exec((err, data) => {
if (err) next (err); if (err) next (err);
if (data.length > 0) { if (data.length > 0) {
res.status(400).json({status: 'Template name already taken'}); res.status(400).json({status: 'Template name already taken'});
@ -63,9 +62,9 @@ router.put('/template/:collection(measurement|treatment)/:name', (req, res, next
} }
function f() { // to resolve async function f() { // to resolve async
collectionModel.findOneAndUpdate({name: req.params.name}, template, {new: true, upsert: true}).lean().exec((err, data) => { model(req).findOneAndUpdate({name: req.params.name}, template, {new: true, upsert: true}).lean().exec((err, data) => {
if (err) return next(err); if (err) return next(err);
res.json(TemplateValidate.output(data)); res.json(TemplateValidate.output(data, req.params.collection));
}); });
} }
}); });
@ -87,4 +86,9 @@ router.put('/template/:collection(measurement|treatment)/:name', (req, res, next
// }); // });
module.exports = router; module.exports = router;
function model (req) {
return req.params.collection === 'treatment' ? TemplateTreatmentModel : TemplateMeasurementModel;
}

View File

@ -6,6 +6,13 @@ export default class TemplateValidate {
name: joi.string() name: joi.string()
.max(128), .max(128),
version: joi.number()
.min(1),
number_prefix: joi.string()
.min(1)
.max(16),
parameters: joi.array() parameters: joi.array()
.min(1) .min(1)
.items( .items(
@ -29,31 +36,63 @@ export default class TemplateValidate {
) )
}; };
static input (data, param) { // validate data, param: new(everything required)/change(available attributes are validated) static input (data, param, template) { // validate data, param: new(everything required)/change(available attributes are validated)
if (param === 'new') { if (param === 'new') {
return joi.object({ if (template === 'treatment') {
name: this.template.name.required(), return joi.object({
parameters: this.template.parameters.required() name: this.template.name.required(),
}).validate(data); number_prefix: this.template.number_prefix.required(),
parameters: this.template.parameters.required()
}).validate(data);
}
else {
return joi.object({
name: this.template.name.required(),
parameters: this.template.parameters.required()
}).validate(data);
}
} }
else if (param === 'change') { else if (param === 'change') {
return joi.object({ if (template === 'treatment') {
name: this.template.name, return joi.object({
parameters: this.template.parameters name: this.template.name,
}).validate(data); number_prefix: this.template.number_prefix,
parameters: this.template.parameters
}).validate(data);
}
else {
return joi.object({
name: this.template.name,
parameters: this.template.parameters
}).validate(data);
}
} }
else { else {
return{error: 'No parameter specified!', value: {}}; return{error: 'No parameter specified!', value: {}};
} }
} }
static output (data) { // validate output from database for needed properties, strip everything else static output (data, template) { // validate output from database for needed properties, strip everything else
data = IdValidate.stringify(data); data = IdValidate.stringify(data);
const {value, error} = joi.object({ let joiObject;
_id: IdValidate.get(), if (template === 'treatment') {
name: this.template.name, joiObject = {
parameters: this.template.parameters _id: IdValidate.get(),
}).validate(data, {stripUnknown: true}); name: this.template.name,
version: this.template.version,
number_prefix: this.template.number_prefix,
parameters: this.template.parameters
};
}
else {
joiObject = {
_id: IdValidate.get(),
name: this.template.name,
version: this.template.version,
parameters: this.template.parameters
};
}
const {value, error} = joi.object(joiObject).validate(data, {stripUnknown: true});
return error !== undefined? null : value; return error !== undefined? null : value;
} }
} }