Merge pull request #12 in ~VLE2FE/dfop-api from status-handling to develop
* commit 'd924ee5a8cfc9d1f7644ff4af9de2d5f36c71522': restore measurements restore materials restore samples adapted /materials adapted /measurements delete measurements with sample adjusted PUT /sample/{id}
This commit is contained in:
commit
c7d25bd7cb
@ -48,7 +48,7 @@
|
|||||||
get:
|
get:
|
||||||
summary: get material details
|
summary: get material details
|
||||||
description: 'Auth: all, levels: read, write, maintain, dev, admin'
|
description: 'Auth: all, levels: read, write, maintain, dev, admin'
|
||||||
x-doc: status handling (accessible (only for maintain/admin))? # TODO after decision
|
x-doc: deleted samples are available only for maintain/admin
|
||||||
tags:
|
tags:
|
||||||
- /material
|
- /material
|
||||||
responses:
|
responses:
|
||||||
@ -67,7 +67,7 @@
|
|||||||
put:
|
put:
|
||||||
summary: change material
|
summary: change material
|
||||||
description: 'Auth: basic, levels: write, maintain, dev, admin'
|
description: 'Auth: basic, levels: write, maintain, dev, admin'
|
||||||
x-doc: status is reset to 0 on any changes
|
x-doc: status is reset to 0 on any changes, deleted samples cannot be changed
|
||||||
tags:
|
tags:
|
||||||
- /material
|
- /material
|
||||||
security:
|
security:
|
||||||
@ -117,6 +117,29 @@
|
|||||||
500:
|
500:
|
||||||
$ref: 'api.yaml#/components/responses/500'
|
$ref: 'api.yaml#/components/responses/500'
|
||||||
|
|
||||||
|
/material/restore/{id}:
|
||||||
|
parameters:
|
||||||
|
- $ref: 'api.yaml#/components/parameters/Id'
|
||||||
|
put:
|
||||||
|
summary: restore material
|
||||||
|
description: 'Auth: basic, levels: maintain, admin'
|
||||||
|
x-doc: status is set to 0
|
||||||
|
tags:
|
||||||
|
- /material
|
||||||
|
security:
|
||||||
|
- BasicAuth: []
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
$ref: 'api.yaml#/components/responses/Ok'
|
||||||
|
401:
|
||||||
|
$ref: 'api.yaml#/components/responses/401'
|
||||||
|
403:
|
||||||
|
$ref: 'api.yaml#/components/responses/403'
|
||||||
|
404:
|
||||||
|
$ref: 'api.yaml#/components/responses/404'
|
||||||
|
500:
|
||||||
|
$ref: 'api.yaml#/components/responses/500'
|
||||||
|
|
||||||
/material/new:
|
/material/new:
|
||||||
post:
|
post:
|
||||||
summary: add material
|
summary: add material
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
get:
|
get:
|
||||||
summary: measurement values by id
|
summary: measurement values by id
|
||||||
description: 'Auth: all, levels: read, write, maintain, dev, admin'
|
description: 'Auth: all, levels: read, write, maintain, dev, admin'
|
||||||
x-doc: status handling (accessible (only for maintain/admin))? # TODO after decision
|
x-doc: deleted samples are available only for maintain/admin
|
||||||
tags:
|
tags:
|
||||||
- /measurement
|
- /measurement
|
||||||
responses:
|
responses:
|
||||||
@ -25,7 +25,7 @@
|
|||||||
put:
|
put:
|
||||||
summary: change measurement
|
summary: change measurement
|
||||||
description: 'Auth: basic, levels: write, maintain, dev, admin'
|
description: 'Auth: basic, levels: write, maintain, dev, admin'
|
||||||
x-doc: status is reset to 0 on any changes
|
x-doc: status is reset to 0 on any changes, deleted measurements cannot be edited
|
||||||
tags:
|
tags:
|
||||||
- /measurement
|
- /measurement
|
||||||
security:
|
security:
|
||||||
@ -77,6 +77,29 @@
|
|||||||
500:
|
500:
|
||||||
$ref: 'api.yaml#/components/responses/500'
|
$ref: 'api.yaml#/components/responses/500'
|
||||||
|
|
||||||
|
/measurement/restore/{id}:
|
||||||
|
parameters:
|
||||||
|
- $ref: 'api.yaml#/components/parameters/Id'
|
||||||
|
put:
|
||||||
|
summary: restore measurement
|
||||||
|
description: 'Auth: basic, levels: maintain, admin'
|
||||||
|
x-doc: status is set to 0
|
||||||
|
tags:
|
||||||
|
- /measurement
|
||||||
|
security:
|
||||||
|
- BasicAuth: []
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
$ref: 'api.yaml#/components/responses/Ok'
|
||||||
|
401:
|
||||||
|
$ref: 'api.yaml#/components/responses/401'
|
||||||
|
403:
|
||||||
|
$ref: 'api.yaml#/components/responses/403'
|
||||||
|
404:
|
||||||
|
$ref: 'api.yaml#/components/responses/404'
|
||||||
|
500:
|
||||||
|
$ref: 'api.yaml#/components/responses/500'
|
||||||
|
|
||||||
/measurement/new:
|
/measurement/new:
|
||||||
post:
|
post:
|
||||||
summary: add measurement
|
summary: add measurement
|
||||||
|
@ -69,7 +69,7 @@
|
|||||||
put:
|
put:
|
||||||
summary: change sample
|
summary: change sample
|
||||||
description: 'Auth: basic, levels: write, maintain, dev, admin <br>Only maintain and admin are allowed to edit samples created by another user'
|
description: 'Auth: basic, levels: write, maintain, dev, admin <br>Only maintain and admin are allowed to edit samples created by another user'
|
||||||
x-doc: status is reset to 0 on any changes
|
x-doc: status is reset to 0 on any changes, deleted samples cannot be changed
|
||||||
tags:
|
tags:
|
||||||
- /sample
|
- /sample
|
||||||
security:
|
security:
|
||||||
@ -119,6 +119,29 @@
|
|||||||
500:
|
500:
|
||||||
$ref: 'api.yaml#/components/responses/500'
|
$ref: 'api.yaml#/components/responses/500'
|
||||||
|
|
||||||
|
/sample/restore/{id}:
|
||||||
|
parameters:
|
||||||
|
- $ref: 'api.yaml#/components/parameters/Id'
|
||||||
|
put:
|
||||||
|
summary: restore sample
|
||||||
|
description: 'Auth: basic, levels: maintain, admin'
|
||||||
|
x-doc: status is set to 0
|
||||||
|
tags:
|
||||||
|
- /sample
|
||||||
|
security:
|
||||||
|
- BasicAuth: []
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
$ref: 'api.yaml#/components/responses/Ok'
|
||||||
|
401:
|
||||||
|
$ref: 'api.yaml#/components/responses/401'
|
||||||
|
403:
|
||||||
|
$ref: 'api.yaml#/components/responses/403'
|
||||||
|
404:
|
||||||
|
$ref: 'api.yaml#/components/responses/404'
|
||||||
|
500:
|
||||||
|
$ref: 'api.yaml#/components/responses/500'
|
||||||
|
|
||||||
/sample/new:
|
/sample/new:
|
||||||
post:
|
post:
|
||||||
summary: add sample
|
summary: add sample
|
||||||
|
@ -14,6 +14,7 @@ import db from './db';
|
|||||||
// TODO: coverage
|
// 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
|
||||||
|
|
||||||
// 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 =====');
|
||||||
|
@ -202,6 +202,23 @@ describe('/material', () => {
|
|||||||
res: {_id: '100000000000000000000007', name: 'Ultramid A4H', supplier: 'BASF', group: 'PA66', mineral: 0, glass_fiber: 0, carbon_fiber: 0, numbers: [{color: 'black', number: ''}]}
|
res: {_id: '100000000000000000000007', name: 'Ultramid A4H', supplier: 'BASF', group: 'PA66', mineral: 0, glass_fiber: 0, carbon_fiber: 0, numbers: [{color: 'black', number: ''}]}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('returns a deleted material for a maintain/admin user', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'get',
|
||||||
|
url: '/material/100000000000000000000008',
|
||||||
|
auth: {basic: 'admin'},
|
||||||
|
httpStatus: 200,
|
||||||
|
res: {_id: '100000000000000000000008', name: 'Latamid 66 H 2 G 30', supplier: 'LATI', group: 'PA66', mineral: 0, glass_fiber: 30, carbon_fiber: 0, numbers: [{color: 'blue', number: '5513943509'}]}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('returns 403 for a write user when requesting a deleted material', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'get',
|
||||||
|
url: '/material/100000000000000000000008',
|
||||||
|
auth: {basic: 'janedoe'},
|
||||||
|
httpStatus: 403
|
||||||
|
});
|
||||||
|
});
|
||||||
it('rejects an invalid id', done => {
|
it('rejects an invalid id', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@ -361,6 +378,15 @@ describe('/material', () => {
|
|||||||
req: {},
|
req: {},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('rejects editing a deleted material', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'put',
|
||||||
|
url: '/material/100000000000000000000008',
|
||||||
|
auth: {basic: 'janedoe'},
|
||||||
|
httpStatus: 403,
|
||||||
|
req: {}
|
||||||
|
});
|
||||||
|
});
|
||||||
it('rejects an API key', done => {
|
it('rejects an API key', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'put',
|
method: 'put',
|
||||||
@ -468,6 +494,61 @@ describe('/material', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('PUT /material/restore/{id}', () => {
|
||||||
|
it('sets the status', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'put',
|
||||||
|
url: '/material/restore/100000000000000000000008',
|
||||||
|
auth: {basic: 'admin'},
|
||||||
|
httpStatus: 200,
|
||||||
|
req: {}
|
||||||
|
}).end((err, res) => {
|
||||||
|
if (err) return done (err);
|
||||||
|
should(res.body).be.eql({status: 'OK'});
|
||||||
|
MaterialModel.findById('100000000000000000000008').lean().exec((err, data: any) => {
|
||||||
|
if (err) return done(err);
|
||||||
|
should(data).have.property('status',globals.status.new);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('rejects an API key', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'put',
|
||||||
|
url: '/material/restore/100000000000000000000008',
|
||||||
|
auth: {key: 'admin'},
|
||||||
|
httpStatus: 401,
|
||||||
|
req: {}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('rejects a write user', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'put',
|
||||||
|
url: '/material/restore/100000000000000000000008',
|
||||||
|
auth: {basic: 'janedoe'},
|
||||||
|
httpStatus: 403,
|
||||||
|
req: {}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('returns 404 for an unknown sample', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'put',
|
||||||
|
url: '/material/restore/000000000000000000000008',
|
||||||
|
auth: {basic: 'admin'},
|
||||||
|
httpStatus: 404,
|
||||||
|
req: {}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('rejects unauthorized requests', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'put',
|
||||||
|
url: '/material/restore/100000000000000000000008',
|
||||||
|
httpStatus: 401,
|
||||||
|
req: {}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('POST /material/new', () => {
|
describe('POST /material/new', () => {
|
||||||
it('returns the right material', done => {
|
it('returns the right material', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
|
@ -34,14 +34,14 @@ router.get('/materials/:group(new|deleted)', (req, res, next) => {
|
|||||||
router.get('/material/' + IdValidate.parameter(), (req, res, next) => {
|
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: any) => {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
if (data) {
|
|
||||||
res.json(MaterialValidate.output(data));
|
if (!data) {
|
||||||
}
|
return res.status(404).json({status: 'Not found'});
|
||||||
else {
|
|
||||||
res.status(404).json({status: 'Not found'});
|
|
||||||
}
|
}
|
||||||
|
if (data.status === globals.status.deleted && !req.auth(res, ['maintain', 'admin'], 'all')) return; // deleted materials only available for maintain/admin
|
||||||
|
res.json(MaterialValidate.output(data));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -55,6 +55,9 @@ router.put('/material/' + IdValidate.parameter(), (req, res, next) => {
|
|||||||
if (!materialData) {
|
if (!materialData) {
|
||||||
return res.status(404).json({status: 'Not found'});
|
return res.status(404).json({status: 'Not found'});
|
||||||
}
|
}
|
||||||
|
if (materialData.status === globals.status.deleted) {
|
||||||
|
return res.status(403).json({status: 'Forbidden'});
|
||||||
|
}
|
||||||
if (material.hasOwnProperty('name') && material.name !== materialData.name) {
|
if (material.hasOwnProperty('name') && material.name !== materialData.name) {
|
||||||
if (!await nameCheck(material, res, next)) return;
|
if (!await nameCheck(material, res, next)) return;
|
||||||
}
|
}
|
||||||
@ -92,6 +95,19 @@ router.delete('/material/' + IdValidate.parameter(), (req, res, next) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.put('/material/restore/' + IdValidate.parameter(), (req, res, next) => {
|
||||||
|
if (!req.auth(res, ['maintain', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
|
MaterialModel.findByIdAndUpdate(req.params.id, {status: globals.status.new}).lean().exec((err, data) => {
|
||||||
|
if (err) return next(err);
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return res.status(404).json({status: 'Not found'});
|
||||||
|
}
|
||||||
|
res.json({status: 'OK'});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
router.post('/material/new', async (req, res, next) => {
|
router.post('/material/new', async (req, res, next) => {
|
||||||
if (!req.auth(res, ['write', 'maintain', 'dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['write', 'maintain', 'dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
|
@ -3,8 +3,6 @@ import MeasurementModel from '../models/measurement';
|
|||||||
import TestHelper from "../test/helper";
|
import TestHelper from "../test/helper";
|
||||||
import globals from '../globals';
|
import globals from '../globals';
|
||||||
|
|
||||||
// TODO: restore measurements for m/a
|
|
||||||
|
|
||||||
|
|
||||||
describe('/measurement', () => {
|
describe('/measurement', () => {
|
||||||
let server;
|
let server;
|
||||||
@ -255,6 +253,15 @@ describe('/measurement', () => {
|
|||||||
httpStatus: 404
|
httpStatus: 404
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('rejects editing a deleted measurement', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'put',
|
||||||
|
url: '/measurement/800000000000000000000004',
|
||||||
|
auth: {basic: 'admin'},
|
||||||
|
httpStatus: 403,
|
||||||
|
req: {}
|
||||||
|
});
|
||||||
|
});
|
||||||
it('rejects an API key', done => {
|
it('rejects an API key', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'put',
|
method: 'put',
|
||||||
@ -358,6 +365,61 @@ describe('/measurement', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('PUT /measurement/restore/{id}', () => {
|
||||||
|
it('sets the status', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'put',
|
||||||
|
url: '/measurement/restore/800000000000000000000004',
|
||||||
|
auth: {basic: 'admin'},
|
||||||
|
httpStatus: 200,
|
||||||
|
req: {}
|
||||||
|
}).end((err, res) => {
|
||||||
|
if (err) return done (err);
|
||||||
|
should(res.body).be.eql({status: 'OK'});
|
||||||
|
MeasurementModel.findById('800000000000000000000004').lean().exec((err, data: any) => {
|
||||||
|
if (err) return done(err);
|
||||||
|
should(data).have.property('status',globals.status.new);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('rejects an API key', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'put',
|
||||||
|
url: '/measurement/restore/800000000000000000000004',
|
||||||
|
auth: {key: 'admin'},
|
||||||
|
httpStatus: 401,
|
||||||
|
req: {}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('rejects a write user', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'put',
|
||||||
|
url: '/measurement/restore/800000000000000000000004',
|
||||||
|
auth: {basic: 'janedoe'},
|
||||||
|
httpStatus: 403,
|
||||||
|
req: {}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('returns 404 for an unknown sample', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'put',
|
||||||
|
url: '/measurement/restore/000000000000000000000004',
|
||||||
|
auth: {basic: 'admin'},
|
||||||
|
httpStatus: 404,
|
||||||
|
req: {}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('rejects unauthorized requests', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'put',
|
||||||
|
url: '/measurement/restore/800000000000000000000004',
|
||||||
|
httpStatus: 401,
|
||||||
|
req: {}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('POST /measurement/new', () => {
|
describe('POST /measurement/new', () => {
|
||||||
it('returns the right measurement', done => {
|
it('returns the right measurement', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
|
@ -38,6 +38,9 @@ router.put('/measurement/' + IdValidate.parameter(), async (req, res, next) => {
|
|||||||
if (!data) {
|
if (!data) {
|
||||||
return res.status(404).json({status: 'Not found'});
|
return res.status(404).json({status: 'Not found'});
|
||||||
}
|
}
|
||||||
|
if (data.status === globals.status.deleted) {
|
||||||
|
return res.status(403).json({status: 'Forbidden'});
|
||||||
|
}
|
||||||
|
|
||||||
// add properties needed for sampleIdCheck
|
// add properties needed for sampleIdCheck
|
||||||
measurement.measurement_template = data.measurement_template;
|
measurement.measurement_template = data.measurement_template;
|
||||||
@ -75,6 +78,19 @@ router.delete('/measurement/' + IdValidate.parameter(), (req, res, next) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.put('/measurement/restore/' + IdValidate.parameter(), (req, res, next) => {
|
||||||
|
if (!req.auth(res, ['maintain', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
|
MeasurementModel.findByIdAndUpdate(req.params.id, {status: globals.status.new}).lean().exec((err, data) => {
|
||||||
|
if (err) return next(err);
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return res.status(404).json({status: 'Not found'});
|
||||||
|
}
|
||||||
|
res.json({status: 'OK'});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
router.post('/measurement/new', async (req, res, next) => {
|
router.post('/measurement/new', async (req, res, next) => {
|
||||||
if (!req.auth(res, ['write', 'maintain', 'dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['write', 'maintain', 'dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
|
@ -2,15 +2,16 @@ import should from 'should/as-function';
|
|||||||
import SampleModel from '../models/sample';
|
import SampleModel from '../models/sample';
|
||||||
import NoteModel from '../models/note';
|
import NoteModel from '../models/note';
|
||||||
import NoteFieldModel from '../models/note_field';
|
import NoteFieldModel from '../models/note_field';
|
||||||
|
import MeasurementModel from '../models/measurement';
|
||||||
import TestHelper from "../test/helper";
|
import TestHelper from "../test/helper";
|
||||||
import globals from '../globals';
|
import globals from '../globals';
|
||||||
|
import mongoose from 'mongoose';
|
||||||
|
|
||||||
// TODO: generate output for ML in format DPT -> data, implement filtering, field selection
|
// TODO: generate output for ML in format DPT -> data, implement filtering, field selection
|
||||||
|
// TODO: generate csv
|
||||||
// TODO: filter by not completely filled/no measurements
|
// TODO: filter by not completely filled/no measurements
|
||||||
// TODO: write script for data import
|
// TODO: write script for data import
|
||||||
// TODO: delete everything (measurements, condition) with sample
|
|
||||||
// TODO: allow adding sample numbers for existing samples
|
// TODO: allow adding sample numbers for existing samples
|
||||||
|
|
||||||
// TODO: Do not allow validation or measurement entry without condition
|
// TODO: Do not allow validation or measurement entry without condition
|
||||||
|
|
||||||
|
|
||||||
@ -187,7 +188,6 @@ describe('/sample', () => {
|
|||||||
res: {_id: '400000000000000000000003', number: '33', type: 'part', color: 'black', batch: '1704-005', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', mineral: 0, glass_fiber: 33, carbon_fiber: 0, numbers: [{color: 'black', number: '5514262406'}]}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'granulate to sample'}], custom_fields: {'not allowed for new applications': true}}, user: 'admin'}
|
res: {_id: '400000000000000000000003', number: '33', type: 'part', color: 'black', batch: '1704-005', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', mineral: 0, glass_fiber: 33, carbon_fiber: 0, numbers: [{color: 'black', number: '5514262406'}]}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'granulate to sample'}], custom_fields: {'not allowed for new applications': true}}, user: 'admin'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('works with an API key', done => {
|
it('works with an API key', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@ -197,7 +197,6 @@ describe('/sample', () => {
|
|||||||
res: {_id: '400000000000000000000003', number: '33', type: 'part', color: 'black', batch: '1704-005', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', mineral: 0, glass_fiber: 33, carbon_fiber: 0, numbers: [{color: 'black', number: '5514262406'}]}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'granulate to sample'}], custom_fields: {'not allowed for new applications': true}}, user: 'admin'}
|
res: {_id: '400000000000000000000003', number: '33', type: 'part', color: 'black', batch: '1704-005', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', mineral: 0, glass_fiber: 33, carbon_fiber: 0, numbers: [{color: 'black', number: '5514262406'}]}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'granulate to sample'}], custom_fields: {'not allowed for new applications': true}}, user: 'admin'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns a deleted sample for a maintain/admin user', done => {
|
it('returns a deleted sample for a maintain/admin user', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@ -207,7 +206,6 @@ describe('/sample', () => {
|
|||||||
res: {_id: '400000000000000000000005', number: 'Rng33', type: 'granulate', color: 'black', batch: '1653000308', condition: {condition_template: '200000000000000000000003'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', mineral: 0, glass_fiber: 33, carbon_fiber: 0, numbers: [{color: 'black', number: '5514262406'}]}, notes: {}, user: 'admin'}
|
res: {_id: '400000000000000000000005', number: 'Rng33', type: 'granulate', color: 'black', batch: '1653000308', condition: {condition_template: '200000000000000000000003'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', mineral: 0, glass_fiber: 33, carbon_fiber: 0, numbers: [{color: 'black', number: '5514262406'}]}, notes: {}, user: 'admin'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns 403 for a write user when requesting a deleted sample', done => {
|
it('returns 403 for a write user when requesting a deleted sample', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@ -216,7 +214,6 @@ describe('/sample', () => {
|
|||||||
httpStatus: 403
|
httpStatus: 403
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns 404 for an unknown sample', done => {
|
it('returns 404 for an unknown sample', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@ -225,7 +222,6 @@ describe('/sample', () => {
|
|||||||
httpStatus: 404
|
httpStatus: 404
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rejects an invalid id', done => {
|
it('rejects an invalid id', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@ -234,7 +230,6 @@ describe('/sample', () => {
|
|||||||
httpStatus: 404
|
httpStatus: 404
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rejects unauthorized requests', done => {
|
it('rejects unauthorized requests', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'get',
|
method: 'get',
|
||||||
@ -589,6 +584,15 @@ describe('/sample', () => {
|
|||||||
res: {status: 'Condition template not available'}
|
res: {status: 'Condition template not available'}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('rejects editing a deleted sample', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'put',
|
||||||
|
url: '/sample/400000000000000000000005',
|
||||||
|
auth: {basic: 'admin'},
|
||||||
|
httpStatus: 403,
|
||||||
|
req: {}
|
||||||
|
});
|
||||||
|
});
|
||||||
it('rejects an API key', done => {
|
it('rejects an API key', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'put',
|
method: 'put',
|
||||||
@ -749,6 +753,24 @@ describe('/sample', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('deletes associated measurements', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'delete',
|
||||||
|
url: '/sample/400000000000000000000001',
|
||||||
|
auth: {basic: 'janedoe'},
|
||||||
|
httpStatus: 200
|
||||||
|
}).end((err, res) => {
|
||||||
|
if (err) return done(err);
|
||||||
|
should(res.body).be.eql({status: 'OK'});
|
||||||
|
MeasurementModel.find({sample_id: mongoose.Types.ObjectId('400000000000000000000001')}).lean().exec((err, data: any) => {
|
||||||
|
if (err) return done(err);
|
||||||
|
should(data).matchEach(sample => {
|
||||||
|
should(sample).have.property('status', -1);
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
it('rejects deleting samples of other users for write users', done => {
|
it('rejects deleting samples of other users for write users', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
@ -765,7 +787,6 @@ describe('/sample', () => {
|
|||||||
httpStatus: 404
|
httpStatus: 404
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('rejects deleting a sample referenced by conditions'); // TODO after decision
|
|
||||||
it('rejects requests from a read user', done => {
|
it('rejects requests from a read user', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
method: 'delete',
|
method: 'delete',
|
||||||
@ -799,6 +820,61 @@ describe('/sample', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('PUT /sample/restore/{id}', () => {
|
||||||
|
it('sets the status', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'put',
|
||||||
|
url: '/sample/restore/400000000000000000000005',
|
||||||
|
auth: {basic: 'admin'},
|
||||||
|
httpStatus: 200,
|
||||||
|
req: {}
|
||||||
|
}).end((err, res) => {
|
||||||
|
if (err) return done (err);
|
||||||
|
should(res.body).be.eql({status: 'OK'});
|
||||||
|
SampleModel.findById('400000000000000000000005').lean().exec((err, data: any) => {
|
||||||
|
if (err) return done(err);
|
||||||
|
should(data).have.property('status',globals.status.new);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('rejects an API key', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'put',
|
||||||
|
url: '/sample/restore/400000000000000000000005',
|
||||||
|
auth: {key: 'admin'},
|
||||||
|
httpStatus: 401,
|
||||||
|
req: {}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('rejects a write user', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'put',
|
||||||
|
url: '/sample/restore/400000000000000000000005',
|
||||||
|
auth: {basic: 'janedoe'},
|
||||||
|
httpStatus: 403,
|
||||||
|
req: {}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('returns 404 for an unknown sample', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'put',
|
||||||
|
url: '/sample/restore/000000000000000000000005',
|
||||||
|
auth: {basic: 'admin'},
|
||||||
|
httpStatus: 404,
|
||||||
|
req: {}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('rejects unauthorized requests', done => {
|
||||||
|
TestHelper.request(server, done, {
|
||||||
|
method: 'put',
|
||||||
|
url: '/sample/restore/400000000000000000000005',
|
||||||
|
httpStatus: 401,
|
||||||
|
req: {}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('POST /sample/new', () => {
|
describe('POST /sample/new', () => {
|
||||||
it('returns the right sample', done => {
|
it('returns the right sample', done => {
|
||||||
TestHelper.request(server, done, {
|
TestHelper.request(server, done, {
|
||||||
|
@ -10,7 +10,7 @@ import MaterialModel from '../models/material';
|
|||||||
import NoteModel from '../models/note';
|
import NoteModel from '../models/note';
|
||||||
import NoteFieldModel from '../models/note_field';
|
import NoteFieldModel from '../models/note_field';
|
||||||
import IdValidate from './validate/id';
|
import IdValidate from './validate/id';
|
||||||
import mongoose from "mongoose";
|
import mongoose from 'mongoose';
|
||||||
import ConditionTemplateModel from '../models/condition_template';
|
import ConditionTemplateModel from '../models/condition_template';
|
||||||
import ParametersValidate from './validate/parameters';
|
import ParametersValidate from './validate/parameters';
|
||||||
import globals from '../globals';
|
import globals from '../globals';
|
||||||
@ -43,7 +43,7 @@ router.get('/sample/' + IdValidate.parameter(), (req, res, next) => {
|
|||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
|
|
||||||
if (sampleData) {
|
if (sampleData) {
|
||||||
if (sampleData.status ===globals.status.deleted && !req.auth(res, ['maintain', 'admin'], 'all')) return; // deleted samples only available for maintain/admin
|
if (sampleData.status === globals.status.deleted && !req.auth(res, ['maintain', 'admin'], 'all')) return; // deleted samples only available for maintain/admin
|
||||||
sampleData.material = sampleData.material_id; // map data to right keys
|
sampleData.material = sampleData.material_id; // map data to right keys
|
||||||
sampleData.user = sampleData.user_id.name;
|
sampleData.user = sampleData.user_id.name;
|
||||||
sampleData.notes = sampleData.note_id ? sampleData.note_id : {};
|
sampleData.notes = sampleData.note_id ? sampleData.note_id : {};
|
||||||
@ -69,6 +69,9 @@ router.put('/sample/' + IdValidate.parameter(), (req, res, next) => {
|
|||||||
if (!sampleData) {
|
if (!sampleData) {
|
||||||
return res.status(404).json({status: 'Not found'});
|
return res.status(404).json({status: 'Not found'});
|
||||||
}
|
}
|
||||||
|
if (sampleData.status === globals.status.deleted) {
|
||||||
|
return res.status(403).json({status: 'Forbidden'});
|
||||||
|
}
|
||||||
|
|
||||||
// only maintain and admin are allowed to edit other user's data
|
// only maintain and admin are allowed to edit other user's data
|
||||||
if (sampleData.user_id.toString() !== req.authDetails.id && !req.auth(res, ['maintain', 'admin'], 'basic')) return;
|
if (sampleData.user_id.toString() !== req.authDetails.id && !req.auth(res, ['maintain', 'admin'], 'basic')) return;
|
||||||
@ -138,22 +141,41 @@ router.delete('/sample/' + IdValidate.parameter(), (req, res, next) => {
|
|||||||
|
|
||||||
await SampleModel.findByIdAndUpdate(req.params.id, {status:globals.status.deleted}).lean().exec(err => { // set sample status
|
await SampleModel.findByIdAndUpdate(req.params.id, {status:globals.status.deleted}).lean().exec(err => { // set sample status
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
if (sampleData.note_id !== null) { // handle notes
|
|
||||||
NoteModel.findById(sampleData.note_id).lean().exec((err, data: any) => { // find notes to update note_fields
|
// set status of associated measurements also to deleted
|
||||||
if (err) return next(err);
|
MeasurementModel.update({sample_id: mongoose.Types.ObjectId(req.params.id)}, {status: -1}).lean().exec(err => {
|
||||||
if (data.hasOwnProperty('custom_fields')) { // update note_fields
|
if (err) return next(err);
|
||||||
customFieldsChange(Object.keys(data.custom_fields), -1);
|
|
||||||
}
|
if (sampleData.note_id !== null) { // handle notes
|
||||||
|
NoteModel.findById(sampleData.note_id).lean().exec((err, data: any) => { // find notes to update note_fields
|
||||||
|
if (err) return next(err);
|
||||||
|
if (data.hasOwnProperty('custom_fields')) { // update note_fields
|
||||||
|
customFieldsChange(Object.keys(data.custom_fields), -1);
|
||||||
|
}
|
||||||
|
res.json({status: 'OK'});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
res.json({status: 'OK'});
|
res.json({status: 'OK'});
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
else {
|
|
||||||
res.json({status: 'OK'});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.put('/sample/restore/' + IdValidate.parameter(), (req, res, next) => {
|
||||||
|
if (!req.auth(res, ['maintain', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
|
SampleModel.findByIdAndUpdate(req.params.id, {status: globals.status.new}).lean().exec((err, data) => {
|
||||||
|
if (err) return next(err);
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return res.status(404).json({status: 'Not found'});
|
||||||
|
}
|
||||||
|
res.json({status: 'OK'});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
router.post('/sample/new', async (req, res, next) => {
|
router.post('/sample/new', async (req, res, next) => {
|
||||||
if (!req.auth(res, ['write', 'maintain', 'dev', 'admin'], 'basic')) return;
|
if (!req.auth(res, ['write', 'maintain', 'dev', 'admin'], 'basic')) return;
|
||||||
|
|
||||||
|
@ -268,6 +268,23 @@
|
|||||||
],
|
],
|
||||||
"status": 0,
|
"status": 0,
|
||||||
"__v": 0
|
"__v": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_id": {"$oid":"100000000000000000000008"},
|
||||||
|
"name": "Latamid 66 H 2 G 30",
|
||||||
|
"supplier": "LATI",
|
||||||
|
"group": "PA66",
|
||||||
|
"mineral": 0,
|
||||||
|
"glass_fiber": 30,
|
||||||
|
"carbon_fiber": 0,
|
||||||
|
"numbers": [
|
||||||
|
{
|
||||||
|
"color": "blue",
|
||||||
|
"number": "5513943509"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"status": -1,
|
||||||
|
"__v": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"measurements": [
|
"measurements": [
|
||||||
|
Reference in New Issue
Block a user