@loopback/rest 4.0.0-alpha.8 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (388) hide show
  1. package/CHANGELOG.md +1822 -0
  2. package/LICENSE +1 -1
  3. package/README.md +30 -58
  4. package/dist/body-parsers/body-parser.d.ts +25 -0
  5. package/dist/body-parsers/body-parser.helpers.d.ts +44 -0
  6. package/dist/body-parsers/body-parser.helpers.js +102 -0
  7. package/dist/body-parsers/body-parser.helpers.js.map +1 -0
  8. package/dist/body-parsers/body-parser.js +159 -0
  9. package/dist/body-parsers/body-parser.js.map +1 -0
  10. package/dist/body-parsers/body-parser.json.d.ts +9 -0
  11. package/dist/body-parsers/body-parser.json.js +43 -0
  12. package/dist/body-parsers/body-parser.json.js.map +1 -0
  13. package/dist/body-parsers/body-parser.raw.d.ts +12 -0
  14. package/dist/body-parsers/body-parser.raw.js +39 -0
  15. package/dist/body-parsers/body-parser.raw.js.map +1 -0
  16. package/dist/body-parsers/body-parser.stream.d.ts +12 -0
  17. package/dist/body-parsers/body-parser.stream.js +28 -0
  18. package/dist/body-parsers/body-parser.stream.js.map +1 -0
  19. package/dist/body-parsers/body-parser.text.d.ts +9 -0
  20. package/dist/body-parsers/body-parser.text.js +38 -0
  21. package/dist/body-parsers/body-parser.text.js.map +1 -0
  22. package/dist/body-parsers/body-parser.urlencoded.d.ts +9 -0
  23. package/dist/body-parsers/body-parser.urlencoded.js +36 -0
  24. package/dist/body-parsers/body-parser.urlencoded.js.map +1 -0
  25. package/dist/body-parsers/index.d.ts +8 -0
  26. package/dist/body-parsers/index.js +16 -0
  27. package/dist/body-parsers/index.js.map +1 -0
  28. package/dist/body-parsers/types.d.ts +51 -0
  29. package/dist/body-parsers/types.js +12 -0
  30. package/dist/body-parsers/types.js.map +1 -0
  31. package/dist/coercion/coerce-parameter.d.ts +9 -0
  32. package/dist/coercion/coerce-parameter.js +166 -0
  33. package/dist/coercion/coerce-parameter.js.map +1 -0
  34. package/dist/coercion/utils.d.ts +43 -0
  35. package/dist/coercion/utils.js +96 -0
  36. package/dist/coercion/utils.js.map +1 -0
  37. package/dist/coercion/validator.d.ts +49 -0
  38. package/dist/coercion/validator.js +85 -0
  39. package/dist/coercion/validator.js.map +1 -0
  40. package/dist/http-handler.d.ts +38 -0
  41. package/dist/http-handler.js +68 -0
  42. package/dist/http-handler.js.map +1 -0
  43. package/dist/index.d.ts +36 -1
  44. package/dist/index.js +40 -6
  45. package/dist/index.js.map +1 -0
  46. package/dist/keys.d.ts +198 -0
  47. package/dist/keys.js +202 -0
  48. package/dist/keys.js.map +1 -0
  49. package/dist/parse-json.d.ts +11 -0
  50. package/dist/parse-json.js +42 -0
  51. package/dist/parse-json.js.map +1 -0
  52. package/dist/parser.d.ts +11 -0
  53. package/dist/parser.js +76 -0
  54. package/dist/parser.js.map +1 -0
  55. package/{dist6/src/providers/find-route.d.ts → dist/providers/find-route.provider.d.ts} +3 -1
  56. package/dist/providers/find-route.provider.js +36 -0
  57. package/dist/providers/find-route.provider.js.map +1 -0
  58. package/dist/providers/index.d.ts +6 -0
  59. package/dist/providers/index.js +14 -0
  60. package/dist/providers/index.js.map +1 -0
  61. package/dist/{src/providers/invoke-method.d.ts → providers/invoke-method.provider.d.ts} +3 -1
  62. package/dist/providers/invoke-method.provider.js +30 -0
  63. package/dist/providers/invoke-method.provider.js.map +1 -0
  64. package/dist/providers/log-error.provider.d.ts +6 -0
  65. package/dist/providers/log-error.provider.js +21 -0
  66. package/dist/providers/log-error.provider.js.map +1 -0
  67. package/dist/providers/parse-params.provider.d.ts +15 -0
  68. package/dist/providers/parse-params.provider.js +41 -0
  69. package/dist/providers/parse-params.provider.js.map +1 -0
  70. package/dist/providers/reject.provider.d.ts +10 -0
  71. package/dist/providers/reject.provider.js +47 -0
  72. package/dist/providers/reject.provider.js.map +1 -0
  73. package/dist/{src/providers/send.d.ts → providers/send.provider.d.ts} +1 -4
  74. package/dist/{src/providers/send.js → providers/send.provider.js} +4 -6
  75. package/dist/providers/send.provider.js.map +1 -0
  76. package/dist/request-context.d.ts +36 -0
  77. package/dist/request-context.js +104 -0
  78. package/dist/request-context.js.map +1 -0
  79. package/dist/rest-http-error.d.ts +37 -0
  80. package/dist/rest-http-error.js +51 -0
  81. package/dist/rest-http-error.js.map +1 -0
  82. package/dist/rest.application.d.ts +232 -0
  83. package/dist/rest.application.js +174 -0
  84. package/dist/rest.application.js.map +1 -0
  85. package/dist/rest.component.d.ts +15 -0
  86. package/dist/rest.component.js +72 -0
  87. package/dist/rest.component.js.map +1 -0
  88. package/dist/rest.server.d.ts +443 -0
  89. package/dist/rest.server.js +748 -0
  90. package/dist/rest.server.js.map +1 -0
  91. package/dist/router/base-route.d.ts +29 -0
  92. package/dist/router/base-route.js +41 -0
  93. package/dist/router/base-route.js.map +1 -0
  94. package/dist/router/controller-route.d.ts +61 -0
  95. package/dist/router/controller-route.js +160 -0
  96. package/dist/router/controller-route.js.map +1 -0
  97. package/dist/router/external-express-routes.d.ts +24 -0
  98. package/dist/router/external-express-routes.js +90 -0
  99. package/dist/router/external-express-routes.js.map +1 -0
  100. package/dist/router/handler-route.d.ts +12 -0
  101. package/dist/router/handler-route.js +30 -0
  102. package/dist/router/handler-route.js.map +1 -0
  103. package/dist/router/index.d.ts +14 -0
  104. package/dist/router/index.js +25 -0
  105. package/dist/router/index.js.map +1 -0
  106. package/dist/router/openapi-path.d.ts +14 -0
  107. package/dist/router/openapi-path.js +64 -0
  108. package/dist/router/openapi-path.js.map +1 -0
  109. package/dist/router/redirect-route.d.ts +23 -0
  110. package/dist/router/redirect-route.js +50 -0
  111. package/dist/router/redirect-route.js.map +1 -0
  112. package/dist/router/regexp-router.d.ts +25 -0
  113. package/dist/router/regexp-router.js +84 -0
  114. package/dist/router/regexp-router.js.map +1 -0
  115. package/dist/router/rest-router.d.ts +35 -0
  116. package/dist/{src/internal-types.js → router/rest-router.js} +2 -2
  117. package/dist/router/rest-router.js.map +1 -0
  118. package/dist/router/route-entry.d.ts +46 -0
  119. package/dist/router/route-entry.js +20 -0
  120. package/dist/router/route-entry.js.map +1 -0
  121. package/dist/router/route-sort.d.ts +7 -0
  122. package/dist/router/route-sort.js +75 -0
  123. package/dist/router/route-sort.js.map +1 -0
  124. package/dist/router/router-base.d.ts +42 -0
  125. package/dist/router/router-base.js +101 -0
  126. package/dist/router/router-base.js.map +1 -0
  127. package/dist/router/router-spec.d.ts +3 -0
  128. package/dist/router/router-spec.js +40 -0
  129. package/dist/router/router-spec.js.map +1 -0
  130. package/dist/router/routing-table.d.ts +32 -0
  131. package/dist/router/routing-table.js +86 -0
  132. package/dist/router/routing-table.js.map +1 -0
  133. package/dist/router/trie-router.d.ts +13 -0
  134. package/dist/router/trie-router.js +55 -0
  135. package/dist/router/trie-router.js.map +1 -0
  136. package/dist/router/trie.d.ts +59 -0
  137. package/dist/router/trie.js +180 -0
  138. package/dist/router/trie.js.map +1 -0
  139. package/{dist6/src → dist}/sequence.d.ts +28 -23
  140. package/dist/sequence.js +112 -0
  141. package/dist/sequence.js.map +1 -0
  142. package/dist/spec-enhancers/consolidate.spec-enhancer.d.ts +68 -0
  143. package/dist/spec-enhancers/consolidate.spec-enhancer.js +145 -0
  144. package/dist/spec-enhancers/consolidate.spec-enhancer.js.map +1 -0
  145. package/dist/spec-enhancers/info.spec-enhancer.d.ts +19 -0
  146. package/dist/spec-enhancers/info.spec-enhancer.js +89 -0
  147. package/dist/spec-enhancers/info.spec-enhancer.js.map +1 -0
  148. package/dist/types.d.ts +178 -0
  149. package/dist/types.js +12 -0
  150. package/dist/types.js.map +1 -0
  151. package/dist/validation/ajv-factory.provider.d.ts +12 -0
  152. package/dist/validation/ajv-factory.provider.js +87 -0
  153. package/dist/validation/ajv-factory.provider.js.map +1 -0
  154. package/dist/validation/request-body.validator.d.ts +14 -0
  155. package/dist/validation/request-body.validator.js +161 -0
  156. package/dist/validation/request-body.validator.js.map +1 -0
  157. package/dist/writer.d.ts +9 -0
  158. package/dist/writer.js +62 -0
  159. package/dist/writer.js.map +1 -0
  160. package/package.json +66 -38
  161. package/src/body-parsers/body-parser.helpers.ts +148 -0
  162. package/src/body-parsers/body-parser.json.ts +46 -0
  163. package/src/body-parsers/body-parser.raw.ts +42 -0
  164. package/src/body-parsers/body-parser.stream.ts +27 -0
  165. package/src/body-parsers/body-parser.text.ts +44 -0
  166. package/src/body-parsers/body-parser.ts +208 -0
  167. package/src/body-parsers/body-parser.urlencoded.ts +42 -0
  168. package/src/body-parsers/index.ts +13 -0
  169. package/src/body-parsers/types.ts +60 -0
  170. package/src/coercion/coerce-parameter.ts +207 -0
  171. package/src/coercion/utils.ts +103 -0
  172. package/src/coercion/validator.ts +98 -0
  173. package/src/http-handler.ts +84 -41
  174. package/src/index.ts +37 -30
  175. package/src/keys.ts +273 -20
  176. package/src/parse-json.ts +42 -0
  177. package/src/parser.ts +89 -104
  178. package/src/providers/{find-route.ts → find-route.provider.ts} +10 -7
  179. package/src/providers/index.ts +7 -9
  180. package/src/providers/{invoke-method.ts → invoke-method.provider.ts} +8 -5
  181. package/src/providers/log-error.provider.ts +27 -0
  182. package/src/providers/parse-params.provider.ts +42 -0
  183. package/src/providers/reject.provider.ts +44 -0
  184. package/src/providers/{send.ts → send.provider.ts} +2 -5
  185. package/src/request-context.ts +123 -0
  186. package/src/rest-http-error.ts +87 -0
  187. package/src/rest.application.ts +390 -0
  188. package/src/rest.component.ts +111 -0
  189. package/src/rest.server.ts +1192 -0
  190. package/src/router/base-route.ts +53 -0
  191. package/src/router/controller-route.ts +241 -0
  192. package/src/router/external-express-routes.ts +139 -0
  193. package/src/router/handler-route.ts +44 -0
  194. package/src/router/index.ts +24 -0
  195. package/src/router/openapi-path.ts +67 -0
  196. package/src/router/redirect-route.ts +64 -0
  197. package/src/router/regexp-router.ts +104 -0
  198. package/src/router/rest-router.ts +48 -0
  199. package/src/router/route-entry.ts +74 -0
  200. package/src/router/route-sort.ts +74 -0
  201. package/src/router/router-base.ts +124 -0
  202. package/src/router/router-spec.ts +36 -0
  203. package/src/router/routing-table.ts +83 -279
  204. package/src/router/trie-router.ts +57 -0
  205. package/src/router/trie.ts +233 -0
  206. package/src/sequence.ts +44 -37
  207. package/src/spec-enhancers/consolidate.spec-enhancer.ts +182 -0
  208. package/src/spec-enhancers/info.spec-enhancer.ts +92 -0
  209. package/src/types.ts +216 -0
  210. package/src/validation/ajv-factory.provider.ts +94 -0
  211. package/src/validation/request-body.validator.ts +208 -0
  212. package/src/writer.ts +41 -68
  213. package/api-docs/.DS_Store +0 -0
  214. package/api-docs/apple-touch-icon-114x114-precomposed.png +0 -0
  215. package/api-docs/apple-touch-icon-144x144-precomposed.png +0 -0
  216. package/api-docs/apple-touch-icon-57x57-precomposed.png +0 -0
  217. package/api-docs/apple-touch-icon-72x72-precomposed.png +0 -0
  218. package/api-docs/apple-touch-icon-precomposed.png +0 -0
  219. package/api-docs/apple-touch-icon.png +0 -0
  220. package/api-docs/css/bootstrap.min.css +0 -9
  221. package/api-docs/css/code-themes/arta.css +0 -158
  222. package/api-docs/css/code-themes/ascetic.css +0 -50
  223. package/api-docs/css/code-themes/brown_paper.css +0 -104
  224. package/api-docs/css/code-themes/brown_papersq.png +0 -0
  225. package/api-docs/css/code-themes/dark.css +0 -103
  226. package/api-docs/css/code-themes/default.css +0 -135
  227. package/api-docs/css/code-themes/far.css +0 -111
  228. package/api-docs/css/code-themes/github.css +0 -127
  229. package/api-docs/css/code-themes/googlecode.css +0 -144
  230. package/api-docs/css/code-themes/idea.css +0 -121
  231. package/api-docs/css/code-themes/ir_black.css +0 -104
  232. package/api-docs/css/code-themes/magula.css +0 -121
  233. package/api-docs/css/code-themes/monokai.css +0 -114
  234. package/api-docs/css/code-themes/pojoaque.css +0 -104
  235. package/api-docs/css/code-themes/pojoaque.jpg +0 -0
  236. package/api-docs/css/code-themes/rainbow.css +0 -114
  237. package/api-docs/css/code-themes/school_book.css +0 -111
  238. package/api-docs/css/code-themes/school_book.png +0 -0
  239. package/api-docs/css/code-themes/sl-theme.css +0 -45
  240. package/api-docs/css/code-themes/solarized_dark.css +0 -88
  241. package/api-docs/css/code-themes/solarized_light.css +0 -88
  242. package/api-docs/css/code-themes/sunburst.css +0 -158
  243. package/api-docs/css/code-themes/tomorrow-night-blue.css +0 -52
  244. package/api-docs/css/code-themes/tomorrow-night-bright.css +0 -51
  245. package/api-docs/css/code-themes/tomorrow-night-eighties.css +0 -51
  246. package/api-docs/css/code-themes/tomorrow-night.css +0 -52
  247. package/api-docs/css/code-themes/tomorrow.css +0 -49
  248. package/api-docs/css/code-themes/vs.css +0 -86
  249. package/api-docs/css/code-themes/xcode.css +0 -154
  250. package/api-docs/css/code-themes/zenburn.css +0 -115
  251. package/api-docs/css/main.css +0 -139
  252. package/api-docs/favicon.ico +0 -0
  253. package/api-docs/fonts/0ihfXUL2emPh0ROJezvraLO3LdcAZYWl9Si6vvxL-qU.woff +0 -0
  254. package/api-docs/fonts/OsJ2DjdpjqFRVUSto6IffLO3LdcAZYWl9Si6vvxL-qU.woff +0 -0
  255. package/api-docs/fonts/_aijTyevf54tkVDLy-dlnLO3LdcAZYWl9Si6vvxL-qU.woff +0 -0
  256. package/api-docs/index.html +0 -7082
  257. package/api-docs/js/main.js +0 -19
  258. package/api-docs/js/vendor/bootstrap.min.js +0 -6
  259. package/api-docs/js/vendor/jquery-1.10.1.min.js +0 -6
  260. package/api-docs/js/vendor/jquery.scrollTo-1.4.3.1.js +0 -218
  261. package/api-docs/js/vendor/modernizr-2.6.2-respond-1.1.0.min.js +0 -11
  262. package/dist/src/http-handler.d.ts +0 -19
  263. package/dist/src/http-handler.js +0 -43
  264. package/dist/src/http-handler.js.map +0 -1
  265. package/dist/src/index.d.ts +0 -14
  266. package/dist/src/index.js +0 -33
  267. package/dist/src/index.js.map +0 -1
  268. package/dist/src/internal-types.d.ts +0 -67
  269. package/dist/src/internal-types.js.map +0 -1
  270. package/dist/src/keys.d.ts +0 -22
  271. package/dist/src/keys.js +0 -35
  272. package/dist/src/keys.js.map +0 -1
  273. package/dist/src/parser.d.ts +0 -11
  274. package/dist/src/parser.js +0 -98
  275. package/dist/src/parser.js.map +0 -1
  276. package/dist/src/providers/bind-element.d.ts +0 -7
  277. package/dist/src/providers/bind-element.js +0 -34
  278. package/dist/src/providers/bind-element.js.map +0 -1
  279. package/dist/src/providers/find-route.d.ts +0 -9
  280. package/dist/src/providers/find-route.js +0 -42
  281. package/dist/src/providers/find-route.js.map +0 -1
  282. package/dist/src/providers/get-from-context.d.ts +0 -7
  283. package/dist/src/providers/get-from-context.js +0 -34
  284. package/dist/src/providers/get-from-context.js.map +0 -1
  285. package/dist/src/providers/index.d.ts +0 -8
  286. package/dist/src/providers/index.js +0 -18
  287. package/dist/src/providers/index.js.map +0 -1
  288. package/dist/src/providers/invoke-method.js +0 -36
  289. package/dist/src/providers/invoke-method.js.map +0 -1
  290. package/dist/src/providers/log-error-provider.d.ts +0 -6
  291. package/dist/src/providers/log-error-provider.js +0 -17
  292. package/dist/src/providers/log-error-provider.js.map +0 -1
  293. package/dist/src/providers/parse-params.d.ts +0 -13
  294. package/dist/src/providers/parse-params.js +0 -22
  295. package/dist/src/providers/parse-params.js.map +0 -1
  296. package/dist/src/providers/reject.d.ts +0 -6
  297. package/dist/src/providers/reject.js +0 -40
  298. package/dist/src/providers/reject.js.map +0 -1
  299. package/dist/src/providers/send.js.map +0 -1
  300. package/dist/src/rest-component.d.ts +0 -12
  301. package/dist/src/rest-component.js +0 -50
  302. package/dist/src/rest-component.js.map +0 -1
  303. package/dist/src/rest-server.d.ts +0 -211
  304. package/dist/src/rest-server.js +0 -426
  305. package/dist/src/rest-server.js.map +0 -1
  306. package/dist/src/router/metadata.d.ts +0 -150
  307. package/dist/src/router/metadata.js +0 -410
  308. package/dist/src/router/metadata.js.map +0 -1
  309. package/dist/src/router/routing-table.d.ts +0 -68
  310. package/dist/src/router/routing-table.js +0 -204
  311. package/dist/src/router/routing-table.js.map +0 -1
  312. package/dist/src/sequence.d.ts +0 -81
  313. package/dist/src/sequence.js +0 -104
  314. package/dist/src/sequence.js.map +0 -1
  315. package/dist/src/writer.d.ts +0 -17
  316. package/dist/src/writer.js +0 -87
  317. package/dist/src/writer.js.map +0 -1
  318. package/dist6/index.d.ts +0 -1
  319. package/dist6/index.js +0 -12
  320. package/dist6/src/http-handler.d.ts +0 -19
  321. package/dist6/src/http-handler.js +0 -53
  322. package/dist6/src/http-handler.js.map +0 -1
  323. package/dist6/src/index.d.ts +0 -14
  324. package/dist6/src/index.js +0 -33
  325. package/dist6/src/index.js.map +0 -1
  326. package/dist6/src/internal-types.d.ts +0 -67
  327. package/dist6/src/internal-types.js +0 -7
  328. package/dist6/src/internal-types.js.map +0 -1
  329. package/dist6/src/keys.d.ts +0 -22
  330. package/dist6/src/keys.js +0 -35
  331. package/dist6/src/keys.js.map +0 -1
  332. package/dist6/src/parser.d.ts +0 -11
  333. package/dist6/src/parser.js +0 -108
  334. package/dist6/src/parser.js.map +0 -1
  335. package/dist6/src/providers/bind-element.d.ts +0 -7
  336. package/dist6/src/providers/bind-element.js +0 -34
  337. package/dist6/src/providers/bind-element.js.map +0 -1
  338. package/dist6/src/providers/find-route.js +0 -42
  339. package/dist6/src/providers/find-route.js.map +0 -1
  340. package/dist6/src/providers/get-from-context.d.ts +0 -7
  341. package/dist6/src/providers/get-from-context.js +0 -34
  342. package/dist6/src/providers/get-from-context.js.map +0 -1
  343. package/dist6/src/providers/index.d.ts +0 -8
  344. package/dist6/src/providers/index.js +0 -18
  345. package/dist6/src/providers/index.js.map +0 -1
  346. package/dist6/src/providers/invoke-method.d.ts +0 -7
  347. package/dist6/src/providers/invoke-method.js +0 -44
  348. package/dist6/src/providers/invoke-method.js.map +0 -1
  349. package/dist6/src/providers/log-error-provider.d.ts +0 -6
  350. package/dist6/src/providers/log-error-provider.js +0 -17
  351. package/dist6/src/providers/log-error-provider.js.map +0 -1
  352. package/dist6/src/providers/parse-params.d.ts +0 -13
  353. package/dist6/src/providers/parse-params.js +0 -22
  354. package/dist6/src/providers/parse-params.js.map +0 -1
  355. package/dist6/src/providers/reject.d.ts +0 -6
  356. package/dist6/src/providers/reject.js +0 -40
  357. package/dist6/src/providers/reject.js.map +0 -1
  358. package/dist6/src/providers/send.d.ts +0 -15
  359. package/dist6/src/providers/send.js +0 -24
  360. package/dist6/src/providers/send.js.map +0 -1
  361. package/dist6/src/rest-component.d.ts +0 -12
  362. package/dist6/src/rest-component.js +0 -50
  363. package/dist6/src/rest-component.js.map +0 -1
  364. package/dist6/src/rest-server.d.ts +0 -211
  365. package/dist6/src/rest-server.js +0 -444
  366. package/dist6/src/rest-server.js.map +0 -1
  367. package/dist6/src/router/metadata.d.ts +0 -150
  368. package/dist6/src/router/metadata.js +0 -410
  369. package/dist6/src/router/metadata.js.map +0 -1
  370. package/dist6/src/router/routing-table.d.ts +0 -68
  371. package/dist6/src/router/routing-table.js +0 -218
  372. package/dist6/src/router/routing-table.js.map +0 -1
  373. package/dist6/src/sequence.js +0 -114
  374. package/dist6/src/sequence.js.map +0 -1
  375. package/dist6/src/writer.d.ts +0 -17
  376. package/dist6/src/writer.js +0 -87
  377. package/dist6/src/writer.js.map +0 -1
  378. package/index.d.ts +0 -6
  379. package/index.js +0 -9
  380. package/src/internal-types.ts +0 -96
  381. package/src/providers/bind-element.ts +0 -15
  382. package/src/providers/get-from-context.ts +0 -16
  383. package/src/providers/log-error-provider.ts +0 -23
  384. package/src/providers/parse-params.ts +0 -20
  385. package/src/providers/reject.ts +0 -27
  386. package/src/rest-component.ts +0 -54
  387. package/src/rest-server.ts +0 -584
  388. package/src/router/metadata.ts +0 -517
@@ -1,108 +1,88 @@
1
- // Copyright IBM Corp. 2017. All Rights Reserved.
1
+ // Copyright IBM Corp. 2017,2020. All Rights Reserved.
2
2
  // Node module: @loopback/rest
3
3
  // This file is licensed under the MIT License.
4
4
  // License text available at https://opensource.org/licenses/MIT
5
5
 
6
6
  import {
7
+ ControllerSpec,
7
8
  OperationObject,
8
9
  ParameterObject,
9
- PathsObject,
10
- } from '@loopback/openapi-spec';
11
- import {Context, Constructor, instantiateClass} from '@loopback/context';
12
- import {ServerRequest} from 'http';
13
- import * as HttpErrors from 'http-errors';
14
-
10
+ PathObject,
11
+ } from '@loopback/openapi-v3';
12
+ import debugFactory from 'debug';
13
+ import HttpErrors from 'http-errors';
14
+ import {Request} from '../types';
15
15
  import {
16
- ParsedRequest,
17
- PathParameterValues,
18
- OperationArgs,
19
- OperationRetval,
20
- } from '../internal-types';
21
-
22
- import {ControllerSpec} from './metadata';
23
-
24
- import * as assert from 'assert';
25
- import * as url from 'url';
26
- const debug = require('debug')('loopback:core:routing-table');
27
-
28
- // TODO(bajtos) Refactor this code to use Trie-based lookup,
29
- // e.g. via wayfarer/trie or find-my-way
30
- // See https://github.com/strongloop/loopback-next/issues/98
31
- import * as pathToRegexp from 'path-to-regexp';
16
+ ControllerClass,
17
+ ControllerFactory,
18
+ createRoutesForController,
19
+ } from './controller-route';
20
+ import {ExternalExpressRoutes} from './external-express-routes';
21
+ import {validateApiPath} from './openapi-path';
22
+ import {RestRouter} from './rest-router';
23
+ import {ResolvedRoute, RouteEntry} from './route-entry';
24
+ import {TrieRouter} from './trie-router';
25
+
26
+ const debug = debugFactory('loopback:rest:routing-table');
32
27
 
33
28
  /**
34
- * Parse the URL of the incoming request and set additional properties
35
- * on this request object:
36
- * - `path`
37
- * - `query`
38
- *
39
- * @private
40
- * @param request
29
+ * Routing table
41
30
  */
42
- export function parseRequestUrl(request: ServerRequest): ParsedRequest {
43
- // TODO(bajtos) The following parsing can be skipped when the router
44
- // is mounted on an express app
45
- const parsedRequest = request as ParsedRequest;
46
- const parsedUrl = url.parse(parsedRequest.url, true);
47
- parsedRequest.path = parsedUrl.pathname || '/';
48
- parsedRequest.query = parsedUrl.query;
49
- return parsedRequest;
50
- }
51
-
52
- // tslint:disable-next-line:no-any
53
- export type ControllerClass = Constructor<any>;
54
-
55
31
  export class RoutingTable {
56
- private readonly _routes: RouteEntry[] = [];
57
-
58
- registerController(controller: ControllerClass, spec: ControllerSpec) {
59
- assert(
60
- typeof spec === 'object' && !!spec,
61
- 'API specification must be a non-null object',
32
+ constructor(
33
+ private readonly _router: RestRouter = new TrieRouter(),
34
+ private _externalRoutes?: ExternalExpressRoutes,
35
+ ) {}
36
+
37
+ /**
38
+ * Register a controller as the route
39
+ * @param spec
40
+ * @param controllerCtor
41
+ * @param controllerFactory
42
+ */
43
+ registerController<T>(
44
+ spec: ControllerSpec,
45
+ controllerCtor: ControllerClass<T>,
46
+ controllerFactory?: ControllerFactory<T>,
47
+ ) {
48
+ const routes = createRoutesForController(
49
+ spec,
50
+ controllerCtor,
51
+ controllerFactory,
62
52
  );
63
- if (!spec.paths || !Object.keys(spec.paths).length) {
64
- return;
65
- }
66
-
67
- debug('Registering Controller with API', spec);
68
-
69
- const basePath = spec.basePath || '/';
70
- for (const p in spec.paths) {
71
- for (const verb in spec.paths[p]) {
72
- const opSpec: OperationObject = spec.paths[p][verb];
73
- const fullPath = RoutingTable.joinPath(basePath, p);
74
- const route = new ControllerRoute(verb, fullPath, opSpec, controller);
75
- this.registerRoute(route);
76
- }
53
+ for (const route of routes) {
54
+ this.registerRoute(route);
77
55
  }
78
56
  }
79
57
 
80
- static joinPath(basePath: string, path: string) {
81
- const fullPath = [basePath, path]
82
- .join('/') // Join by /
83
- .replace(/(\/){2,}/g, '/') // Remove extra /
84
- .replace(/\/$/, '') // Remove trailing /
85
- .replace(/^(\/)?/, '/'); // Add leading /
86
- return fullPath;
87
- }
88
-
58
+ /**
59
+ * Register a route
60
+ * @param route - A route entry
61
+ */
89
62
  registerRoute(route: RouteEntry) {
90
63
  // TODO(bajtos) handle the case where opSpec.parameters contains $ref
91
64
  // See https://github.com/strongloop/loopback-next/issues/435
92
- debug(
93
- 'Registering route %s %s -> %s(%s)',
94
- route.verb,
95
- route.path,
96
- route.describe(),
97
- describeOperationParameters(route.spec),
98
- );
99
- this._routes.push(route);
65
+ /* istanbul ignore if */
66
+ if (debug.enabled) {
67
+ debug(
68
+ 'Registering route %s %s -> %s(%s)',
69
+ route.verb.toUpperCase(),
70
+ route.path,
71
+ route.describe(),
72
+ describeOperationParameters(route.spec),
73
+ );
74
+ }
75
+
76
+ validateApiPath(route.path);
77
+
78
+ this._router.add(route);
100
79
  }
101
80
 
102
- describeApiPaths(): PathsObject {
103
- const paths: PathsObject = {};
81
+ describeApiPaths(): PathObject {
82
+ const paths: PathObject = {};
104
83
 
105
- for (const route of this._routes) {
84
+ for (const route of this._router.list()) {
85
+ if (route.spec['x-visibility'] === 'undocumented') continue;
106
86
  if (!paths[route.path]) {
107
87
  paths[route.path] = {};
108
88
  }
@@ -113,215 +93,39 @@ export class RoutingTable {
113
93
  return paths;
114
94
  }
115
95
 
116
- find(request: ParsedRequest): ResolvedRoute {
117
- for (const entry of this._routes) {
118
- const match = entry.match(request);
119
- if (match) return match;
120
- }
96
+ /**
97
+ * Map a request to a route
98
+ * @param request
99
+ */
100
+ find(request: Request): ResolvedRoute {
101
+ debug('Finding route for %s %s', request.method, request.path);
121
102
 
122
- throw new HttpErrors.NotFound(
123
- `Endpoint "${request.method} ${request.path}" not found.`,
124
- );
125
- }
126
- }
127
-
128
- export interface RouteEntry {
129
- readonly verb: string;
130
- readonly path: string;
131
- readonly spec: OperationObject;
103
+ const found = this._router.find(request);
132
104
 
133
- match(request: ParsedRequest): ResolvedRoute | undefined;
134
-
135
- updateBindings(requestContext: Context): void;
136
- invokeHandler(
137
- requestContext: Context,
138
- args: OperationArgs,
139
- ): Promise<OperationRetval>;
140
-
141
- describe(): string;
142
- }
143
-
144
- export interface ResolvedRoute extends RouteEntry {
145
- readonly pathParams: PathParameterValues;
146
- }
147
-
148
- export abstract class BaseRoute implements RouteEntry {
149
- public readonly verb: string;
150
- private readonly _keys: pathToRegexp.Key[] = [];
151
- private readonly _pathRegexp: RegExp;
152
-
153
- constructor(
154
- verb: string,
155
- public readonly path: string,
156
- public readonly spec: OperationObject,
157
- ) {
158
- this.verb = verb.toLowerCase();
159
-
160
- // In Swagger, path parameters are wrapped in `{}`.
161
- // In Express.js, path parameters are prefixed with `:`
162
- path = path.replace(/{([^}]*)}(\/|$)/g, ':$1$2');
163
- this._pathRegexp = pathToRegexp(path, this._keys, {
164
- strict: false,
165
- end: true,
166
- });
167
- }
168
-
169
- match(request: ParsedRequest): ResolvedRoute | undefined {
170
- debug('trying endpoint', this);
171
- if (this.verb !== request.method!.toLowerCase()) {
172
- debug(' -> verb mismatch');
173
- return undefined;
105
+ if (found) {
106
+ debug('Route matched: %j', found);
107
+ return found;
174
108
  }
175
109
 
176
- const match = this._pathRegexp.exec(request.path);
177
- if (!match) {
178
- debug(' -> path mismatch');
179
- return undefined;
180
- }
181
-
182
- const pathParams = this._buildPathParams(match);
183
- debug(' -> found with params: %j', pathParams);
184
-
185
- return createResolvedRoute(this, pathParams);
186
- }
187
-
188
- abstract updateBindings(requestContext: Context): void;
189
-
190
- abstract invokeHandler(
191
- requestContext: Context,
192
- args: OperationArgs,
193
- ): Promise<OperationRetval>;
194
-
195
- describe(): string {
196
- return `"${this.verb} ${this.path}"`;
197
- }
198
-
199
- private _buildPathParams(pathMatch: RegExpExecArray): PathParameterValues {
200
- const pathParams = Object.create(null);
201
- for (const ix in this._keys) {
202
- const key = this._keys[ix];
203
- const matchIndex = +ix + 1;
204
- pathParams[key.name] = pathMatch[matchIndex];
205
- }
206
- return pathParams;
207
- }
208
- }
209
-
210
- export function createResolvedRoute(
211
- route: RouteEntry,
212
- pathParams: PathParameterValues,
213
- ): ResolvedRoute {
214
- return Object.create(route, {
215
- pathParams: {
216
- writable: false,
217
- value: pathParams,
218
- },
219
- });
220
- }
221
-
222
- export class Route extends BaseRoute {
223
- constructor(
224
- verb: string,
225
- path: string,
226
- public readonly spec: OperationObject,
227
- protected readonly _handler: Function,
228
- ) {
229
- super(verb, path, spec);
230
- }
231
-
232
- describe(): string {
233
- return this._handler.name || super.describe();
234
- }
235
-
236
- updateBindings(requestContext: Context) {
237
- // no-op
238
- }
239
-
240
- async invokeHandler(
241
- requestContext: Context,
242
- args: OperationArgs,
243
- ): Promise<OperationRetval> {
244
- return await this._handler(...args);
245
- }
246
- }
247
-
248
- type ControllerInstance = {[opName: string]: Function};
249
-
250
- export class ControllerRoute extends BaseRoute {
251
- protected readonly _methodName: string;
252
-
253
- constructor(
254
- verb: string,
255
- path: string,
256
- spec: OperationObject,
257
- protected readonly _controllerCtor: ControllerClass,
258
- methodName?: string,
259
- ) {
260
- super(
261
- verb,
262
- path,
263
- // Add x-controller-name and x-operation-name if not present
264
- Object.assign(
265
- {
266
- 'x-controller-name': _controllerCtor.name,
267
- 'x-operation-name': methodName,
268
- },
269
- spec,
270
- ),
271
- );
272
-
273
- if (!methodName) {
274
- methodName = this.spec['x-operation-name'];
275
- }
276
-
277
- if (!methodName) {
278
- throw new Error(
279
- 'methodName must be provided either via the ControllerRoute argument ' +
280
- 'or via "x-operation-name" extension field in OpenAPI spec. ' +
281
- `Operation: "${verb} ${path}" ` +
282
- `Controller: ${this._controllerCtor.name}.`,
110
+ if (this._externalRoutes) {
111
+ debug(
112
+ 'No API route found for %s %s, trying to find an external Express route',
113
+ request.method,
114
+ request.path,
283
115
  );
284
- }
285
116
 
286
- this._methodName = methodName;
287
- }
288
-
289
- describe(): string {
290
- return `${this._controllerCtor.name}.${this._methodName}`;
291
- }
292
-
293
- updateBindings(requestContext: Context) {
294
- const ctor = this._controllerCtor;
295
- requestContext.bind('controller.current.ctor').to(ctor);
296
- requestContext.bind('controller.current.operation').to(this._methodName);
297
- }
298
-
299
- async invokeHandler(
300
- requestContext: Context,
301
- args: OperationArgs,
302
- ): Promise<OperationRetval> {
303
- const controller = await this._createControllerInstance(requestContext);
304
- if (typeof controller[this._methodName] !== 'function') {
305
- throw new HttpErrors.NotFound(
306
- `Controller method not found: ${this.describe()}`,
307
- );
117
+ return this._externalRoutes.find(request);
308
118
  }
309
- return await controller[this._methodName](...args);
310
- }
311
119
 
312
- private async _createControllerInstance(
313
- requestContext: Context,
314
- ): Promise<ControllerInstance> {
315
- const valueOrPromise = instantiateClass(
316
- this._controllerCtor,
317
- requestContext,
120
+ debug('No route found for %s %s', request.method, request.path);
121
+ throw new HttpErrors.NotFound(
122
+ `Endpoint "${request.method} ${request.path}" not found.`,
318
123
  );
319
- return (await Promise.resolve(valueOrPromise)) as ControllerInstance;
320
124
  }
321
125
  }
322
126
 
323
127
  function describeOperationParameters(opSpec: OperationObject) {
324
128
  return ((opSpec.parameters as ParameterObject[]) || [])
325
- .map(p => p.name)
129
+ .map(p => p?.name || '')
326
130
  .join(', ');
327
131
  }
@@ -0,0 +1,57 @@
1
+ // Copyright IBM Corp. 2018,2019. All Rights Reserved.
2
+ // Node module: @loopback/rest
3
+ // This file is licensed under the MIT License.
4
+ // License text available at https://opensource.org/licenses/MIT
5
+
6
+ import {inject} from '@loopback/context';
7
+ import {inspect} from 'util';
8
+ import {RestBindings} from '../keys';
9
+ import {RestRouterOptions} from './rest-router';
10
+ import {createResolvedRoute, ResolvedRoute, RouteEntry} from './route-entry';
11
+ import {BaseRouter} from './router-base';
12
+ import {Trie} from './trie';
13
+
14
+ const debug = require('debug')('loopback:rest:router:trie');
15
+
16
+ /**
17
+ * Router implementation based on trie
18
+ */
19
+ export class TrieRouter extends BaseRouter {
20
+ private trie = new Trie<RouteEntry>();
21
+
22
+ constructor(
23
+ @inject(RestBindings.ROUTER_OPTIONS, {optional: true})
24
+ options?: RestRouterOptions,
25
+ ) {
26
+ super(options);
27
+ }
28
+
29
+ protected addRouteWithPathVars(route: RouteEntry) {
30
+ // Add the route to the trie
31
+ const key = this.getKeyForRoute(route);
32
+ this.trie.create(key, route);
33
+ }
34
+
35
+ protected findRouteWithPathVars(
36
+ verb: string,
37
+ path: string,
38
+ ): ResolvedRoute | undefined {
39
+ const key = this.getKey(verb, path);
40
+
41
+ const found = this.trie.match(key);
42
+ debug('Route matched: %j', found);
43
+
44
+ if (found) {
45
+ const route = found.node.value!;
46
+ if (route) {
47
+ debug('Route found: %s', inspect(route, {depth: 5}));
48
+ return createResolvedRoute(route, found.params ?? {});
49
+ }
50
+ }
51
+ return undefined;
52
+ }
53
+
54
+ protected listRoutesWithPathVars() {
55
+ return this.trie.list().map(n => n.value);
56
+ }
57
+ }
@@ -0,0 +1,233 @@
1
+ // Copyright IBM Corp. 2018,2019. All Rights Reserved.
2
+ // Node module: @loopback/rest
3
+ // This file is licensed under the MIT License.
4
+ // License text available at https://opensource.org/licenses/MIT
5
+
6
+ import {Key, pathToRegexp} from 'path-to-regexp';
7
+ import {PathParameterValues} from '../types';
8
+ import {toExpressPath} from './openapi-path';
9
+
10
+ /**
11
+ * A Node in the trie
12
+ */
13
+ export interface Node<T> {
14
+ /**
15
+ * Key of the node
16
+ */
17
+ key: string;
18
+ /**
19
+ * Value of the node
20
+ */
21
+ value?: T;
22
+ /**
23
+ * Children of the node
24
+ */
25
+ readonly children: {[key: string]: Node<T>};
26
+
27
+ /**
28
+ * Regular expression for the template
29
+ */
30
+ regexp?: RegExp;
31
+ /**
32
+ * Names of the node if it contains named parameters
33
+ */
34
+ names?: string[];
35
+ }
36
+
37
+ export type NodeWithValue<T> = Node<T> & {value: T};
38
+
39
+ export interface ResolvedNode<T> {
40
+ node: Node<T>;
41
+ params?: PathParameterValues;
42
+ }
43
+
44
+ /**
45
+ * An implementation of trie for routes. The key hierarchy is built with parts
46
+ * of the route path delimited by `/`
47
+ */
48
+ export class Trie<T> {
49
+ readonly root: Node<T> = {key: '', children: {}};
50
+
51
+ /**
52
+ * Create a node for a given path template
53
+ * @param pathTemplate - The path template,
54
+ * @param value - Value of the route
55
+ */
56
+ create(routeTemplate: string, value: T) {
57
+ const keys = routeTemplate.split('/').filter(Boolean);
58
+ return createNode(keys, 0, value, this.root);
59
+ }
60
+
61
+ /**
62
+ * Match a route path against the trie
63
+ * @param path - The route path, such as `/customers/c01`
64
+ */
65
+ match(
66
+ path: string,
67
+ ): (ResolvedNode<T> & {node: NodeWithValue<T>}) | undefined {
68
+ const keys = path.split('/').filter(Boolean);
69
+ const params = {};
70
+ const resolved = search(keys, 0, params, this.root);
71
+ if (resolved == null || !isNodeWithValue(resolved.node)) return undefined;
72
+ return {
73
+ node: resolved.node,
74
+ params: resolved.params,
75
+ };
76
+ }
77
+
78
+ /**
79
+ * List all nodes with value of the trie
80
+ */
81
+ list(): NodeWithValue<T>[] {
82
+ const nodes: NodeWithValue<T>[] = [];
83
+ traverse(this.root, node => {
84
+ nodes.push(node);
85
+ });
86
+ return nodes;
87
+ }
88
+ }
89
+
90
+ function isNodeWithValue<T>(node: Node<T>): node is NodeWithValue<T> {
91
+ return node.value != null;
92
+ }
93
+
94
+ /**
95
+ * Use depth-first preorder traversal to list all nodes with values
96
+ * @param root - Root node
97
+ * @param visitor - A function to process nodes with values
98
+ */
99
+ function traverse<T>(root: Node<T>, visitor: (node: NodeWithValue<T>) => void) {
100
+ if (isNodeWithValue(root)) visitor(root);
101
+ for (const k in root.children) {
102
+ traverse(root.children[k], visitor);
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Match the given key to one or more children of the parent node
108
+ * @param key - Key
109
+ * @param parent - Parent node
110
+ */
111
+ function matchChildren<T>(key: string, parent: Node<T>): ResolvedNode<T>[] {
112
+ const resolvedNodes: ResolvedNode<T>[] = [];
113
+ // Match key literal first
114
+ let child = parent.children[key];
115
+ if (child) {
116
+ resolvedNodes.push({
117
+ node: child,
118
+ });
119
+ return resolvedNodes;
120
+ }
121
+
122
+ // Match templates
123
+ for (const k in parent.children) {
124
+ child = parent.children[k];
125
+ if (!child.names || !child.regexp) continue;
126
+ const match = child.regexp.exec(key);
127
+ if (match) {
128
+ const resolved: ResolvedNode<T> = {params: {}, node: child};
129
+ let i = 0;
130
+ for (const n of child.names) {
131
+ const val = match[++i];
132
+ resolved.params![n] = decodeURIComponent(val);
133
+ }
134
+ resolvedNodes.push(resolved);
135
+ }
136
+ }
137
+ return resolvedNodes;
138
+ }
139
+
140
+ /**
141
+ * Search a sub list of keys against the parent node
142
+ * @param keys - An array of keys
143
+ * @param index - Starting index of the key list
144
+ * @param params - An object to receive resolved parameter values
145
+ * @param parent - Parent node
146
+ */
147
+ function search<T>(
148
+ keys: string[],
149
+ index: number,
150
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
151
+ params: {[name: string]: any},
152
+ parent: Node<T>,
153
+ ): ResolvedNode<T> | undefined {
154
+ const key = keys[index];
155
+ const resolved: ResolvedNode<T> = {node: parent, params};
156
+ if (key === undefined) return resolved;
157
+
158
+ const children = matchChildren(key, parent);
159
+ if (children.length === 0) return undefined;
160
+ // There might be multiple matches, such as `/users/{id}` and `/users/{userId}`
161
+ for (const child of children) {
162
+ const result = search(keys, index + 1, params, child.node);
163
+ if (result && isNodeWithValue(result.node)) {
164
+ Object.assign(params, child.params);
165
+ return result;
166
+ }
167
+ }
168
+ // no matches found
169
+ return undefined;
170
+ }
171
+
172
+ /**
173
+ * Create a node for a sub list of keys against the parent node
174
+ * @param keys - An array of keys
175
+ * @param index - Starting index of the key list
176
+ * @param value - Value of the node
177
+ * @param parent - Parent node
178
+ */
179
+ function createNode<T>(
180
+ keys: string[],
181
+ index: number,
182
+ value: T,
183
+ parent: Node<T>,
184
+ ): Node<T> {
185
+ const key = keys[index];
186
+ if (key === undefined) return parent;
187
+
188
+ const isLast = keys.length - 1 === index;
189
+ let child = parent.children[key];
190
+ if (child != null) {
191
+ // Found an existing node
192
+ if (isLast) {
193
+ if (child.value == null) {
194
+ child.value = value;
195
+ } else {
196
+ if (child.value !== value) {
197
+ throw new Error(
198
+ 'Duplicate key found with different value: ' + keys.join('/'),
199
+ );
200
+ }
201
+ }
202
+ }
203
+ return createNode(keys, index + 1, value, child);
204
+ }
205
+
206
+ /**
207
+ * Build a new node
208
+ */
209
+ child = {
210
+ children: {},
211
+ key: key,
212
+ };
213
+
214
+ if (isLast) {
215
+ child.value = value;
216
+ }
217
+
218
+ // Check if the key has variables such as `{var}`
219
+ const path = toExpressPath(key);
220
+ const params: Key[] = [];
221
+ const re = pathToRegexp(path, params);
222
+
223
+ if (params.length) {
224
+ child.names = params.map(p => `${p.name}`);
225
+ child.regexp = re;
226
+ }
227
+
228
+ // Add the node to the parent
229
+ parent.children[key] = child;
230
+
231
+ // Create nodes for rest of the keys
232
+ return createNode(keys, index + 1, value, child);
233
+ }