diff --git a/api/sample.yaml b/api/sample.yaml index eae0ddc..1810a65 100644 --- a/api/sample.yaml +++ b/api/sample.yaml @@ -5,6 +5,13 @@ x-doc: returns only samples with status 10 tags: - /sample + parameters: + - name: status + description: 'values: validated|new|all, defaults to validated' + in: query + schema: + type: string + example: all responses: 200: description: samples overview @@ -14,6 +21,8 @@ type: array items: $ref: 'api.yaml#/components/schemas/SampleRefs' + 400: + $ref: 'api.yaml#/components/responses/400' 401: $ref: 'api.yaml#/components/responses/401' 500: diff --git a/src/helpers/authorize.ts b/src/helpers/authorize.ts index 21d43d5..71a42c2 100644 --- a/src/helpers/authorize.ts +++ b/src/helpers/authorize.ts @@ -89,6 +89,7 @@ function key (req, next): any { // checks API key and returns changed user obje if (err) return next(err); if (data.length === 1) { // one user found resolve({level: data[0].level, name: data[0].name, id: data[0]._id.toString(), location: data[0].location}); + delete req.query.key; // delete query parameter to avoid interference with later validation } else { resolve(null); diff --git a/src/index.ts b/src/index.ts index d274b89..9af77cf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -48,7 +48,7 @@ app.use(require('./helpers/authorize')); // handle authentication // redirect /api routes for Angular proxy in development if (process.env.NODE_ENV !== 'production') { - app.use('/api/:url', (req, res) => { + app.use('/api/:url([^]+)', (req, res) => { req.url = '/' + req.params.url; app.handle(req, res); }); diff --git a/src/routes/sample.spec.ts b/src/routes/sample.spec.ts index 97b9eb3..ee4b07e 100644 --- a/src/routes/sample.spec.ts +++ b/src/routes/sample.spec.ts @@ -71,6 +71,40 @@ describe('/sample', () => { done(); }); }); + it('allows filtering by state', done => { + TestHelper.request(server, done, { + method: 'get', + url: '/samples?status=new', + 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.filter(e => e.status ===globals.status.new).length); + should(res.body).matchEach(sample => { + should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id'); + should(sample).have.property('_id').be.type('string'); + should(sample).have.property('number').be.type('string'); + should(sample).have.property('type').be.type('string'); + should(sample).have.property('color').be.type('string'); + should(sample).have.property('batch').be.type('string'); + should(sample).have.property('condition').be.type('object'); + should(sample).have.property('material_id').be.type('string'); + should(sample).have.property('note_id'); + should(sample).have.property('user_id').be.type('string'); + }); + done(); + }); + }); + it('rejects an invalid state name', done => { + TestHelper.request(server, done, { + method: 'get', + url: '/samples?status=xxx', + auth: {basic: 'janedoe'}, + httpStatus: 400, + res: {status: 'Invalid body format', details: '"status" must be one of [validated, new, all]'} + }); + }); it('rejects unauthorized requests', done => { TestHelper.request(server, done, { method: 'get', diff --git a/src/routes/sample.ts b/src/routes/sample.ts index 3966c9b..487711b 100644 --- a/src/routes/sample.ts +++ b/src/routes/sample.ts @@ -22,7 +22,24 @@ const router = express.Router(); router.get('/samples', (req, res, next) => { if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'all')) return; - SampleModel.find({status: globals.status.validated}).lean().exec((err, data) => { + const {error, value: filters} = SampleValidate.query(req.query); + if (error) return res400(error, res); + + let conditions; + + if (filters.hasOwnProperty('status')) { + if(filters.status === 'all') { + conditions = {$or: [{status: globals.status.validated}, {status: globals.status.new}]} + } + else { + conditions = {status: globals.status[filters.status]}; + } + } + else { // default + conditions = {status: globals.status.validated}; + } + + SampleModel.find(conditions).lean().exec((err, data) => { if (err) return next(err); res.json(_.compact(data.map(e => SampleValidate.output(e)))); // validate all and filter null values from validation errors }) diff --git a/src/routes/validate/sample.ts b/src/routes/validate/sample.ts index 58c33ba..616e060 100644 --- a/src/routes/validate/sample.ts +++ b/src/routes/validate/sample.ts @@ -118,4 +118,10 @@ export default class SampleValidate { const {value, error} = Joi.object(joiObject).validate(data, {stripUnknown: true}); return error !== undefined? null : value; } + + static query (data) { + return Joi.object({ + status: Joi.string().valid('validated', 'new', 'all') + }).validate(data); + } } \ No newline at end of file