Archived
2

small material changes for easier UI implementation

This commit is contained in:
VLE2FE 2020-08-31 13:47:12 +02:00
parent 36ca627d1b
commit b59b3ea9ea
4 changed files with 70 additions and 16 deletions

View File

@ -20,7 +20,13 @@
schema: schema:
type: array type: array
items: items:
$ref: 'api.yaml#/components/schemas/Material' allOf:
- $ref: 'api.yaml#/components/schemas/Material'
properties:
status:
type: string
description: can be deleted/new/validated
example: new
401: 401:
$ref: 'api.yaml#/components/responses/401' $ref: 'api.yaml#/components/responses/401'
500: 500:

View File

@ -24,11 +24,12 @@ describe('/material', () => {
const json = require('../test/db.json'); const json = require('../test/db.json');
should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === 'validated').length); should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === 'validated').length);
should(res.body).matchEach(material => { should(res.body).matchEach(material => {
should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers'); should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers', 'status');
should(material).have.property('_id').be.type('string'); should(material).have.property('_id').be.type('string');
should(material).have.property('name').be.type('string'); should(material).have.property('name').be.type('string');
should(material).have.property('supplier').be.type('string'); should(material).have.property('supplier').be.type('string');
should(material).have.property('group').be.type('string'); should(material).have.property('group').be.type('string');
should(material).have.property('status', 'validated');
should(material.properties).have.property('material_template').be.type('string'); should(material.properties).have.property('material_template').be.type('string');
should(material.numbers).be.instanceof(Array); should(material.numbers).be.instanceof(Array);
}); });
@ -46,11 +47,12 @@ describe('/material', () => {
const json = require('../test/db.json'); const json = require('../test/db.json');
should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === 'validated').length); should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === 'validated').length);
should(res.body).matchEach(material => { should(res.body).matchEach(material => {
should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers'); should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers', 'status');
should(material).have.property('_id').be.type('string'); should(material).have.property('_id').be.type('string');
should(material).have.property('name').be.type('string'); should(material).have.property('name').be.type('string');
should(material).have.property('supplier').be.type('string'); should(material).have.property('supplier').be.type('string');
should(material).have.property('group').be.type('string'); should(material).have.property('group').be.type('string');
should(material).have.property('status', 'validated');
should(material.properties).have.property('material_template').be.type('string'); should(material.properties).have.property('material_template').be.type('string');
should(material.numbers).be.instanceof(Array); should(material.numbers).be.instanceof(Array);
}); });
@ -60,7 +62,7 @@ describe('/material', () => {
it('allows filtering by state', done => { it('allows filtering by state', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'get', method: 'get',
url: '/materials?status=new', url: '/materials?status[]=new',
auth: {basic: 'janedoe'}, auth: {basic: 'janedoe'},
httpStatus: 200 httpStatus: 200
}).end((err, res) => { }).end((err, res) => {
@ -68,24 +70,57 @@ describe('/material', () => {
const json = require('../test/db.json'); const json = require('../test/db.json');
should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === 'new').length); should(res.body).have.lengthOf(json.collections.materials.filter(e => e.status === 'new').length);
should(res.body).matchEach(material => { should(res.body).matchEach(material => {
should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers'); should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers', 'status');
should(material).have.property('_id').be.type('string'); should(material).have.property('_id').be.type('string');
should(material).have.property('name').be.type('string'); should(material).have.property('name').be.type('string');
should(material).have.property('supplier').be.type('string'); should(material).have.property('supplier').be.type('string');
should(material).have.property('group').be.type('string'); should(material).have.property('group').be.type('string');
should(material).have.property('status', 'new');
should(material.properties).have.property('material_template').be.type('string'); should(material.properties).have.property('material_template').be.type('string');
should(material.numbers).be.instanceof(Array); should(material.numbers).be.instanceof(Array);
}); });
done(); done();
}); });
}); });
it('allows filtering by deleted state for admins', done => {
TestHelper.request(server, done, {
method: 'get',
url: '/materials?status[]=deleted',
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.materials.filter(e => e.status === 'deleted').length);
should(res.body).matchEach(material => {
should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'properties', 'numbers', 'status');
should(material).have.property('_id').be.type('string');
should(material).have.property('name').be.type('string');
should(material).have.property('supplier').be.type('string');
should(material).have.property('group').be.type('string');
should(material).have.property('status', 'deleted');
should(material.properties).have.property('material_template').be.type('string');
should(material.numbers).be.instanceof(Array);
});
done();
});
});
it('rejects filtering by deleted state for a write user', done => {
TestHelper.request(server, done, {
method: 'get',
url: '/materials?status[]=deleted',
auth: {basic: 'janedoe'},
httpStatus: 400,
res: {status: 'Invalid body format', details: '"status[0]" must be one of [validated, new]'}
});
});
it('rejects an invalid state name', done => { it('rejects an invalid state name', done => {
TestHelper.request(server, done, { TestHelper.request(server, done, {
method: 'get', method: 'get',
url: '/materials?status=xxx', url: '/materials?status[]=xxx',
auth: {basic: 'janedoe'}, auth: {basic: 'janedoe'},
httpStatus: 400, 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 => { it('rejects unauthorized requests', done => {

View File

@ -21,7 +21,8 @@ const router = express.Router();
router.get('/materials', (req, res, next) => { router.get('/materials', (req, res, next) => {
if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'all')) return; if (!req.auth(res, ['read', 'write', 'dev', 'admin'], 'all')) return;
const {error, value: filters} = MaterialValidate.query(req.query); const {error, value: filters} =
MaterialValidate.query(req.query, ['dev', 'admin'].indexOf(req.authDetails.level) >= 0);
if (error) return res400(error, res); if (error) return res400(error, res);
let conditions; let conditions;
@ -38,11 +39,12 @@ router.get('/materials', (req, res, next) => {
conditions = {status: globals.status.val}; conditions = {status: globals.status.val};
} }
MaterialModel.find(conditions).populate('group_id').populate('supplier_id').lean().exec((err, data) => { MaterialModel.find(conditions).sort({name: 1}).populate('group_id').populate('supplier_id')
.lean().exec((err, data) => {
if (err) return next(err); if (err) return next(err);
// validate all and filter null values from validation errors // validate all and filter null values from validation errors
res.json(_.compact(data.map(e => MaterialValidate.output(e)))); res.json(_.compact(data.map(e => MaterialValidate.output(e, true))));
}); });
}); });

View File

@ -20,7 +20,10 @@ export default class MaterialValidate { // validate input for material
.items( .items(
Joi.string() Joi.string()
.max(64) .max(64)
) ),
status: Joi.string()
.valid(...Object.values(globals.status))
}; };
static input (data, param) { // validate input, set param to 'new' to make all attributes required static input (data, param) { // validate input, set param to 'new' to make all attributes required
@ -47,18 +50,22 @@ export default class MaterialValidate { // validate input for material
} }
} }
static output (data) { // validate output and strip unwanted properties, returns null if not valid static output (data, status = false) { // validate output and strip unwanted properties, returns null if not valid
data = IdValidate.stringify(data); data = IdValidate.stringify(data);
data.group = data.group_id.name; data.group = data.group_id.name;
data.supplier = data.supplier_id.name; data.supplier = data.supplier_id.name;
const {value, error} = Joi.object({ const validate: any = {
_id: IdValidate.get(), _id: IdValidate.get(),
name: this.material.name, name: this.material.name,
supplier: this.material.supplier, supplier: this.material.supplier,
group: this.material.group, group: this.material.group,
properties: this.material.properties, properties: this.material.properties,
numbers: this.material.numbers numbers: this.material.numbers
}).validate(data, {stripUnknown: true}); };
if (status) {
validate.status = this.material.status;
}
const {value, error} = Joi.object(validate).validate(data, {stripUnknown: true});
return error !== undefined? null : value; return error !== undefined? null : value;
} }
@ -83,9 +90,13 @@ export default class MaterialValidate { // validate input for material
}); });
} }
static query (data) { static query (data, dev = false) {
const acceptedStatuses = [globals.status.val, globals.status.new];
if (dev) { // dev and admin can also access deleted samples
acceptedStatuses.push(globals.status.del)
}
return Joi.object({ return Joi.object({
status: Joi.string().valid(globals.status.val, globals.status.new, 'all') status: Joi.array().items(Joi.string().valid(...acceptedStatuses)).default([globals.status.val])
}).validate(data); }).validate(data);
} }
} }