UNPKG

@dominion-framework/dominion

Version:

Declarative Promise based Node.js framework for REST API with zero dependencies.

112 lines (90 loc) 4.12 kB
const Config = require('./../../config'); const FN_ARGS = /^[^\(]*\([\{\s]*([^\)\}]*)[\}\s]*\)/m; const FN_ARG_SPLIT = /,/; const FN_ARG = /^\s*(\S+?)([iI]d)?\s*(=\s*.+)?$/; const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; const ID_PATTERN_END = '/(' + Config.router.primaryKeyPattern + ')'; const ID_PATTERN = '/(' + Config.router.primaryKeyPattern + ')/'; const FN_GET_ANNOTATION = /\/\/\s+@(\w+):?\s(.*)/g; const ID_OPTIONAL_START = '(?=.*'; const ID_OPTIONAL_END = '=([^&\\s]+)|.*)'; const CAMEL_TO_KEBAB = string => string.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase(); const makeRoute = function(method, handler, rootPath = '', factory, permission = null){ const parsedFunction = reflection(handler); return { method, handler, factory, arguments: parsedFunction.arguments, annotations: parsedFunction.annotations, permission: parsedFunction.annotations.permission ? parsedFunction.annotations.permission : permission? permission.toLowerCase() : null, pattern: parsedFunction.annotations.path ? getPatternForArgs({required: [],optional: parsedFunction.arguments.optional}, parsedFunction.annotations.path) : getPatternForArgs(parsedFunction.arguments, rootPath) } }; const reflection = function(fn){ const fnString = Function.prototype.toString.call(fn); const fnTextArguments = fnString.replace(STRIP_COMMENTS, ''); const fnDescription = { annotations: {}, arguments : { required: [], optional: [] }}; fnString.replace(FN_GET_ANNOTATION, function (a, key, parameter) { fnDescription.annotations[key] = parameter; }); fnTextArguments.match(FN_ARGS)[1].split(FN_ARG_SPLIT).forEach(arg => { arg.replace(FN_ARG, (all, name, id, optional) => { fnDescription.arguments[optional ? 'optional' : 'required'].push({ name: name + (optional && id || ''), varName: name + (id || '') }); }); }); return fnDescription; }; const getPatternForArgs = function (args, rootPath) { let urlPrefix = (Config.router && Config.router.urlPrefix) || ''; let stringRegexp = getStringRegexp(args, rootPath); return new RegExp(`^${urlPrefix + stringRegexp}$`); }; const getStringRegexp = function (args, rootPath) { let stringRegexp; let stringRegexpOptional_1; let stringRegexpOptional_2; if (args.required.length === 0) { stringRegexp = rootPath; } else { stringRegexp = args.required.reduce(function (previousValue, currentItem, index) { if (currentItem.name.toLowerCase() === rootPath.replace(/\W/g,'').toLowerCase()) { return previousValue + rootPath + ID_PATTERN_END + (args.required.length - 1 !== index? "/" : ""); } else { if (args.required.length - 1 === index) { return previousValue + currentItem.name + ID_PATTERN + rootPath; } else { return previousValue + currentItem.name + ID_PATTERN; } } }, ""); } stringRegexp = CAMEL_TO_KEBAB(stringRegexp); if (args.optional.length > 0) { stringRegexpOptional_1 = args.optional.reduce(function (previousValue, currentItem) { return previousValue + ID_OPTIONAL_START + currentItem.name + ID_OPTIONAL_END; }, "(?:\\?"); stringRegexpOptional_2 = args.optional.reduce(function (previousValue, currentItem, index) { if (args.optional.length - 1 !== index) { return previousValue + currentItem.name + "|"; } else { return previousValue + currentItem.name; } }, "(?:&?(?:"); stringRegexp += stringRegexpOptional_1 + stringRegexpOptional_2 + ")=[^&\\s]+)*)?"; } return stringRegexp; }; module.exports = makeRoute;