implemented first /sample methods
This commit is contained in:
parent
af071a9445
commit
20f57acd2a
@ -1,6 +1,6 @@
|
|||||||
/samples:
|
/samples:
|
||||||
get:
|
get:
|
||||||
summary: TODO all samples in overview
|
summary: all samples in overview
|
||||||
description: 'Auth: all, levels: read, write, maintain, dev, admin'
|
description: 'Auth: all, levels: read, write, maintain, dev, admin'
|
||||||
tags:
|
tags:
|
||||||
- /sample
|
- /sample
|
||||||
@ -10,7 +10,9 @@
|
|||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: 'api.yaml#/components/schemas/Samples'
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: 'api.yaml#/components/schemas/SampleRefs'
|
||||||
401:
|
401:
|
||||||
$ref: 'api.yaml#/components/responses/401'
|
$ref: 'api.yaml#/components/responses/401'
|
||||||
500:
|
500:
|
||||||
@ -39,7 +41,7 @@
|
|||||||
500:
|
500:
|
||||||
$ref: 'api.yaml#/components/responses/500'
|
$ref: 'api.yaml#/components/responses/500'
|
||||||
put:
|
put:
|
||||||
summary: TODO add/change sample
|
summary: TODO change sample
|
||||||
description: 'Auth: basic, levels: write, maintain, dev, admin'
|
description: 'Auth: basic, levels: write, maintain, dev, admin'
|
||||||
tags:
|
tags:
|
||||||
- /sample
|
- /sample
|
||||||
@ -88,10 +90,41 @@
|
|||||||
$ref: 'api.yaml#/components/responses/404'
|
$ref: 'api.yaml#/components/responses/404'
|
||||||
500:
|
500:
|
||||||
$ref: 'api.yaml#/components/responses/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:
|
/sample/notes/fields:
|
||||||
get:
|
get:
|
||||||
summary: TODO list all existing field names for custom notes fields
|
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:
|
tags:
|
||||||
- /sample
|
- /sample
|
||||||
responses:
|
responses:
|
||||||
|
@ -14,16 +14,17 @@ Color:
|
|||||||
example: black
|
example: black
|
||||||
SampleProperties:
|
SampleProperties:
|
||||||
properties:
|
properties:
|
||||||
sample_number:
|
number:
|
||||||
type: string
|
type: string
|
||||||
|
example: Rng172
|
||||||
type:
|
type:
|
||||||
type: string
|
type: string
|
||||||
|
example: granulate
|
||||||
batch:
|
batch:
|
||||||
type: string
|
type: string
|
||||||
validated:
|
example: 1560237365
|
||||||
type: boolean
|
|
||||||
|
|
||||||
Samples:
|
SampleRefs:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: 'api.yaml#/components/schemas/_Id'
|
- $ref: 'api.yaml#/components/schemas/_Id'
|
||||||
- $ref: 'api.yaml#/components/schemas/Color'
|
- $ref: 'api.yaml#/components/schemas/Color'
|
||||||
@ -41,17 +42,23 @@ Sample:
|
|||||||
- $ref: 'api.yaml#/components/schemas/Color'
|
- $ref: 'api.yaml#/components/schemas/Color'
|
||||||
- $ref: 'api.yaml#/components/schemas/SampleProperties'
|
- $ref: 'api.yaml#/components/schemas/SampleProperties'
|
||||||
properties:
|
properties:
|
||||||
material:
|
material_id:
|
||||||
$ref: 'api.yaml#/components/schemas/Material'
|
allOf:
|
||||||
|
- $ref: 'api.yaml#/components/schemas/Id'
|
||||||
notes:
|
notes:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
comments:
|
comment:
|
||||||
type: string
|
type: string
|
||||||
sample_references:
|
sample_references:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: 'api.yaml#/components/schemas/Id'
|
properties:
|
||||||
|
id:
|
||||||
|
$ref: 'api.yaml#/components/schemas/Id'
|
||||||
|
relation:
|
||||||
|
type: string
|
||||||
|
example: part to this sample
|
||||||
SampleDetail:
|
SampleDetail:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: 'api.yaml#/components/schemas/_Id'
|
- $ref: 'api.yaml#/components/schemas/_Id'
|
||||||
@ -63,7 +70,7 @@ SampleDetail:
|
|||||||
notes:
|
notes:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
comments:
|
comment:
|
||||||
type: string
|
type: string
|
||||||
sample_references:
|
sample_references:
|
||||||
type: array
|
type: array
|
||||||
|
@ -37,7 +37,7 @@ export default class db {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// connect to 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);
|
if (err) done(err);
|
||||||
});
|
});
|
||||||
mongoose.connection.on('error', console.error.bind(console, 'connection error:'));
|
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
|
Object.keys(json.collections).forEach(collectionName => { // create each collection
|
||||||
for(let i in json.collections[collectionName]) { // convert $oid fields to actual ObjectIds
|
for(let i in json.collections[collectionName]) { // convert $oid fields to actual ObjectIds
|
||||||
Object.keys(json.collections[collectionName][i]).forEach(key => {
|
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) => {
|
this.state.db.createCollection(collectionName, (err, collection) => {
|
||||||
|
@ -9,7 +9,7 @@ import UserModel from '../models/user';
|
|||||||
|
|
||||||
module.exports = async (req, res, next) => {
|
module.exports = async (req, res, next) => {
|
||||||
let givenMethod = ''; // authorization method given by client, basic taken preferred
|
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
|
// test authentications
|
||||||
const userBasic = await basic(req, next);
|
const userBasic = await basic(req, next);
|
||||||
@ -45,7 +45,8 @@ module.exports = async (req, res, next) => {
|
|||||||
req.authDetails = {
|
req.authDetails = {
|
||||||
method: givenMethod,
|
method: givenMethod,
|
||||||
username: user.name,
|
username: user.name,
|
||||||
level: user.level
|
level: user.level,
|
||||||
|
id: user.id
|
||||||
};
|
};
|
||||||
|
|
||||||
next();
|
next();
|
||||||
@ -57,12 +58,12 @@ function basic (req, next): any { // checks basic auth and returns changed user
|
|||||||
const auth = basicAuth(req);
|
const auth = basicAuth(req);
|
||||||
if (auth !== undefined) { // basic auth available
|
if (auth !== undefined) { // basic auth available
|
||||||
UserModel.find({name: auth.name}).lean().exec( (err, data: any) => { // find user
|
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
|
if (data.length === 1) { // one user found
|
||||||
bcrypt.compare(auth.pass, data[0].pass, (err, res) => { // check password
|
bcrypt.compare(auth.pass, data[0].pass, (err, res) => { // check password
|
||||||
if (err) next(err);
|
if (err) return next(err);
|
||||||
if (res === true) {
|
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 {
|
else {
|
||||||
resolve(null);
|
resolve(null);
|
||||||
@ -84,9 +85,9 @@ function key (req, next): any { // checks API key and returns changed user obje
|
|||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
if (req.query.key !== undefined) {
|
if (req.query.key !== undefined) {
|
||||||
UserModel.find({key: req.query.key}).lean().exec( (err, data: any) => { // find user
|
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
|
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 {
|
else {
|
||||||
resolve(null);
|
resolve(null);
|
||||||
|
@ -14,6 +14,7 @@ export default class TestHelper {
|
|||||||
401: {status: 'Unauthorized'},
|
401: {status: 'Unauthorized'},
|
||||||
403: {status: 'Forbidden'},
|
403: {status: 'Forbidden'},
|
||||||
404: {status: 'Not found'},
|
404: {status: 'Not found'},
|
||||||
|
500: {status: 'Internal server error'}
|
||||||
}
|
}
|
||||||
|
|
||||||
static before (done) {
|
static before (done) {
|
||||||
|
@ -45,9 +45,10 @@ app.use(require('./helpers/authorize')); // handle authentication
|
|||||||
|
|
||||||
// require routes
|
// require routes
|
||||||
app.use('/', require('./routes/root'));
|
app.use('/', require('./routes/root'));
|
||||||
app.use('/', require('./routes/user'));
|
app.use('/', require('./routes/sample'));
|
||||||
app.use('/', require('./routes/material'));
|
app.use('/', require('./routes/material'));
|
||||||
app.use('/', require('./routes/template'));
|
app.use('/', require('./routes/template'));
|
||||||
|
app.use('/', require('./routes/user'));
|
||||||
|
|
||||||
// static files
|
// static files
|
||||||
app.use('/static', express.static('static'));
|
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) => {
|
}).end((err, res) => {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
const json = require('../test/db.json');
|
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(res.body).matchEach(material => {
|
||||||
should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'mineral', 'glass_fiber', 'carbon_fiber', 'numbers');
|
should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'mineral', 'glass_fiber', 'carbon_fiber', 'numbers');
|
||||||
should(material).have.property('_id').be.type('string');
|
should(material).have.property('_id').be.type('string');
|
||||||
@ -47,7 +47,7 @@ describe('/material', () => {
|
|||||||
}).end((err, res) => {
|
}).end((err, res) => {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
const json = require('../test/db.json');
|
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(res.body).matchEach(material => {
|
||||||
should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'mineral', 'glass_fiber', 'carbon_fiber', 'numbers');
|
should(material).have.only.keys('_id', 'name', 'supplier', 'group', 'mineral', 'glass_fiber', 'carbon_fiber', 'numbers');
|
||||||
should(material).have.property('_id').be.type('string');
|
should(material).have.property('_id').be.type('string');
|
||||||
@ -82,7 +82,7 @@ describe('/material', () => {
|
|||||||
url: '/material/100000000000000000000001',
|
url: '/material/100000000000000000000001',
|
||||||
auth: {basic: 'janedoe'},
|
auth: {basic: 'janedoe'},
|
||||||
httpStatus: 200,
|
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 => {
|
it('returns the right material for an API key', done => {
|
||||||
@ -127,7 +127,7 @@ describe('/material', () => {
|
|||||||
auth: {basic: 'janedoe'},
|
auth: {basic: 'janedoe'},
|
||||||
httpStatus: 200,
|
httpStatus: 200,
|
||||||
req: {},
|
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 => {
|
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}]}
|
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) => {
|
}).end((err, res) => {
|
||||||
if (err) return done (err);
|
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.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('_id').be.type('string');
|
||||||
should(res.body).have.property('name', 'Crastin CE 2510');
|
should(res.body).have.property('name', 'Crastin CE 2510');
|
||||||
@ -324,7 +323,7 @@ describe('/material', () => {
|
|||||||
if (err) return done (err);
|
if (err) return done (err);
|
||||||
MaterialModel.find({name: 'Crastin CE 2510'}).lean().exec((err, data: any) => {
|
MaterialModel.find({name: 'Crastin CE 2510'}).lean().exec((err, data: any) => {
|
||||||
if (err) return done (err);
|
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.only.keys('_id', 'name', 'supplier', 'group', 'mineral', 'glass_fiber', 'carbon_fiber', 'numbers', '__v');
|
||||||
should(data[0]).have.property('_id');
|
should(data[0]).have.property('_id');
|
||||||
should(data[0]).have.property('name', 'Crastin CE 2510');
|
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;
|
if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'all')) return;
|
||||||
|
|
||||||
MaterialModel.find({}).lean().exec((err, data) => {
|
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
|
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;
|
if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'all')) return;
|
||||||
|
|
||||||
MaterialModel.findById(req.params.id).lean().exec((err, data) => {
|
MaterialModel.findById(req.params.id).lean().exec((err, data) => {
|
||||||
if(err) next(err);
|
if (err) return next(err);
|
||||||
console.log(data);
|
|
||||||
if (data) {
|
if (data) {
|
||||||
res.json(MaterialValidate.output(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;
|
if (!req.auth(res, ['write', 'maintain', 'dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
const {error, value: material} = MaterialValidate.input(req.body, 'change');
|
const {error, value: material} = MaterialValidate.input(req.body, 'change');
|
||||||
if(error !== undefined) {
|
if (error) {
|
||||||
res.status(400).json({status: 'Invalid body format'});
|
res.status(400).json({status: 'Invalid body format'});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (material.hasOwnProperty('name')) {
|
if (material.hasOwnProperty('name')) {
|
||||||
MaterialModel.find({name: material.name}).lean().exec((err, data) => {
|
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) {
|
if (data.length > 0 && data[0]._id != req.params.id) {
|
||||||
res.status(400).json({status: 'Material name already taken'});
|
res.status(400).json({status: 'Material name already taken'});
|
||||||
return;
|
return;
|
||||||
@ -58,7 +57,7 @@ router.put('/material/' + IdValidate.parameter(), (req, res, next) => {
|
|||||||
|
|
||||||
function f() { // to resolve async
|
function f() { // to resolve async
|
||||||
MaterialModel.findByIdAndUpdate(req.params.id, material, {new: true}).lean().exec((err, data) => {
|
MaterialModel.findByIdAndUpdate(req.params.id, material, {new: true}).lean().exec((err, data) => {
|
||||||
if (err) next(err);
|
if (err) return next(err);
|
||||||
if (data) {
|
if (data) {
|
||||||
res.json(MaterialValidate.output(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;
|
if (!req.auth(res, ['write', 'maintain', 'dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
MaterialModel.findByIdAndDelete(req.params.id).lean().exec((err, data) => {
|
MaterialModel.findByIdAndDelete(req.params.id).lean().exec((err, data) => {
|
||||||
if (err) next(err);
|
if (err) return next(err);
|
||||||
if (data) {
|
if (data) {
|
||||||
res.json({status: 'OK'})
|
res.json({status: 'OK'})
|
||||||
}
|
}
|
||||||
@ -88,20 +87,20 @@ router.post('/material/new', (req, res, next) => {
|
|||||||
|
|
||||||
// validate input
|
// validate input
|
||||||
const {error, value: material} = MaterialValidate.input(req.body, 'new');
|
const {error, value: material} = MaterialValidate.input(req.body, 'new');
|
||||||
if(error !== undefined) {
|
if (error) {
|
||||||
res.status(400).json({status: 'Invalid body format'});
|
res.status(400).json({status: 'Invalid body format'});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MaterialModel.find({name: material.name}).lean().exec((err, data) => {
|
MaterialModel.find({name: material.name}).lean().exec((err, data) => {
|
||||||
if(err) next(err);
|
if (err) return next(err);
|
||||||
if (data.length > 0) {
|
if (data.length > 0) {
|
||||||
res.status(400).json({status: 'Material name already taken'});
|
res.status(400).json({status: 'Material name already taken'});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
new MaterialModel(material).save((err, data) => {
|
new MaterialModel(material).save((err, data) => {
|
||||||
if(err) next(err);
|
if (err) return next(err);
|
||||||
res.json(MaterialValidate.output(data.toObject()));
|
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);
|
if (err) return done(err);
|
||||||
TemplateTreatmentModel.find({name: 'heat aging'}).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);
|
||||||
console.log(data);
|
|
||||||
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', 'parameters', '__v');
|
||||||
should(data[0]).have.property('name', 'heat aging');
|
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);
|
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);
|
||||||
if(error !== undefined) {
|
if (error) {
|
||||||
res.status(400).json({status: 'Invalid body format'});
|
res.status(400).json({status: 'Invalid body format'});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -64,7 +64,7 @@ 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) => {
|
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));
|
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)
|
(req.params.collection === 'treatment' ? TemplateTreatmentModel : TemplateMeasurementModel)
|
||||||
.findOneAndDelete({name: req.params.name}).lean().exec((err, data) => {
|
.findOneAndDelete({name: req.params.name}).lean().exec((err, data) => {
|
||||||
if (err) next(err);
|
if (err) return next(err);
|
||||||
if (data) {
|
if (data) {
|
||||||
res.json({status: 'OK'})
|
res.json({status: 'OK'})
|
||||||
}
|
}
|
||||||
@ -87,5 +87,4 @@ router.delete('/template/:collection(measurement|treatment)/:name', (req, res, n
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = router;
|
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) => {
|
UserModel.findOne({name: username}).lean().exec( (err, data:any) => {
|
||||||
if (err) next(err);
|
if (err) return next(err);
|
||||||
if (data) {
|
if (data) {
|
||||||
res.json(UserValidate.output(data)); // validate all and filter null values from validation errors
|
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;
|
username = req.params.username;
|
||||||
}
|
}
|
||||||
const {error, value: user} = UserValidate.input(req.body, 'change' + (req.authDetails.level === 'admin'? 'admin' : ''));
|
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'});
|
res.status(400).json({status: 'Invalid body format'});
|
||||||
return;
|
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
|
// check that user does not already exist if new name was specified
|
||||||
if (user.hasOwnProperty('name') && user.name !== username) {
|
if (user.hasOwnProperty('name') && user.name !== username) {
|
||||||
UserModel.find({name: user.name}).lean().exec( (err, data:any) => {
|
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)) {
|
if (data.length > 0 || UserValidate.isSpecialName(user.name)) {
|
||||||
res.status(400).json({status: 'Username already taken'});
|
res.status(400).json({status: 'Username already taken'});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UserModel.findOneAndUpdate({name: username}, user, {new: true}).lean().exec( (err, data:any) => {
|
UserModel.findOneAndUpdate({name: username}, user, {new: true}).lean().exec( (err, data:any) => {
|
||||||
if (err) next(err);
|
if (err) return next(err);
|
||||||
if (data) {
|
if (data) {
|
||||||
res.json(UserValidate.output(data));
|
res.json(UserValidate.output(data));
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ router.put('/user:username([/](?!key|new).?*|/?)', (req, res, next) => { // thi
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
UserModel.findOneAndUpdate({name: username}, user, {new: true}).lean().exec( (err, data:any) => {
|
UserModel.findOneAndUpdate({name: username}, user, {new: true}).lean().exec( (err, data:any) => {
|
||||||
if (err) next(err);
|
if (err) return next(err);
|
||||||
if (data) {
|
if (data) {
|
||||||
res.json(UserValidate.output(data)); // validate all and filter null values from validation errors
|
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) => {
|
UserModel.findOneAndDelete({name: username}).lean().exec( (err, data:any) => {
|
||||||
if (err) next(err);
|
if (err) return next(err);
|
||||||
if (data) {
|
if (data) {
|
||||||
res.json({status: 'OK'})
|
res.json({status: 'OK'})
|
||||||
}
|
}
|
||||||
@ -109,11 +109,10 @@ router.delete('/user:username([/](?!key|new).?*|/?)', (req, res, next) => { //
|
|||||||
});
|
});
|
||||||
|
|
||||||
router.get('/user/key', (req, res, next) => {
|
router.get('/user/key', (req, res, next) => {
|
||||||
console.log('hmm');
|
|
||||||
if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
UserModel.findOne({name: req.authDetails.username}).lean().exec( (err, data:any) => {
|
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});
|
res.json({key: data.key});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -123,14 +122,14 @@ router.post('/user/new', (req, res, next) => {
|
|||||||
|
|
||||||
// validate input
|
// validate input
|
||||||
const {error, value: user} = UserValidate.input(req.body, 'new');
|
const {error, value: user} = UserValidate.input(req.body, 'new');
|
||||||
if(error !== undefined) {
|
if (error) {
|
||||||
res.status(400).json({status: 'Invalid body format'});
|
res.status(400).json({status: 'Invalid body format'});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check that user does not already exist
|
// check that user does not already exist
|
||||||
UserModel.find({name: user.name}).lean().exec( (err, data:any) => {
|
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)) {
|
if (data.length > 0 || UserValidate.isSpecialName(user.name)) {
|
||||||
res.status(400).json({status: 'Username already taken'});
|
res.status(400).json({status: 'Username already taken'});
|
||||||
return;
|
return;
|
||||||
@ -140,7 +139,7 @@ router.post('/user/new', (req, res, next) => {
|
|||||||
bcrypt.hash(user.pass, 10, (err, hash) => { // password hashing
|
bcrypt.hash(user.pass, 10, (err, hash) => { // password hashing
|
||||||
user.pass = hash;
|
user.pass = hash;
|
||||||
new UserModel(user).save((err, data) => { // store user
|
new UserModel(user).save((err, data) => { // store user
|
||||||
if (err) next(err);
|
if (err) return next(err);
|
||||||
res.json(UserValidate.output(data.toObject()));
|
res.json(UserValidate.output(data.toObject()));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -150,15 +149,15 @@ router.post('/user/new', (req, res, next) => {
|
|||||||
router.post('/user/passreset', (req, res, next) => {
|
router.post('/user/passreset', (req, res, next) => {
|
||||||
// check if user/email combo exists
|
// check if user/email combo exists
|
||||||
UserModel.find({name: req.body.name, email: req.body.email}).lean().exec( (err, data: any) => {
|
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
|
if (data.length === 1) { // it exists
|
||||||
const newPass = Math.random().toString(36).substring(2);
|
const newPass = Math.random().toString(36).substring(2);
|
||||||
bcrypt.hash(newPass, 10, (err, hash) => { // password hashing
|
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
|
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 => {
|
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'});
|
res.json({status: 'OK'});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -11,7 +11,16 @@ export default class IdValidate {
|
|||||||
return this.id.validate(id).error === undefined;
|
return this.id.validate(id).error === undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
static parameter() { // :id url parameter
|
static parameter () { // :id url parameter
|
||||||
return ':id([0-9a-f]{24})';
|
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
|
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({
|
const {value, error} = joi.object({
|
||||||
_id: IdValidate.get(),
|
_id: IdValidate.get(),
|
||||||
name: this.material.name,
|
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
|
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({
|
const {value, error} = joi.object({
|
||||||
_id: IdValidate.get(),
|
_id: IdValidate.get(),
|
||||||
name: this.template.name,
|
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
|
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({
|
const {value, error} = joi.object({
|
||||||
_id: IdValidate.get(),
|
_id: IdValidate.get(),
|
||||||
name: this.user.name,
|
name: this.user.name,
|
||||||
|
178
src/test/db.json
178
src/test/db.json
@ -1,38 +1,93 @@
|
|||||||
{
|
{
|
||||||
"collections": {
|
"collections": {
|
||||||
"users": [
|
"samples": [
|
||||||
{
|
{
|
||||||
"_id": {"$oid":"000000000000000000000001"},
|
"_id": {"$oid":"400000000000000000000001"},
|
||||||
"email": "user@bosch.com",
|
"number": "1",
|
||||||
"name": "user",
|
"type": "granulate",
|
||||||
"pass": "$2a$10$di26XKF63OG0V00PL1kSK.ceCcTxDExBMOg.jkHiCnXcY7cN7DlPi",
|
"color": "black",
|
||||||
"level": "read",
|
"batch": "",
|
||||||
"location": "Rng",
|
"validated": true,
|
||||||
"device_name": "Alpha I",
|
"material_id": {"$oid":"100000000000000000000004"},
|
||||||
"key": "000000000000000000001001",
|
"note_id": null,
|
||||||
|
"user_id": {"$oid":"000000000000000000000002"},
|
||||||
"__v": 0
|
"__v": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"_id": {"$oid":"000000000000000000000002"},
|
"_id": {"$oid":"400000000000000000000002"},
|
||||||
"email": "jane.doe@bosch.com",
|
"number": "21",
|
||||||
"name": "janedoe",
|
"type": "granulate",
|
||||||
"pass": "$2a$10$di26XKF63OG0V00PL1kSK.ceCcTxDExBMOg.jkHiCnXcY7cN7DlPi",
|
"color": "natural",
|
||||||
"level": "write",
|
"batch": "1560237365",
|
||||||
"location": "Rng",
|
"validated": true,
|
||||||
"device_name": "Alpha I",
|
"material_id": {"$oid":"100000000000000000000001"},
|
||||||
"key": "000000000000000000001002",
|
"note_id": {"$oid":"500000000000000000000001"},
|
||||||
|
"user_id": {"$oid":"000000000000000000000002"},
|
||||||
"__v": 0
|
"__v": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"_id": {"$oid":"000000000000000000000003"},
|
"_id": {"$oid":"400000000000000000000003"},
|
||||||
"email": "a.d.m.i.n@bosch.com",
|
"number": "33",
|
||||||
"name": "admin",
|
"type": "part",
|
||||||
"pass": "$2a$10$i872o3qR5V3JnbDArD8Z.eDo.BNPDBaR7dUX9KSEtl9pUjLyucy2K",
|
"color": "black",
|
||||||
"level": "admin",
|
"batch": "1704-005",
|
||||||
"location": "Rng",
|
"validated": false,
|
||||||
"device_name": "",
|
"material_id": {"$oid":"100000000000000000000005"},
|
||||||
"key": "000000000000000000001003",
|
"note_id": {"$oid":"500000000000000000000002"},
|
||||||
"__v": "0"
|
"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": [
|
"materials": [
|
||||||
@ -48,6 +103,10 @@
|
|||||||
{
|
{
|
||||||
"color": "black",
|
"color": "black",
|
||||||
"number": 5514263423
|
"number": 5514263423
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "natural",
|
||||||
|
"number": 5514263422
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"__v": 0
|
"__v": 0
|
||||||
@ -83,6 +142,38 @@
|
|||||||
"numbers": [
|
"numbers": [
|
||||||
],
|
],
|
||||||
"__v": 0
|
"__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": [
|
"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,
|
"sourceMap": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
|
"incremental": true,
|
||||||
|
"diagnostics": true,
|
||||||
"typeRoots": [
|
"typeRoots": [
|
||||||
"src/customTypings",
|
"src/customTypings",
|
||||||
"node_modules/@types"
|
"node_modules/@types"
|
||||||
|
Reference in New Issue
Block a user