model routes
This commit is contained in:
parent
d363064dba
commit
08a9a12372
@ -37,6 +37,9 @@
|
||||
<w>lati</w>
|
||||
<w>lyucy</w>
|
||||
<w>materialnumber</w>
|
||||
<w>modela</w>
|
||||
<w>modelb</w>
|
||||
<w>modelx</w>
|
||||
<w>nvmrc</w>
|
||||
<w>oldpass</w>
|
||||
<w>opblock</w>
|
||||
|
@ -2,7 +2,7 @@
|
||||
parameters:
|
||||
- $ref: 'api.yaml#/components/parameters/Name'
|
||||
get:
|
||||
summary: TODO get model data by name
|
||||
summary: get model data by name
|
||||
description: 'Auth: all, levels: dev, admin'
|
||||
tags:
|
||||
- /model
|
||||
@ -22,14 +22,14 @@
|
||||
$ref: 'api.yaml#/components/responses/404'
|
||||
500:
|
||||
$ref: 'api.yaml#/components/responses/500'
|
||||
put:
|
||||
summary: TODO add/replace model data by name
|
||||
post:
|
||||
summary: add/replace model data by name
|
||||
description: 'Auth: all, levels: dev, admin'
|
||||
tags:
|
||||
- /model
|
||||
requestBody:
|
||||
required: true
|
||||
description: binary model data
|
||||
description: binary model data, Content-Type header must be set to application/octet-stream
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
@ -38,18 +38,14 @@
|
||||
responses:
|
||||
200:
|
||||
$ref: 'api.yaml#/components/responses/Ok'
|
||||
400:
|
||||
$ref: 'api.yaml#/components/responses/400'
|
||||
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'
|
||||
delete:
|
||||
summary: TODO delete model data
|
||||
summary: delete model data
|
||||
description: 'Auth: basic, levels: dev, admin'
|
||||
tags:
|
||||
- /model
|
||||
|
@ -111,9 +111,10 @@ if (process.env.NODE_ENV !== 'production') {
|
||||
app.use('/', require('./routes/root'));
|
||||
app.use('/', require('./routes/sample'));
|
||||
app.use('/', require('./routes/material'));
|
||||
app.use('/', require('./routes/template'));
|
||||
app.use('/', require('./routes/user'));
|
||||
app.use('/', require('./routes/measurement'));
|
||||
app.use('/', require('./routes/template'));
|
||||
app.use('/', require('./routes/model'));
|
||||
app.use('/', require('./routes/user'));
|
||||
|
||||
// static files
|
||||
app.use('/static', express.static('static'));
|
||||
|
8
src/models/model.ts
Normal file
8
src/models/model.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import mongoose from 'mongoose';
|
||||
|
||||
const ModelSchema = new mongoose.Schema({
|
||||
name: {type: String, index: {unique: true}},
|
||||
data: Buffer
|
||||
});
|
||||
|
||||
export default mongoose.model<any, mongoose.Model<any, any>>('model', ModelSchema);
|
197
src/routes/model.spec.ts
Normal file
197
src/routes/model.spec.ts
Normal file
@ -0,0 +1,197 @@
|
||||
import should from 'should/as-function';
|
||||
import ModelModel from '../models/model';
|
||||
import TestHelper from "../test/helper";
|
||||
|
||||
|
||||
describe('/model', () => {
|
||||
let server;
|
||||
before(done => TestHelper.before(done));
|
||||
beforeEach(done => server = TestHelper.beforeEach(server, done));
|
||||
afterEach(done => TestHelper.afterEach(server, done));
|
||||
after(done => TestHelper.after(done));
|
||||
|
||||
describe('GET /model/{name}', (() => {
|
||||
it('returns the binary data', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'get',
|
||||
url: '/model/modela',
|
||||
auth: {basic: 'admin'},
|
||||
httpStatus: 200,
|
||||
contentType: 'application/octet-stream; charset=utf-8',
|
||||
}).end((err, res) => {
|
||||
if (err) return done (err);
|
||||
should(res.body.toString()).be.eql('binary data');
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('returns the binary data for an API key', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'get',
|
||||
url: '/model/modela',
|
||||
auth: {key: 'admin'},
|
||||
httpStatus: 200,
|
||||
contentType: 'application/octet-stream; charset=utf-8',
|
||||
}).end((err, res) => {
|
||||
if (err) return done (err);
|
||||
should(res.body.toString()).be.eql('binary data');
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('returns 404 for an unknown name', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'get',
|
||||
url: '/model/modelx',
|
||||
auth: {basic: 'admin'},
|
||||
httpStatus: 404
|
||||
})
|
||||
});
|
||||
it('rejects requests from a write user', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'get',
|
||||
url: '/model/modela',
|
||||
auth: {basic: 'janedoe'},
|
||||
httpStatus: 403
|
||||
})
|
||||
});
|
||||
it('rejects unauthorized requests', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'get',
|
||||
url: '/model/modela',
|
||||
httpStatus: 401
|
||||
})
|
||||
});
|
||||
}));
|
||||
|
||||
describe('POST /model/{name}', () => {
|
||||
it('stores the data', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'post',
|
||||
url: '/model/modelb',
|
||||
auth: {basic: 'admin'},
|
||||
httpStatus: 200,
|
||||
reqContentType: 'application/octet-stream',
|
||||
req: 'another binary data'
|
||||
}).end((err, res) => {
|
||||
if (err) return done (err);
|
||||
should(res.body).be.eql({status: 'OK'});
|
||||
ModelModel.find({name: 'modelb'}).lean().exec((err, data) => {
|
||||
if (err) return done (err);
|
||||
should(data).have.lengthOf(1);
|
||||
should(data[0]).have.only.keys('_id', 'name', 'data', '__v');
|
||||
should(data[0]).have.property('name', 'modelb');
|
||||
should(data[0].data.buffer.toString()).be.eql('another binary data');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
it('stores the data with an API key', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'post',
|
||||
url: '/model/modelb',
|
||||
auth: {key: 'admin'},
|
||||
httpStatus: 200,
|
||||
reqContentType: 'application/octet-stream',
|
||||
req: 'another binary data'
|
||||
}).end((err, res) => {
|
||||
if (err) return done (err);
|
||||
should(res.body).be.eql({status: 'OK'});
|
||||
ModelModel.find({name: 'modelb'}).lean().exec((err, data) => {
|
||||
if (err) return done (err);
|
||||
should(data).have.lengthOf(1);
|
||||
should(data[0]).have.only.keys('_id', 'name', 'data', '__v');
|
||||
should(data[0]).have.property('name', 'modelb');
|
||||
should(data[0].data.buffer.toString()).be.eql('another binary data');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
it('overwrites existing data', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'post',
|
||||
url: '/model/modela',
|
||||
auth: {basic: 'admin'},
|
||||
httpStatus: 200,
|
||||
reqContentType: 'application/octet-stream',
|
||||
req: 'another binary data'
|
||||
}).end((err, res) => {
|
||||
if (err) return done (err);
|
||||
should(res.body).be.eql({status: 'OK'});
|
||||
ModelModel.find({name: 'modela'}).lean().exec((err, data) => {
|
||||
if (err) return done (err);
|
||||
should(data).have.lengthOf(1);
|
||||
should(data[0]).have.only.keys('_id', 'name', 'data', '__v');
|
||||
should(data[0]).have.property('name', 'modela');
|
||||
should(data[0].data.buffer.toString()).be.eql('another binary data');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
it('rejects requests from a write user', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'post',
|
||||
url: '/model/modelb',
|
||||
auth: {basic: 'janedoe'},
|
||||
httpStatus: 403,
|
||||
req: 'another binary data'
|
||||
});
|
||||
});
|
||||
it('rejects unauthorized requests', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'post',
|
||||
url: '/model/modelb',
|
||||
httpStatus: 401,
|
||||
req: 'another binary data'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('DELETE /model/{name}', () => {
|
||||
it('deletes the data', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'delete',
|
||||
url: '/model/modela',
|
||||
auth: {basic: 'admin'},
|
||||
httpStatus: 200
|
||||
}).end((err, res) => {
|
||||
if (err) return done(err);
|
||||
should(res.body).be.eql({status: 'OK'});
|
||||
ModelModel.find({name: 'modela'}).lean().exec((err, data) => {
|
||||
if (err) return done(err);
|
||||
should(data).have.lengthOf(0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
it('returns 404 for an unknown name', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'delete',
|
||||
url: '/model/modelx',
|
||||
auth: {basic: 'admin'},
|
||||
httpStatus: 404
|
||||
});
|
||||
});
|
||||
it('rejects an API key', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'delete',
|
||||
url: '/model/modela',
|
||||
auth: {key: 'admin'},
|
||||
httpStatus: 401
|
||||
});
|
||||
});
|
||||
it('rejects a write user', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'delete',
|
||||
url: '/model/modela',
|
||||
auth: {basic: 'janedoe'},
|
||||
httpStatus: 403
|
||||
});
|
||||
});
|
||||
it('rejects an unauthorized request', done => {
|
||||
TestHelper.request(server, done, {
|
||||
method: 'delete',
|
||||
url: '/model/modela',
|
||||
httpStatus: 401
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
47
src/routes/model.ts
Normal file
47
src/routes/model.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import express from 'express';
|
||||
import bodyParser from 'body-parser';
|
||||
|
||||
import ModelModel from '../models/model';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/model/:name', (req, res, next) => {
|
||||
if (!req.auth(res, ['dev', 'admin'], 'all')) return;
|
||||
|
||||
ModelModel.findOne({name: req.params.name}).lean().exec((err, data) => {
|
||||
if (err) return next(err);
|
||||
if (data) {
|
||||
res.set('Content-Type', 'application/octet-stream');
|
||||
res.send(data.data.buffer);
|
||||
}
|
||||
else {
|
||||
res.status(404).json({status: 'Not found'});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/model/:name', bodyParser.raw({limit: '500kb'}), (req, res, next) => {
|
||||
if (!req.auth(res, ['dev', 'admin'], 'all')) return;
|
||||
|
||||
ModelModel.replaceOne({name: req.params.name}, {name: req.params.name, data: req.body}).setOptions({upsert: true})
|
||||
.lean().exec(err => {
|
||||
if (err) return next(err);
|
||||
res.json({status: 'OK'});
|
||||
});
|
||||
});
|
||||
|
||||
router.delete('/model/:name', (req, res, next) => {
|
||||
if (!req.auth(res, ['dev', 'admin'], 'basic')) return;
|
||||
|
||||
ModelModel.findOneAndDelete({name: req.params.name}).lean().exec((err, data) => {
|
||||
if (err) return next(err);
|
||||
if (data) {
|
||||
res.json({status: 'OK'});
|
||||
}
|
||||
else {
|
||||
res.status(404).json({status: 'Not found'});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = router;
|
@ -678,6 +678,13 @@
|
||||
"__v": 0
|
||||
}
|
||||
],
|
||||
"models": [
|
||||
{
|
||||
"_id": {"$oid":"140000000000000000000001"},
|
||||
"name": "modela",
|
||||
"data": {"buffer": "binary data"}
|
||||
}
|
||||
],
|
||||
"users": [
|
||||
{
|
||||
"_id": {"$oid":"000000000000000000000001"},
|
||||
|
@ -69,6 +69,9 @@ export default class TestHelper {
|
||||
if (options.hasOwnProperty('req')) { // request body
|
||||
st = st.send(options.req);
|
||||
}
|
||||
if (options.hasOwnProperty('reqContentType')) { // request body
|
||||
st = st.set('Content-Type', options.reqContentType);
|
||||
}
|
||||
if (options.hasOwnProperty('auth') && options.auth.hasOwnProperty('basic')) { // resolve basic auth
|
||||
if (this.auth.hasOwnProperty(options.auth.basic)) {
|
||||
st = st.auth(options.auth.basic, this.auth[options.auth.basic].pass)
|
||||
|
Reference in New Issue
Block a user