Commit dc3358ce authored by A. Koch's avatar A. Koch

update generic api module, improve error handling a bit

parent 4eb781a6
...@@ -136,7 +136,7 @@ ...@@ -136,7 +136,7 @@
}, },
"@types/events": { "@types/events": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", "resolved": "http://registry.npmjs.org/@types/events/-/events-1.2.0.tgz",
"integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==" "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA=="
}, },
"@types/express": { "@types/express": {
...@@ -182,9 +182,9 @@ ...@@ -182,9 +182,9 @@
"integrity": "sha512-A2TAGbTFdBw9azHbpVd+/FkdW2T6msN1uct1O9bH3vTerEHKZhTXJUQXy+hNq1B0RagfU8U+KBdqiZpxjhOUQA==" "integrity": "sha512-A2TAGbTFdBw9azHbpVd+/FkdW2T6msN1uct1O9bH3vTerEHKZhTXJUQXy+hNq1B0RagfU8U+KBdqiZpxjhOUQA=="
}, },
"@types/node": { "@types/node": {
"version": "10.11.6", "version": "10.12.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.11.6.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.0.tgz",
"integrity": "sha512-fnA7yvqg3oKQDb3skBif9w5RRKVKAaeKeNuLzZL37XcSiWL4IoSXQnnbchR3UnBu2EMLHBip7ZVEkqoIVBP8QQ==" "integrity": "sha512-3TUHC3jsBAB7qVRGxT6lWyYo2v96BMmD2PTcl47H25Lu7UXtFH/2qqmKiVrnel6Ne//0TFYf6uvNX+HW2FRkLQ=="
}, },
"@types/range-parser": { "@types/range-parser": {
"version": "1.2.2", "version": "1.2.2",
...@@ -1058,7 +1058,7 @@ ...@@ -1058,7 +1058,7 @@
}, },
"enabled": { "enabled": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", "resolved": "http://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz",
"integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=",
"requires": { "requires": {
"env-variable": "0.0.x" "env-variable": "0.0.x"
...@@ -1085,7 +1085,7 @@ ...@@ -1085,7 +1085,7 @@
}, },
"es6-promise": { "es6-promise": {
"version": "3.2.1", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", "resolved": "http://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
"integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q=" "integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q="
}, },
"escape-string-regexp": { "escape-string-regexp": {
...@@ -1344,7 +1344,7 @@ ...@@ -1344,7 +1344,7 @@
"dependencies": { "dependencies": {
"async": { "async": {
"version": "1.5.2", "version": "1.5.2",
"resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz",
"integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
} }
} }
...@@ -1636,7 +1636,7 @@ ...@@ -1636,7 +1636,7 @@
}, },
"http-errors": { "http-errors": {
"version": "1.6.3", "version": "1.6.3",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
"integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
"requires": { "requires": {
"depd": "~1.1.2", "depd": "~1.1.2",
...@@ -2230,9 +2230,9 @@ ...@@ -2230,9 +2230,9 @@
} }
}, },
"mbjs-generic-api": { "mbjs-generic-api": {
"version": "0.0.16", "version": "0.0.19",
"resolved": "https://registry.npmjs.org/mbjs-generic-api/-/mbjs-generic-api-0.0.16.tgz", "resolved": "https://registry.npmjs.org/mbjs-generic-api/-/mbjs-generic-api-0.0.19.tgz",
"integrity": "sha512-dwA1tRE8eDN+HhY+BNZuC72lZYF1gEHL5VEhTK/NXnpymbVqUxBlKJ020gb+OLm6d3AReWiqFn62VC5Z7XxnoQ==", "integrity": "sha512-3Gqg8adzgzRANAeItgXSrl9PXPh+fV3H0zcXOiDaHbtEPPkNX0mAcguZA3oUME1FnRvTz4HMNnQ5KF6kp9an0A==",
"requires": { "requires": {
"@polka/send-type": "^0.4.0", "@polka/send-type": "^0.4.0",
"acl": "^0.4.11", "acl": "^0.4.11",
......
...@@ -38,14 +38,6 @@ module.exports.setupArchives = (api, mapService, annotationService) => { ...@@ -38,14 +38,6 @@ module.exports.setupArchives = (api, mapService, annotationService) => {
}) })
}) })
}) })
api.app.get('/archives/maps/:id', async (req, res) => {
const filename = `map_archive_${req.params.id}.zip`
const filePath = path.join(os.tmpdir(), filename)
const file = fs.createReadStream(filePath)
res.setHeader('Content-Type', 'application/zip')
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`)
file.pipe(res)
})
api.app.post('/archives/maps/upload', async function (req, res) { api.app.post('/archives/maps/upload', async function (req, res) {
upload.single('file')(req, res, async () => { upload.single('file')(req, res, async () => {
const results = await exports.readArchive(req.file.path) const results = await exports.readArchive(req.file.path)
...@@ -126,21 +118,24 @@ module.exports.setupArchives = (api, mapService, annotationService) => { ...@@ -126,21 +118,24 @@ module.exports.setupArchives = (api, mapService, annotationService) => {
}) })
} }
module.exports.createArchive = async (data) => { module.exports.createArchive = async data => {
Assert.isType(data.map, 'object', 'data.map must be object') Assert.isType(data.map, 'object', 'data.map must be object')
Assert.ok(Array.isArray(data.annotations), 'data.annotations must be array') Assert.ok(Array.isArray(data.annotations), 'data.annotations must be array')
const const dir = path.join(os.tmpdir(), `archive_${ObjectUtil.slug(data.map.title)}_${data.map.uuid}`)
dir = path.join(os.tmpdir(), `archive_${ObjectUtil.slug(data.map.title)}_${data.map.uuid}`),
archive = new yazl.ZipFile()
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
rimraf(dir, err => { rimraf(dir, err => {
if (err) return reject(err) if (err) {
api.captureException(err)
return reject(err)
}
resolve() resolve()
}) })
}) })
const archive = new yazl.ZipFile()
await fs.mkdir(dir) await fs.mkdir(dir)
await fs.mkdir(path.join(dir, 'maps')) await fs.mkdir(path.join(dir, 'maps'))
await fs.mkdir(path.join(dir, 'annotations')) await fs.mkdir(path.join(dir, 'annotations'))
......
...@@ -13,12 +13,20 @@ class Profiles extends TinyEmitter { ...@@ -13,12 +13,20 @@ class Profiles extends TinyEmitter {
this._client = new MongoDB(ObjectUtil.merge({ name: 'profiles', logger: console }, config.get('profiles.mongodb')), 'uuid') this._client = new MongoDB(ObjectUtil.merge({ name: 'profiles', logger: console }, config.get('profiles.mongodb')), 'uuid')
const _this = this const _this = this
this._api = api
api.app.get('/profiles/:id', (req, res) => _this.getHandler(req, res)) api.app.get('/profiles/:id', (req, res) => _this.getHandler(req, res))
api.app.post('/profiles', async (req, res) => { api.app.post('/profiles', async (req, res) => {
req.body.uuid = ObjectUtil.uuid4() req.body.uuid = ObjectUtil.uuid4()
const result = await this.client.create(req.body) let result
try {
result = await this.client.create(req.body)
}
catch (err) {
api.captureException(err)
return _this._errorResponse(res, 500)
}
if (result) { if (result) {
return _this._response(req, res, result) return _this._response(req, res, result)
} }
...@@ -30,7 +38,14 @@ class Profiles extends TinyEmitter { ...@@ -30,7 +38,14 @@ class Profiles extends TinyEmitter {
req.body.uuid = undefined req.body.uuid = undefined
req.body.user = undefined req.body.user = undefined
const data = req.body const data = req.body
let results = await this.client.find({ user: req.params.id }, req.params) let results
try {
results = await this.client.find({ user: req.params.id }, req.params)
}
catch (err) {
api.captureException(err)
return _this._errorResponse(res, 500)
}
if (results.length) { if (results.length) {
data.uuid = results[0].uuid data.uuid = results[0].uuid
results = await this.client.update(data.uuid, data, req.params) results = await this.client.update(data.uuid, data, req.params)
...@@ -40,7 +55,14 @@ class Profiles extends TinyEmitter { ...@@ -40,7 +55,14 @@ class Profiles extends TinyEmitter {
}) })
api.app.patch('/profiles/:id', async (req, res) => { api.app.patch('/profiles/:id', async (req, res) => {
let results = await this.client.find({ user: req.params.id }, req.params) let results
try {
results = await this.client.find({user: req.params.id}, req.params)
}
catch (err) {
api.captureException(err)
return _this._errorResponse(res, 500)
}
req.body._id = undefined req.body._id = undefined
req.body.uuid = undefined req.body.uuid = undefined
req.body.user = undefined req.body.user = undefined
...@@ -50,16 +72,35 @@ class Profiles extends TinyEmitter { ...@@ -50,16 +72,35 @@ class Profiles extends TinyEmitter {
}) })
if (results.length) { if (results.length) {
results = ObjectUtil.merge(results[0], copy) results = ObjectUtil.merge(results[0], copy)
await this.client.update(results.uuid, results, req.params) try {
await this.client.update(results.uuid, results, req.params)
}
catch (err) {
api.captureException(err)
return _this._errorResponse(res, 500)
}
return _this._response(req, res, results) return _this._response(req, res, results)
} }
send(res, 404) send(res, 404)
}) })
api.app.delete('/profiles/:id', async (req, res) => { api.app.delete('/profiles/:id', async (req, res) => {
let results = await this.client.find({ user: req.params.id }, req.params) let results
try {
results = await this.client.find({user: req.params.id}, req.params)
}
catch (err) {
api.captureException(err)
return _this._errorResponse(res, 500)
}
if (results.length) { if (results.length) {
results = await this.client.remove(results[0].uuid, req.params) try {
results = await this.client.remove(results[0].uuid, req.params)
}
catch (err) {
api.captureException(err)
return _this._errorResponse(res, 500)
}
if (results) { if (results) {
return _this._response(req, res, results) return _this._response(req, res, results)
} }
...@@ -69,7 +110,14 @@ class Profiles extends TinyEmitter { ...@@ -69,7 +110,14 @@ class Profiles extends TinyEmitter {
} }
async getHandler (req, res) { async getHandler (req, res) {
const results = await this._client.find({ user: req.params.id }, req.params) let results
try {
results = await this._client.find({user: req.params.id}, req.params)
}
catch (err) {
this._api.captureException(err)
return this._errorResponse(res, 500)
}
if (results.length) { if (results.length) {
return this._response(req, res, results[0]) return this._response(req, res, results[0])
} }
......
...@@ -12,6 +12,7 @@ class Service extends TinyEmitter { ...@@ -12,6 +12,7 @@ class Service extends TinyEmitter {
const _this = this const _this = this
this._name = name this._name = name
this._captureException = api.captureException
this._acl = api.acl this._acl = api.acl
this._logger = api.logger this._logger = api.logger
this._Model = model this._Model = model
...@@ -27,7 +28,14 @@ class Service extends TinyEmitter { ...@@ -27,7 +28,14 @@ class Service extends TinyEmitter {
} }
async findHandler (req, res) { async findHandler (req, res) {
let results = await this._client.find(JSON.parse(req.query.query || '{}'), req.params) let results
try {
results = await this._client.find(JSON.parse(req.query.query || '{}'), req.params)
}
catch (err) {
this._captureException(err)
return this._errorResponse(res, 500)
}
const userId = req.user ? req.user.uuid : 'anon' const userId = req.user ? req.user.uuid : 'anon'
const roles = req.user ? req.user.profile.roles : ['public'] const roles = req.user ? req.user.profile.roles : ['public']
const items = [] const items = []
...@@ -39,7 +47,7 @@ class Service extends TinyEmitter { ...@@ -39,7 +47,7 @@ class Service extends TinyEmitter {
allowed = await this._acl.areAnyRolesAllowed(roles, entry.uuid, ['get']) allowed = await this._acl.areAnyRolesAllowed(roles, entry.uuid, ['get'])
} }
catch (err) { catch (err) {
this._logger.error(`ACL error: ${err.message}`) this._captureException(err)
} }
} }
if (allowed) items.push(entry) if (allowed) items.push(entry)
...@@ -48,7 +56,14 @@ class Service extends TinyEmitter { ...@@ -48,7 +56,14 @@ class Service extends TinyEmitter {
} }
async getHandler (req, res) { async getHandler (req, res) {
const result = await this.client.get(req.params.id, req.params) let result
try {
result = await this.client.get(req.params.id, req.params)
}
catch (err) {
this._captureException(err)
return this._errorResponse(res, 500)
}
const roles = req.user ? req.user.profile.roles : ['public'] const roles = req.user ? req.user.profile.roles : ['public']
if (result) { if (result) {
let allowed = false let allowed = false
...@@ -58,7 +73,7 @@ class Service extends TinyEmitter { ...@@ -58,7 +73,7 @@ class Service extends TinyEmitter {
allowed = await this._acl.areAnyRolesAllowed(['public'].concat(roles), result.uuid, ['get']) allowed = await this._acl.areAnyRolesAllowed(['public'].concat(roles), result.uuid, ['get'])
} }
catch (err) { catch (err) {
this._logger.error(`ACL error: ${err.message}`) this._captureException(err)
} }
} }
if (allowed) { if (allowed) {
...@@ -75,52 +90,104 @@ class Service extends TinyEmitter { ...@@ -75,52 +90,104 @@ class Service extends TinyEmitter {
ctx = this, ctx = this,
data = req.body data = req.body
if (Array.isArray(data)) { if (Array.isArray(data)) {
const results = await Promise.all(data.map(entry => { try {
return ctx.create(entry, req.params) const results = await Promise.all(data.map(entry => {
})) return ctx.create(entry, req.params)
return this._response(req, res, results) }))
return this._response(req, res, results)
}
catch (err) {
this._captureException(err)
return this._errorResponse(res, 500)
}
} }
// TODO: allow for full array inserts instead just single requests // TODO: allow for full array inserts instead just single requests
const instance = new this.ModelConstructor(data), // TODO: throw bad request error / validate input
result = await this.client.create(instance, req.params) try {
instance.populate(result) const instance = new this.ModelConstructor(data),
return this._response(req, res, instance) result = await this.client.create(instance, req.params)
instance.populate(result)
this._response(req, res, instance)
}
catch (err) {
this._captureException(err)
this._errorResponse(res, 500)
}
} }
async putHandler (req, res) { async putHandler (req, res) {
const data = req.body const data = req.body
let result = await this.client.get(req.params.id) let result
try {
result = await this.client.get(req.params.id)
}
catch (err) {
this._captureException(err)
return this._errorResponse(res, 500)
}
if (result) { if (result) {
// TODO: transactions anyone?! // TODO: transactions anyone?!
if (req.user.uuid !== result.author.id) return this._errorResponse(res, 403) if (req.user.uuid !== result.author.id) return this._errorResponse(res, 403)
data.uuid = req.params.id data.uuid = req.params.id
let instance = new this.ModelConstructor(data, req.params.id) try {
await this.client.update(req.params.id, instance, req.params) let instance = new this.ModelConstructor(data, req.params.id)
return this._response(req, res, instance) await this.client.update(req.params.id, instance, req.params)
return this._response(req, res, instance)
}
catch (err) {
this._captureException(err)
this._errorResponse(res, 500)
}
} }
else return this._errorResponse(res, 404) else return this._errorResponse(res, 404)
} }
async patchHandler (req, res) { async patchHandler (req, res) {
const data = req.body const data = req.body
let existing = await this.client.get(req.params.id) let existing
try {
existing = await this.client.get(req.params.id)
}
catch (err) {
this._captureException(err)
return this._errorResponse(res, 500)
}
if (existing) { if (existing) {
if (req.user.uuid !== existing.author.id) return this._errorResponse(res, 403) if (req.user.uuid !== existing.author.id) return this._errorResponse(res, 403)
let instance = new this.ModelConstructor(existing, req.params.id) try {
instance.populate(ObjectUtil.merge(instance.toObject(), data)) let instance = new this.ModelConstructor(existing, req.params.id)
await this.client.update(req.params.id, instance, req.params) instance.populate(ObjectUtil.merge(instance.toObject(), data))
return this._response(req, res, instance) await this.client.update(req.params.id, instance, req.params)
return this._response(req, res, instance)
}
catch (err) {
this._captureException(err)
this._errorResponse(res, 500)
}
} }
else return this._errorResponse(res, 404) else return this._errorResponse(res, 404)
} }
async deleteHandler (req, res) { async deleteHandler (req, res) {
let existing = await this.client.get(req.params.id) let existing
try {
existing = await this.client.get(req.params.id)
}
catch (err) {
this._captureException(err)
return this._errorResponse(res, 500)
}
if (existing) { if (existing) {
if (req.user.uuid !== existing.author.id) return this._errorResponse(res, 403) if (req.user.uuid !== existing.author.id) return this._errorResponse(res, 403)
const result = await this.client.remove(req.params.id, req.params) try {
if (result) { const result = await this.client.remove(req.params.id, req.params)
return this._response(req, res, existing) if (result) {
return this._response(req, res, existing)
}
}
catch (err) {
this._captureException(err)
this._errorResponse(res, 500)
} }
} }
else return this._errorResponse(res, 404) else return this._errorResponse(res, 404)
......
...@@ -9,7 +9,13 @@ const setup = async function (api, profileService) { ...@@ -9,7 +9,13 @@ const setup = async function (api, profileService) {
}, },
user: req.user user: req.user
} }
const result = await profileService.getHandler(r) let result
try {
result = await profileService.getHandler(r)
}
catch (err) {
api.captureException(err)
}
req.user.profile = ObjectUtil.merge({}, req.user.profile, result ? result.data : undefined) req.user.profile = ObjectUtil.merge({}, req.user.profile, result ? result.data : undefined)
if (req.method.toLowerCase() === 'post') { if (req.method.toLowerCase() === 'post') {
req.body.author = { req.body.author = {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment