@kitmi/jacaranda
Version:
JavaScript application framework
152 lines (130 loc) • 6.07 kB
JavaScript
import { _, naming, text, hasMethod, eachAsync_, batchAsync_, get as _get, set as _set, replaceAll, url as urlUtil } from '@kitmi/utils';
import { InvalidArgument } from '@kitmi/types';
import { loadControllers_ } from '../helpers/loadModuleFrom_';
/**
* Jacaranda Restful API Spec (jacaranda) router.
* @module Router_JaREST
*/
const appendId = (baseEndpoint, idName) => (idName ? `${baseEndpoint}/:${idName}` : baseEndpoint);
/**
* Create a jaREST router.
* @param {*} app
* @param {string} baseRoute
* @param {object} options
* @property {string} [options.$controllerPath]
* @property {object|array} [options.$middlewares]
* @property {boolean} [options.$urlDasherize]
* @example
* '<base path>': {
* jacaranda: {
* $controllerPath:
* $source: registry | runtime | direct | project
* $middlewares:
* $errorOptions
* $urlDasherize: false
* 'controller To remap': '/special/:abc/url'
* ...
* }
* }
*
* route http method function of ctrl
* /:resource get query_
* /:resource post post_
* /:resource/:id get get_
* /:resource/:id put put_
* /:resource/:id patch patch_
* /:resource/:id delete delete_
* /:resource put putMany_
* /:resource patch patchMany_
* /:resource delete deleteMany_
*/
const jaRestRouter = async (app, baseRoute, options) => {
const settingGroups = _.castArray(options);
await eachAsync_(settingGroups, async (options) => {
const router = app.router.createRouter(baseRoute);
const resourcesPath = options.$controllerPath || 'resources';
const kebabify = options.$urlDasherize;
app.useMiddleware(
router,
await (
await app.getMiddlewareFactory_('jsonError')
)(options.$errorOptions, app),
'jsonError'
);
if (options.$middlewares) {
await app.useMiddlewares_(router, options.$middlewares);
}
const controllers = await loadControllers_(app, options.$source, resourcesPath, options.$packageName);
await batchAsync_(controllers, async (ControllerClass, entityNameWithPath) => {
const controller = typeof ControllerClass === 'function' ? new ControllerClass(app) : ControllerClass;
// compatible with the api generator
entityNameWithPath = replaceAll(entityNameWithPath, '__', '/');
const pathNodes = entityNameWithPath.split('/');
const entityName = pathNodes[pathNodes.length - 1];
let baseEndpoint;
if (entityNameWithPath in options) {
baseEndpoint = text.ensureStartsWith(text.dropIfEndsWith(options[entityNameWithPath], '/'), '/');
} else {
const urlPath = pathNodes.map((p) => (kebabify ? naming.kebabCase(p) : p)).join('/');
baseEndpoint = text.ensureStartsWith(urlPath, '/');
}
let idName = naming.camelCase(entityName) + 'Id';
let endpointWithId = appendId(baseEndpoint, idName);
//todo: add options
async function addRoute_(methodName, httpMethod) {
if (hasMethod(controller, methodName)) {
const _action = controller[methodName].bind(controller);
const _middlewares = controller[methodName].__metaMiddlewares;
await app.addRoute_(
router,
httpMethod,
baseEndpoint,
_middlewares ? [..._middlewares, _action] : _action
);
}
}
async function addCustomRoute_(methodName, httpMethod, subRoute) {
if (hasMethod(controller, methodName)) {
const _action = controller[methodName].bind(controller);
const _middlewares = controller[methodName].__metaMiddlewares;
await app.addRoute_(
router,
httpMethod,
urlUtil.join(baseEndpoint, subRoute),
_middlewares ? [..._middlewares, _action] : _action
);
} else {
throw new InvalidArgument(`Jacaranda controller "${entityNameWithPath}" does not have method: ${methodName}`);
}
}
async function addRouteWithId_(methodName, httpMethod) {
if (hasMethod(controller, methodName)) {
const _action = (ctx) => controller[methodName](ctx, ctx.params[idName]);
const _middlewares = controller[methodName].__metaMiddlewares;
await app.addRoute_(
router,
httpMethod,
endpointWithId,
_middlewares ? [..._middlewares, _action] : _action
);
}
}
await addRoute_('query_', 'get');
await addRouteWithId_('get_', 'get');
await addRoute_('post_', 'post');
await addRouteWithId_('put_', 'put');
await addRouteWithId_('patch_', 'patch');
await addRouteWithId_('delete_', 'delete');
await addRoute_('putMany_', 'put');
await addRoute_('patchMany_', 'patch');
await addRoute_('deleteMany_', 'delete');
if (ControllerClass.$remap) {
await eachAsync_(ControllerClass.$remap, async ({ method, route }, methodName) => {
await addCustomRoute_(methodName, method, route);
});
}
});
app.addRouter(router);
});
};
export default jaRestRouter;