matchMock.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. const pathToRegexp = require('path-to-regexp');
  2. const bodyParser = require('body-parser');
  3. const mockFile = require('./index');
  4. const BODY_PARSED_METHODS = ['post', 'put', 'patch'];
  5. const debug = console.log;
  6. function parseKey(key) {
  7. let method = 'get';
  8. let path = key;
  9. if (key.indexOf(' ') > -1) {
  10. const spliced = key.split(' ');
  11. method = spliced[0].toLowerCase();
  12. path = spliced[1]; // eslint-disable-line
  13. }
  14. const routerBasePath = `${path}`;
  15. return {
  16. method,
  17. path: routerBasePath,
  18. };
  19. }
  20. function createHandler(method, path, handler) {
  21. return (req, res, next) => {
  22. function sendData() {
  23. if (typeof handler === 'function') {
  24. handler(req, res, next);
  25. } else {
  26. res.json(handler);
  27. }
  28. }
  29. if (BODY_PARSED_METHODS.includes(method)) {
  30. bodyParser.json({ limit: '5mb', strict: false })(req, res, () => {
  31. bodyParser.urlencoded({ limit: '5mb', extended: true })(req, res, () => {
  32. sendData();
  33. });
  34. });
  35. } else {
  36. sendData();
  37. }
  38. };
  39. }
  40. function normalizeConfig(config) {
  41. return Object.keys(config).reduce((memo, key) => {
  42. const handler = config[key];
  43. const { method, path } = parseKey(key);
  44. const keys = [];
  45. const re = pathToRegexp(path, keys);
  46. memo.push({
  47. method,
  48. path,
  49. re,
  50. keys,
  51. handler: createHandler(method, path, handler),
  52. });
  53. return memo;
  54. }, []);
  55. }
  56. const mockData = normalizeConfig(mockFile);
  57. function matchMock(req) {
  58. const { path: exceptPath } = req;
  59. const exceptMethod = req.method.toLowerCase();
  60. function decodeParam(val) {
  61. if (typeof val !== 'string' || val.length === 0) {
  62. return val;
  63. }
  64. try {
  65. return decodeURIComponent(val);
  66. } catch (err) {
  67. if (err instanceof URIError) {
  68. err.message = `Failed to decode param ' ${val} '`;
  69. err.statusCode = 400;
  70. err.status = 400;
  71. }
  72. throw err;
  73. }
  74. }
  75. // eslint-disable-next-line no-restricted-syntax
  76. for (const mock of mockData) {
  77. const { method, re, keys } = mock;
  78. if (method === exceptMethod) {
  79. const match = re.exec(req.path);
  80. if (match) {
  81. const params = {};
  82. for (let i = 1; i < match.length; i += 1) {
  83. const key = keys[i - 1];
  84. const prop = key.name;
  85. const val = decodeParam(match[i]);
  86. if (val !== undefined || !hasOwnProperty.call(params, prop)) {
  87. params[prop] = val;
  88. }
  89. }
  90. req.params = params;
  91. return mock;
  92. }
  93. }
  94. }
  95. return mockData.filter(({ method, re }) => method === exceptMethod && re.test(exceptPath))[0];
  96. }
  97. module.exports = (req, res, next) => {
  98. const match = matchMock(req);
  99. if (match) {
  100. debug(`mock matched: [${match.method}] ${match.path}`);
  101. return match.handler(req, res, next);
  102. }
  103. return next();
  104. };