|
2 | 2 |
|
3 | 3 | let path = require('path'); |
4 | 4 | let error = require('./error'); |
| 5 | +let { isFunction, isString } = require('./util'); |
5 | 6 |
|
6 | | -const METHODS = [ |
7 | | - 'all', 'checkout', 'copy', 'delete', 'get', 'head', 'lock', 'merge', |
8 | | - 'mkactivity', 'mkcol', 'move', 'm-search', 'notify', 'options', 'patch', 'post', |
9 | | - 'purge', 'put', 'report', 'search', 'subscribe', 'trace', 'unlock', 'unsubscribe', |
10 | | -]; |
| 7 | +const METHODS = ['get', 'post', 'put', 'delete']; |
11 | 8 |
|
12 | | -// module.exports = { |
13 | | -// scope: { |
14 | | -// profileController, |
15 | | -// }, |
16 | | -// async test() { |
17 | | -// await this.profileController.getCurrentUser(); |
18 | | -// }, |
19 | | -// }; |
20 | | - |
21 | | -// let router = api('profile', profileController); |
22 | | -// |
23 | | -// router.get('login', async ctx => { |
24 | | -// await auth.test(ctx); |
25 | | -// return ctx.getProfile(ctx.req.body); |
26 | | -// }); |
27 | | - |
28 | | -// let route = require('begin-server/route'); |
29 | | -// let express = require('express'); |
30 | | -// let app = express(); |
31 | | -// let api = route(app, 'v1'); |
32 | | - |
33 | | -// api(); |
34 | | -// api('profile'); |
35 | | -// api(profileController); |
36 | | -// api('profile', profileController); |
37 | | - |
38 | | -module.exports = (app, root = '') => Object.assign((base, controller = {}) => { |
39 | | - if (typeof base !== 'string') { |
40 | | - controller = base; |
41 | | - base = ''; |
| 9 | +module.exports = (app, options = {}) => controller => { |
| 10 | + if (!isFunction(controller.routes)) { |
| 11 | + return; |
42 | 12 | } |
43 | | - base = path.join('/', root, base); |
44 | | - return METHODS.reduce((out, method) => { |
45 | | - out[method] = (endpoint, cb) => { |
46 | | - if (typeof endpoint !== 'string') { |
47 | | - cb = endpoint; |
48 | | - endpoint = ''; |
49 | | - } |
50 | | - endpoint = path.join(base, endpoint); |
51 | | - app[method](endpoint, async (req, res) => { |
52 | | - let bind = details => { |
53 | | - let bound = Object.create(details); |
54 | | - if (bound.scope) { |
55 | | - Object.entries(bound.scope).forEach(([key, value]) => { |
56 | | - bound[key] = bind(value); |
57 | | - }); |
58 | | - } |
59 | | - return Object.assign(bound, { req, res }); |
60 | | - }; |
61 | | - try { |
62 | | - res.json(await cb(bind(controller))); |
63 | | - } catch (e) { |
64 | | - let status = e.status || 500; |
65 | | - res.status(status); |
66 | | - let apiError = { error: error.ERROR_CODES.serverError.message }; |
67 | | - if (!error.isError(e) || status === 500) { |
68 | | - // TODO: support different log handler or error handler |
69 | | - console.error(e); |
70 | | - } else { |
71 | | - apiError.error = e.message; |
72 | | - } |
73 | | - res.json(apiError); |
| 13 | + let { |
| 14 | + root = '', |
| 15 | + onError = console.error, |
| 16 | + beforeEach = () => {}, |
| 17 | + } = options; |
| 18 | + let base = root; |
| 19 | + // Proxy methods in `app` with functions that take `(endpoint, callback)` |
| 20 | + let api = (...apiArgs) => { |
| 21 | + let set = ({ resType = 'json' } = {}) => { |
| 22 | + let handler = method => (endpoint, cb) => { |
| 23 | + let interceptor = beforeEach(...apiArgs); |
| 24 | + if (!isString(endpoint)) { |
| 25 | + cb = endpoint; |
| 26 | + endpoint = ''; |
74 | 27 | } |
| 28 | + endpoint = path.join(base, endpoint); |
| 29 | + // register the endpoint to `app` and maintain the callback arguments |
| 30 | + app[method](endpoint, async (req, res) => { |
| 31 | + let bind = details => { |
| 32 | + let bound = Object.create(details); |
| 33 | + if (bound.scope) { |
| 34 | + Object.entries(bound.scope).forEach(([key, value]) => { |
| 35 | + bound[key] = bind(value); |
| 36 | + }); |
| 37 | + } |
| 38 | + return Object.assign(bound, { req, res }); |
| 39 | + }; |
| 40 | + try { |
| 41 | + // first try running the beforeEach function |
| 42 | + await interceptor(req, res); |
| 43 | + let out = await cb(req, res)(bind(controller)); |
| 44 | + if (resType) { |
| 45 | + res[resType](out); |
| 46 | + } |
| 47 | + } catch (e) { |
| 48 | + let status = e.status || 500; |
| 49 | + res.status(status); |
| 50 | + let apiError = { error: error.ERROR_CODES.serverError.message }; |
| 51 | + if (!error.isError(e) || status === 500) { |
| 52 | + onError(e); |
| 53 | + } else { |
| 54 | + apiError.error = e.message; |
| 55 | + } |
| 56 | + res.json(apiError); |
| 57 | + } |
| 58 | + }); |
| 59 | + }; |
| 60 | + return METHODS.reduce((out, method) => { |
| 61 | + out[method] = handler(method); |
| 62 | + return out; |
| 63 | + }, { |
| 64 | + other(method, ...args) { |
| 65 | + return handler(method)(...args); |
| 66 | + }, |
75 | 67 | }); |
76 | 68 | }; |
| 69 | + return Object.assign(set(), { set }); |
| 70 | + }; |
| 71 | + |
| 72 | + // create new functions with names derrived from the controller |
| 73 | + let scope = Object.entries(controller).reduce((out, [key, fn]) => { |
| 74 | + // Each derrived function returns a new function that expects the scope to bind to. |
| 75 | + out[key] = (...args) => ctx => fn.call(ctx, ...args); |
77 | 76 | return out; |
78 | 77 | }, {}); |
79 | | -}, { error }); |
| 78 | + |
| 79 | + let apiArg = Object.assign(api, api(), { |
| 80 | + // A function to change the base route for this instance of `api` |
| 81 | + path(route) { |
| 82 | + base = path.join('/', root, route); |
| 83 | + }, |
| 84 | + }); |
| 85 | + |
| 86 | + let optionsArg = Object.assign({}, options, { |
| 87 | + root, |
| 88 | + onError, |
| 89 | + beforeEach, |
| 90 | + noRes: { resType: false }, |
| 91 | + }); |
| 92 | + |
| 93 | + controller.routes.call(scope, apiArg, optionsArg); |
| 94 | +}; |
0 commit comments