only allowed latest template version and allowed admin to set sample number
This commit is contained in:
parent
0fcb902499
commit
74080d0902
@ -170,7 +170,7 @@
|
|||||||
/sample/new:
|
/sample/new:
|
||||||
post:
|
post:
|
||||||
summary: add sample
|
summary: add sample
|
||||||
description: 'Auth: basic, levels: write, maintain, dev, admin'
|
description: 'Auth: basic, levels: write, maintain, dev, admin. Number property is only for admin when adding existing samples'
|
||||||
x-doc: 'Adds status: 0 automatically'
|
x-doc: 'Adds status: 0 automatically'
|
||||||
tags:
|
tags:
|
||||||
- /sample
|
- /sample
|
||||||
@ -181,7 +181,12 @@
|
|||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: 'api.yaml#/components/schemas/Sample'
|
allOf:
|
||||||
|
- $ref: 'api.yaml#/components/schemas/Sample'
|
||||||
|
properties:
|
||||||
|
number:
|
||||||
|
type: string
|
||||||
|
readOnly: false
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: samples details
|
description: samples details
|
||||||
|
@ -69,6 +69,7 @@ Sample:
|
|||||||
relation:
|
relation:
|
||||||
type: string
|
type: string
|
||||||
example: part to this sample
|
example: part to this sample
|
||||||
|
|
||||||
SampleDetail:
|
SampleDetail:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: 'api.yaml#/components/schemas/_Id'
|
- $ref: 'api.yaml#/components/schemas/_Id'
|
||||||
|
@ -7,14 +7,12 @@ import db from './db';
|
|||||||
|
|
||||||
// TODO: changelog
|
// TODO: changelog
|
||||||
// TODO: check executing index.js/move everything needed into dist
|
// TODO: check executing index.js/move everything needed into dist
|
||||||
// TODO: One condition per sample
|
|
||||||
// TODO: validation: VZ, Humidity: min/max value, DPT: filename
|
// TODO: validation: VZ, Humidity: min/max value, DPT: filename
|
||||||
// TODO: condition values not needed on initial add
|
// TODO: add multiple samples at once (only GUI)
|
||||||
// TODO: add multiple samples at once
|
|
||||||
// TODO: coverage
|
|
||||||
// TODO: think about the display of deleted/new samples and validation in data and UI
|
// TODO: think about the display of deleted/new samples and validation in data and UI
|
||||||
// TODO: improve error coverage
|
// TODO: improve error coverage
|
||||||
// TODO: guess properties from material name in UI
|
// TODO: guess properties from material name in UI
|
||||||
|
// TODO: mongodb user
|
||||||
|
|
||||||
// tell if server is running in debug or production environment
|
// tell if server is running in debug or production environment
|
||||||
console.info(process.env.NODE_ENV === 'production' ? '===== PRODUCTION =====' : process.env.NODE_ENV === 'test' ? '' :'===== DEVELOPMENT =====');
|
console.info(process.env.NODE_ENV === 'production' ? '===== PRODUCTION =====' : process.env.NODE_ENV === 'test' ? '' :'===== DEVELOPMENT =====');
|
||||||
|
@ -586,14 +586,24 @@ describe('/measurement', () => {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('rejects no values', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'post',
|
||||||
|
url: '/measurement/new',
|
||||||
|
auth: {basic: 'janedoe'},
|
||||||
|
httpStatus: 400,
|
||||||
|
req: {sample_id: '400000000000000000000001', values: {}, measurement_template: '300000000000000000000002'},
|
||||||
|
res: {status: 'At least one value is required'}
|
||||||
|
});
|
||||||
|
});
|
||||||
it('rejects a value not in the value range', done => {
|
it('rejects a value not in the value range', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
url: '/measurement/new',
|
url: '/measurement/new',
|
||||||
auth: {basic: 'janedoe'},
|
auth: {basic: 'janedoe'},
|
||||||
httpStatus: 400,
|
httpStatus: 400,
|
||||||
req: {sample_id: '400000000000000000000001', values: {val1: 4}, measurement_template: '300000000000000000000003'},
|
req: {sample_id: '400000000000000000000001', values: {val2: 5}, measurement_template: '300000000000000000000004'},
|
||||||
res: {status: 'Invalid body format', details: '"val1" must be one of [1, 2, 3, null]'}
|
res: {status: 'Invalid body format', details: '"val2" must be one of [1, 2, 3, 4, null]'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('rejects a value below minimum range', done => {
|
it('rejects a value below minimum range', done => {
|
||||||
@ -664,6 +674,16 @@ describe('/measurement', () => {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('rejects an old version of a measurement template', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'post',
|
||||||
|
url: '/measurement/new',
|
||||||
|
auth: {basic: 'janedoe'},
|
||||||
|
httpStatus: 400,
|
||||||
|
req: {sample_id: '400000000000000000000001', values: {val1: 2}, measurement_template: '300000000000000000000003'},
|
||||||
|
res: {status: 'Old template version not allowed'}
|
||||||
|
});
|
||||||
|
});
|
||||||
it('rejects an API key', done => {
|
it('rejects an API key', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
|
@ -130,6 +130,14 @@ async function templateCheck (measurement, param, res, next) { // validate meas
|
|||||||
|
|
||||||
// fill not given values for new measurements
|
// fill not given values for new measurements
|
||||||
if (param === 'new') {
|
if (param === 'new') {
|
||||||
|
// get all template versions and check if given is latest
|
||||||
|
const templateVersions = await MeasurementTemplateModel.find({first_id: templateData.first_id}).sort({version: -1}).lean().exec().catch(err => next(err)) as any;
|
||||||
|
if (templateVersions instanceof Error) return false;
|
||||||
|
if (measurement.measurement_template !== templateVersions[0]._id.toString()) { // template not latest
|
||||||
|
res.status(400).json({status: 'Old template version not allowed'});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (Object.keys(measurement.values).length === 0) {
|
if (Object.keys(measurement.values).length === 0) {
|
||||||
res.status(400).json({status: 'At least one value is required'});
|
res.status(400).json({status: 'At least one value is required'});
|
||||||
return false
|
return false
|
||||||
|
@ -454,6 +454,16 @@ describe('/sample', () => {
|
|||||||
res: {status: 'Color not available for material'}
|
res: {status: 'Color not available for material'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('rejects an undefined color for the same material', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'put',
|
||||||
|
url: '/sample/400000000000000000000001',
|
||||||
|
auth: {basic: 'janedoe'},
|
||||||
|
httpStatus: 400,
|
||||||
|
req: {type: 'part', color: 'signalviolet', batch: '114531', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
|
||||||
|
res: {status: 'Color not available for material'}
|
||||||
|
});
|
||||||
|
});
|
||||||
it('rejects an unknown material id', done => {
|
it('rejects an unknown material id', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'put',
|
method: 'put',
|
||||||
@ -573,6 +583,26 @@ describe('/sample', () => {
|
|||||||
res: {_id: '400000000000000000000006', number: 'Rng36', type: 'granulate', color: 'black', batch: '', condition: {}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'}
|
res: {_id: '400000000000000000000006', number: 'Rng36', type: 'granulate', color: 'black', batch: '', condition: {}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('rejects an old version of a condition template', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'put',
|
||||||
|
url: '/sample/400000000000000000000001',
|
||||||
|
auth: {basic: 'janedoe'},
|
||||||
|
httpStatus: 400,
|
||||||
|
req: {condition: {p1: 36, condition_template: '200000000000000000000004'}},
|
||||||
|
res: {status: 'Old template version not allowed'}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('allows keeping an old version of a condition template', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'put',
|
||||||
|
url: '/sample/400000000000000000000004',
|
||||||
|
auth: {basic: 'admin'},
|
||||||
|
httpStatus: 200,
|
||||||
|
req: {condition: {p1: 36, condition_template: '200000000000000000000004'}},
|
||||||
|
res: {_id: '400000000000000000000004', number: '32', type: 'granulate', color: 'black', batch: '1653000308', condition: {p1: 36, condition_template: '200000000000000000000004'}, material_id: '100000000000000000000005', note_id: '500000000000000000000003', user_id: '000000000000000000000003'}
|
||||||
|
});
|
||||||
|
});
|
||||||
it('rejects an changing back to an empty condition', done => {
|
it('rejects an changing back to an empty condition', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'put',
|
method: 'put',
|
||||||
@ -1108,7 +1138,7 @@ describe('/sample', () => {
|
|||||||
res: {status: 'Material not available'}
|
res: {status: 'Material not available'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('rejects a sample number', done => {
|
it('rejects a sample number for a write user', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
url: '/sample/new',
|
url: '/sample/new',
|
||||||
@ -1118,6 +1148,38 @@ describe('/sample', () => {
|
|||||||
res: {status: 'Invalid body format', details: '"number" is not allowed'}
|
res: {status: 'Invalid body format', details: '"number" is not allowed'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('allows a sample number for an admin user', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'post',
|
||||||
|
url: '/sample/new',
|
||||||
|
auth: {basic: 'admin'},
|
||||||
|
httpStatus: 200,
|
||||||
|
req: {number: 'Rng34', color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_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', 'condition', 'material_id', 'note_id', 'user_id');
|
||||||
|
should(res.body).have.property('_id').be.type('string');
|
||||||
|
should(res.body).have.property('number', 'Rng34');
|
||||||
|
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('condition', {});
|
||||||
|
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', '000000000000000000000003');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('rejects an existing sample number for an admin user', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'post',
|
||||||
|
url: '/sample/new',
|
||||||
|
auth: {basic: 'admin'},
|
||||||
|
httpStatus: 400,
|
||||||
|
req: {number: 'Rng33', color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
|
||||||
|
res: {status: 'Sample number already taken'}
|
||||||
|
});
|
||||||
|
});
|
||||||
it('rejects an invalid sample reference', done => {
|
it('rejects an invalid sample reference', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
@ -1208,6 +1270,16 @@ describe('/sample', () => {
|
|||||||
res: {status: 'Condition template not available'}
|
res: {status: 'Condition template not available'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('rejects an old version of a condition template', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'post',
|
||||||
|
url: '/sample/new',
|
||||||
|
auth: {basic: 'janedoe'},
|
||||||
|
httpStatus: 400,
|
||||||
|
req: {color: 'black', type: 'granulate', batch: '1560237365', condition: {p1: 36, condition_template: '200000000000000000000004'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
|
||||||
|
res: {status: 'Old template version not allowed'}
|
||||||
|
});
|
||||||
|
});
|
||||||
it('rejects a missing color', done => {
|
it('rejects a missing color', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
|
@ -90,7 +90,7 @@ router.put('/sample/' + IdValidate.parameter(), (req, res, next) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sample.hasOwnProperty('condition') && !(_.isEmpty(sample.condition) && _.isEmpty(sampleData.condition))) { // do not execute check if condition is and was empty
|
if (sample.hasOwnProperty('condition') && !(_.isEmpty(sample.condition) && _.isEmpty(sampleData.condition))) { // do not execute check if condition is and was empty
|
||||||
if (!await conditionCheck(sample.condition, 'change', res, next)) return;
|
if (!await conditionCheck(sample.condition, 'change', res, next, sampleData.condition.condition_template.toString() !== sample.condition.condition_template)) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sample.hasOwnProperty('notes')) {
|
if (sample.hasOwnProperty('notes')) {
|
||||||
@ -217,7 +217,7 @@ router.post('/sample/new', async (req, res, next) => {
|
|||||||
req.body.condition = {};
|
req.body.condition = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const {error, value: sample} = SampleValidate.input(req.body, 'new');
|
const {error, value: sample} = SampleValidate.input(req.body, 'new' + (req.authDetails.level === 'admin' ? '-admin' : ''));
|
||||||
if (error) return res400(error, res);
|
if (error) return res400(error, res);
|
||||||
|
|
||||||
if (!await materialCheck(sample, res, next)) return;
|
if (!await materialCheck(sample, res, next)) return;
|
||||||
@ -232,7 +232,12 @@ router.post('/sample/new', async (req, res, next) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sample.status = globals.status.new; // set status to new
|
sample.status = globals.status.new; // set status to new
|
||||||
sample.number = await numberGenerate(sample, req, res, next);
|
if (sample.hasOwnProperty('number')) {
|
||||||
|
if (!await numberCheck(sample, res, next)) return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sample.number = await numberGenerate(sample, req, res, next);
|
||||||
|
}
|
||||||
if (!sample.number) return;
|
if (!sample.number) return;
|
||||||
|
|
||||||
await new NoteModel(sample.notes).save((err, data) => { // save notes
|
await new NoteModel(sample.notes).save((err, data) => { // save notes
|
||||||
@ -272,6 +277,15 @@ async function numberGenerate (sample, req, res, next) { // generate number in
|
|||||||
return req.authDetails.location + (sampleData ? Number(sampleData.number.replace(/[^0-9]+/g, '')) + 1 : 1);
|
return req.authDetails.location + (sampleData ? Number(sampleData.number.replace(/[^0-9]+/g, '')) + 1 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function numberCheck(sample, res, next) {
|
||||||
|
const sampleData = await SampleModel.findOne({number: sample.number}).lean().exec().catch(err => {next(err); return false;});
|
||||||
|
if (sampleData) { // found entry with sample number
|
||||||
|
res.status(400).json({status: 'Sample number already taken'});
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
async function materialCheck (sample, res, next, id = sample.material_id) { // validate material_id and color, returns false if invalid
|
async function materialCheck (sample, res, next, id = sample.material_id) { // validate material_id and color, returns false if invalid
|
||||||
const materialData = await MaterialModel.findById(id).lean().exec().catch(err => next(err)) as any;
|
const materialData = await MaterialModel.findById(id).lean().exec().catch(err => next(err)) as any;
|
||||||
if (materialData instanceof Error) return false;
|
if (materialData instanceof Error) return false;
|
||||||
@ -286,7 +300,7 @@ async function materialCheck (sample, res, next, id = sample.material_id) { //
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function conditionCheck (condition, param, res, next) { // validate treatment template, returns false if invalid, otherwise template data
|
async function conditionCheck (condition, param, res, next, checkVersion = true) { // validate treatment template, returns false if invalid, otherwise template data
|
||||||
if (!condition.condition_template || !IdValidate.valid(condition.condition_template)) { // template id not found
|
if (!condition.condition_template || !IdValidate.valid(condition.condition_template)) { // template id not found
|
||||||
res.status(400).json({status: 'Condition template not available'});
|
res.status(400).json({status: 'Condition template not available'});
|
||||||
return false;
|
return false;
|
||||||
@ -298,6 +312,16 @@ async function conditionCheck (condition, param, res, next) { // validate treat
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (checkVersion) {
|
||||||
|
// get all template versions and check if given is latest
|
||||||
|
const conditionVersions = await ConditionTemplateModel.find({first_id: conditionData.first_id}).sort({version: -1}).lean().exec().catch(err => next(err)) as any;
|
||||||
|
if (conditionVersions instanceof Error) return false;
|
||||||
|
if (condition.condition_template !== conditionVersions[0]._id.toString()) { // template not latest
|
||||||
|
res.status(400).json({status: 'Old template version not allowed'});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// validate parameters
|
// validate parameters
|
||||||
const {error, value: ignore} = ParametersValidate.input(_.omit(condition, 'condition_template'), conditionData.parameters, param);
|
const {error, value: ignore} = ParametersValidate.input(_.omit(condition, 'condition_template'), conditionData.parameters, param);
|
||||||
if (error) {res400(error, res); return false;}
|
if (error) {res400(error, res); return false;}
|
||||||
|
@ -4,7 +4,6 @@ import TemplateConditionModel from '../models/condition_template';
|
|||||||
import TemplateMeasurementModel from '../models/measurement_template';
|
import TemplateMeasurementModel from '../models/measurement_template';
|
||||||
import TestHelper from "../test/helper";
|
import TestHelper from "../test/helper";
|
||||||
|
|
||||||
// TODO: do not allow usage of old templates for new samples
|
|
||||||
|
|
||||||
describe('/template', () => {
|
describe('/template', () => {
|
||||||
let server;
|
let server;
|
||||||
@ -644,7 +643,7 @@ describe('/template', () => {
|
|||||||
req: {parameters: [{name: 'weight %', range: {}}]}
|
req: {parameters: [{name: 'weight %', range: {}}]}
|
||||||
}).end((err, res) => {
|
}).end((err, res) => {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
should(_.omit(res.body, '_id')).be.eql({name: 'kf', version: 3, parameters: [{name: 'weight %', range: {}}]});
|
should(_.omit(res.body, '_id')).be.eql({name: 'kf', version: 2, parameters: [{name: 'weight %', range: {}}]});
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -67,6 +67,17 @@ export default class SampleValidate {
|
|||||||
notes: this.sample.notes,
|
notes: this.sample.notes,
|
||||||
}).validate(data);
|
}).validate(data);
|
||||||
}
|
}
|
||||||
|
else if (param === 'new-admin') {
|
||||||
|
return Joi.object({
|
||||||
|
number: this.sample.number.required(),
|
||||||
|
color: this.sample.color.required(),
|
||||||
|
type: this.sample.type.required(),
|
||||||
|
batch: this.sample.batch.required(),
|
||||||
|
condition: this.sample.condition.required(),
|
||||||
|
material_id: IdValidate.get().required(),
|
||||||
|
notes: this.sample.notes.required()
|
||||||
|
}).validate(data);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
return{error: 'No parameter specified!', value: {}};
|
return{error: 'No parameter specified!', value: {}};
|
||||||
}
|
}
|
||||||
|
@ -59,9 +59,8 @@
|
|||||||
"color": "black",
|
"color": "black",
|
||||||
"batch": "1653000308",
|
"batch": "1653000308",
|
||||||
"condition": {
|
"condition": {
|
||||||
"material": "hot air",
|
"p1": 44,
|
||||||
"weeks": 5,
|
"condition_template": {"$oid":"200000000000000000000004"}
|
||||||
"condition_template": {"$oid":"200000000000000000000001"}
|
|
||||||
},
|
},
|
||||||
"material_id": {"$oid":"100000000000000000000005"},
|
"material_id": {"$oid":"100000000000000000000005"},
|
||||||
"note_id": {"$oid":"500000000000000000000003"},
|
"note_id": {"$oid":"500000000000000000000003"},
|
||||||
@ -447,24 +446,37 @@
|
|||||||
"__v": 0
|
"__v": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"_id": {"$oid":"200000000000000000000002"},
|
"_id": {"$oid":"200000000000000000000003"},
|
||||||
"first_id": {"$oid":"200000000000000000000001"},
|
"first_id": {"$oid":"200000000000000000000003"},
|
||||||
"name": "heat treatment 2",
|
"name": "raw material",
|
||||||
"version": 2,
|
"version": 1,
|
||||||
|
"parameters": [
|
||||||
|
],
|
||||||
|
"__v": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": {"$oid":"200000000000000000000004"},
|
||||||
|
"first_id": {"$oid":"200000000000000000000004"},
|
||||||
|
"name": "old condition",
|
||||||
|
"version": 1,
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"name": "material",
|
"name": "p1",
|
||||||
"range": {}
|
"range": {}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"__v": 0
|
"__v": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"_id": {"$oid":"200000000000000000000003"},
|
"_id": {"$oid":"200000000000000000000005"},
|
||||||
"first_id": {"$oid":"200000000000000000000003"},
|
"first_id": {"$oid":"200000000000000000000004"},
|
||||||
"name": "raw material",
|
"name": "new condition",
|
||||||
"version": 1,
|
"version": 2,
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "p11",
|
||||||
|
"range": {}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"__v": 0
|
"__v": 0
|
||||||
}
|
}
|
||||||
@ -487,9 +499,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"_id": {"$oid":"300000000000000000000002"},
|
"_id": {"$oid":"300000000000000000000002"},
|
||||||
"first_id": {"$oid":"300000000000000000000001"},
|
"first_id": {"$oid":"300000000000000000000002"},
|
||||||
"name": "kf",
|
"name": "kf",
|
||||||
"version": 2,
|
"version": 1,
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"name": "weight %",
|
"name": "weight %",
|
||||||
@ -522,6 +534,21 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"__v": 0
|
"__v": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": {"$oid":"300000000000000000000004"},
|
||||||
|
"first_id": {"$oid":"300000000000000000000003"},
|
||||||
|
"name": "mt 31",
|
||||||
|
"version": 2,
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "val2",
|
||||||
|
"range": {
|
||||||
|
"values": [1,2,3,4]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"__v": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"users": [
|
"users": [
|
||||||
|
Reference in New Issue
Block a user