implemented first /sample methods
This commit is contained in:
parent
af071a9445
commit
20f57acd2a
@ -1,6 +1,6 @@
|
||||
/samples:
|
||||
get:
|
||||
summary: TODO all samples in overview
|
||||
summary: all samples in overview
|
||||
description: 'Auth: all, levels: read, write, maintain, dev, admin'
|
||||
tags:
|
||||
- /sample
|
||||
@ -10,7 +10,9 @@
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: 'api.yaml#/components/schemas/Samples'
|
||||
type: array
|
||||
items:
|
||||
$ref: 'api.yaml#/components/schemas/SampleRefs'
|
||||
401:
|
||||
$ref: 'api.yaml#/components/responses/401'
|
||||
500:
|
||||
@ -39,7 +41,7 @@
|
||||
500:
|
||||
$ref: 'api.yaml#/components/responses/500'
|
||||
put:
|
||||
summary: TODO add/change sample
|
||||
summary: TODO change sample
|
||||
description: 'Auth: basic, levels: write, maintain, dev, admin'
|
||||
tags:
|
||||
- /sample
|
||||
@ -88,10 +90,41 @@
|
||||
$ref: 'api.yaml#/components/responses/404'
|
||||
500:
|
||||
$ref: 'api.yaml#/components/responses/500'
|
||||
|
||||
/sample/new:
|
||||
post:
|
||||
summary: add sample
|
||||
description: 'Auth: basic, levels: write, maintain, dev, admin'
|
||||
tags:
|
||||
- /sample
|
||||
security:
|
||||
- BasicAuth: []
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: 'api.yaml#/components/schemas/Sample'
|
||||
responses:
|
||||
200:
|
||||
description: samples details
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: 'api.yaml#/components/schemas/SampleRefs'
|
||||
400:
|
||||
$ref: 'api.yaml#/components/responses/400'
|
||||
401:
|
||||
$ref: 'api.yaml#/components/responses/401'
|
||||
403:
|
||||
$ref: 'api.yaml#/components/responses/403'
|
||||
500:
|
||||
$ref: 'api.yaml#/components/responses/500'
|
||||
|
||||
/sample/notes/fields:
|
||||
get:
|
||||
summary: TODO list all existing field names for custom notes fields
|
||||
description: 'Auth: all, levels: write, maintain, dev, admin'
|
||||
description: 'Auth: all, levels: read, write, maintain, dev, admin'
|
||||
tags:
|
||||
- /sample
|
||||
responses:
|
||||
|
@ -14,16 +14,17 @@ Color:
|
||||
example: black
|
||||
SampleProperties:
|
||||
properties:
|
||||
sample_number:
|
||||
number:
|
||||
type: string
|
||||
example: Rng172
|
||||
type:
|
||||
type: string
|
||||
example: granulate
|
||||
batch:
|
||||
type: string
|
||||
validated:
|
||||
type: boolean
|
||||
example: 1560237365
|
||||
|
||||
Samples:
|
||||
SampleRefs:
|
||||
allOf:
|
||||
- $ref: 'api.yaml#/components/schemas/_Id'
|
||||
- $ref: 'api.yaml#/components/schemas/Color'
|
||||
@ -41,17 +42,23 @@ Sample:
|
||||
- $ref: 'api.yaml#/components/schemas/Color'
|
||||
- $ref: 'api.yaml#/components/schemas/SampleProperties'
|
||||
properties:
|
||||
material:
|
||||
$ref: 'api.yaml#/components/schemas/Material'
|
||||
material_id:
|
||||
allOf:
|
||||
- $ref: 'api.yaml#/components/schemas/Id'
|
||||
notes:
|
||||
type: object
|
||||
properties:
|
||||
comments:
|
||||
comment:
|
||||
type: string
|
||||
sample_references:
|
||||
type: array
|
||||
items:
|
||||
$ref: 'api.yaml#/components/schemas/Id'
|
||||
properties:
|
||||
id:
|
||||
$ref: 'api.yaml#/components/schemas/Id'
|
||||
relation:
|
||||
type: string
|
||||
example: part to this sample
|
||||
SampleDetail:
|
||||
allOf:
|
||||
- $ref: 'api.yaml#/components/schemas/_Id'
|
||||
@ -63,7 +70,7 @@ SampleDetail:
|
||||
notes:
|
||||
type: object
|
||||
properties:
|
||||
comments:
|
||||
comment:
|
||||
type: string
|
||||
sample_references:
|
||||
type: array
|
||||
|
@ -37,7 +37,7 @@ export default class db {
|
||||
}
|
||||
|
||||
// connect to db
|
||||
mongoose.connect(connectionString, {useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true}, err => {
|
||||
mongoose.connect(connectionString, {useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true, connectTimeoutMS: 10000}, err => {
|
||||
if (err) done(err);
|
||||
});
|
||||
mongoose.connection.on('error', console.error.bind(console, 'connection error:'));
|
||||
@ -92,7 +92,9 @@ export default class db {
|
||||
Object.keys(json.collections).forEach(collectionName => { // create each collection
|
||||
for(let i in json.collections[collectionName]) { // convert $oid fields to actual ObjectIds
|
||||
Object.keys(json.collections[collectionName][i]).forEach(key => {
|
||||
json.collections[collectionName][i][key] = json.collections[collectionName][i][key].hasOwnProperty('$oid') ? mongoose.Types.ObjectId(json.collections[collectionName][i][key].$oid) : json.collections[collectionName][i][key];
|
||||
if (json.collections[collectionName][i][key] !== null && json.collections[collectionName][i][key].hasOwnProperty('$oid')) {
|
||||
json.collections[collectionName][i][key] = mongoose.Types.ObjectId(json.collections[collectionName][i][key].$oid);
|
||||
}
|
||||
})
|
||||
}
|
||||
this.state.db.createCollection(collectionName, (err, collection) => {
|
||||
|
@ -9,7 +9,7 @@ import UserModel from '../models/user';
|
||||
|
||||
module.exports = async (req, res, next) => {
|
||||
let givenMethod = ''; // authorization method given by client, basic taken preferred
|
||||
let user = {name: '', level: ''}; // user object
|
||||
let user = {name: '', level: '', id: ''}; // user object
|
||||
|
||||
// test authentications
|
||||
const userBasic = await basic(req, next);
|
||||
@ -45,7 +45,8 @@ module.exports = async (req, res, next) => {
|
||||
req.authDetails = {
|
||||
method: givenMethod,
|
||||
username: user.name,
|
||||
level: user.level
|
||||
level: user.level,
|
||||
id: user.id
|
||||
};
|
||||
|
||||
next();
|
||||
@ -57,12 +58,12 @@ function basic (req, next): any { // checks basic auth and returns changed user
|
||||
const auth = basicAuth(req);
|
||||
if (auth !== undefined) { // basic auth available
|
||||
UserModel.find({name: auth.name}).lean().exec( (err, data: any) => { // find user
|
||||
if (err) next(err);
|
||||
if (err) return next(err);
|
||||
if (data.length === 1) { // one user found
|
||||
bcrypt.compare(auth.pass, data[0].pass, (err, res) => { // check password
|
||||
if (err) next(err);
|
||||
if (err) return next(err);
|
||||
if (res === true) {
|
||||
resolve({level: data[0].level, name: data[0].name});
|
||||
resolve({level: data[0].level, name: data[0].name, id: data[0]._id.toString()});
|
||||
}
|
||||
else {
|
||||
resolve(null);
|
||||
@ -84,9 +85,9 @@ function key (req, next): any { // checks API key and returns changed user obje
|
||||
return new Promise(resolve => {
|
||||
if (req.query.key !== undefined) {
|
||||
UserModel.find({key: req.query.key}).lean().exec( (err, data: any) => { // find user
|
||||
if (err) next(err);
|
||||
if (err) return next(err);
|
||||
if (data.length === 1) { // one user found
|
||||
resolve({level: data[0].level, name: data[0].name});
|
||||
resolve({level: data[0].level, name: data[0].name, id: data[0]._id.toString()});
|
||||
}
|
||||
else {
|
||||
resolve(null);
|
||||
|
@ -14,6 +14,7 @@ export default class TestHelper {
|
||||
401: {status: 'Unauthorized'},
|
||||
403: {status: 'Forbidden'},
|
||||
404: {status: 'Not found'},
|
||||
500: {status: 'Internal server error'}
|
||||
}
|
||||
|
||||
static before (done) {
|
||||
|
@ -45,9 +45,10 @@ app.use(require('./helpers/authorize')); // handle authentication
|
||||
|
||||
// require routes
|
||||
app.use('/', require('./routes/root'));
|
||||
app.use('/', require('./routes/user'));
|
||||
app.use('/', require('./routes/sample'));
|
||||
app.use('/', require('./routes/material'));
|
||||
app.use('/', require('./routes/template'));
|
||||
app.use('/', require('./routes/user'));
|
||||
|
||||
// static files
|
||||
app.use('/static', express.static('static'));
|
||||
|
12
src/models/note.ts
Normal file
12
src/models/note.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import mongoose from 'mongoose';
|
||||
|
||||
const NoteSchema = new mongoose.Schema({
|
||||
comment: String,
|
||||
sample_references: [{
|
||||
id: mongoose.Schema.Types.ObjectId,
|
||||
relation: String
|
||||
}],
|
||||
custom_fields: mongoose.Schema.Types.Mixed
|
||||
});
|
||||
|
||||
export default mongoose.model('note', NoteSchema);
|
8
src/models/note_field.ts
Normal file
8
src/models/note_field.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import mongoose from 'mongoose';
|
||||
|
||||
const NoteFieldSchema = new mongoose.Schema({
|
||||
name: {type: String, index: {unique: true}},
|
||||
qty: Number
|
||||
});
|
||||
|
||||
export default mongoose.model('note_field', NoteFieldSchema);
|
18
src/models/sample.ts
Normal file
18
src/models/sample.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import mongoose from 'mongoose';
|
||||
|
||||
import MaterialModel from './material';
|
||||
import NoteModel from './note';
|
||||
import UserModel from './user';
|
||||
|
||||
const SampleSchema = new mongoose.Schema({
|
||||
number: {type: String, index: {unique: true}},
|
||||
type: String,
|
||||
color: String,
|
||||
batch: String,
|
||||
validated: Boolean,
|
||||
material_id: {type: mongoose.Schema.Types.ObjectId, ref: MaterialModel},
|
||||
note_id: {type: mongoose.Schema.Types.ObjectId, ref: NoteModel},
|
||||
user_id: {type: mongoose.Schema.Types.ObjectId, ref: UserModel}
|
||||
});
|
||||
|
||||
export default mongoose.model('sample', SampleSchema);
|
@ -19,7 +19,7 @@ describe('/material', () => {
|
||||
}).end((err, res) => {
|
||||
if (err) return done(err);
|
||||
const json = require('../test/db.json');
|
||||
should(res.body).have.lengthOf(json.collections.users.length);
|
||||
should(res.body).have.lengthOf(json.collections.materials.length);
|
||||
should(res.body).matchEach(material => {
|
||||
should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'mineral', 'glass_fiber', 'carbon_fiber', 'numbers');
|
||||
should(material).have.property('_id').be.type('string');
|
||||
@ -47,7 +47,7 @@ describe('/material', () => {
|
||||
}).end((err, res) => {
|
||||
if (err) return done(err);
|
||||
const json = require('../test/db.json');
|
||||
should(res.body).have.lengthOf(json.collections.users.length);
|
||||
should(res.body).have.lengthOf(json.collections.materials.length);
|
||||
should(res.body).matchEach(material => {
|
||||
should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'mineral', 'glass_fiber', 'carbon_fiber', 'numbers');
|
||||
should(material).have.property('_id').be.type('string');
|
||||
@ -82,7 +82,7 @@ describe('/material', () => {
|
||||
url: '/material/100000000000000000000001',
|
||||
auth: {basic: 'janedoe'},
|
||||
httpStatus: 200,
|
||||
res: {_id: '100000000000000000000001', name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', mineral: 0, glass_fiber: 40, carbon_fiber: 0, numbers: [{color: 'black', number: 5514263423}]}
|
||||
res: {_id: '100000000000000000000001', name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', mineral: 0, glass_fiber: 40, carbon_fiber: 0, numbers: [{color: 'black', number: 5514263423}, {color: 'natural', number: 5514263422}]}
|
||||
});
|
||||
});
|
||||
it('returns the right material for an API key', done => {
|
||||
@ -127,7 +127,7 @@ describe('/material', () => {
|
||||
auth: {basic: 'janedoe'},
|
||||
httpStatus: 200,
|
||||
req: {},
|
||||
res: {_id: '100000000000000000000001', name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', mineral: 0, glass_fiber: 40, carbon_fiber: 0, numbers: [{color: 'black', number: 5514263423}]}
|
||||
res: {_id: '100000000000000000000001', name: 'Stanyl TW 200 F8', supplier: 'DSM', group: 'PA46', mineral: 0, glass_fiber: 40, carbon_fiber: 0, numbers: [{color: 'black', number: 5514263423}, {color: 'natural', number: 5514263422}]}
|
||||
});
|
||||
});
|
||||
it('keeps unchanged properties', done => {
|
||||
@ -296,7 +296,6 @@ describe('/material', () => {
|
||||
req: {name: 'Crastin CE 2510', supplier: 'Du Pont', group: 'PBT', mineral: 0, glass_fiber: 30, carbon_fiber: 0, numbers: [{color: 'black', number: 5515798402}]}
|
||||
}).end((err, res) => {
|
||||
if (err) return done (err);
|
||||
console.log(res.body);
|
||||
should(res.body).have.only.keys('_id', 'name', 'supplier', 'group', 'mineral', 'glass_fiber', 'carbon_fiber', 'numbers');
|
||||
should(res.body).have.property('_id').be.type('string');
|
||||
should(res.body).have.property('name', 'Crastin CE 2510');
|
||||
@ -324,7 +323,7 @@ describe('/material', () => {
|
||||
if (err) return done (err);
|
||||
MaterialModel.find({name: 'Crastin CE 2510'}).lean().exec((err, data: any) => {
|
||||
if (err) return done (err);
|
||||
console.log(data[0]);
|
||||
should(data).have.lengthOf(1);
|
||||
should(data[0]).have.only.keys('_id', 'name', 'supplier', 'group', 'mineral', 'glass_fiber', 'carbon_fiber', 'numbers', '__v');
|
||||
should(data[0]).have.property('_id');
|
||||
should(data[0]).have.property('name', 'Crastin CE 2510');
|
||||
|
@ -11,7 +11,7 @@ router.get('/materials', (req, res, next) => {
|
||||
if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'all')) return;
|
||||
|
||||
MaterialModel.find({}).lean().exec((err, data) => {
|
||||
if(err) next(err);
|
||||
if (err) return next(err);
|
||||
res.json(data.map(e => MaterialValidate.output(e)).filter(e => e !== null)); // validate all and filter null values from validation errors
|
||||
});
|
||||
});
|
||||
@ -20,8 +20,7 @@ router.get('/material/' + IdValidate.parameter(), (req, res, next) => {
|
||||
if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'all')) return;
|
||||
|
||||
MaterialModel.findById(req.params.id).lean().exec((err, data) => {
|
||||
if(err) next(err);
|
||||
console.log(data);
|
||||
if (err) return next(err);
|
||||
if (data) {
|
||||
res.json(MaterialValidate.output(data));
|
||||
}
|
||||
@ -35,14 +34,14 @@ router.put('/material/' + IdValidate.parameter(), (req, res, next) => {
|
||||
if (!req.auth(res, ['write', 'maintain', 'dev', 'admin'], 'basic')) return;
|
||||
|
||||
const {error, value: material} = MaterialValidate.input(req.body, 'change');
|
||||
if(error !== undefined) {
|
||||
if (error) {
|
||||
res.status(400).json({status: 'Invalid body format'});
|
||||
return;
|
||||
}
|
||||
|
||||
if (material.hasOwnProperty('name')) {
|
||||
MaterialModel.find({name: material.name}).lean().exec((err, data) => {
|
||||
if(err) next(err);
|
||||
if (err) return next(err);
|
||||
if (data.length > 0 && data[0]._id != req.params.id) {
|
||||
res.status(400).json({status: 'Material name already taken'});
|
||||
return;
|
||||
@ -58,7 +57,7 @@ router.put('/material/' + IdValidate.parameter(), (req, res, next) => {
|
||||
|
||||
function f() { // to resolve async
|
||||
MaterialModel.findByIdAndUpdate(req.params.id, material, {new: true}).lean().exec((err, data) => {
|
||||
if (err) next(err);
|
||||
if (err) return next(err);
|
||||
if (data) {
|
||||
res.json(MaterialValidate.output(data));
|
||||
}
|
||||
@ -73,7 +72,7 @@ router.delete('/material/' + IdValidate.parameter(), (req, res, next) => {
|
||||
if (!req.auth(res, ['write', 'maintain', 'dev', 'admin'], 'basic')) return;
|
||||
|
||||
MaterialModel.findByIdAndDelete(req.params.id).lean().exec((err, data) => {
|
||||
if (err) next(err);
|
||||
if (err) return next(err);
|
||||
if (data) {
|
||||
res.json({status: 'OK'})
|
||||
}
|
||||
@ -88,20 +87,20 @@ router.post('/material/new', (req, res, next) => {
|
||||
|
||||
// validate input
|
||||
const {error, value: material} = MaterialValidate.input(req.body, 'new');
|
||||
if(error !== undefined) {
|
||||
if (error) {
|
||||
res.status(400).json({status: 'Invalid body format'});
|
||||
return;
|
||||
}
|
||||
|
||||
MaterialModel.find({name: material.name}).lean().exec((err, data) => {
|
||||
if(err) next(err);
|
||||
if (err) return next(err);
|
||||
if (data.length > 0) {
|
||||
res.status(400).json({status: 'Material name already taken'});
|
||||
return;
|
||||
}
|
||||
|
||||
new MaterialModel(material).save((err, data) => {
|
||||
if(err) next(err);
|
||||
if (err) return next(err);
|
||||
res.json(MaterialValidate.output(data.toObject()));
|
||||
});
|
||||
});
|
||||
|
336
src/routes/sample.spec.ts
Normal file
336
src/routes/sample.spec.ts
Normal file
@ -0,0 +1,336 @@
|
||||
import should from 'should/as-function';
|
||||
import SampleModel from '../models/sample';
|
||||
import NoteModel from '../models/note';
|
||||
import NoteFieldModel from '../models/note_field';
|
||||
import TestHelper from "../helpers/test";
|
||||
|
||||
|
||||
describe('/sample', () => {
|
||||
let server;
|
||||
before(done => TestHelper.before(done));
|
||||
beforeEach(done => server = TestHelper.beforeEach(server, done));
|
||||
afterEach(done => TestHelper.afterEach(server, done));
|
||||
|
||||
describe('GET /samples', () => {
|
||||
it('returns all samples', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'get',
|
||||
url: '/samples',
|
||||
auth: {basic: 'janedoe'},
|
||||
httpStatus: 200
|
||||
}).end((err, res) => {
|
||||
if (err) return done(err);
|
||||
const json = require('../test/db.json');
|
||||
should(res.body).have.lengthOf(json.collections.samples.length);
|
||||
should(res.body).matchEach(material => {
|
||||
should(material).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'material_id', 'note_id', 'user_id');
|
||||
should(material).have.property('_id').be.type('string');
|
||||
should(material).have.property('number').be.type('string');
|
||||
should(material).have.property('type').be.type('string');
|
||||
should(material).have.property('color').be.type('string');
|
||||
should(material).have.property('batch').be.type('string');
|
||||
should(material).have.property('material_id').be.type('string');
|
||||
should(material).have.property('note_id');
|
||||
should(material).have.property('user_id').be.type('string');
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('works with an API key', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'get',
|
||||
url: '/samples',
|
||||
auth: {key: 'janedoe'},
|
||||
httpStatus: 200
|
||||
}).end((err, res) => {
|
||||
if (err) return done(err);
|
||||
const json = require('../test/db.json');
|
||||
should(res.body).have.lengthOf(json.collections.samples.length);
|
||||
should(res.body).matchEach(material => {
|
||||
should(material).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'material_id', 'note_id', 'user_id');
|
||||
should(material).have.property('_id').be.type('string');
|
||||
should(material).have.property('number').be.type('string');
|
||||
should(material).have.property('type').be.type('string');
|
||||
should(material).have.property('color').be.type('string');
|
||||
should(material).have.property('batch').be.type('string');
|
||||
should(material).have.property('material_id').be.type('string');
|
||||
should(material).have.property('note_id');
|
||||
should(material).have.property('user_id').be.type('string');
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('rejects unauthorized requests', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'get',
|
||||
url: '/samples',
|
||||
httpStatus: 401
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /sample/new', () => {
|
||||
it('returns the right sample', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'post',
|
||||
url: '/sample/new',
|
||||
auth: {basic: 'janedoe'},
|
||||
httpStatus: 200,
|
||||
req: {number: 'Rng172', color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}
|
||||
}).end((err, res) => {
|
||||
if (err) return done (err);
|
||||
should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'material_id', 'note_id', 'user_id');
|
||||
should(res.body).have.property('_id').be.type('string');
|
||||
should(res.body).have.property('number', 'Rng172');
|
||||
should(res.body).have.property('color', 'black');
|
||||
should(res.body).have.property('type', 'granulate');
|
||||
should(res.body).have.property('batch', '1560237365');
|
||||
should(res.body).have.property('material_id', '100000000000000000000001');
|
||||
should(res.body).have.property('note_id').be.type('string');
|
||||
should(res.body).have.property('user_id', '000000000000000000000002');
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('stores the sample', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'post',
|
||||
url: '/sample/new',
|
||||
auth: {basic: 'janedoe'},
|
||||
httpStatus: 200,
|
||||
req: {number: 'Rng172', color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}
|
||||
}).end(err => {
|
||||
if (err) return done (err);
|
||||
SampleModel.find({number: 'Rng172'}).lean().exec((err, data: any) => {
|
||||
if (err) return done (err);
|
||||
should(data).have.lengthOf(1);
|
||||
should(data[0]).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'material_id', 'note_id', 'user_id', '__v');
|
||||
should(data[0]).have.property('_id');
|
||||
should(data[0]).have.property('number', 'Rng172');
|
||||
should(data[0]).have.property('color', 'black');
|
||||
should(data[0]).have.property('type', 'granulate');
|
||||
should(data[0]).have.property('batch', '1560237365');
|
||||
should(data[0].material_id.toString()).be.eql('100000000000000000000001');
|
||||
should(data[0].user_id.toString()).be.eql('000000000000000000000002');
|
||||
should(data[0]).have.property('note_id');
|
||||
NoteModel.findById(data[0].note_id).lean().exec((err, data: any) => {
|
||||
if (err) return done (err);
|
||||
should(data).have.property('_id');
|
||||
should(data).have.property('comment', 'Testcomment');
|
||||
should(data).have.property('sample_references');
|
||||
should(data.sample_references).have.lengthOf(1);
|
||||
should(data.sample_references[0].id.toString()).be.eql('400000000000000000000003');
|
||||
should(data.sample_references[0]).have.property('relation', 'part to this sample');
|
||||
done();
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
it('stores the custom fields', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'post',
|
||||
url: '/sample/new',
|
||||
auth: {basic: 'janedoe'},
|
||||
httpStatus: 200,
|
||||
req: {number: 'Rng172', color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [], custom_fields: {field1: 'a', field2: 'b', 'not allowed for new applications': true}}}
|
||||
}).end((err, res) => {
|
||||
if (err) return done (err);
|
||||
NoteModel.findById(res.body.note_id).lean().exec((err, data: any) => {
|
||||
if (err) return done(err);
|
||||
should(data).have.property('_id');
|
||||
should(data).have.property('comment', 'Testcomment');
|
||||
should(data).have.property('sample_references').have.lengthOf(0);
|
||||
should(data).have.property('custom_fields');
|
||||
should(data.custom_fields).have.property('field1', 'a');
|
||||
should(data.custom_fields).have.property('field2', 'b');
|
||||
should(data.custom_fields).have.property('not allowed for new applications', true);
|
||||
NoteFieldModel.find({name: 'field1'}).lean().exec((err, data) => {
|
||||
if (err) return done(err);
|
||||
should(data).have.lengthOf(1);
|
||||
should(data[0]).have.property('qty', 1);
|
||||
NoteFieldModel.find({name: 'field2'}).lean().exec((err, data) => {
|
||||
if (err) return done(err);
|
||||
should(data).have.lengthOf(1);
|
||||
should(data[0]).have.property('qty', 1);
|
||||
NoteFieldModel.find({name: 'not allowed for new applications'}).lean().exec((err, data) => {
|
||||
if (err) return done(err);
|
||||
should(data).have.lengthOf(1);
|
||||
should(data[0]).have.property('qty', 3);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
it('rejects a color not defined for the material', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'post',
|
||||
url: '/sample/new',
|
||||
auth: {basic: 'janedoe'},
|
||||
httpStatus: 400,
|
||||
req: {number: 'Rng172', color: 'green', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}},
|
||||
res: {status: 'Color not available for material'}
|
||||
});
|
||||
});
|
||||
it('rejects an unknown material id', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'post',
|
||||
url: '/sample/new',
|
||||
auth: {basic: 'janedoe'},
|
||||
httpStatus: 400,
|
||||
req: {number: 'Rng172', color: 'black', type: 'granulate', batch: '1560237365', material_id: '000000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}},
|
||||
res: {status: 'Material not available'}
|
||||
});
|
||||
});
|
||||
it('rejects a sample number in use', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'post',
|
||||
url: '/sample/new',
|
||||
auth: {basic: 'janedoe'},
|
||||
httpStatus: 400,
|
||||
req: {number: '1', color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}},
|
||||
res: {status: 'Sample number already taken'}
|
||||
});
|
||||
});
|
||||
it('rejects an invalid sample reference', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'post',
|
||||
url: '/sample/new',
|
||||
auth: {basic: 'janedoe'},
|
||||
httpStatus: 400,
|
||||
req: {number: 'Rng172', color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '000000000000000000000003', relation: 'part to this sample'}]}},
|
||||
res: {status: 'Sample reference not available'}
|
||||
});
|
||||
});
|
||||
it('rejects a missing color', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'post',
|
||||
url: '/sample/new',
|
||||
auth: {basic: 'janedoe'},
|
||||
httpStatus: 400,
|
||||
req: {number: 'Rng172', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}},
|
||||
res: {status: 'Invalid body format'}
|
||||
});
|
||||
});
|
||||
it('rejects a missing sample number', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'post',
|
||||
url: '/sample/new',
|
||||
auth: {basic: 'janedoe'},
|
||||
httpStatus: 400,
|
||||
req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}},
|
||||
res: {status: 'Invalid body format'}
|
||||
});
|
||||
});
|
||||
it('rejects a missing type', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'post',
|
||||
url: '/sample/new',
|
||||
auth: {basic: 'janedoe'},
|
||||
httpStatus: 400,
|
||||
req: {number: 'Rng172', color: 'black', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}},
|
||||
res: {status: 'Invalid body format'}
|
||||
});
|
||||
});
|
||||
it('rejects a missing batch', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'post',
|
||||
url: '/sample/new',
|
||||
auth: {basic: 'janedoe'},
|
||||
httpStatus: 400,
|
||||
req: {number: 'Rng172', color: 'black', type: 'granulate', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}},
|
||||
res: {status: 'Invalid body format'}
|
||||
});
|
||||
});
|
||||
it('rejects a missing material id', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'post',
|
||||
url: '/sample/new',
|
||||
auth: {basic: 'janedoe'},
|
||||
httpStatus: 400,
|
||||
req: {number: 'Rng172', color: 'black', type: 'granulate', batch: '1560237365', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}},
|
||||
res: {status: 'Invalid body format'}
|
||||
});
|
||||
});
|
||||
it('rejects an invalid material id', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'post',
|
||||
url: '/sample/new',
|
||||
auth: {basic: 'janedoe'},
|
||||
httpStatus: 400,
|
||||
req: {number: 'Rng172', color: 'black', type: 'granulate', batch: '1560237365', material_id: '10000000000h000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}},
|
||||
res: {status: 'Invalid body format'}
|
||||
});
|
||||
});
|
||||
it('rejects an API key', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'post',
|
||||
url: '/sample/new',
|
||||
auth: {key: 'janedoe'},
|
||||
httpStatus: 401,
|
||||
req: {number: 'Rng172', color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}
|
||||
});
|
||||
});
|
||||
it('rejects requests from a read user', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'post',
|
||||
url: '/sample/new',
|
||||
auth: {basic: 'user'},
|
||||
httpStatus: 403,
|
||||
req: {number: 'Rng172', color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}
|
||||
});
|
||||
});
|
||||
it('rejects unauthorized requests', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'post',
|
||||
url: '/sample/new',
|
||||
httpStatus: 401,
|
||||
req: {number: 'Rng172', color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{id: '400000000000000000000003', relation: 'part to this sample'}]}}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /sample/notes/fields', () => {
|
||||
it('returns all fields', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'get',
|
||||
url: '/sample/notes/fields',
|
||||
auth: {basic: 'user'},
|
||||
httpStatus: 200
|
||||
}).end((err, res) => {
|
||||
if (err) return done(err);
|
||||
const json = require('../test/db.json');
|
||||
should(res.body).have.lengthOf(json.collections.note_fields.length);
|
||||
should(res.body).matchEach(material => {
|
||||
should(material).have.only.keys('name', 'qty');
|
||||
should(material).have.property('qty').be.type('number');
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('works with an API key', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'get',
|
||||
url: '/sample/notes/fields',
|
||||
auth: {key: 'user'},
|
||||
httpStatus: 200
|
||||
}).end((err, res) => {
|
||||
if (err) return done(err);
|
||||
const json = require('../test/db.json');
|
||||
should(res.body).have.lengthOf(json.collections.note_fields.length);
|
||||
should(res.body).matchEach(material => {
|
||||
should(material).have.only.keys('name', 'qty');
|
||||
should(material).have.property('qty').be.type('number');
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('rejects unauthorized requests', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'get',
|
||||
url: '/sample/notes/fields',
|
||||
httpStatus: 401
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
109
src/routes/sample.ts
Normal file
109
src/routes/sample.ts
Normal file
@ -0,0 +1,109 @@
|
||||
import express from 'express';
|
||||
|
||||
import SampleValidate from './validate/sample';
|
||||
import NoteFieldValidate from './validate/note_field';
|
||||
import SampleModel from '../models/sample'
|
||||
import MaterialModel from '../models/material';
|
||||
import NoteModel from '../models/note';
|
||||
import NoteFieldModel from '../models/note_field';
|
||||
|
||||
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/samples', (req, res, next) => {
|
||||
if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'all')) return;
|
||||
|
||||
SampleModel.find({}).lean().exec((err, data) => {
|
||||
if (err) return next(err);
|
||||
res.json(data.map(e => SampleValidate.output(e)).filter(e => e !== null)); // validate all and filter null values from validation errors
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
router.post('/sample/new', (req, res, next) => {
|
||||
if (!req.auth(res, ['write', 'maintain', 'dev', 'admin'], 'basic')) return;
|
||||
|
||||
const {error, value: sample} = SampleValidate.input(req.body, 'new');
|
||||
if (error) {
|
||||
return res.status(400).json({status: 'Invalid body format'});
|
||||
}
|
||||
|
||||
MaterialModel.findById(sample.material_id).lean().exec((err, data: any) => { // validate material_id
|
||||
if (err) return next(err);
|
||||
if (!data) { // could not find material_id
|
||||
return res.status(400).json({status: 'Material not available'});
|
||||
}
|
||||
if (!data.numbers.find(e => e.color === sample.color)) { // color for material not specified
|
||||
return res.status(400).json({status: 'Color not available for material'});
|
||||
}
|
||||
SampleModel.findOne({number: sample.number}).lean().exec((err, data) => { // validate sample number
|
||||
if (err) return next(err);
|
||||
if (data) { // found entry with sample number
|
||||
return res.status(400).json({status: 'Sample number already taken'});
|
||||
}
|
||||
|
||||
if (sample.notes.sample_references.length > 0) { // validate sample_references
|
||||
let referencesCount = sample.notes.sample_references.length;
|
||||
sample.notes.sample_references.forEach(reference => {
|
||||
SampleModel.findById(reference.id).lean().exec((err, data) => {
|
||||
if (err) return next(err);
|
||||
if (!data) {
|
||||
return res.status(400).json({status: 'Sample reference not available'});
|
||||
}
|
||||
referencesCount --;
|
||||
if (referencesCount <= 0) {
|
||||
f();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
f();
|
||||
}
|
||||
|
||||
if (sample.notes.hasOwnProperty('custom_fields') && Object.keys(sample.notes.custom_fields).length > 0) {
|
||||
customFieldsAdd(Object.keys(sample.notes.custom_fields));
|
||||
}
|
||||
|
||||
function f() { // to resolve async
|
||||
new NoteModel(sample.notes).save((err, data) => {
|
||||
if (err) return next(err);
|
||||
delete sample.notes;
|
||||
sample.note_id = data._id;
|
||||
sample.user_id = req.authDetails.id;
|
||||
new SampleModel(sample).save((err, data) => {
|
||||
if (err) return next(err);
|
||||
res.json(SampleValidate.output(data.toObject()));
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
router.get('/sample/notes/fields', (req, res, next) => {
|
||||
if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'all')) return;
|
||||
|
||||
NoteFieldModel.find({}).lean().exec((err, data) => {
|
||||
if (err) return next(err);
|
||||
res.json(data.map(e => NoteFieldValidate.output(e)).filter(e => e !== null)); // validate all and filter null values from validation errors
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
module.exports = router;
|
||||
|
||||
|
||||
function customFieldsAdd (fields) {
|
||||
fields.forEach(field => {
|
||||
NoteFieldModel.findOneAndUpdate({name: field}, {$inc: {qty: 1}}).lean().exec((err, data) => { // check if field exists
|
||||
if (err) return console.error(err);
|
||||
if (!data) { // new field
|
||||
new NoteFieldModel({name: field, qty: 1}).save(err => {
|
||||
if (err) return console.error(err);
|
||||
})
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
@ -172,7 +172,6 @@ describe('/template', () => {
|
||||
if (err) return done(err);
|
||||
TemplateTreatmentModel.find({name: 'heat aging'}).lean().exec((err, data:any) => {
|
||||
if (err) return done(err);
|
||||
console.log(data);
|
||||
should(data).have.lengthOf(1);
|
||||
should(data[0]).have.only.keys('_id', 'name', 'parameters', '__v');
|
||||
should(data[0]).have.property('name', 'heat aging');
|
||||
|
@ -41,7 +41,7 @@ router.put('/template/:collection(measurement|treatment)/:name', (req, res, next
|
||||
if (err) next (err);
|
||||
const templateState = data? 'change': 'new';
|
||||
const {error, value: template} = TemplateValidate.input(req.body, templateState);
|
||||
if(error !== undefined) {
|
||||
if (error) {
|
||||
res.status(400).json({status: 'Invalid body format'});
|
||||
return;
|
||||
}
|
||||
@ -64,7 +64,7 @@ router.put('/template/:collection(measurement|treatment)/:name', (req, res, next
|
||||
|
||||
function f() { // to resolve async
|
||||
collectionModel.findOneAndUpdate({name: req.params.name}, template, {new: true, upsert: true}).lean().exec((err, data) => {
|
||||
if (err) next(err);
|
||||
if (err) return next(err);
|
||||
res.json(TemplateValidate.output(data));
|
||||
});
|
||||
}
|
||||
@ -76,7 +76,7 @@ router.delete('/template/:collection(measurement|treatment)/:name', (req, res, n
|
||||
|
||||
(req.params.collection === 'treatment' ? TemplateTreatmentModel : TemplateMeasurementModel)
|
||||
.findOneAndDelete({name: req.params.name}).lean().exec((err, data) => {
|
||||
if (err) next(err);
|
||||
if (err) return next(err);
|
||||
if (data) {
|
||||
res.json({status: 'OK'})
|
||||
}
|
||||
@ -87,5 +87,4 @@ router.delete('/template/:collection(measurement|treatment)/:name', (req, res, n
|
||||
});
|
||||
|
||||
|
||||
|
||||
module.exports = router;
|
@ -27,7 +27,7 @@ router.get('/user:username([/](?!key|new).?*|/?)', (req, res, next) => { // thi
|
||||
}
|
||||
|
||||
UserModel.findOne({name: username}).lean().exec( (err, data:any) => {
|
||||
if (err) next(err);
|
||||
if (err) return next(err);
|
||||
if (data) {
|
||||
res.json(UserValidate.output(data)); // validate all and filter null values from validation errors
|
||||
}
|
||||
@ -46,7 +46,7 @@ router.put('/user:username([/](?!key|new).?*|/?)', (req, res, next) => { // thi
|
||||
username = req.params.username;
|
||||
}
|
||||
const {error, value: user} = UserValidate.input(req.body, 'change' + (req.authDetails.level === 'admin'? 'admin' : ''));
|
||||
if(error !== undefined) {
|
||||
if (error) {
|
||||
res.status(400).json({status: 'Invalid body format'});
|
||||
return;
|
||||
}
|
||||
@ -58,14 +58,14 @@ router.put('/user:username([/](?!key|new).?*|/?)', (req, res, next) => { // thi
|
||||
// check that user does not already exist if new name was specified
|
||||
if (user.hasOwnProperty('name') && user.name !== username) {
|
||||
UserModel.find({name: user.name}).lean().exec( (err, data:any) => {
|
||||
if (err) next(err);
|
||||
if (err) return next(err);
|
||||
if (data.length > 0 || UserValidate.isSpecialName(user.name)) {
|
||||
res.status(400).json({status: 'Username already taken'});
|
||||
return;
|
||||
}
|
||||
|
||||
UserModel.findOneAndUpdate({name: username}, user, {new: true}).lean().exec( (err, data:any) => {
|
||||
if (err) next(err);
|
||||
if (err) return next(err);
|
||||
if (data) {
|
||||
res.json(UserValidate.output(data));
|
||||
}
|
||||
@ -77,7 +77,7 @@ router.put('/user:username([/](?!key|new).?*|/?)', (req, res, next) => { // thi
|
||||
}
|
||||
else {
|
||||
UserModel.findOneAndUpdate({name: username}, user, {new: true}).lean().exec( (err, data:any) => {
|
||||
if (err) next(err);
|
||||
if (err) return next(err);
|
||||
if (data) {
|
||||
res.json(UserValidate.output(data)); // validate all and filter null values from validation errors
|
||||
}
|
||||
@ -98,7 +98,7 @@ router.delete('/user:username([/](?!key|new).?*|/?)', (req, res, next) => { //
|
||||
}
|
||||
|
||||
UserModel.findOneAndDelete({name: username}).lean().exec( (err, data:any) => {
|
||||
if (err) next(err);
|
||||
if (err) return next(err);
|
||||
if (data) {
|
||||
res.json({status: 'OK'})
|
||||
}
|
||||
@ -109,11 +109,10 @@ router.delete('/user:username([/](?!key|new).?*|/?)', (req, res, next) => { //
|
||||
});
|
||||
|
||||
router.get('/user/key', (req, res, next) => {
|
||||
console.log('hmm');
|
||||
if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'basic')) return;
|
||||
|
||||
UserModel.findOne({name: req.authDetails.username}).lean().exec( (err, data:any) => {
|
||||
if (err) next(err);
|
||||
if (err) return next(err);
|
||||
res.json({key: data.key});
|
||||
});
|
||||
});
|
||||
@ -123,14 +122,14 @@ router.post('/user/new', (req, res, next) => {
|
||||
|
||||
// validate input
|
||||
const {error, value: user} = UserValidate.input(req.body, 'new');
|
||||
if(error !== undefined) {
|
||||
if (error) {
|
||||
res.status(400).json({status: 'Invalid body format'});
|
||||
return;
|
||||
}
|
||||
|
||||
// check that user does not already exist
|
||||
UserModel.find({name: user.name}).lean().exec( (err, data:any) => {
|
||||
if (err) next(err);
|
||||
if (err) return next(err);
|
||||
if (data.length > 0 || UserValidate.isSpecialName(user.name)) {
|
||||
res.status(400).json({status: 'Username already taken'});
|
||||
return;
|
||||
@ -140,7 +139,7 @@ router.post('/user/new', (req, res, next) => {
|
||||
bcrypt.hash(user.pass, 10, (err, hash) => { // password hashing
|
||||
user.pass = hash;
|
||||
new UserModel(user).save((err, data) => { // store user
|
||||
if (err) next(err);
|
||||
if (err) return next(err);
|
||||
res.json(UserValidate.output(data.toObject()));
|
||||
});
|
||||
});
|
||||
@ -150,15 +149,15 @@ router.post('/user/new', (req, res, next) => {
|
||||
router.post('/user/passreset', (req, res, next) => {
|
||||
// check if user/email combo exists
|
||||
UserModel.find({name: req.body.name, email: req.body.email}).lean().exec( (err, data: any) => {
|
||||
if (err) next(err);
|
||||
if (err) return next(err);
|
||||
if (data.length === 1) { // it exists
|
||||
const newPass = Math.random().toString(36).substring(2);
|
||||
bcrypt.hash(newPass, 10, (err, hash) => { // password hashing
|
||||
if (err) next(err);
|
||||
if (err) return next(err);
|
||||
UserModel.findByIdAndUpdate(data[0]._id, {pass: hash}, err => { // write new password
|
||||
if (err) next(err);
|
||||
if (err) return next(err);
|
||||
mail(data[0].email, 'Your new password for the DFOP database', 'Hi, <br><br> You requested to reset your password.<br>Your new password is:<br><br>' + newPass + '<br><br>If you did not request a password reset, talk to the sysadmin quickly!<br><br>Have a nice day.<br><br>The DFOP team', err => {
|
||||
if (err) next(err);
|
||||
if (err) return next(err);
|
||||
res.json({status: 'OK'});
|
||||
});
|
||||
});
|
||||
|
@ -11,7 +11,16 @@ export default class IdValidate {
|
||||
return this.id.validate(id).error === undefined;
|
||||
}
|
||||
|
||||
static parameter() { // :id url parameter
|
||||
static parameter () { // :id url parameter
|
||||
return ':id([0-9a-f]{24})';
|
||||
}
|
||||
|
||||
static stringify (data) {
|
||||
Object.keys(data).forEach(key => {
|
||||
if (data[key] !== null && data[key].hasOwnProperty('_bsontype') && data[key]._bsontype === 'ObjectID') {
|
||||
data[key] = data[key].toString();
|
||||
}
|
||||
});
|
||||
return data;
|
||||
}
|
||||
}
|
@ -66,7 +66,7 @@ export default class MaterialValidate { // validate input for material
|
||||
}
|
||||
|
||||
static output (data) { // validate output from database for needed properties, strip everything else
|
||||
data._id = data._id.toString();
|
||||
data = IdValidate.stringify(data);
|
||||
const {value, error} = joi.object({
|
||||
_id: IdValidate.get(),
|
||||
name: this.material.name,
|
||||
|
18
src/routes/validate/note_field.ts
Normal file
18
src/routes/validate/note_field.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import joi from '@hapi/joi';
|
||||
|
||||
export default class NoteFieldValidate {
|
||||
private static note_field = {
|
||||
name: joi.string()
|
||||
.max(128),
|
||||
|
||||
qty: joi.number()
|
||||
};
|
||||
|
||||
static output (data) {
|
||||
const {value, error} = joi.object({
|
||||
name: this.note_field.name,
|
||||
qty: this.note_field.qty
|
||||
}).validate(data, {stripUnknown: true});
|
||||
return error !== undefined? null : value;
|
||||
}
|
||||
}
|
77
src/routes/validate/sample.ts
Normal file
77
src/routes/validate/sample.ts
Normal file
@ -0,0 +1,77 @@
|
||||
import joi from '@hapi/joi';
|
||||
|
||||
import IdValidate from './id';
|
||||
|
||||
export default class SampleValidate {
|
||||
private static sample = {
|
||||
number: joi.string()
|
||||
.max(128),
|
||||
|
||||
color: joi.string()
|
||||
.max(128),
|
||||
|
||||
type: joi.string()
|
||||
.max(128),
|
||||
|
||||
batch: joi.string()
|
||||
.max(128)
|
||||
.allow(''),
|
||||
|
||||
notes: joi.object({
|
||||
comment: joi.string()
|
||||
.max(512),
|
||||
|
||||
sample_references: joi.array()
|
||||
.items(joi.object({
|
||||
id: IdValidate.get(),
|
||||
|
||||
relation: joi.string()
|
||||
.max(128)
|
||||
})),
|
||||
|
||||
custom_fields: joi.object()
|
||||
.pattern(/.*/, joi.alternatives()
|
||||
.try(
|
||||
joi.string().max(128),
|
||||
joi.number(),
|
||||
joi.boolean(),
|
||||
joi.date()
|
||||
)
|
||||
)
|
||||
})
|
||||
};
|
||||
|
||||
static input (data, param) { // validate data, param: new(everything required)/change(available attributes are validated)
|
||||
if (param === 'new') {
|
||||
return joi.object({
|
||||
number: this.sample.number.required(),
|
||||
color: this.sample.color.required(),
|
||||
type: this.sample.type.required(),
|
||||
batch: this.sample.batch.required(),
|
||||
material_id: IdValidate.get().required(),
|
||||
notes: this.sample.notes.required()
|
||||
}).validate(data);
|
||||
}
|
||||
else if (param === 'change') {
|
||||
return{error: 'Not implemented!', value: {}};
|
||||
}
|
||||
else {
|
||||
return{error: 'No parameter specified!', value: {}};
|
||||
}
|
||||
}
|
||||
|
||||
static output (data) {
|
||||
data = IdValidate.stringify(data);
|
||||
const {value, error} = joi.object({
|
||||
_id: IdValidate.get(),
|
||||
number: this.sample.number,
|
||||
color: this.sample.color,
|
||||
type: this.sample.type,
|
||||
batch: this.sample.batch,
|
||||
material_id: IdValidate.get(),
|
||||
note_id: IdValidate.get().allow(null),
|
||||
user_id: IdValidate.get()
|
||||
}).validate(data, {stripUnknown: true});
|
||||
return error !== undefined? null : value;
|
||||
}
|
||||
}
|
@ -48,7 +48,7 @@ export default class TemplateValidate {
|
||||
}
|
||||
|
||||
static output (data) { // validate output from database for needed properties, strip everything else
|
||||
data._id = data._id.toString();
|
||||
data = IdValidate.stringify(data);
|
||||
const {value, error} = joi.object({
|
||||
_id: IdValidate.get(),
|
||||
name: this.template.name,
|
||||
|
@ -69,7 +69,7 @@ export default class UserValidate { // validate input for user
|
||||
}
|
||||
|
||||
static output (data) { // validate output from database for needed properties, strip everything else
|
||||
data._id = data._id.toString();
|
||||
data = IdValidate.stringify(data);
|
||||
const {value, error} = joi.object({
|
||||
_id: IdValidate.get(),
|
||||
name: this.user.name,
|
||||
|
178
src/test/db.json
178
src/test/db.json
@ -1,38 +1,93 @@
|
||||
{
|
||||
"collections": {
|
||||
"users": [
|
||||
"samples": [
|
||||
{
|
||||
"_id": {"$oid":"000000000000000000000001"},
|
||||
"email": "user@bosch.com",
|
||||
"name": "user",
|
||||
"pass": "$2a$10$di26XKF63OG0V00PL1kSK.ceCcTxDExBMOg.jkHiCnXcY7cN7DlPi",
|
||||
"level": "read",
|
||||
"location": "Rng",
|
||||
"device_name": "Alpha I",
|
||||
"key": "000000000000000000001001",
|
||||
"_id": {"$oid":"400000000000000000000001"},
|
||||
"number": "1",
|
||||
"type": "granulate",
|
||||
"color": "black",
|
||||
"batch": "",
|
||||
"validated": true,
|
||||
"material_id": {"$oid":"100000000000000000000004"},
|
||||
"note_id": null,
|
||||
"user_id": {"$oid":"000000000000000000000002"},
|
||||
"__v": 0
|
||||
},
|
||||
{
|
||||
"_id": {"$oid":"000000000000000000000002"},
|
||||
"email": "jane.doe@bosch.com",
|
||||
"name": "janedoe",
|
||||
"pass": "$2a$10$di26XKF63OG0V00PL1kSK.ceCcTxDExBMOg.jkHiCnXcY7cN7DlPi",
|
||||
"level": "write",
|
||||
"location": "Rng",
|
||||
"device_name": "Alpha I",
|
||||
"key": "000000000000000000001002",
|
||||
"_id": {"$oid":"400000000000000000000002"},
|
||||
"number": "21",
|
||||
"type": "granulate",
|
||||
"color": "natural",
|
||||
"batch": "1560237365",
|
||||
"validated": true,
|
||||
"material_id": {"$oid":"100000000000000000000001"},
|
||||
"note_id": {"$oid":"500000000000000000000001"},
|
||||
"user_id": {"$oid":"000000000000000000000002"},
|
||||
"__v": 0
|
||||
},
|
||||
{
|
||||
"_id": {"$oid":"000000000000000000000003"},
|
||||
"email": "a.d.m.i.n@bosch.com",
|
||||
"name": "admin",
|
||||
"pass": "$2a$10$i872o3qR5V3JnbDArD8Z.eDo.BNPDBaR7dUX9KSEtl9pUjLyucy2K",
|
||||
"level": "admin",
|
||||
"location": "Rng",
|
||||
"device_name": "",
|
||||
"key": "000000000000000000001003",
|
||||
"__v": "0"
|
||||
"_id": {"$oid":"400000000000000000000003"},
|
||||
"number": "33",
|
||||
"type": "part",
|
||||
"color": "black",
|
||||
"batch": "1704-005",
|
||||
"validated": false,
|
||||
"material_id": {"$oid":"100000000000000000000005"},
|
||||
"note_id": {"$oid":"500000000000000000000002"},
|
||||
"user_id": {"$oid":"000000000000000000000003"},
|
||||
"__v": 0
|
||||
},
|
||||
{
|
||||
"_id": {"$oid":"400000000000000000000004"},
|
||||
"number": "32",
|
||||
"type": "granulate",
|
||||
"color": "black",
|
||||
"batch": "1653000308",
|
||||
"validated": false,
|
||||
"material_id": {"$oid":"100000000000000000000005"},
|
||||
"note_id": {"$oid":"500000000000000000000003"},
|
||||
"user_id": {"$oid":"000000000000000000000003"},
|
||||
"__v": 0
|
||||
}
|
||||
],
|
||||
"notes": [
|
||||
{
|
||||
"_id": {"$oid":"500000000000000000000001"},
|
||||
"comment": "Stoff gesperrt",
|
||||
"sample_references": [],
|
||||
"__v": 0
|
||||
},
|
||||
{
|
||||
"_id": {"$oid":"500000000000000000000002"},
|
||||
"comment": "",
|
||||
"sample_references": [{
|
||||
"id": "400000000000000000000004",
|
||||
"relation": "granulate to sample"
|
||||
}],
|
||||
"custom_fields": {
|
||||
"not allowed for new applications": true
|
||||
},
|
||||
"__v": 0
|
||||
},
|
||||
{
|
||||
"_id": {"$oid":"500000000000000000000003"},
|
||||
"comment": "",
|
||||
"sample_references": [{
|
||||
"id": "400000000000000000000003",
|
||||
"relation": "part to sample"
|
||||
}],
|
||||
"custom_fields": {
|
||||
"not allowed for new applications": true
|
||||
},
|
||||
"__v": 0
|
||||
}
|
||||
],
|
||||
"note_fields": [
|
||||
{
|
||||
"_id": {"$oid":"600000000000000000000001"},
|
||||
"name": "not allowed for new applications",
|
||||
"qty": 2,
|
||||
"__v": 0
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
@ -48,6 +103,10 @@
|
||||
{
|
||||
"color": "black",
|
||||
"number": 5514263423
|
||||
},
|
||||
{
|
||||
"color": "natural",
|
||||
"number": 5514263422
|
||||
}
|
||||
],
|
||||
"__v": 0
|
||||
@ -83,6 +142,38 @@
|
||||
"numbers": [
|
||||
],
|
||||
"__v": 0
|
||||
},
|
||||
{
|
||||
"_id": {"$oid":"100000000000000000000004"},
|
||||
"name": "Schulamid 66 GF 25 H",
|
||||
"supplier": "Schulmann",
|
||||
"group": "PA66",
|
||||
"mineral": 0,
|
||||
"glass_fiber": 25,
|
||||
"carbon_fiber": 0,
|
||||
"numbers": [
|
||||
{
|
||||
"color": "black",
|
||||
"number": 5513933405
|
||||
}
|
||||
],
|
||||
"__v": 0
|
||||
},
|
||||
{
|
||||
"_id": {"$oid":"100000000000000000000005"},
|
||||
"name": "Amodel A 1133 HS",
|
||||
"supplier": "Solvay",
|
||||
"group": "PPA",
|
||||
"mineral": 0,
|
||||
"glass_fiber": 33,
|
||||
"carbon_fiber": 0,
|
||||
"numbers": [
|
||||
{
|
||||
"color": "black",
|
||||
"number": 5514262406
|
||||
}
|
||||
],
|
||||
"__v": 0
|
||||
}
|
||||
],
|
||||
"treatment_templates": [
|
||||
@ -150,6 +241,41 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"users": [
|
||||
{
|
||||
"_id": {"$oid":"000000000000000000000001"},
|
||||
"email": "user@bosch.com",
|
||||
"name": "user",
|
||||
"pass": "$2a$10$di26XKF63OG0V00PL1kSK.ceCcTxDExBMOg.jkHiCnXcY7cN7DlPi",
|
||||
"level": "read",
|
||||
"location": "Rng",
|
||||
"device_name": "Alpha I",
|
||||
"key": "000000000000000000001001",
|
||||
"__v": 0
|
||||
},
|
||||
{
|
||||
"_id": {"$oid":"000000000000000000000002"},
|
||||
"email": "jane.doe@bosch.com",
|
||||
"name": "janedoe",
|
||||
"pass": "$2a$10$di26XKF63OG0V00PL1kSK.ceCcTxDExBMOg.jkHiCnXcY7cN7DlPi",
|
||||
"level": "write",
|
||||
"location": "Rng",
|
||||
"device_name": "Alpha I",
|
||||
"key": "000000000000000000001002",
|
||||
"__v": 0
|
||||
},
|
||||
{
|
||||
"_id": {"$oid":"000000000000000000000003"},
|
||||
"email": "a.d.m.i.n@bosch.com",
|
||||
"name": "admin",
|
||||
"pass": "$2a$10$i872o3qR5V3JnbDArD8Z.eDo.BNPDBaR7dUX9KSEtl9pUjLyucy2K",
|
||||
"level": "admin",
|
||||
"location": "Rng",
|
||||
"device_name": "",
|
||||
"key": "000000000000000000001003",
|
||||
"__v": "0"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -6,6 +6,8 @@
|
||||
"sourceMap": true,
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"incremental": true,
|
||||
"diagnostics": true,
|
||||
"typeRoots": [
|
||||
"src/customTypings",
|
||||
"node_modules/@types"
|
||||
|
Reference in New Issue
Block a user