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:
commit
f3f3c021bc
@ -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