@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
@@ -0,0 +1,44 @@
1
+ // Copyright IBM Corp. 2018. 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 {text} from 'body-parser';
8
+ import {is} from 'type-is';
9
+ import {RestBindings} from '../keys';
10
+ import {Request, RequestBodyParserOptions} from '../types';
11
+ import {
12
+ BodyParserMiddleware,
13
+ getParserOptions,
14
+ invokeBodyParserMiddleware,
15
+ builtinParsers,
16
+ } from './body-parser.helpers';
17
+ import {BodyParser, RequestBody} from './types';
18
+
19
+ export class TextBodyParser implements BodyParser {
20
+ name = builtinParsers.text;
21
+ private textParser: BodyParserMiddleware;
22
+
23
+ constructor(
24
+ @inject(RestBindings.REQUEST_BODY_PARSER_OPTIONS, {optional: true})
25
+ options: RequestBodyParserOptions = {},
26
+ ) {
27
+ const textOptions = Object.assign(
28
+ {type: 'text/*'},
29
+ getParserOptions('text', options),
30
+ );
31
+ this.textParser = text(textOptions);
32
+ }
33
+
34
+ supports(mediaType: string) {
35
+ // Please note that `text/*` matches `text/plain` and `text/html` but`text`
36
+ // does not.
37
+ return !!is(mediaType, 'text/*');
38
+ }
39
+
40
+ async parse(request: Request): Promise<RequestBody> {
41
+ const body = await invokeBodyParserMiddleware(this.textParser, request);
42
+ return {value: body};
43
+ }
44
+ }
@@ -0,0 +1,208 @@
1
+ // Copyright IBM Corp. 2018,2020. 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 {
7
+ compareByOrder,
8
+ Constructor,
9
+ Context,
10
+ filterByTag,
11
+ inject,
12
+ instantiateClass,
13
+ } from '@loopback/context';
14
+ import {isReferenceObject, OperationObject} from '@loopback/openapi-v3';
15
+ import debugModule from 'debug';
16
+ import {is} from 'type-is';
17
+ import {RestHttpErrors} from '../rest-http-error';
18
+ import {Request} from '../types';
19
+ import {
20
+ builtinParsers,
21
+ getContentType,
22
+ normalizeParsingError,
23
+ } from './body-parser.helpers';
24
+ import {
25
+ BodyParser,
26
+ BodyParserFunction,
27
+ RequestBody,
28
+ REQUEST_BODY_PARSER_TAG,
29
+ } from './types';
30
+
31
+ const debug = debugModule('loopback:rest:body-parser');
32
+
33
+ export class RequestBodyParser {
34
+ readonly parsers: BodyParser[];
35
+
36
+ constructor(
37
+ @inject(filterByTag(REQUEST_BODY_PARSER_TAG), {optional: true})
38
+ parsers?: BodyParser[],
39
+ @inject.context() private readonly ctx?: Context,
40
+ ) {
41
+ this.parsers = sortParsers(parsers ?? []);
42
+ if (debug.enabled) {
43
+ debug(
44
+ 'Body parsers: ',
45
+ this.parsers.map(p => p.name),
46
+ );
47
+ }
48
+ }
49
+
50
+ async loadRequestBodyIfNeeded(
51
+ operationSpec: OperationObject,
52
+ request: Request,
53
+ ): Promise<RequestBody> {
54
+ const {requestBody, customParser} = await this._matchRequestBodySpec(
55
+ operationSpec,
56
+ request,
57
+ );
58
+ if (!operationSpec.requestBody) return requestBody;
59
+ const matchedMediaType = requestBody.mediaType!;
60
+ try {
61
+ if (customParser) {
62
+ // Invoke the custom parser
63
+ const body = await this._invokeCustomParser(customParser, request);
64
+ return Object.assign(requestBody, body);
65
+ } else {
66
+ const parser = this._findParser(matchedMediaType);
67
+ if (parser) {
68
+ const body = await parser.parse(request);
69
+ return Object.assign(requestBody, body);
70
+ }
71
+ }
72
+ } catch (err) {
73
+ throw normalizeParsingError(err);
74
+ }
75
+
76
+ throw RestHttpErrors.unsupportedMediaType(matchedMediaType);
77
+ }
78
+
79
+ /**
80
+ * Match the http request to a given media type of the request body spec
81
+ */
82
+ private async _matchRequestBodySpec(
83
+ operationSpec: OperationObject,
84
+ request: Request,
85
+ ) {
86
+ const requestBody: RequestBody = {
87
+ value: undefined,
88
+ };
89
+ if (!operationSpec.requestBody) return {requestBody};
90
+
91
+ const contentType = getContentType(request) ?? 'application/json';
92
+ debug('Loading request body with content type %j', contentType);
93
+
94
+ // the type of `operationSpec.requestBody` could be `RequestBodyObject`
95
+ // or `ReferenceObject`, resolving a `$ref` value is not supported yet.
96
+ if (isReferenceObject(operationSpec.requestBody)) {
97
+ throw new Error('$ref requestBody is not supported yet.');
98
+ }
99
+
100
+ let content = operationSpec.requestBody.content || {};
101
+ if (!Object.keys(content).length) {
102
+ content = {
103
+ // default to allow json and urlencoded
104
+ 'application/json': {schema: {type: 'object'}},
105
+ 'application/x-www-form-urlencoded': {schema: {type: 'object'}},
106
+ };
107
+ }
108
+
109
+ // Check of the request content type matches one of the expected media
110
+ // types in the request body spec
111
+ let matchedMediaType: string | false = false;
112
+ let customParser = undefined;
113
+ for (const type in content) {
114
+ matchedMediaType = is(contentType, type);
115
+ if (matchedMediaType) {
116
+ debug('Matched media type: %s -> %s', type, contentType);
117
+ requestBody.mediaType = contentType;
118
+ requestBody.schema = content[type].schema;
119
+ customParser = content[type]['x-parser'];
120
+ break;
121
+ }
122
+ }
123
+
124
+ if (!matchedMediaType) {
125
+ // No matching media type found, fail fast
126
+ throw RestHttpErrors.unsupportedMediaType(
127
+ contentType,
128
+ Object.keys(content),
129
+ );
130
+ }
131
+
132
+ return {requestBody, customParser};
133
+ }
134
+
135
+ /**
136
+ * Find a body parser that supports the media type
137
+ * @param matchedMediaType - Media type
138
+ */
139
+ private _findParser(matchedMediaType: string) {
140
+ for (const parser of this.parsers) {
141
+ if (!parser.supports(matchedMediaType)) {
142
+ debug(
143
+ 'Body parser %s does not support %s',
144
+ parser.name,
145
+ matchedMediaType,
146
+ );
147
+ continue;
148
+ }
149
+ debug('Body parser %s found for %s', parser.name, matchedMediaType);
150
+ return parser;
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Resolve and invoke a custom parser
156
+ * @param customParser - The parser name, class or function
157
+ * @param request - Http request
158
+ */
159
+ private async _invokeCustomParser(
160
+ customParser: string | Constructor<BodyParser> | BodyParserFunction,
161
+ request: Request,
162
+ ) {
163
+ if (typeof customParser === 'string') {
164
+ const parser = this.parsers.find(
165
+ p =>
166
+ p.name === customParser ||
167
+ p.name === builtinParsers.mapping[customParser],
168
+ );
169
+ if (parser) {
170
+ debug('Using custom parser %s', customParser);
171
+ return parser.parse(request);
172
+ }
173
+ } else if (typeof customParser === 'function') {
174
+ if (isBodyParserClass(customParser)) {
175
+ debug('Using custom parser class %s', customParser.name);
176
+ const parser = await instantiateClass<BodyParser>(
177
+ customParser as Constructor<BodyParser>,
178
+ this.ctx!,
179
+ );
180
+ return parser.parse(request);
181
+ } else {
182
+ debug('Using custom parser function %s', customParser.name);
183
+ return customParser(request);
184
+ }
185
+ }
186
+ throw new Error('Custom parser not found: ' + customParser);
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Test if a function is a body parser class or plain function
192
+ * @param fn
193
+ */
194
+ function isBodyParserClass(
195
+ fn: Constructor<BodyParser> | BodyParserFunction,
196
+ ): fn is Constructor<BodyParser> {
197
+ return fn.toString().startsWith('class ');
198
+ }
199
+
200
+ /**
201
+ * Sort body parsers so that built-in ones are used after extensions
202
+ * @param parsers
203
+ */
204
+ function sortParsers(parsers: BodyParser[]) {
205
+ return parsers.sort((a, b) =>
206
+ compareByOrder(a.name, b.name, builtinParsers.names),
207
+ );
208
+ }
@@ -0,0 +1,42 @@
1
+ // Copyright IBM Corp. 2018. 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 {urlencoded} from 'body-parser';
8
+ import {is} from 'type-is';
9
+ import {RestBindings} from '../keys';
10
+ import {Request, RequestBodyParserOptions} from '../types';
11
+ import {
12
+ BodyParserMiddleware,
13
+ getParserOptions,
14
+ invokeBodyParserMiddleware,
15
+ builtinParsers,
16
+ } from './body-parser.helpers';
17
+ import {BodyParser, RequestBody} from './types';
18
+
19
+ export class UrlEncodedBodyParser implements BodyParser {
20
+ name = builtinParsers.urlencoded;
21
+ private urlencodedParser: BodyParserMiddleware;
22
+
23
+ constructor(
24
+ @inject(RestBindings.REQUEST_BODY_PARSER_OPTIONS, {optional: true})
25
+ options: RequestBodyParserOptions = {},
26
+ ) {
27
+ const urlencodedOptions = getParserOptions('urlencoded', options);
28
+ this.urlencodedParser = urlencoded(urlencodedOptions);
29
+ }
30
+
31
+ supports(mediaType: string) {
32
+ return !!is(mediaType, 'urlencoded');
33
+ }
34
+
35
+ async parse(request: Request): Promise<RequestBody> {
36
+ const body = await invokeBodyParserMiddleware(
37
+ this.urlencodedParser,
38
+ request,
39
+ );
40
+ return {value: body, coercionRequired: true};
41
+ }
42
+ }
@@ -0,0 +1,13 @@
1
+ // Copyright IBM Corp. 2018. 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
+ export * from './body-parser';
7
+ export * from './body-parser.helpers';
8
+ export * from './body-parser.json';
9
+ export * from './body-parser.raw';
10
+ export * from './body-parser.stream';
11
+ export * from './body-parser.text';
12
+ export * from './body-parser.urlencoded';
13
+ export * from './types';
@@ -0,0 +1,60 @@
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 {ReferenceObject, SchemaObject} from '@loopback/openapi-v3';
7
+ import {Request} from '../types';
8
+ /**
9
+ * Request body with metadata
10
+ */
11
+ export type RequestBody = {
12
+ /**
13
+ * Parsed value of the request body
14
+ */
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ value: any | undefined;
17
+ /**
18
+ * Is coercion required? Some forms of request such as urlencoded don't
19
+ * have rich types such as number or boolean.
20
+ */
21
+ coercionRequired?: boolean;
22
+ /**
23
+ * Resolved media type
24
+ */
25
+ mediaType?: string;
26
+ /**
27
+ * Corresponding schema for the request body
28
+ */
29
+ schema?: SchemaObject | ReferenceObject;
30
+ };
31
+
32
+ /**
33
+ * Interface to be implemented by body parser extensions
34
+ */
35
+ export interface BodyParser {
36
+ /**
37
+ * Name of the parser
38
+ */
39
+ name: string | symbol;
40
+ /**
41
+ * Indicate if the given media type is supported
42
+ * @param mediaType - Media type
43
+ */
44
+ supports(mediaType: string): boolean;
45
+ /**
46
+ * Parse the request body
47
+ * @param request - http request
48
+ */
49
+ parse(request: Request): Promise<RequestBody>;
50
+ }
51
+
52
+ /**
53
+ * Plain function for body parsing
54
+ */
55
+ export type BodyParserFunction = (request: Request) => Promise<RequestBody>;
56
+
57
+ /**
58
+ * Binding tag for request body parser extensions
59
+ */
60
+ export const REQUEST_BODY_PARSER_TAG = 'rest.requestBodyParser';
@@ -0,0 +1,207 @@
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 {isReferenceObject, ParameterObject} from '@loopback/openapi-v3';
7
+ import debugModule from 'debug';
8
+ import {RestHttpErrors} from '../';
9
+ import {parseJson} from '../parse-json';
10
+ import {
11
+ DateCoercionOptions,
12
+ getOAIPrimitiveType,
13
+ IntegerCoercionOptions,
14
+ isEmpty,
15
+ isFalse,
16
+ isTrue,
17
+ isValidDateTime,
18
+ matchDateFormat,
19
+ } from './utils';
20
+ import {Validator} from './validator';
21
+ const isRFC3339 = require('validator/lib/isRFC3339');
22
+ const debug = debugModule('loopback:rest:coercion');
23
+
24
+ /**
25
+ * Coerce the http raw data to a JavaScript type data of a parameter
26
+ * according to its OpenAPI schema specification.
27
+ *
28
+ * @param data - The raw data get from http request
29
+ * @param schema - The parameter's schema defined in OpenAPI specification
30
+ */
31
+ export function coerceParameter(
32
+ data: string | undefined | object,
33
+ spec: ParameterObject,
34
+ ) {
35
+ let schema = spec.schema;
36
+
37
+ // If a query parameter is a url encoded Json object, the schema is defined under content['application/json']
38
+ if (!schema && spec.in === 'query' && spec.content) {
39
+ const jsonSpec = spec.content['application/json'];
40
+ schema = jsonSpec.schema;
41
+ }
42
+
43
+ if (!schema || isReferenceObject(schema)) {
44
+ debug(
45
+ 'The parameter with schema %s is not coerced since schema' +
46
+ 'dereference is not supported yet.',
47
+ schema,
48
+ );
49
+ return data;
50
+ }
51
+ const OAIType = getOAIPrimitiveType(schema.type, schema.format);
52
+ const validator = new Validator({parameterSpec: spec});
53
+
54
+ validator.validateParamBeforeCoercion(data);
55
+ if (data === undefined) return data;
56
+
57
+ switch (OAIType) {
58
+ case 'byte':
59
+ return coerceBuffer(data, spec);
60
+ case 'date':
61
+ return coerceDatetime(data, spec, {dateOnly: true});
62
+ case 'date-time':
63
+ return coerceDatetime(data, spec);
64
+ case 'float':
65
+ case 'double':
66
+ case 'number':
67
+ return coerceNumber(data, spec);
68
+ case 'long':
69
+ return coerceInteger(data, spec, {isLong: true});
70
+ case 'integer':
71
+ return coerceInteger(data, spec);
72
+ case 'boolean':
73
+ return coerceBoolean(data, spec);
74
+ case 'object':
75
+ return coerceObject(data, spec);
76
+ case 'string':
77
+ case 'password':
78
+ return coerceString(data, spec);
79
+ default:
80
+ return data;
81
+ }
82
+ }
83
+
84
+ function coerceString(data: string | object, spec: ParameterObject) {
85
+ if (typeof data !== 'string')
86
+ throw RestHttpErrors.invalidData(data, spec.name);
87
+
88
+ debug('data of type string is coerced to %s', data);
89
+ return data;
90
+ }
91
+
92
+ function coerceBuffer(data: string | object, spec: ParameterObject) {
93
+ if (typeof data === 'object')
94
+ throw RestHttpErrors.invalidData(data, spec.name);
95
+ return Buffer.from(data, 'base64');
96
+ }
97
+
98
+ function coerceDatetime(
99
+ data: string | object,
100
+ spec: ParameterObject,
101
+ options?: DateCoercionOptions,
102
+ ) {
103
+ if (typeof data === 'object' || isEmpty(data))
104
+ throw RestHttpErrors.invalidData(data, spec.name);
105
+
106
+ if (options?.dateOnly) {
107
+ if (!matchDateFormat(data))
108
+ throw RestHttpErrors.invalidData(data, spec.name);
109
+ } else {
110
+ if (!isRFC3339(data)) throw RestHttpErrors.invalidData(data, spec.name);
111
+ }
112
+
113
+ const coercedDate = new Date(data);
114
+ if (!isValidDateTime(coercedDate))
115
+ throw RestHttpErrors.invalidData(data, spec.name);
116
+ return coercedDate;
117
+ }
118
+
119
+ function coerceNumber(data: string | object, spec: ParameterObject) {
120
+ if (typeof data === 'object' || isEmpty(data))
121
+ throw RestHttpErrors.invalidData(data, spec.name);
122
+
123
+ const coercedNum = Number(data);
124
+ if (isNaN(coercedNum)) throw RestHttpErrors.invalidData(data, spec.name);
125
+
126
+ debug('data of type number is coerced to %s', coercedNum);
127
+ return coercedNum;
128
+ }
129
+
130
+ function coerceInteger(
131
+ data: string | object,
132
+ spec: ParameterObject,
133
+ options?: IntegerCoercionOptions,
134
+ ) {
135
+ if (typeof data === 'object' || isEmpty(data))
136
+ throw RestHttpErrors.invalidData(data, spec.name);
137
+
138
+ const coercedInt = Number(data);
139
+ if (isNaN(coercedInt!)) throw RestHttpErrors.invalidData(data, spec.name);
140
+
141
+ if (options?.isLong) {
142
+ if (!Number.isInteger(coercedInt))
143
+ throw RestHttpErrors.invalidData(data, spec.name);
144
+ } else {
145
+ if (!Number.isSafeInteger(coercedInt))
146
+ throw RestHttpErrors.invalidData(data, spec.name);
147
+ }
148
+
149
+ debug('data of type integer is coerced to %s', coercedInt);
150
+ return coercedInt;
151
+ }
152
+
153
+ function coerceBoolean(data: string | object, spec: ParameterObject) {
154
+ if (typeof data === 'object' || isEmpty(data))
155
+ throw RestHttpErrors.invalidData(data, spec.name);
156
+ if (isTrue(data)) return true;
157
+ if (isFalse(data)) return false;
158
+ throw RestHttpErrors.invalidData(data, spec.name);
159
+ }
160
+
161
+ function coerceObject(input: string | object, spec: ParameterObject) {
162
+ const data = parseJsonIfNeeded(input, spec);
163
+
164
+ if (data === undefined) {
165
+ // Skip any further checks and coercions, nothing we can do with `undefined`
166
+ return undefined;
167
+ }
168
+
169
+ if (typeof data !== 'object' || Array.isArray(data))
170
+ throw RestHttpErrors.invalidData(input, spec.name);
171
+
172
+ // TODO(bajtos) apply coercion based on properties defined by spec.schema
173
+ return data;
174
+ }
175
+
176
+ function parseJsonIfNeeded(
177
+ data: string | object,
178
+ spec: ParameterObject,
179
+ ): string | object | undefined {
180
+ if (typeof data !== 'string') return data;
181
+
182
+ if (spec.in !== 'query' || (spec.in === 'query' && !spec.content)) {
183
+ debug(
184
+ 'Skipping JSON.parse, argument %s is not a url encoded json object query parameter (since content field is missing in parameter schema)',
185
+ spec.name,
186
+ );
187
+ return data;
188
+ }
189
+
190
+ if (data === '') {
191
+ debug('Converted empty string to object value `undefined`');
192
+ return undefined;
193
+ }
194
+
195
+ try {
196
+ const result = parseJson(data);
197
+ debug('Parsed parameter %s as %j', spec.name, result);
198
+ return result;
199
+ } catch (err) {
200
+ debug('Cannot parse %s value %j as JSON: %s', spec.name, data, err.message);
201
+ throw RestHttpErrors.invalidData(data, spec.name, {
202
+ details: {
203
+ syntaxError: err.message,
204
+ },
205
+ });
206
+ }
207
+ }
@@ -0,0 +1,103 @@
1
+ // Copyright IBM Corp. 2018,2020. 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 debugModule from 'debug';
7
+ const debug = debugModule('loopback:rest:coercion');
8
+
9
+ /**
10
+ * Options for function coerceDatetime
11
+ */
12
+ export type DateCoercionOptions = {
13
+ dateOnly?: boolean;
14
+ };
15
+
16
+ /**
17
+ * Options for function coerceInteger
18
+ */
19
+ export type IntegerCoercionOptions = {
20
+ isLong?: boolean;
21
+ };
22
+
23
+ export function isEmpty(data: string) {
24
+ const result = data === '';
25
+ debug('isEmpty(%j) -> %s', data, result);
26
+ return result;
27
+ }
28
+ /**
29
+ * A set of truthy values. A data in this set will be coerced to `true`.
30
+ *
31
+ * @param data - The raw data get from http request
32
+ * @returns The corresponding coerced boolean type
33
+ */
34
+ export function isTrue(data: string): boolean {
35
+ return ['TRUE', '1'].includes(data.toUpperCase());
36
+ }
37
+
38
+ /**
39
+ * A set of falsy values. A data in this set will be coerced to `false`.
40
+ * @param data - The raw data get from http request
41
+ * @returns The corresponding coerced boolean type
42
+ */
43
+ export function isFalse(data: string): boolean {
44
+ return ['FALSE', '0'].includes(data.toUpperCase());
45
+ }
46
+
47
+ /**
48
+ * Return false for invalid date
49
+ */
50
+ export function isValidDateTime(data: Date) {
51
+ return isNaN(data.getTime()) ? false : true;
52
+ }
53
+
54
+ const REGEX_RFC3339_DATE = /^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])$/;
55
+
56
+ /**
57
+ * Return true when a date follows the RFC3339 standard
58
+ *
59
+ * @param date - The date to verify
60
+ */
61
+ export function matchDateFormat(date: string) {
62
+ const pattern = new RegExp(REGEX_RFC3339_DATE);
63
+ const result = pattern.test(date);
64
+ debug('matchDateFormat(%j) -> %s', date, result);
65
+ return result;
66
+ }
67
+
68
+ /**
69
+ * Return the corresponding OpenAPI data type given an OpenAPI schema
70
+ *
71
+ * @param type - The type in an OpenAPI schema specification
72
+ * @param format - The format in an OpenAPI schema specification
73
+ */
74
+ export function getOAIPrimitiveType(type?: string, format?: string) {
75
+ if (type === 'object' || type === 'array') return type;
76
+ if (type === 'string') {
77
+ switch (format) {
78
+ case 'byte':
79
+ return 'byte';
80
+ case 'binary':
81
+ return 'binary';
82
+ case 'date':
83
+ return 'date';
84
+ case 'date-time':
85
+ return 'date-time';
86
+ case 'password':
87
+ return 'password';
88
+ default:
89
+ return 'string';
90
+ }
91
+ }
92
+ if (type === 'boolean') return 'boolean';
93
+ if (type === 'number')
94
+ switch (format) {
95
+ case 'float':
96
+ return 'float';
97
+ case 'double':
98
+ return 'double';
99
+ default:
100
+ return 'number';
101
+ }
102
+ if (type === 'integer') return format === 'int64' ? 'long' : 'integer';
103
+ }