UNPKG

apeman-app-rest

Version:
162 lines (146 loc) 4.1 kB
/** * Endpoint for list * @augments Endpoint * @class ListEndpoint */ 'use strict' const Endpoint = require('./endpoint') const { toData, toOffset, toLimit, toOrder, toWhere, defineList } = require('apemanmodel') const validateInclude = require('../validating/validate_include') const qs = require('qs') const co = require('co') /** @lends ListEndpoint */ class ListEndpoint extends Endpoint { /** * @override */ getDefaultConfig () { return { name: 'list' } } /** * @override */ handle (ctx, next) { const s = this let succeed = s.bindSucceed(ctx) let fail = s.bindFail(ctx) let { state } = ctx let { sort, page, include, model } = state let { associations } = model return co(function * () { let error = s.validate(null, state) if (error) { return fail(error) } let offset = toOffset(model, page) let limit = toLimit(model, page) let order = toOrder(model, sort) let { total, found } = yield s.list(offset, limit, order, state) let data = toData(model, found).map((data, i) => { Object.assign(data, { links: { self: `./${data.id}` }, relationships: ListEndpoint.relationships(found[ i ], associations) }) if (include) { include.forEach((include) => { delete data.attributes[ include ] }) } return data }) return succeed( { total, offset, limit }, data, ListEndpoint.links(total, offset, limit), ListEndpoint.included(found, associations, include) ) }) } /** * @override */ validate (data, state) { let { model, include } = state let includeError = validateInclude(model, include) if (includeError) { return includeError } return null } list (offset, limit, order, state) { let { model, transaction, filter, include } = state return co(function * () { let list = defineList(model, transaction) let result = yield list({ offset, limit, order, include, where: toWhere(model, filter) }) return { total: result.count, found: result.rows } }) } } Object.assign(ListEndpoint, { links (total, offset, limit) { let size = Number(limit) let first = 1 let last = parseInt(total / size) + 1 let self = parseInt(offset / size) + 1 let prev = self - 1 let next = self + 1 let _link = (number) => '.?' + qs.stringify({ page: { number, size } }) return { self: _link(self), first: first < self ? _link(first) : null, last: self < last ? _link(last) : null, prev: first <= prev ? _link(prev) : null, next: next <= last ? _link(last) : null } }, included (found, associations, include) { let hasInclude = include && (include.length > 0) if (!hasInclude) { return undefined } return include.map((name) => { let association = associations[ name ] let relatedModel = association.target let hash = {} found.forEach((found) => { let data = toData(relatedModel, found[ name ] || []) for (let included of [].concat(data || [])) { hash[ included.id ] = included } }) return Object.keys(hash).map((id) => hash[ id ]) }).reduce((a, b) => [].concat(a, b), []) }, relationships (found, associations) { let names = associations && Object.keys(associations) let hasRelationships = names.length > 0 if (!hasRelationships) { return undefined } return names.reduce((result, name) => { let association = associations[ name ] let relatedModel = association.target let links = { self: `./${found.id}/relationships/${name}/`, related: `./${found.id}/${name}` } let data = toData.withoutAttributes(relatedModel.$name, found[ name ]) return Object.assign(result, { [name]: { data, links } }) }, {}) } }) module.exports = ListEndpoint