Merge pull request #41 in ~VLE2FE/definma-api from develop to master
* commit 'c891933d11976a6efd2a6593168d58f1933b519e': implemented /model/files updated to new model format
This commit is contained in:
		| @@ -73,6 +73,32 @@ | |||||||
|       500: |       500: | ||||||
|         $ref: 'api.yaml#/components/responses/500' |         $ref: 'api.yaml#/components/responses/500' | ||||||
|  |  | ||||||
|  | /model/files: | ||||||
|  |   get: | ||||||
|  |     summary: list all stored models | ||||||
|  |     description: 'Auth: basic, levels: dev, admin' | ||||||
|  |     tags: | ||||||
|  |       - /model | ||||||
|  |     responses: | ||||||
|  |       200: | ||||||
|  |         description: model details list | ||||||
|  |         content: | ||||||
|  |           application/json: | ||||||
|  |             schema: | ||||||
|  |               properties: | ||||||
|  |                 name: | ||||||
|  |                   type: string | ||||||
|  |                   example: model_VN_A3WG6_V1 | ||||||
|  |                 size: | ||||||
|  |                   type: number | ||||||
|  |                   example: 4177449 | ||||||
|  |       401: | ||||||
|  |         $ref: 'api.yaml#/components/responses/401' | ||||||
|  |       403: | ||||||
|  |         $ref: 'api.yaml#/components/responses/403' | ||||||
|  |       500: | ||||||
|  |         $ref: 'api.yaml#/components/responses/500' | ||||||
|  |  | ||||||
| /model/file/{name}: | /model/file/{name}: | ||||||
|   parameters: |   parameters: | ||||||
|     - $ref: 'api.yaml#/components/parameters/Name' |     - $ref: 'api.yaml#/components/parameters/Name' | ||||||
|   | |||||||
| @@ -26,11 +26,10 @@ describe('/model', () => { | |||||||
|           should(group).have.only.keys('group', 'models'); |           should(group).have.only.keys('group', 'models'); | ||||||
|           should(group).have.property('group').be.type('string'); |           should(group).have.property('group').be.type('string'); | ||||||
|           should(group.models).matchEach(model => { |           should(group.models).matchEach(model => { | ||||||
|             should(model).have.only.keys('_id', 'name', 'url', 'label'); |             should(model).have.only.keys('_id', 'name', 'url'); | ||||||
|             should(model).have.property('_id').be.type('string'); |             should(model).have.property('_id').be.type('string'); | ||||||
|             should(model).have.property('name').be.type('string'); |             should(model).have.property('name').be.type('string'); | ||||||
|             should(model).have.property('url').be.type('string'); |             should(model).have.property('url').be.type('string'); | ||||||
|             should(model).have.property('label').be.type('string'); |  | ||||||
|           }); |           }); | ||||||
|         }); |         }); | ||||||
|         done(); |         done(); | ||||||
| @@ -48,7 +47,7 @@ describe('/model', () => { | |||||||
|         should(res.body).matchEach(group => { |         should(res.body).matchEach(group => { | ||||||
|           should(group).have.only.keys('group', 'models'); |           should(group).have.only.keys('group', 'models'); | ||||||
|           should(group).have.property('group').be.type('string'); |           should(group).have.property('group').be.type('string'); | ||||||
|           should(group).have.property('models', [{_id: '120000000000000000000001', name: 'Model A', url: 'http://model-a.com', label: 'ml/g'}]); |           should(group).have.property('models', [{_id: '120000000000000000000001', name: 'Model A', url: 'http://model-a.com'}]); | ||||||
|         }); |         }); | ||||||
|         done(); |         done(); | ||||||
|       }); |       }); | ||||||
| @@ -77,7 +76,7 @@ describe('/model', () => { | |||||||
|         url: '/model/VN', |         url: '/model/VN', | ||||||
|         auth: {basic: 'admin'}, |         auth: {basic: 'admin'}, | ||||||
|         httpStatus: 200, |         httpStatus: 200, | ||||||
|         req: {name: 'Model C', url: 'http://model-c.com', label: 'ml/g'} |         req: {name: 'Model C', url: 'http://model-c.com'} | ||||||
|       }).end((err, res) => { |       }).end((err, res) => { | ||||||
|         if (err) return done(err); |         if (err) return done(err); | ||||||
|         should(res.body).be.eql({status: 'OK'}); |         should(res.body).be.eql({status: 'OK'}); | ||||||
| @@ -85,7 +84,6 @@ describe('/model', () => { | |||||||
|           if (err) return done(err); |           if (err) return done(err); | ||||||
|           const model = res.models.find(e => e.name === 'Model C'); |           const model = res.models.find(e => e.name === 'Model C'); | ||||||
|           should(model).have.property('url', 'http://model-c.com'); |           should(model).have.property('url', 'http://model-c.com'); | ||||||
|           should(model).have.property('label', 'ml/g'); |  | ||||||
|           done(); |           done(); | ||||||
|         }); |         }); | ||||||
|       }); |       }); | ||||||
| @@ -96,7 +94,7 @@ describe('/model', () => { | |||||||
|         url: '/model/classification', |         url: '/model/classification', | ||||||
|         auth: {basic: 'admin'}, |         auth: {basic: 'admin'}, | ||||||
|         httpStatus: 200, |         httpStatus: 200, | ||||||
|         req: {name: 'Model 0.1', url: 'http://model-0-1.com', label: 'group'} |         req: {name: 'Model 0.1', url: 'http://model-0-1.com'} | ||||||
|       }).end((err, res) => { |       }).end((err, res) => { | ||||||
|         if (err) return done(err); |         if (err) return done(err); | ||||||
|         should(res.body).be.eql({status: 'OK'}); |         should(res.body).be.eql({status: 'OK'}); | ||||||
| @@ -104,10 +102,9 @@ describe('/model', () => { | |||||||
|           if (err) return done(err); |           if (err) return done(err); | ||||||
|           should(res).have.only.keys('_id', 'group', 'models', '__v'); |           should(res).have.only.keys('_id', 'group', 'models', '__v'); | ||||||
|           should(res).have.property('group', 'classification'); |           should(res).have.property('group', 'classification'); | ||||||
|           should(res.models[0]).have.only.keys('_id', 'name', 'url', 'label'); |           should(res.models[0]).have.only.keys('_id', 'name', 'url'); | ||||||
|           should(res.models[0]).have.property('name', 'Model 0.1'); |           should(res.models[0]).have.property('name', 'Model 0.1'); | ||||||
|           should(res.models[0]).have.property('url', 'http://model-0-1.com'); |           should(res.models[0]).have.property('url', 'http://model-0-1.com'); | ||||||
|           should(res.models[0]).have.property('label', 'group'); |  | ||||||
|           done(); |           done(); | ||||||
|         }); |         }); | ||||||
|       }); |       }); | ||||||
| @@ -118,7 +115,7 @@ describe('/model', () => { | |||||||
|         url: '/model/VN', |         url: '/model/VN', | ||||||
|         auth: {basic: 'admin'}, |         auth: {basic: 'admin'}, | ||||||
|         httpStatus: 200, |         httpStatus: 200, | ||||||
|         req: {name: 'Model A', url: 'http://model-a-new.com', label: 'ml/cm3'} |         req: {name: 'Model A', url: 'http://model-a-new.com'} | ||||||
|       }).end((err, res) => { |       }).end((err, res) => { | ||||||
|         if (err) return done(err); |         if (err) return done(err); | ||||||
|         should(res.body).be.eql({status: 'OK'}); |         should(res.body).be.eql({status: 'OK'}); | ||||||
| @@ -126,26 +123,6 @@ describe('/model', () => { | |||||||
|           if (err) return done(err); |           if (err) return done(err); | ||||||
|           const model = res.models.find(e => e.name === 'Model A'); |           const model = res.models.find(e => e.name === 'Model A'); | ||||||
|           should(model).have.property('url', 'http://model-a-new.com'); |           should(model).have.property('url', 'http://model-a-new.com'); | ||||||
|           should(model).have.property('label', 'ml/cm3'); |  | ||||||
|           done(); |  | ||||||
|         }); |  | ||||||
|       }); |  | ||||||
|     }); |  | ||||||
|     it('accepts an empty label', done => { |  | ||||||
|       TestHelper.request(server, done, { |  | ||||||
|         method: 'post', |  | ||||||
|         url: '/model/VN', |  | ||||||
|         auth: {basic: 'admin'}, |  | ||||||
|         httpStatus: 200, |  | ||||||
|         req: {name: 'Model C', url: 'http://model-c.com', label: ''} |  | ||||||
|       }).end((err, res) => { |  | ||||||
|         if (err) return done(err); |  | ||||||
|         should(res.body).be.eql({status: 'OK'}); |  | ||||||
|         ModelModel.findOne({group: 'VN'}).lean().exec((err, res) => { |  | ||||||
|           if (err) return done(err); |  | ||||||
|           const model = res.models.find(e => e.name === 'Model C'); |  | ||||||
|           should(model).have.property('url', 'http://model-c.com'); |  | ||||||
|           should(model).have.property('label', ''); |  | ||||||
|           done(); |           done(); | ||||||
|         }); |         }); | ||||||
|       }); |       }); | ||||||
| @@ -156,7 +133,7 @@ describe('/model', () => { | |||||||
|         url: '/model/VN', |         url: '/model/VN', | ||||||
|         auth: {basic: 'admin'}, |         auth: {basic: 'admin'}, | ||||||
|         httpStatus: 400, |         httpStatus: 400, | ||||||
|         req: {name: '', url: 'http://model-c.com', label: 'ml/g'}, |         req: {name: '', url: 'http://model-c.com'}, | ||||||
|         res:{status: 'Invalid body format', details: '"name" is not allowed to be empty'} |         res:{status: 'Invalid body format', details: '"name" is not allowed to be empty'} | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
| @@ -166,7 +143,7 @@ describe('/model', () => { | |||||||
|         url: '/model/VN', |         url: '/model/VN', | ||||||
|         auth: {basic: 'admin'}, |         auth: {basic: 'admin'}, | ||||||
|         httpStatus: 400, |         httpStatus: 400, | ||||||
|         req: {url: 'http://model-c.com', label: 'ml/g'}, |         req: {url: 'http://model-c.com'}, | ||||||
|         res:{status: 'Invalid body format', details: '"name" is required'} |         res:{status: 'Invalid body format', details: '"name" is required'} | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
| @@ -176,7 +153,7 @@ describe('/model', () => { | |||||||
|         url: '/model/VN', |         url: '/model/VN', | ||||||
|         auth: {basic: 'admin'}, |         auth: {basic: 'admin'}, | ||||||
|         httpStatus: 400, |         httpStatus: 400, | ||||||
|         req: {name: 'Model C', url: 'model-c', label: 'ml/g'}, |         req: {name: 'Model C', url: 'model-c'}, | ||||||
|         res:{status: 'Invalid body format', details: '"url" must be a valid uri'} |         res:{status: 'Invalid body format', details: '"url" must be a valid uri'} | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
| @@ -186,7 +163,7 @@ describe('/model', () => { | |||||||
|         url: '/model/VN', |         url: '/model/VN', | ||||||
|         auth: {basic: 'admin'}, |         auth: {basic: 'admin'}, | ||||||
|         httpStatus: 400, |         httpStatus: 400, | ||||||
|         req: {name: 'Model C', label: 'ml/g'}, |         req: {name: 'Model C'}, | ||||||
|         res:{status: 'Invalid body format', details: '"url" is required'} |         res:{status: 'Invalid body format', details: '"url" is required'} | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
| @@ -196,7 +173,7 @@ describe('/model', () => { | |||||||
|         url: '/model/VN', |         url: '/model/VN', | ||||||
|         auth: {basic: 'janedoe'}, |         auth: {basic: 'janedoe'}, | ||||||
|         httpStatus: 403, |         httpStatus: 403, | ||||||
|         req: {name: 'Model C', url: 'http://model-c.com', label: 'ml/g'} |         req: {name: 'Model C', url: 'http://model-c.com'} | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
|     it('rejects an API key', done => { |     it('rejects an API key', done => { | ||||||
| @@ -205,7 +182,7 @@ describe('/model', () => { | |||||||
|         url: '/model/VN', |         url: '/model/VN', | ||||||
|         auth: {key: 'admin'}, |         auth: {key: 'admin'}, | ||||||
|         httpStatus: 401, |         httpStatus: 401, | ||||||
|         req: {name: 'Model C', url: 'http://model-c.com', label: 'ml/g'} |         req: {name: 'Model C', url: 'http://model-c.com'} | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
|     it('rejects an unauthorized request', done => { |     it('rejects an unauthorized request', done => { | ||||||
| @@ -213,7 +190,7 @@ describe('/model', () => { | |||||||
|         method: 'post', |         method: 'post', | ||||||
|         url: '/model/VN', |         url: '/model/VN', | ||||||
|         httpStatus: 401, |         httpStatus: 401, | ||||||
|         req: {name: 'Model C', url: 'http://model-c.com', label: 'ml/g'} |         req: {name: 'Model C', url: 'http://model-c.com'} | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
| @@ -232,10 +209,9 @@ describe('/model', () => { | |||||||
|           if (err) return done(err); |           if (err) return done(err); | ||||||
|           should(res).have.only.keys('_id', 'group', 'models'); |           should(res).have.only.keys('_id', 'group', 'models'); | ||||||
|           should(res).have.property('group', 'VN'); |           should(res).have.property('group', 'VN'); | ||||||
|           should(res.models[0]).have.only.keys('_id', 'name', 'url', 'label'); |           should(res.models[0]).have.only.keys('_id', 'name', 'url'); | ||||||
|           should(res.models[0]).have.property('name', 'Model B'); |           should(res.models[0]).have.property('name', 'Model B'); | ||||||
|           should(res.models[0]).have.property('url', 'http://model-b.com'); |           should(res.models[0]).have.property('url', 'http://model-b.com'); | ||||||
|           should(res.models[0]).have.property('label', 'ml/g'); |  | ||||||
|           done(); |           done(); | ||||||
|         }); |         }); | ||||||
|       }); |       }); | ||||||
| @@ -297,6 +273,32 @@ describe('/model', () => { | |||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|  |   describe('GET /model/files', () => { | ||||||
|  |     it('rejects a write user', done => { | ||||||
|  |       TestHelper.request(server, done, { | ||||||
|  |         method: 'get', | ||||||
|  |         url: '/model/files', | ||||||
|  |         auth: {basic: 'janedoe'}, | ||||||
|  |         httpStatus: 403, | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |     it('rejects an API key', done => { | ||||||
|  |       TestHelper.request(server, done, { | ||||||
|  |         method: 'get', | ||||||
|  |         url: '/model/files', | ||||||
|  |         auth: {key: 'admin'}, | ||||||
|  |         httpStatus: 401, | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |     it('rejects an unauthorized request', done => { | ||||||
|  |       TestHelper.request(server, done, { | ||||||
|  |         method: 'get', | ||||||
|  |         url: '/model/files', | ||||||
|  |         httpStatus: 401, | ||||||
|  |       }); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|   describe('GET /model/file/{name}', (() => { |   describe('GET /model/file/{name}', (() => { | ||||||
|     it('returns the binary data', done => { |     it('returns the binary data', done => { | ||||||
|       TestHelper.request(server, done, { |       TestHelper.request(server, done, { | ||||||
|   | |||||||
| @@ -33,7 +33,6 @@ router.post('/model/:group', (req, res, next) => { | |||||||
|   if (!req.auth(res, ['dev', 'admin'], 'basic')) return; |   if (!req.auth(res, ['dev', 'admin'], 'basic')) return; | ||||||
|  |  | ||||||
|   const {error, value: model} = ModelValidate.input(req.body); |   const {error, value: model} = ModelValidate.input(req.body); | ||||||
|   console.log(error); |  | ||||||
|   if (error) return res400(error, res); |   if (error) return res400(error, res); | ||||||
|  |  | ||||||
|   ModelModel.findOne({group: req.params.group}).lean().exec((err, data) => { |   ModelModel.findOne({group: req.params.group}).lean().exec((err, data) => { | ||||||
| @@ -96,6 +95,15 @@ router.delete('/model/:group(((?!file)[^\\/]+?))/:name', (req, res, next) => { | |||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | router.get('/model/files', (req, res, next) => { | ||||||
|  |   if (!req.auth(res, ['dev', 'admin'], 'basic')) return; | ||||||
|  |  | ||||||
|  |   ModelFileModel.find().exec((err, data) => { | ||||||
|  |     if (err) return next(err); | ||||||
|  |     res.json(data.map(e => ModelValidate.fileOutput(e))); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
|  |  | ||||||
| router.get('/model/file/:name', (req, res, next) => { | router.get('/model/file/:name', (req, res, next) => { | ||||||
|   if (!req.auth(res, ['dev', 'admin'], 'all')) return; |   if (!req.auth(res, ['dev', 'admin'], 'all')) return; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -16,11 +16,6 @@ export default class ModelValidate {  // validate input for model | |||||||
|         url: Joi.string() |         url: Joi.string() | ||||||
|           .uri() |           .uri() | ||||||
|           .max(512) |           .max(512) | ||||||
|           .required(), |  | ||||||
|  |  | ||||||
|         label: Joi.string() |  | ||||||
|           .allow('') |  | ||||||
|           .max(128) |  | ||||||
|           .required() |           .required() | ||||||
|       }) |       }) | ||||||
|   }; |   }; | ||||||
| @@ -37,4 +32,11 @@ export default class ModelValidate {  // validate input for model | |||||||
|     }).validate(data, {stripUnknown: true}); |     }).validate(data, {stripUnknown: true}); | ||||||
|     return error !== undefined? null : value; |     return error !== undefined? null : value; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   static fileOutput (data) { | ||||||
|  |     return { | ||||||
|  |       name: data.name, | ||||||
|  |       size: data.data.length | ||||||
|  |     } | ||||||
|  |   } | ||||||
| } | } | ||||||
| @@ -736,14 +736,12 @@ | |||||||
|           { |           { | ||||||
|             "_id": {"$oid":"120000000000000000000001"}, |             "_id": {"$oid":"120000000000000000000001"}, | ||||||
|             "name": "Model A", |             "name": "Model A", | ||||||
|             "url": "http://model-a.com", |             "url": "http://model-a.com" | ||||||
|             "label": "ml/g" |  | ||||||
|           }, |           }, | ||||||
|           { |           { | ||||||
|             "_id": {"$oid":"120000000000000000000002"}, |             "_id": {"$oid":"120000000000000000000002"}, | ||||||
|             "name": "Model B", |             "name": "Model B", | ||||||
|             "url": "http://model-b.com", |             "url": "http://model-b.com" | ||||||
|             "label": "ml/g" |  | ||||||
|           } |           } | ||||||
|         ] |         ] | ||||||
|       }, |       }, | ||||||
| @@ -753,8 +751,7 @@ | |||||||
|           { |           { | ||||||
|             "_id": {"$oid":"120000000000000000000003"}, |             "_id": {"$oid":"120000000000000000000003"}, | ||||||
|             "name": "Model 1", |             "name": "Model 1", | ||||||
|             "url": "http://model-1.com", |             "url": "http://model-1.com" | ||||||
|             "label": "weight %" |  | ||||||
|           } |           } | ||||||
|         ] |         ] | ||||||
|       } |       } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Veit Lukas (PEA4-Fe)
					Veit Lukas (PEA4-Fe)