apeman-app-rest
Version:
apeman app to handle restful endpoint.
117 lines (107 loc) • 3.32 kB
JavaScript
/**
* Define a rest route.
* @function restRoute
* @returns {Array.<function>}
*/
const assert = require('assert')
const co = require('co')
const validate = require('apeman-app-validate')
const { defineOne } = require('apemanmodel')
const parseQuery = require('../parsing/parse_query')
const parseParams = require('../parsing/parse_params')
const parseBody = require('../parsing/parse_body')
/** @lends restRoute */
function restRoute (model, name, ...endpoints) {
assert.ok(model, 'model is required.')
assert.ok(name, 'name is required.')
let db = model.getDB()
return [
co.wrap(function * knock (ctx, next) {
ctx.set('ap-action', name)
Object.assign(ctx.state,
{ name, model },
parseBody(ctx.request.body),
parseParams(ctx.params),
parseQuery(ctx.request.querystring)
)
yield next()
}),
co.wrap(function * applyTransaction (ctx, next) {
if (!db) {
yield next()
return
}
let transactionId = ctx.headers[ 'ap-transaction-id' ]
let transaction = transactionId ? db.getTransaction(transactionId) : null
if (transaction) {
ctx.state.transaction = transaction
yield next()
} else {
yield new Promise((resolve, reject) =>
db.transaction((transaction) =>
co(function * withTransaction () {
ctx.state.transaction = transaction
yield next()
}).then(resolve, reject)
))
}
}),
co.wrap(function * instanceById (ctx, next) {
if (!db) {
yield next()
return
}
let { id, model, transaction } = ctx.state
if (typeof id !== 'undefined') {
let one = defineOne(model, transaction)
ctx.state.instance = yield one(id)
}
yield next()
}),
...endpoints
.map((endpoint) => {
let stop = (endpoint === false)
if (stop) {
return (ctx) => {}
}
let skip = (endpoint === true) || (typeof endpoint === 'undefined')
if (skip) {
return null
}
if (typeof endpoint === 'function') {
return restRoute.wrapHandle(endpoint)
} else {
let { handle, schema, singleton } = endpoint
return [
singleton ? restRoute.registerSingleton() : null,
handle ? restRoute.wrapHandle(handle.bind(endpoint)) : null,
schema ? validate(schema) : null
].filter((handler) => !!handler)
}
})
.reduce((handlers, handler) => handlers.concat(handler), [])
.filter((handler) => !!handler),
(ctx) => {} // Flush response
].reduce((route, handler) => route.concat(handler), [])
}
Object.assign(restRoute, {
registerSingleton () {
return co.wrap(function * rest (ctx, next) {
let id = 'singleton'
let { state } = ctx
let invalidId = state && state.id && (state.id !== id)
if (invalidId) {
throw new Error(`Invalid id detected for singleton endpoint: ${state.id}`)
}
ctx.state = Object.assign(state, { id })
yield next()
})
},
wrapHandle (handle) {
return co.wrap(function * rest (ctx, next) {
yield Promise.resolve(handle(ctx, next))
})
}
})
module.exports = restRoute