@decaf-ts/for-nest 0.11.2 → 0.12.0

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 (322) hide show
  1. package/README.md +274 -2
  2. package/dist/for-nest.cjs +1 -1
  3. package/dist/for-nest.cjs.map +1 -1
  4. package/dist/for-nest.js +1 -1
  5. package/dist/for-nest.js.map +1 -1
  6. package/lib/cjs/auth/AuthInterceptor.cjs +93 -0
  7. package/lib/cjs/auth/AuthInterceptor.cjs.map +1 -0
  8. package/lib/cjs/auth/DecafAuthHandler.cjs +37 -0
  9. package/lib/cjs/auth/DecafAuthHandler.cjs.map +1 -0
  10. package/lib/cjs/auth/DecafAuthModule.cjs +50 -0
  11. package/lib/cjs/auth/DecafAuthModule.cjs.map +1 -0
  12. package/lib/cjs/auth/constants.cjs +10 -0
  13. package/lib/cjs/auth/constants.cjs.map +1 -0
  14. package/lib/cjs/auth/decorators.cjs +28 -0
  15. package/lib/cjs/auth/decorators.cjs.map +1 -0
  16. package/lib/cjs/{ram → auth}/index.cjs +6 -2
  17. package/lib/cjs/auth/index.cjs.map +1 -0
  18. package/lib/cjs/bin/cli.cjs +1 -1
  19. package/lib/cjs/bin/cli.cjs.map +1 -1
  20. package/lib/cjs/cli-module.cjs +63 -6
  21. package/lib/cjs/cli-module.cjs.map +1 -1
  22. package/lib/cjs/constants.cjs +4 -5
  23. package/lib/cjs/constants.cjs.map +1 -1
  24. package/lib/cjs/controllers.cjs +15 -112
  25. package/lib/cjs/controllers.cjs.map +1 -1
  26. package/lib/cjs/core-module.cjs +7 -7
  27. package/lib/cjs/core-module.cjs.map +1 -1
  28. package/lib/cjs/decaf-model/DecafModelModule.cjs +27 -3
  29. package/lib/cjs/decaf-model/DecafModelModule.cjs.map +1 -1
  30. package/lib/cjs/decaf-model/FromModelController.cjs +618 -792
  31. package/lib/cjs/decaf-model/FromModelController.cjs.map +1 -1
  32. package/lib/cjs/decaf-model/decorators/ApiOperationFromModel.cjs +52 -15
  33. package/lib/cjs/decaf-model/decorators/ApiOperationFromModel.cjs.map +1 -1
  34. package/lib/cjs/decaf-model/decorators/ApiParamsFromModel.cjs +1 -1
  35. package/lib/cjs/decaf-model/decorators/ApiParamsFromModel.cjs.map +1 -1
  36. package/lib/cjs/decaf-model/decorators/DecafBody.cjs +1 -1
  37. package/lib/cjs/decaf-model/decorators/DecafBody.cjs.map +1 -1
  38. package/lib/cjs/decaf-model/decorators/DecafParams.cjs +1 -1
  39. package/lib/cjs/decaf-model/decorators/DecafParams.cjs.map +1 -1
  40. package/lib/cjs/decaf-model/decorators/controller-config.cjs +29 -0
  41. package/lib/cjs/decaf-model/decorators/controller-config.cjs.map +1 -0
  42. package/lib/cjs/decaf-model/decorators/decorators.cjs +4 -44
  43. package/lib/cjs/decaf-model/decorators/decorators.cjs.map +1 -1
  44. package/lib/cjs/decaf-model/decorators/expose.cjs +13 -0
  45. package/lib/cjs/decaf-model/decorators/expose.cjs.map +1 -0
  46. package/lib/cjs/decaf-model/decorators/index.cjs +2 -1
  47. package/lib/cjs/decaf-model/decorators/index.cjs.map +1 -1
  48. package/lib/cjs/decaf-model/decorators/types.cjs +1 -1
  49. package/lib/cjs/decaf-model/decorators/types.cjs.map +1 -1
  50. package/lib/cjs/decaf-model/decorators/utils.cjs +3 -2
  51. package/lib/cjs/decaf-model/decorators/utils.cjs.map +1 -1
  52. package/lib/cjs/decaf-model/index.cjs +27 -18
  53. package/lib/cjs/decaf-model/index.cjs.map +1 -1
  54. package/lib/cjs/decaf-model/types.cjs +1 -1
  55. package/lib/cjs/decaf-model/types.cjs.map +1 -1
  56. package/lib/cjs/decaf-model/utils.cjs +10 -4
  57. package/lib/cjs/decaf-model/utils.cjs.map +1 -1
  58. package/lib/cjs/decoration.cjs +1 -1
  59. package/lib/cjs/decoration.cjs.map +1 -1
  60. package/lib/cjs/decorators.cjs +93 -0
  61. package/lib/cjs/decorators.cjs.map +1 -0
  62. package/lib/cjs/events-module/DecafStreamModule.cjs +1 -1
  63. package/lib/cjs/events-module/DecafStreamModule.cjs.map +1 -1
  64. package/lib/cjs/events-module/EventsController.cjs +1 -1
  65. package/lib/cjs/events-module/EventsController.cjs.map +1 -1
  66. package/lib/cjs/events-module/constant.cjs +1 -1
  67. package/lib/cjs/events-module/constant.cjs.map +1 -1
  68. package/lib/cjs/events-module/index.cjs +1 -1
  69. package/lib/cjs/events-module/index.cjs.map +1 -1
  70. package/lib/cjs/events-module/utils.cjs +1 -1
  71. package/lib/cjs/events-module/utils.cjs.map +1 -1
  72. package/lib/cjs/factory/NestBootstraper.cjs +11 -4
  73. package/lib/cjs/factory/NestBootstraper.cjs.map +1 -1
  74. package/lib/cjs/factory/errors/cors.cjs +1 -1
  75. package/lib/cjs/factory/errors/cors.cjs.map +1 -1
  76. package/lib/cjs/factory/errors/index.cjs +1 -1
  77. package/lib/cjs/factory/errors/index.cjs.map +1 -1
  78. package/lib/cjs/factory/errors/throttling.cjs +1 -1
  79. package/lib/cjs/factory/errors/throttling.cjs.map +1 -1
  80. package/lib/cjs/factory/exceptions/DecafErrorFilter.cjs +105 -6
  81. package/lib/cjs/factory/exceptions/DecafErrorFilter.cjs.map +1 -1
  82. package/lib/cjs/factory/exceptions/decorators.cjs +4 -1
  83. package/lib/cjs/factory/exceptions/decorators.cjs.map +1 -1
  84. package/lib/cjs/factory/exceptions/index.cjs +1 -1
  85. package/lib/cjs/factory/exceptions/index.cjs.map +1 -1
  86. package/lib/cjs/factory/index.cjs +1 -1
  87. package/lib/cjs/factory/index.cjs.map +1 -1
  88. package/lib/cjs/factory/openapi/DtoBuilder.cjs +36 -5
  89. package/lib/cjs/factory/openapi/DtoBuilder.cjs.map +1 -1
  90. package/lib/cjs/factory/openapi/SwaggerBuilder.cjs +1 -1
  91. package/lib/cjs/factory/openapi/SwaggerBuilder.cjs.map +1 -1
  92. package/lib/cjs/factory/openapi/SwaggerCustomUI.cjs +1 -1
  93. package/lib/cjs/factory/openapi/SwaggerCustomUI.cjs.map +1 -1
  94. package/lib/cjs/factory/openapi/constants.cjs +1 -1
  95. package/lib/cjs/factory/openapi/constants.cjs.map +1 -1
  96. package/lib/cjs/factory/openapi/index.cjs +1 -1
  97. package/lib/cjs/factory/openapi/index.cjs.map +1 -1
  98. package/lib/cjs/index.cjs +6 -5
  99. package/lib/cjs/index.cjs.map +1 -1
  100. package/lib/cjs/interceptors/DecafRequestHandlerInterceptor.cjs +24 -20
  101. package/lib/cjs/interceptors/DecafRequestHandlerInterceptor.cjs.map +1 -1
  102. package/lib/cjs/interceptors/index.cjs +2 -3
  103. package/lib/cjs/interceptors/index.cjs.map +1 -1
  104. package/lib/cjs/module.cjs +4 -1
  105. package/lib/cjs/module.cjs.map +1 -1
  106. package/lib/cjs/overrides/Adapter.cjs +1 -1
  107. package/lib/cjs/overrides/Adapter.cjs.map +1 -1
  108. package/lib/cjs/overrides/ModelBuilderExtensions.cjs +1 -1
  109. package/lib/cjs/overrides/ModelBuilderExtensions.cjs.map +1 -1
  110. package/lib/cjs/overrides/constants.cjs +1 -1
  111. package/lib/cjs/overrides/constants.cjs.map +1 -1
  112. package/lib/cjs/overrides/decoration.cjs +1 -1
  113. package/lib/cjs/overrides/decoration.cjs.map +1 -1
  114. package/lib/cjs/overrides/helpers.cjs +1 -1
  115. package/lib/cjs/overrides/helpers.cjs.map +1 -1
  116. package/lib/cjs/overrides/index.cjs +2 -1
  117. package/lib/cjs/overrides/index.cjs.map +1 -1
  118. package/lib/cjs/overrides/logging.cjs +5 -0
  119. package/lib/cjs/overrides/logging.cjs.map +1 -0
  120. package/lib/cjs/overrides/overrides.cjs +1 -1
  121. package/lib/cjs/overrides/overrides.cjs.map +1 -1
  122. package/lib/cjs/request/DecafAuthHandler.cjs +4 -38
  123. package/lib/cjs/request/DecafAuthHandler.cjs.map +1 -1
  124. package/lib/cjs/request/DecafHandlerExecutor.cjs +1 -1
  125. package/lib/cjs/request/DecafHandlerExecutor.cjs.map +1 -1
  126. package/lib/cjs/request/DecafRequestContext.cjs +12 -19
  127. package/lib/cjs/request/DecafRequestContext.cjs.map +1 -1
  128. package/lib/cjs/request/DecafResponseInterceptor.cjs +5 -3
  129. package/lib/cjs/request/DecafResponseInterceptor.cjs.map +1 -1
  130. package/lib/cjs/request/index.cjs +1 -1
  131. package/lib/cjs/request/index.cjs.map +1 -1
  132. package/lib/cjs/swagger-types.cjs +1 -1
  133. package/lib/cjs/swagger-types.cjs.map +1 -1
  134. package/lib/cjs/types.cjs +1 -1
  135. package/lib/cjs/types.cjs.map +1 -1
  136. package/lib/cjs/utils.cjs +1 -1
  137. package/lib/cjs/utils.cjs.map +1 -1
  138. package/lib/cjs/webhooks/DecafWebhookModule.cjs +4 -3
  139. package/lib/cjs/webhooks/DecafWebhookModule.cjs.map +1 -1
  140. package/lib/cjs/webhooks/controllers.cjs +49 -54
  141. package/lib/cjs/webhooks/controllers.cjs.map +1 -1
  142. package/lib/cjs/webhooks/index.cjs +1 -1
  143. package/lib/cjs/webhooks/index.cjs.map +1 -1
  144. package/lib/cjs/webhooks/types.cjs +1 -1
  145. package/lib/cjs/webhooks/types.cjs.map +1 -1
  146. package/lib/esm/auth/AuthInterceptor.js +84 -0
  147. package/lib/esm/auth/AuthInterceptor.js.map +1 -0
  148. package/lib/esm/auth/DecafAuthHandler.js +31 -0
  149. package/lib/esm/auth/DecafAuthHandler.js.map +1 -0
  150. package/lib/esm/auth/DecafAuthModule.js +42 -0
  151. package/lib/esm/auth/DecafAuthModule.js.map +1 -0
  152. package/lib/esm/auth/constants.js +6 -0
  153. package/lib/esm/auth/constants.js.map +1 -0
  154. package/lib/esm/auth/decorators.js +22 -0
  155. package/lib/esm/auth/decorators.js.map +1 -0
  156. package/lib/esm/auth/index.js +6 -0
  157. package/lib/esm/auth/index.js.map +1 -0
  158. package/lib/esm/cli-module.js +62 -5
  159. package/lib/esm/cli-module.js.map +1 -1
  160. package/lib/esm/constants.js +2 -3
  161. package/lib/esm/constants.js.map +1 -1
  162. package/lib/esm/controllers.js +15 -112
  163. package/lib/esm/controllers.js.map +1 -1
  164. package/lib/esm/core-module.js +6 -6
  165. package/lib/esm/core-module.js.map +1 -1
  166. package/lib/esm/decaf-model/DecafModelModule.js +26 -2
  167. package/lib/esm/decaf-model/DecafModelModule.js.map +1 -1
  168. package/lib/esm/decaf-model/FromModelController.js +622 -796
  169. package/lib/esm/decaf-model/FromModelController.js.map +1 -1
  170. package/lib/esm/decaf-model/decorators/ApiOperationFromModel.js +52 -14
  171. package/lib/esm/decaf-model/decorators/ApiOperationFromModel.js.map +1 -1
  172. package/lib/esm/decaf-model/decorators/controller-config.js +25 -0
  173. package/lib/esm/decaf-model/decorators/controller-config.js.map +1 -0
  174. package/lib/esm/decaf-model/decorators/decorators.js +1 -42
  175. package/lib/esm/decaf-model/decorators/decorators.js.map +1 -1
  176. package/lib/esm/decaf-model/decorators/expose.js +9 -0
  177. package/lib/esm/decaf-model/decorators/expose.js.map +1 -0
  178. package/lib/esm/decaf-model/decorators/index.js +1 -0
  179. package/lib/esm/decaf-model/decorators/index.js.map +1 -1
  180. package/lib/esm/decaf-model/decorators/utils.js +2 -1
  181. package/lib/esm/decaf-model/decorators/utils.js.map +1 -1
  182. package/lib/esm/decaf-model/index.js +10 -3
  183. package/lib/esm/decaf-model/index.js.map +1 -1
  184. package/lib/esm/decaf-model/utils.js +9 -3
  185. package/lib/esm/decaf-model/utils.js.map +1 -1
  186. package/lib/esm/decorators.js +87 -0
  187. package/lib/esm/decorators.js.map +1 -0
  188. package/lib/esm/events-module/utils.js.map +1 -1
  189. package/lib/esm/factory/NestBootstraper.js +10 -3
  190. package/lib/esm/factory/NestBootstraper.js.map +1 -1
  191. package/lib/esm/factory/exceptions/DecafErrorFilter.js +105 -7
  192. package/lib/esm/factory/exceptions/DecafErrorFilter.js.map +1 -1
  193. package/lib/esm/factory/exceptions/decorators.js +3 -0
  194. package/lib/esm/factory/exceptions/decorators.js.map +1 -1
  195. package/lib/esm/factory/openapi/DtoBuilder.js +35 -4
  196. package/lib/esm/factory/openapi/DtoBuilder.js.map +1 -1
  197. package/lib/esm/index.js +5 -4
  198. package/lib/esm/index.js.map +1 -1
  199. package/lib/esm/interceptors/DecafRequestHandlerInterceptor.js +24 -20
  200. package/lib/esm/interceptors/DecafRequestHandlerInterceptor.js.map +1 -1
  201. package/lib/esm/interceptors/index.js +1 -2
  202. package/lib/esm/interceptors/index.js.map +1 -1
  203. package/lib/esm/module.js +3 -0
  204. package/lib/esm/module.js.map +1 -1
  205. package/lib/esm/overrides/index.js +1 -0
  206. package/lib/esm/overrides/index.js.map +1 -1
  207. package/lib/esm/overrides/logging.js +2 -0
  208. package/lib/esm/overrides/logging.js.map +1 -0
  209. package/lib/esm/request/DecafAuthHandler.js +1 -35
  210. package/lib/esm/request/DecafAuthHandler.js.map +1 -1
  211. package/lib/esm/request/DecafRequestContext.js +11 -18
  212. package/lib/esm/request/DecafRequestContext.js.map +1 -1
  213. package/lib/esm/request/DecafResponseInterceptor.js +4 -2
  214. package/lib/esm/request/DecafResponseInterceptor.js.map +1 -1
  215. package/lib/esm/webhooks/DecafWebhookModule.js +2 -1
  216. package/lib/esm/webhooks/DecafWebhookModule.js.map +1 -1
  217. package/lib/esm/webhooks/controllers.js +48 -53
  218. package/lib/esm/webhooks/controllers.js.map +1 -1
  219. package/lib/types/{interceptors → auth}/AuthInterceptor.d.cts +4 -1
  220. package/lib/types/{interceptors → auth}/AuthInterceptor.d.mts +4 -1
  221. package/lib/types/auth/DecafAuthHandler.d.cts +22 -0
  222. package/lib/types/auth/DecafAuthHandler.d.mts +22 -0
  223. package/lib/types/auth/DecafAuthModule.d.cts +9 -0
  224. package/lib/types/auth/DecafAuthModule.d.mts +9 -0
  225. package/lib/types/auth/constants.d.cts +5 -0
  226. package/lib/types/auth/constants.d.mts +5 -0
  227. package/lib/types/auth/decorators.d.cts +4 -0
  228. package/lib/types/auth/decorators.d.mts +4 -0
  229. package/lib/types/auth/index.d.cts +6 -0
  230. package/lib/types/auth/index.d.mts +6 -0
  231. package/lib/types/cli-module.d.cts +1 -4
  232. package/lib/types/cli-module.d.mts +1 -4
  233. package/lib/types/constants.d.cts +2 -3
  234. package/lib/types/constants.d.mts +2 -3
  235. package/lib/types/controllers.d.cts +7 -12
  236. package/lib/types/controllers.d.mts +7 -12
  237. package/lib/types/decaf-model/DecafModelModule.d.cts +1 -0
  238. package/lib/types/decaf-model/DecafModelModule.d.mts +1 -0
  239. package/lib/types/decaf-model/FromModelController.d.cts +32 -52
  240. package/lib/types/decaf-model/FromModelController.d.mts +32 -52
  241. package/lib/types/decaf-model/decorators/ApiOperationFromModel.d.cts +0 -2
  242. package/lib/types/decaf-model/decorators/ApiOperationFromModel.d.mts +0 -2
  243. package/lib/types/decaf-model/decorators/controller-config.d.cts +18 -0
  244. package/lib/types/decaf-model/decorators/controller-config.d.mts +18 -0
  245. package/lib/types/decaf-model/decorators/decorators.d.cts +1 -29
  246. package/lib/types/decaf-model/decorators/decorators.d.mts +1 -29
  247. package/lib/types/decaf-model/decorators/expose.d.cts +1 -0
  248. package/lib/types/decaf-model/decorators/expose.d.mts +1 -0
  249. package/lib/types/decaf-model/decorators/index.d.cts +1 -0
  250. package/lib/types/decaf-model/decorators/index.d.mts +1 -0
  251. package/lib/types/decaf-model/decorators/types.d.cts +8 -21
  252. package/lib/types/decaf-model/decorators/types.d.mts +8 -21
  253. package/lib/types/decaf-model/index.d.cts +11 -3
  254. package/lib/types/decaf-model/index.d.mts +11 -3
  255. package/lib/types/decaf-model/utils.d.cts +2 -1
  256. package/lib/types/decaf-model/utils.d.mts +2 -1
  257. package/lib/types/decorators.d.cts +35 -0
  258. package/lib/types/decorators.d.mts +35 -0
  259. package/lib/types/factory/NestBootstraper.d.cts +5 -0
  260. package/lib/types/factory/NestBootstraper.d.mts +5 -0
  261. package/lib/types/factory/exceptions/DecafErrorFilter.d.cts +33 -1
  262. package/lib/types/factory/exceptions/DecafErrorFilter.d.mts +33 -1
  263. package/lib/types/factory/openapi/DtoBuilder.d.cts +7 -3
  264. package/lib/types/factory/openapi/DtoBuilder.d.mts +7 -3
  265. package/lib/types/index.d.cts +5 -4
  266. package/lib/types/index.d.mts +5 -4
  267. package/lib/types/interceptors/DecafRequestHandlerInterceptor.d.cts +1 -2
  268. package/lib/types/interceptors/DecafRequestHandlerInterceptor.d.mts +1 -2
  269. package/lib/types/interceptors/index.d.cts +1 -2
  270. package/lib/types/interceptors/index.d.mts +1 -2
  271. package/lib/types/overrides/Adapter.d.cts +1 -1
  272. package/lib/types/overrides/Adapter.d.mts +1 -1
  273. package/lib/types/overrides/index.d.cts +1 -0
  274. package/lib/types/overrides/index.d.mts +1 -0
  275. package/lib/types/overrides/logging.d.cts +1 -0
  276. package/lib/types/overrides/logging.d.mts +1 -0
  277. package/lib/types/request/DecafAuthHandler.d.cts +1 -10
  278. package/lib/types/request/DecafAuthHandler.d.mts +1 -10
  279. package/lib/types/request/DecafRequestContext.d.cts +5 -5
  280. package/lib/types/request/DecafRequestContext.d.mts +5 -5
  281. package/lib/types/types.d.cts +12 -24
  282. package/lib/types/types.d.mts +12 -24
  283. package/package.json +12 -13
  284. package/lib/cjs/interceptors/AuthInterceptor.cjs +0 -52
  285. package/lib/cjs/interceptors/AuthInterceptor.cjs.map +0 -1
  286. package/lib/cjs/interceptors/context.cjs +0 -18
  287. package/lib/cjs/interceptors/context.cjs.map +0 -1
  288. package/lib/cjs/migrations/index.cjs +0 -35
  289. package/lib/cjs/migrations/index.cjs.map +0 -1
  290. package/lib/cjs/migrations/migration-module.cjs +0 -60
  291. package/lib/cjs/migrations/migration-module.cjs.map +0 -1
  292. package/lib/cjs/migrations/migration-options.cjs +0 -4
  293. package/lib/cjs/migrations/migration-options.cjs.map +0 -1
  294. package/lib/cjs/ram/RamRequestTransformer.cjs +0 -26
  295. package/lib/cjs/ram/RamRequestTransformer.cjs.map +0 -1
  296. package/lib/cjs/ram/index.cjs.map +0 -1
  297. package/lib/esm/interceptors/AuthInterceptor.js +0 -43
  298. package/lib/esm/interceptors/AuthInterceptor.js.map +0 -1
  299. package/lib/esm/interceptors/context.js +0 -12
  300. package/lib/esm/interceptors/context.js.map +0 -1
  301. package/lib/esm/migrations/index.js +0 -18
  302. package/lib/esm/migrations/index.js.map +0 -1
  303. package/lib/esm/migrations/migration-module.js +0 -52
  304. package/lib/esm/migrations/migration-module.js.map +0 -1
  305. package/lib/esm/migrations/migration-options.js +0 -7
  306. package/lib/esm/migrations/migration-options.js.map +0 -1
  307. package/lib/esm/ram/RamRequestTransformer.js +0 -21
  308. package/lib/esm/ram/RamRequestTransformer.js.map +0 -1
  309. package/lib/esm/ram/index.js +0 -2
  310. package/lib/esm/ram/index.js.map +0 -1
  311. package/lib/types/interceptors/context.d.cts +0 -6
  312. package/lib/types/interceptors/context.d.mts +0 -6
  313. package/lib/types/migrations/index.d.cts +0 -17
  314. package/lib/types/migrations/index.d.mts +0 -17
  315. package/lib/types/migrations/migration-module.d.cts +0 -15
  316. package/lib/types/migrations/migration-module.d.mts +0 -15
  317. package/lib/types/migrations/migration-options.d.cts +0 -11
  318. package/lib/types/migrations/migration-options.d.mts +0 -11
  319. package/lib/types/ram/RamRequestTransformer.d.cts +0 -5
  320. package/lib/types/ram/RamRequestTransformer.d.mts +0 -5
  321. package/lib/types/ram/index.d.cts +0 -1
  322. package/lib/types/ram/index.d.mts +0 -1
@@ -15,9 +15,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
15
15
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function")
16
16
  return Reflect.metadata(k, v);
17
17
  };
18
- var __param = (this && this.__param) || function (paramIndex, decorator) {
19
- return function (target, key) { decorator(target, key, paramIndex); };
20
- };
18
+ var DynamicModelController_1;
21
19
  const common_1 = require("@nestjs/common");
22
20
  const swagger_1 = require("@nestjs/swagger");
23
21
  const core_1 = require("@decaf-ts/core");
@@ -26,64 +24,24 @@ const logging_1 = require("@decaf-ts/logging");
26
24
  const db_decorators_1 = require("@decaf-ts/db-decorators");
27
25
  const decoration_1 = require("@decaf-ts/decoration");
28
26
  const index_js_1 = require("./decorators/index.cjs");
27
+ const utils_js_1 = require("./decorators/utils.cjs");
29
28
  const index_js_2 = require("./../request/index.cjs");
30
29
  const constants_js_1 = require("./../constants.cjs");
31
- const utils_js_1 = require("./utils.cjs");
32
- const decorators_js_1 = require("./decorators/decorators.cjs");
30
+ const constants_js_2 = require("./../auth/constants.cjs");
31
+ const decorators_js_1 = require("./../auth/decorators.cjs");
33
32
  const controllers_js_1 = require("./../controllers.cjs");
34
33
  const DtoBuilder_js_1 = require("./../factory/openapi/DtoBuilder.cjs");
35
34
  require("./../overrides/index.cjs");
36
- /**
37
- * @description
38
- * Factory and utilities for generating dynamic NestJS controllers from Decaf {@link Model} definitions.
39
- *
40
- * @summary
41
- * The `FromModelController` class provides the infrastructure necessary to automatically generate
42
- * strongly-typed CRUD controllers based on a given {@link ModelConstructor}. It inspects metadata from
43
- * the model, derives route paths, parameters, and generates a dynamic controller class at runtime with
44
- * full support for querying, creation, update, and deletion of model entities through a {@link Repo}.
45
- *
46
- * @template T The {@link Model} type associated with the generated controller.
47
- *
48
- * @param ModelClazz The model class to generate the controller from.
49
- *
50
- * @class FromModelController
51
- *
52
- * @example
53
- * ```ts
54
- * // Given a Decaf Model:
55
- * class User extends Model<User> {
56
- * id!: string;
57
- * name!: string;
58
- * }
59
- *
60
- * // Register controller:
61
- * const UserController = FromModelController.create(User);
62
- *
63
- * // NestJS will expose:
64
- * // POST /user
65
- * // GET /user/:id
66
- * // GET /user/query/:method
67
- * // PUT /user/:id
68
- * // DELETE /user/:id
69
- * ```
70
- *
71
- * @mermaid
72
- * sequenceDiagram
73
- * participant Client
74
- * participant Controller
75
- * participant Repo
76
- * participant DB
77
- *
78
- * Client->>Controller: HTTP Request
79
- * Controller->>Repo: Resolve repository for Model
80
- * Repo->>DB: Execute DB operation
81
- * DB-->>Repo: DB Result
82
- * Repo-->>Controller: Model Instance(s)
83
- * Controller-->>Client: JSON Response
84
- */
35
+ const server_1 = require("@decaf-ts/for-http/server");
85
36
  class FromModelController {
86
37
  static { this.log = logging_1.Logging.for(FromModelController.name); }
38
+ static toDecafError(error, fallbackMessage) {
39
+ if (error instanceof db_decorators_1.BaseError)
40
+ return error;
41
+ return new db_decorators_1.InternalError(error instanceof Error
42
+ ? `${fallbackMessage}: ${error.message}`
43
+ : fallbackMessage);
44
+ }
87
45
  static getPersistence(ModelClazz) {
88
46
  try {
89
47
  return core_1.Service.get(ModelClazz);
@@ -91,756 +49,537 @@ class FromModelController {
91
49
  catch (e) {
92
50
  try {
93
51
  return core_1.ModelService.getService(ModelClazz);
94
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
95
52
  }
96
- catch (e) {
53
+ catch (e2) {
97
54
  return core_1.Repository.forModel(ModelClazz);
98
55
  }
99
56
  }
100
57
  }
101
58
  static createQueryRoutesFromRepository(persistence, prefix = core_1.PersistenceKeys.QUERY) {
102
- const log = FromModelController.log.for(FromModelController.createQueryRoutesFromRepository);
103
59
  const repo = persistence instanceof core_1.ModelService ? persistence.repo : persistence;
104
- const ModelConstr = repo.class;
105
60
  const queryMethods = decoration_1.Metadata.get(repo.constructor, decoration_1.Metadata.key(core_1.PersistenceKeys.QUERY)) ?? {};
106
61
  const routeMethods = decoration_1.Metadata.get(persistence.constructor, decoration_1.Metadata.key(constants_js_1.DECAF_ROUTE)) ?? {};
107
- // create base class
108
62
  class QueryController extends controllers_js_1.DecafModelController {
109
63
  get class() {
110
- throw new Error("Method not implemented.");
64
+ throw new db_decorators_1.InternalError("Method not implemented.");
111
65
  }
112
66
  constructor(clientContext, name) {
113
67
  super(clientContext, name);
114
68
  }
115
69
  }
116
70
  for (const [methodName, params] of Object.entries(routeMethods)) {
117
- // regex to trim slashes from start and end
118
71
  const routePath = [params.path.replace(/^\/+|\/+$/g, "")]
119
72
  .filter((segment) => segment && segment.trim())
120
73
  .join("/");
121
- // const handler = params.handler.value;
122
- const handler = (0, utils_js_1.createRouteHandler)(methodName);
123
- if (!handler) {
124
- const message = `Invalid or missing handler for model ${ModelConstr.name} on decorated method ${methodName}`;
125
- log.error(message);
126
- throw new Error(message);
127
- }
128
- const descriptor = (0, utils_js_1.defineRouteMethod)(QueryController, methodName, handler);
129
- if (descriptor) {
130
- const decorators = (0, utils_js_1.getApiDecorators)(methodName, routePath, params.httpMethod);
131
- (0, utils_js_1.applyApiDecorators)(QueryController, methodName, descriptor, decorators);
132
- }
74
+ const handler = FromModelController.createComplexQueryHandler(methodName);
75
+ FromModelController.defineMethod(QueryController, methodName, handler);
76
+ const httpDecorator = (0, utils_js_1.HttpVerbToDecorator)(params.httpMethod)(routePath || undefined);
77
+ const decorators = FromModelController.getQueryDecorators(methodName, routePath, params.httpMethod);
78
+ FromModelController.applyDecorators(QueryController, methodName, [
79
+ httpDecorator,
80
+ ...decorators,
81
+ ]);
133
82
  }
134
83
  for (const [methodName, objValues] of Object.entries(queryMethods)) {
135
84
  const fields = objValues.fields ?? [];
136
85
  const routePath = [prefix, methodName, ...fields.map((f) => `:${f}`)]
137
86
  .filter((segment) => segment && segment.trim())
138
87
  .join("/");
139
- const handler = (0, utils_js_1.createRouteHandler)(methodName);
140
- const descriptor = (0, utils_js_1.defineRouteMethod)(QueryController, methodName, handler);
141
- if (descriptor) {
142
- const decorators = (0, utils_js_1.getApiDecorators)(methodName, routePath, "GET", true);
143
- (0, utils_js_1.applyApiDecorators)(QueryController, methodName, descriptor, decorators);
144
- }
88
+ const handler = FromModelController.createComplexQueryHandler(methodName);
89
+ FromModelController.defineMethod(QueryController, methodName, handler);
90
+ const httpDecorator = (0, utils_js_1.HttpVerbToDecorator)("GET")(routePath || undefined);
91
+ const decorators = FromModelController.getQueryDecorators(methodName, routePath, "GET", true);
92
+ FromModelController.applyDecorators(QueryController, methodName, [
93
+ httpDecorator,
94
+ ...decorators,
95
+ ]);
145
96
  }
146
97
  return QueryController;
147
98
  }
148
- static create(ModelConstr) {
99
+ static create(ModelConstr, moduleConfigOverrides, globalDefaults) {
149
100
  const log = FromModelController.log.for(FromModelController.create);
150
101
  const tableName = decorator_validation_1.Model.tableName(ModelConstr);
151
102
  const routePath = (0, logging_1.toKebabCase)(tableName);
152
103
  const modelClazzName = ModelConstr.name;
153
104
  const persistence = FromModelController.getPersistence(ModelConstr);
154
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
155
- const { description, getPK, apiProperties, path } = FromModelController.getRouteParametersFromModel(ModelConstr);
156
- log.debug(`Creating controller for model: ${modelClazzName}`);
157
- const BaseController = FromModelController.createQueryRoutesFromRepository(persistence // instanceof ModelService ? persistence.repo : persistence
158
- );
159
- let DynamicModelController = class DynamicModelController extends BaseController {
105
+ // When persistence is a ModelService, the @query/@route metadata lives on
106
+ // the underlying repository class (custom repo), not on ModelService itself.
107
+ // Pass the repo to the factory so addComplexQueries() can discover them.
108
+ const factoryPersistence = persistence instanceof core_1.ModelService ? persistence.repo : persistence;
109
+ const decoratorConfig = decoration_1.Metadata.get(ModelConstr, decoration_1.Metadata.key(constants_js_1.DECAF_CONTROLLER_CONFIG));
110
+ const moduleOverride = moduleConfigOverrides?.[ModelConstr.name];
111
+ const mergedConfig = {
112
+ ...(globalDefaults || {}),
113
+ ...(decoratorConfig || {}),
114
+ ...(moduleOverride || {}),
115
+ };
116
+ const FactoryController = server_1.ModelControllerFactory.create(ModelConstr, factoryPersistence, mergedConfig);
117
+ const factoryRoutes = FactoryController.__routes__;
118
+ const { getPK, apiProperties, path: pkPath, } = FromModelController.getRouteParametersFromModel(ModelConstr);
119
+ log.debug(`Creating controller for model: ${modelClazzName} with ${factoryRoutes?.length ?? 0} factory routes`);
120
+ const authConfig = mergedConfig.auth;
121
+ function applyClassAuth(target) {
122
+ if (authConfig?.public) {
123
+ (0, decorators_js_1.Public)()(target);
124
+ }
125
+ else if (authConfig?.roles?.length) {
126
+ (0, decorators_js_1.RequireRoles)(...authConfig.roles)(target);
127
+ }
128
+ else {
129
+ (0, decorators_js_1.Auth)(ModelConstr)(target);
130
+ }
131
+ if (authConfig?.skipModelRoles) {
132
+ (0, common_1.SetMetadata)(constants_js_2.SKIP_MODEL_ROLES_KEY, true)(target);
133
+ }
134
+ }
135
+ let DynamicModelController = DynamicModelController_1 = class DynamicModelController extends controllers_js_1.DecafModelController {
160
136
  static get class() {
161
137
  return ModelConstr;
162
138
  }
163
139
  get class() {
164
140
  return ModelConstr;
165
- // return DynamicModelController.class;
166
141
  }
167
142
  constructor(clientContext) {
168
- super(clientContext);
143
+ super(clientContext, DynamicModelController_1.name);
169
144
  this.pk = decorator_validation_1.Model.pk(ModelConstr);
170
145
  log.info(`Registering dynamic controller for model: ${this.class.name} route: /${routePath}`);
171
146
  }
172
- //
173
- // @ApiOperationFromModel(ModelClazz, "GET", "query/:condition/:orderBy")
174
- // @ApiOperation({ summary: `Retrieve ${modelClazzName} records by query.` })
175
- // @ApiParam({ name: "method", description: "Query method to be called" })
176
- // @ApiOkResponse({
177
- // description: `${modelClazzName} retrieved successfully.`,
178
- // })
179
- // @ApiNotFoundResponse({
180
- // description: `No ${modelClazzName} records matches the query.`,
181
- // })
182
- // async query(
183
- // @Param("condition") condition: Condition<any>,
184
- // @Param("orderBy") orderBy: string,
185
- // @QueryDetails() details: DirectionLimitOffset,
186
- // ) {
187
- // const {direction, limit, offset} = details;
188
- // return this.persistence.query(condition, orderBy as keyof Model, direction, limit, offset);
189
- // }
190
- async listBy(key, details) {
191
- const { ctx } = (await this.logCtx([], core_1.PreparedStatementKeys.LIST_BY, true)).for(this.listBy);
192
- return this.persistence(ctx).listBy(key, details.direction, ctx);
193
- }
194
- async paginateBy(key, details) {
195
- const { ctx } = (await this.logCtx([], core_1.PreparedStatementKeys.PAGE_BY, true)).for(this.paginateBy);
196
- return this.persistence(ctx).paginateBy(key, details.direction, details, ctx);
197
- }
198
- async find(value, details) {
199
- const { ctx } = (await this.logCtx([], core_1.PreparedStatementKeys.FIND, true)).for(this.find);
200
- const direction = (details.direction ??
201
- core_1.OrderDirection.ASC);
202
- return (0, utils_js_1.resolvePersistenceMethod)(this.persistence(ctx), this.find.name, value, direction, ctx);
203
- }
204
- async page(value, details) {
205
- const { ctx } = (await this.logCtx([], core_1.PreparedStatementKeys.PAGE, true)).for(this.page);
206
- const { direction = core_1.OrderDirection.ASC, limit, offset, bookmark, } = details;
207
- const ref = {
208
- offset: offset ?? 1,
209
- limit: limit ?? 10,
210
- bookmark,
211
- };
212
- return (0, utils_js_1.resolvePersistenceMethod)(this.persistence(ctx), this.page.name, value, direction, ref, ctx);
213
- }
214
- async findOneBy(key, value) {
215
- const { ctx } = (await this.logCtx([], core_1.PreparedStatementKeys.FIND_ONE_BY, true)).for(this.findOneBy);
216
- return this.persistence(ctx).findOneBy(key, value, ctx);
217
- }
218
- async findBy(key, value, details) {
219
- const { ctx } = (await this.logCtx([], core_1.PreparedStatementKeys.FIND_BY, true)).for(this.findBy);
220
- return this.persistence(ctx)
221
- .for(ctx.toOverrides())
222
- .findBy(key, value, ctx);
223
- }
224
- async statement(name, args, details) {
225
- const { ctx } = (await this.logCtx([], core_1.PersistenceKeys.STATEMENT, true)).for(this.statement);
226
- const { direction, offset, limit, bookmark } = details;
227
- args = args.map((a) => (typeof a === "string" ? parseInt(a) : a) || a);
228
- const pathDirection = args.length > 1 ? args[1] : undefined;
229
- const resolvedDirection = (direction ?? pathDirection);
230
- if (resolvedDirection && args.length > 1)
231
- args[1] = resolvedDirection;
232
- switch (name) {
233
- case core_1.PreparedStatementKeys.FIND:
234
- case core_1.PreparedStatementKeys.FIND_BY:
235
- break;
236
- case core_1.PreparedStatementKeys.LIST_BY:
237
- args.push(direction);
238
- break;
239
- case core_1.PreparedStatementKeys.PAGE:
240
- case core_1.PreparedStatementKeys.PAGE_BY:
241
- args = [
242
- args[0],
243
- resolvedDirection,
244
- {
245
- limit: limit,
246
- offset: offset,
247
- bookmark: bookmark,
248
- },
249
- ];
250
- break;
251
- case core_1.PreparedStatementKeys.FIND_ONE_BY:
252
- break;
253
- case core_1.PreparedStatementKeys.COUNT_OF:
254
- case core_1.PreparedStatementKeys.MAX_OF:
255
- case core_1.PreparedStatementKeys.MIN_OF:
256
- case core_1.PreparedStatementKeys.AVG_OF:
257
- case core_1.PreparedStatementKeys.SUM_OF:
258
- case core_1.PreparedStatementKeys.DISTINCT_OF:
259
- case core_1.PreparedStatementKeys.GROUP_OF:
260
- // Aggregation methods - args[0] is the field name (if provided)
261
- break;
147
+ };
148
+ DynamicModelController = DynamicModelController_1 = __decorate([
149
+ (0, common_1.Controller)(routePath),
150
+ (0, swagger_1.ApiTags)(modelClazzName),
151
+ (0, swagger_1.ApiExtraModels)(ModelConstr),
152
+ __metadata("design:paramtypes", [index_js_2.DecafRequestContext])
153
+ ], DynamicModelController);
154
+ applyClassAuth(DynamicModelController);
155
+ if (factoryRoutes) {
156
+ const sortedRoutes = [...factoryRoutes].sort((a, b) => {
157
+ const aSegments = a.path.split("/").filter(Boolean);
158
+ const bSegments = b.path.split("/").filter(Boolean);
159
+ const aParamCount = aSegments.filter((s) => s.startsWith(":")).length;
160
+ const bParamCount = bSegments.filter((s) => s.startsWith(":")).length;
161
+ const aLiteralCount = aSegments.length - aParamCount;
162
+ const bLiteralCount = bSegments.length - bParamCount;
163
+ if (aLiteralCount !== bLiteralCount)
164
+ return bLiteralCount - aLiteralCount;
165
+ if (aParamCount !== bParamCount)
166
+ return aParamCount - bParamCount;
167
+ return 0;
168
+ });
169
+ for (const route of sortedRoutes) {
170
+ const methodName = route.implementation?.name || route.method.toLowerCase();
171
+ const registration = FromModelController.matchRoute(methodName, route, pkPath, apiProperties, getPK, ModelConstr, modelClazzName);
172
+ if (!registration)
173
+ continue;
174
+ const { methodName: registeredMethodName, handler, decorators, paramDecorators, } = registration;
175
+ const descriptor = FromModelController.defineMethod(DynamicModelController, registeredMethodName, handler);
176
+ if (descriptor) {
177
+ const httpDecorator = (0, utils_js_1.HttpVerbToDecorator)(route.method)(route.path.replace(/^\/+|\/+$/g, "") || undefined);
178
+ FromModelController.applyDecorators(DynamicModelController, registeredMethodName, [httpDecorator, ...decorators], paramDecorators);
262
179
  }
263
- return this.persistence(ctx).statement(name, ...args, ctx);
264
180
  }
265
- //
266
- // @ApiOperationFromModel(ModelConstr, "GET", "countOf")
267
- // @ApiOperation({ summary: `Count all ${modelClazzName} records.` })
268
- // @ApiOkResponse({
269
- // description: `Count of ${modelClazzName} records.`,
270
- // type: Number,
271
- // })
272
- // async countOf() {
273
- // const { ctx } = (
274
- // await this.logCtx([], PreparedStatementKeys.COUNT_OF, true)
275
- // ).for(this.countOf);
276
- // return this.persistence(ctx).statement(
277
- // PreparedStatementKeys.COUNT_OF,
278
- // ctx
279
- // );
280
- // }
281
- //
282
- // @ApiOperationFromModel(ModelConstr, "GET", "countOf/:field")
283
- // @ApiOperation({ summary: `Count ${modelClazzName} records by field.` })
284
- // @ApiParam({ name: "field", description: "The field to count" })
285
- // @ApiOkResponse({
286
- // description: `Count of ${modelClazzName} records.`,
287
- // type: Number,
288
- // })
289
- // async countOfField(@Param("field") field: string) {
290
- // const { ctx } = (
291
- // await this.logCtx([], PreparedStatementKeys.COUNT_OF, true)
292
- // ).for(this.countOfField);
293
- // return this.persistence(ctx).statement(
294
- // PreparedStatementKeys.COUNT_OF,
295
- // field,
296
- // ctx
297
- // );
298
- // }
299
- //
300
- // @ApiOperationFromModel(ModelConstr, "GET", "maxOf/:field")
301
- // @ApiOperation({
302
- // summary: `Find maximum value of a field in ${modelClazzName}.`,
303
- // })
304
- // @ApiParam({ name: "field", description: "The field to find max of" })
305
- // @ApiOkResponse({
306
- // description: `Maximum value of the field in ${modelClazzName}.`,
307
- // })
308
- // async maxOf(@Param("field") field: string) {
309
- // const { ctx } = (
310
- // await this.logCtx([], PreparedStatementKeys.MAX_OF, true)
311
- // ).for(this.maxOf);
312
- // return this.persistence(ctx).statement(
313
- // PreparedStatementKeys.MAX_OF,
314
- // field,
315
- // ctx
316
- // );
317
- // }
318
- //
319
- // @ApiOperationFromModel(ModelConstr, "GET", "minOf/:field")
320
- // @ApiOperation({
321
- // summary: `Find minimum value of a field in ${modelClazzName}.`,
322
- // })
323
- // @ApiParam({ name: "field", description: "The field to find min of" })
324
- // @ApiOkResponse({
325
- // description: `Minimum value of the field in ${modelClazzName}.`,
326
- // })
327
- // async minOf(@Param("field") field: string) {
328
- // const { ctx } = (
329
- // await this.logCtx([], PreparedStatementKeys.MIN_OF, true)
330
- // ).for(this.minOf);
331
- // return this.persistence(ctx).statement(
332
- // PreparedStatementKeys.MIN_OF,
333
- // field,
334
- // ctx
335
- // );
336
- // }
337
- //
338
- // @ApiOperationFromModel(ModelConstr, "GET", "avgOf/:field")
339
- // @ApiOperation({
340
- // summary: `Calculate average of a field in ${modelClazzName}.`,
341
- // })
342
- // @ApiParam({
343
- // name: "field",
344
- // description: "The field to calculate average of",
345
- // })
346
- // @ApiOkResponse({
347
- // description: `Average value of the field in ${modelClazzName}.`,
348
- // type: Number,
349
- // })
350
- // async avgOf(@Param("field") field: string) {
351
- // const { ctx } = (
352
- // await this.logCtx([], PreparedStatementKeys.AVG_OF, true)
353
- // ).for(this.avgOf);
354
- // return this.persistence(ctx).statement(
355
- // PreparedStatementKeys.AVG_OF,
356
- // field,
357
- // ctx
358
- // );
359
- // }
360
- //
361
- // @ApiOperationFromModel(ModelConstr, "GET", "sumOf/:field")
362
- // @ApiOperation({
363
- // summary: `Calculate sum of a field in ${modelClazzName}.`,
364
- // })
365
- // @ApiParam({ name: "field", description: "The field to calculate sum of" })
366
- // @ApiOkResponse({
367
- // description: `Sum of the field in ${modelClazzName}.`,
368
- // type: Number,
369
- // })
370
- // async sumOf(@Param("field") field: string) {
371
- // const { ctx } = (
372
- // await this.logCtx([], PreparedStatementKeys.SUM_OF, true)
373
- // ).for(this.sumOf);
374
- // return this.persistence(ctx).statement(
375
- // PreparedStatementKeys.SUM_OF,
376
- // field,
377
- // ctx
378
- // );
379
- // }
380
- //
381
- // @ApiOperationFromModel(ModelConstr, "GET", "distinctOf/:field")
382
- // @ApiOperation({
383
- // summary: `Find distinct values of a field in ${modelClazzName}.`,
384
- // })
385
- // @ApiParam({
386
- // name: "field",
387
- // description: "The field to find distinct values of",
388
- // })
389
- // @ApiOkResponse({
390
- // description: `Distinct values of the field in ${modelClazzName}.`,
391
- // type: [String],
392
- // })
393
- // async distinctOf(@Param("field") field: string) {
394
- // const { ctx } = (
395
- // await this.logCtx([], PreparedStatementKeys.DISTINCT_OF, true)
396
- // ).for(this.distinctOf);
397
- // return this.persistence(ctx).statement(
398
- // PreparedStatementKeys.DISTINCT_OF,
399
- // field,
400
- // ctx
401
- // );
402
- // }
403
- //
404
- // @ApiOperationFromModel(ModelConstr, "GET", "groupOf/:field")
405
- // @ApiOperation({
406
- // summary: `Group ${modelClazzName} records by a field.`,
407
- // })
408
- // @ApiParam({ name: "field", description: "The field to group by" })
409
- // @ApiOkResponse({
410
- // description: `${modelClazzName} records grouped by the field.`,
411
- // })
412
- // async groupOf(@Param("field") field: string) {
413
- // const { ctx } = (
414
- // await this.logCtx([], PreparedStatementKeys.GROUP_OF, true)
415
- // ).for(this.groupOf);
416
- // return this.persistence(ctx).statement(
417
- // PreparedStatementKeys.GROUP_OF,
418
- // field,
419
- // ctx
420
- // );
421
- // }
422
- async createAll(data, resp) {
423
- const { ctx, log } = (await this.logCtx([], db_decorators_1.BulkCrudOperationKeys.CREATE_ALL, true)).for(this.createAll);
424
- log.verbose(`creating new ${modelClazzName}`);
425
- let created;
426
- try {
427
- created = await this.persistence(ctx).createAll(data.map((d) => new ModelConstr(d)), ctx);
428
- }
429
- catch (e) {
430
- log.error(`Failed to create new ${modelClazzName}`, e);
431
- throw e;
432
- }
433
- log.info(`created new ${modelClazzName} with id ${created[this.pk]}`);
434
- ctx.toResponse(resp);
435
- return created;
181
+ }
182
+ return DynamicModelController;
183
+ }
184
+ static getRouteParametersFromModel(ModelClazz) {
185
+ const pk = decorator_validation_1.Model.pk(ModelClazz);
186
+ const composed = decoration_1.Metadata.get(ModelClazz, decoration_1.Metadata.key(db_decorators_1.DBKeys.COMPOSED, pk));
187
+ const composedKeys = composed?.args ?? [];
188
+ const uniqueKeys = Array.isArray(composedKeys) && composedKeys.length > 0
189
+ ? Array.from(new Set([...composedKeys]))
190
+ : Array.from(new Set([pk]));
191
+ const description = decoration_1.Metadata.description(ModelClazz) ?? "";
192
+ const path = `:${uniqueKeys.join("/:")}`;
193
+ const apiProperties = uniqueKeys.map((key) => {
194
+ return {
195
+ name: key,
196
+ description: decoration_1.Metadata.description(ModelClazz, key),
197
+ required: true,
198
+ type: String,
199
+ };
200
+ });
201
+ return {
202
+ path,
203
+ description,
204
+ apiProperties,
205
+ getPK: (...params) => composed?.separator ? params.join(composed.separator) : params.join(""),
206
+ };
207
+ }
208
+ static routeParamDecorators(path) {
209
+ let paramIndex = 0;
210
+ return path
211
+ .split("/")
212
+ .filter(Boolean)
213
+ .flatMap((segment) => {
214
+ if (segment.startsWith(":")) {
215
+ return [
216
+ { decorator: (0, common_1.Param)(segment.slice(1)), index: paramIndex++ },
217
+ ];
436
218
  }
437
- async create(data, resp) {
438
- const { ctx, log } = (await this.logCtx([], db_decorators_1.OperationKeys.CREATE, true)).for(this.create);
439
- log.verbose(`creating new ${modelClazzName}`);
440
- let created;
441
- try {
442
- const persistence = this.persistence(ctx);
443
- created = await persistence.create(data, ctx);
444
- }
445
- catch (e) {
446
- log.error(`Failed to create new ${modelClazzName}`, e);
447
- throw e;
448
- }
449
- log.info(`created new ${modelClazzName} with id ${created[this.pk]}`);
450
- ctx.toResponse(resp);
451
- return created;
219
+ if (segment.startsWith("*")) {
220
+ return [
221
+ { decorator: (0, common_1.Param)(segment.slice(1)), index: paramIndex++ },
222
+ ];
452
223
  }
453
- async readAll(ids) {
454
- const { ctx, log } = (await this.logCtx([], db_decorators_1.BulkCrudOperationKeys.READ_ALL, true)).for(this.readAll);
455
- const normalizedIds = Array.isArray(ids) ? ids : [ids];
456
- let read;
457
- try {
458
- log.debug(`reading ${normalizedIds} ${modelClazzName}: ${normalizedIds}`);
459
- const persistence = this.persistence(ctx);
460
- read = await persistence.readAll(normalizedIds, ctx);
461
- }
462
- catch (e) {
463
- log.error(`Failed to ${modelClazzName} with id ${normalizedIds}`, e);
464
- throw e;
465
- }
466
- log.info(`read ${read.length} ${modelClazzName}`);
467
- return read;
224
+ return [];
225
+ });
226
+ }
227
+ static defineMethod(target, methodName, handler) {
228
+ Object.defineProperty(target.prototype || target, methodName, {
229
+ value: handler,
230
+ writable: false,
231
+ configurable: true,
232
+ enumerable: false,
233
+ });
234
+ return Object.getOwnPropertyDescriptor(target.prototype || target, methodName);
235
+ }
236
+ static applyDecorators(target, methodName, methodDecorators, paramDecorators = []) {
237
+ const proto = target?.prototype ?? target;
238
+ const descriptor = Object.getOwnPropertyDescriptor(proto, methodName);
239
+ methodDecorators.forEach((d) => d(proto, methodName, descriptor));
240
+ paramDecorators.forEach(({ decorator, index }) => decorator(proto, methodName, index));
241
+ }
242
+ static matchRoute(methodName, route, pkPath, apiProperties, getPK, ModelConstr, modelClazzName) {
243
+ const { method, path } = route;
244
+ const normalizedPath = path.replace(/^\/+|\/+$/g, "");
245
+ const handler = route.implementation;
246
+ if (method === "POST" && normalizedPath === "") {
247
+ return FromModelController.createRegistration("create", handler, FromModelController.createCreateDecorators(ModelConstr, modelClazzName), [
248
+ { decorator: (0, index_js_1.DecafBody)(), index: 0 },
249
+ { decorator: (0, common_1.Response)({ passthrough: true }), index: 1 },
250
+ ]);
251
+ }
252
+ if (method === "POST" && normalizedPath === "bulk") {
253
+ return FromModelController.createRegistration("createAll", handler, FromModelController.bulkCreateDecorators(ModelConstr, modelClazzName), [
254
+ { decorator: (0, index_js_1.DecafBody)(), index: 0 },
255
+ { decorator: (0, common_1.Response)({ passthrough: true }), index: 1 },
256
+ ]);
257
+ }
258
+ if (method === "GET" && normalizedPath === "bulk") {
259
+ return FromModelController.createRegistration("readAll", handler, FromModelController.bulkReadDecorators(ModelConstr, modelClazzName), [{ decorator: (0, common_1.Query)("ids"), index: 0 }]);
260
+ }
261
+ if (method === "PUT" && normalizedPath === "bulk") {
262
+ return FromModelController.createRegistration("updateAll", handler, FromModelController.bulkUpdateDecorators(ModelConstr, modelClazzName, apiProperties), [
263
+ { decorator: (0, index_js_1.DecafBody)(), index: 0 },
264
+ { decorator: (0, common_1.Response)({ passthrough: true }), index: 1 },
265
+ ]);
266
+ }
267
+ if (method === "DELETE" && normalizedPath === "bulk") {
268
+ return FromModelController.createRegistration("deleteAll", handler, FromModelController.bulkDeleteDecorators(ModelConstr, modelClazzName, apiProperties), [
269
+ { decorator: (0, common_1.Query)("ids"), index: 0 },
270
+ { decorator: (0, common_1.Response)({ passthrough: true }), index: 1 },
271
+ ]);
272
+ }
273
+ if (method === "GET" && normalizedPath === pkPath) {
274
+ return FromModelController.createRegistration("read", handler, FromModelController.readDecorators(ModelConstr, modelClazzName, apiProperties, pkPath), FromModelController.routeParamDecorators(normalizedPath));
275
+ }
276
+ if (method === "PUT" && normalizedPath === pkPath) {
277
+ const routeParams = FromModelController.routeParamDecorators(normalizedPath);
278
+ return FromModelController.createRegistration("update", handler, FromModelController.updateDecorators(ModelConstr, modelClazzName, apiProperties, pkPath), [
279
+ { decorator: (0, index_js_1.DecafBody)(), index: 0 },
280
+ ...routeParams.map(({ decorator, index }) => ({
281
+ decorator,
282
+ index: index + 1,
283
+ })),
284
+ ]);
285
+ }
286
+ if (method === "DELETE" && normalizedPath === pkPath) {
287
+ const routeParams = FromModelController.routeParamDecorators(normalizedPath);
288
+ return FromModelController.createRegistration("delete", handler, FromModelController.deleteDecorators(ModelConstr, modelClazzName, apiProperties, pkPath), routeParams);
289
+ }
290
+ // Composed PK fallback routes (filterEmpty) — path differs from pkPath
291
+ const fallbackSegments = normalizedPath.split("/").filter(Boolean);
292
+ const isAllParams = fallbackSegments.length > 0 &&
293
+ fallbackSegments.every((s) => s.startsWith(":"));
294
+ if (isAllParams && normalizedPath !== pkPath) {
295
+ const fallbackApiProps = fallbackSegments
296
+ .map((s) => s.slice(1))
297
+ .map((name) => ({
298
+ name,
299
+ description: `${name} parameter`,
300
+ required: true,
301
+ type: String,
302
+ }));
303
+ const suffix = fallbackSegments.map((s) => s.slice(1)).join("And");
304
+ const routeParams = FromModelController.routeParamDecorators(normalizedPath);
305
+ if (method === "GET") {
306
+ return FromModelController.createRegistration(`readBy${suffix}`, handler, FromModelController.readDecorators(ModelConstr, modelClazzName, fallbackApiProps, normalizedPath), routeParams);
468
307
  }
469
- async read(routeParams) {
470
- const { ctx, log } = (await this.logCtx([], db_decorators_1.OperationKeys.READ, true)).for(this.read);
471
- const id = getPK(...routeParams.valuesInOrder);
472
- if (typeof id === "undefined")
473
- throw new db_decorators_1.ValidationError(`No ${this.pk} provided`);
474
- let read;
475
- try {
476
- log.debug(`reading ${modelClazzName} with ${this.pk} ${id}`);
477
- const persistence = this.persistence(ctx);
478
- read = await persistence.read(id, ctx);
479
- }
480
- catch (e) {
481
- log.error(`Failed to read ${modelClazzName} with id ${id}`, e);
482
- throw e;
483
- }
484
- log.info(`read ${modelClazzName} with id ${read[this.pk]}`);
485
- return read;
308
+ if (method === "PUT") {
309
+ return FromModelController.createRegistration(`updateBy${suffix}`, handler, FromModelController.updateDecorators(ModelConstr, modelClazzName, fallbackApiProps, normalizedPath), [
310
+ { decorator: (0, index_js_1.DecafBody)(), index: 0 },
311
+ ...routeParams.map(({ decorator, index }) => ({
312
+ decorator,
313
+ index: index + 1,
314
+ })),
315
+ ]);
486
316
  }
487
- async updateAll(body, resp) {
488
- const { ctx, log } = (await this.logCtx([], db_decorators_1.BulkCrudOperationKeys.UPDATE_ALL, true)).for(this.updateAll);
489
- let updated;
490
- try {
491
- log.info(`updating ${body.length} ${modelClazzName}`);
492
- updated = await this.persistence(ctx).updateAll(body, ctx);
493
- }
494
- catch (e) {
495
- log.error(e);
496
- throw e;
497
- }
498
- ctx.toResponse(resp);
499
- return updated;
317
+ if (method === "DELETE") {
318
+ return FromModelController.createRegistration(`deleteBy${suffix}`, handler, FromModelController.deleteDecorators(ModelConstr, modelClazzName, fallbackApiProps, normalizedPath), routeParams);
500
319
  }
501
- async update(routeParams, body, resp) {
502
- const { ctx, log } = (await this.logCtx([], db_decorators_1.OperationKeys.UPDATE, true)).for(this.update);
503
- const id = getPK(...routeParams.valuesInOrder);
504
- if (typeof id === "undefined")
505
- throw new db_decorators_1.ValidationError(`No ${this.pk} provided`);
506
- let updated;
507
- try {
508
- log.info(`updating ${modelClazzName} with ${this.pk} ${id}`);
509
- const payload = JSON.parse(JSON.stringify(body));
510
- const persistence = this.persistence(ctx);
511
- updated = await persistence.update(new ModelConstr({
512
- ...payload,
513
- [this.pk]: id,
514
- }), ctx);
320
+ }
321
+ if (method === "GET" && normalizedPath === "statement/:method/*args") {
322
+ return FromModelController.createRegistration("statement", handler, FromModelController.statementDecorators(ModelConstr, modelClazzName), [
323
+ { decorator: (0, common_1.Param)("method"), index: 0 },
324
+ { decorator: (0, common_1.Param)("args"), index: 1 },
325
+ { decorator: (0, index_js_1.DecafQuery)(), index: 2 },
326
+ ]);
327
+ }
328
+ if (method === "GET" && normalizedPath === "findOneBy/:key/:value") {
329
+ return FromModelController.createRegistration("findOneBy", handler, FromModelController.statementShortcutDecorators(ModelConstr, modelClazzName, normalizedPath, core_1.PreparedStatementKeys.FIND_ONE_BY), FromModelController.statementShortcutParams(normalizedPath));
330
+ }
331
+ const statementRoutes = {
332
+ "listBy/:key": core_1.PreparedStatementKeys.LIST_BY,
333
+ "paginateBy/:key/:page": core_1.PreparedStatementKeys.PAGE_BY,
334
+ "find/:value": core_1.PreparedStatementKeys.FIND,
335
+ "page/:value": core_1.PreparedStatementKeys.PAGE,
336
+ "findOneBy/:key/:value": core_1.PreparedStatementKeys.FIND_ONE_BY,
337
+ "findBy/:key/:value": core_1.PreparedStatementKeys.FIND_BY,
338
+ "countOf/:field": core_1.PreparedStatementKeys.COUNT_OF,
339
+ "maxOf/:field": core_1.PreparedStatementKeys.MAX_OF,
340
+ "minOf/:field": core_1.PreparedStatementKeys.MIN_OF,
341
+ "avgOf/:field": core_1.PreparedStatementKeys.AVG_OF,
342
+ "sumOf/:field": core_1.PreparedStatementKeys.SUM_OF,
343
+ "distinctOf/:field": core_1.PreparedStatementKeys.DISTINCT_OF,
344
+ "groupOf/:field": core_1.PreparedStatementKeys.GROUP_OF,
345
+ };
346
+ const statementKey = statementRoutes[normalizedPath];
347
+ if (statementKey && method === "GET") {
348
+ return FromModelController.createRegistration(FromModelController.statementMethodName(normalizedPath), handler, FromModelController.statementShortcutDecorators(ModelConstr, modelClazzName, normalizedPath, statementKey), FromModelController.statementShortcutParams(normalizedPath));
349
+ }
350
+ if (method === "GET" && normalizedPath.startsWith("query/")) {
351
+ const queryMethod = normalizedPath.replace(/^query\//, "").split("/")[0];
352
+ return FromModelController.createRegistration(queryMethod, handler, FromModelController.getQueryDecorators(queryMethod, normalizedPath, "GET", true), FromModelController.complexQueryParams(normalizedPath));
353
+ }
354
+ const pathSegments = normalizedPath.split("/").filter(Boolean);
355
+ const knownPrefixes = new Set([
356
+ "listBy",
357
+ "findBy",
358
+ "findByPaginate",
359
+ "findOneBy",
360
+ "paginateBy",
361
+ "find",
362
+ "page",
363
+ "countOf",
364
+ "maxOf",
365
+ "minOf",
366
+ "avgOf",
367
+ "sumOf",
368
+ "distinctOf",
369
+ "groupOf",
370
+ "statement",
371
+ "bulk",
372
+ "query",
373
+ ]);
374
+ if (pathSegments.length > 0 &&
375
+ !normalizedPath.startsWith("query/") &&
376
+ !normalizedPath.startsWith("statement/") &&
377
+ !knownPrefixes.has(pathSegments[0])) {
378
+ const apiPathParams = pathSegments
379
+ .filter((s) => s.startsWith(":"))
380
+ .map((s) => s.slice(1))
381
+ .map((name) => ({
382
+ name,
383
+ description: `${name} parameter for the query`,
384
+ required: true,
385
+ type: String,
386
+ }));
387
+ return FromModelController.createRegistration(methodName, handler, [
388
+ ...apiPathParams.map((p) => (0, swagger_1.ApiParam)(p)),
389
+ (0, swagger_1.ApiOperation)({ summary: `Retrieve records using "${methodName}".` }),
390
+ (0, swagger_1.ApiOkResponse)({ description: "Result successfully retrieved." }),
391
+ (0, swagger_1.ApiNoContentResponse)({
392
+ description: "No content returned by the method.",
393
+ }),
394
+ ], FromModelController.routeParamDecorators(normalizedPath));
395
+ }
396
+ return undefined;
397
+ }
398
+ static createRegistration(methodName, handler, decorators, paramDecorators) {
399
+ return { methodName, handler, decorators, paramDecorators };
400
+ }
401
+ static statementMethodName(path) {
402
+ const firstSegment = path.split("/")[0];
403
+ return firstSegment;
404
+ }
405
+ static statementShortcutParams(path) {
406
+ const segments = path.split("/").filter((s) => s.startsWith(":"));
407
+ const params = [];
408
+ segments.forEach((seg, i) => {
409
+ const name = seg.replace(":", "");
410
+ params.push({ decorator: (0, common_1.Param)(name), index: i });
411
+ });
412
+ if (path.startsWith("listBy/") ||
413
+ path.startsWith("paginateBy/") ||
414
+ path.startsWith("find/") ||
415
+ path.startsWith("page/")) {
416
+ params.push({ decorator: (0, index_js_1.DecafQuery)(), index: segments.length });
417
+ }
418
+ return params;
419
+ }
420
+ static complexQueryParams(path) {
421
+ const segments = path.split("/").filter((s) => s.startsWith(":"));
422
+ const params = [];
423
+ segments.forEach((seg, i) => {
424
+ const name = seg.replace(":", "");
425
+ params.push({ decorator: (0, common_1.Param)(name), index: i });
426
+ });
427
+ if (path.startsWith("query/")) {
428
+ params.push({ decorator: (0, index_js_1.DecafQuery)(), index: segments.length });
429
+ }
430
+ return params;
431
+ }
432
+ static createComplexQueryHandler(methodName) {
433
+ return async function complexQuery(...args) {
434
+ const log = this.log?.for?.(complexQuery);
435
+ try {
436
+ if (log)
437
+ log.debug(`Invoking custom query "${methodName}"`);
438
+ const { ctx } = (await this.logCtx([], methodName, true)).for(complexQuery);
439
+ const persistence = this.persistence(ctx);
440
+ const spreadArgs = FromModelController.normalizeQueryArgs(args);
441
+ if (persistence?.repo &&
442
+ typeof persistence.repo[methodName] === "function") {
443
+ return persistence.repo[methodName](...spreadArgs);
515
444
  }
516
- catch (e) {
517
- log.error(e);
518
- throw e;
445
+ if (typeof persistence[methodName] === "function") {
446
+ return persistence[methodName](...spreadArgs);
519
447
  }
520
- ctx.toResponse(resp);
521
- return updated;
522
- }
523
- async deleteAll(ids, resp) {
524
- const { ctx, log } = (await this.logCtx([], db_decorators_1.BulkCrudOperationKeys.DELETE_ALL, true)).for(this.deleteAll);
525
- const normalizedIds = Array.isArray(ids) ? ids : [ids];
526
- let read;
527
- try {
528
- log.debug(`deleting ${normalizedIds.length} ${modelClazzName}: ${normalizedIds}`);
529
- read = await this.persistence(ctx).deleteAll(normalizedIds, ctx);
448
+ if (persistence?.query && typeof persistence.query === "function") {
449
+ return persistence.query(methodName, ...spreadArgs);
530
450
  }
531
- catch (e) {
532
- log.error(`Failed to delete ${modelClazzName} with id ${normalizedIds}`, e);
533
- throw e;
451
+ if (typeof persistence.statement === "function") {
452
+ return persistence.statement(methodName, ...spreadArgs, ctx);
534
453
  }
535
- log.info(`deleted ${read.length} ${modelClazzName}`);
536
- ctx.toResponse(resp);
537
- return read;
454
+ throw new db_decorators_1.InternalError(`Persistence method "${methodName}" not found on ${persistence?.constructor?.name}`);
538
455
  }
539
- async delete(routeParams, resp) {
540
- const { ctx, log } = (await this.logCtx([], db_decorators_1.OperationKeys.DELETE, true)).for(this.delete);
541
- const id = getPK(...routeParams.valuesInOrder);
542
- if (typeof id === "undefined")
543
- throw new db_decorators_1.ValidationError(`No ${this.pk} provided`);
544
- let del;
545
- try {
546
- log.debug(`deleting ${modelClazzName} with ${this.pk} ${id}`);
547
- del = await this.persistence(ctx).delete(id, ctx);
548
- }
549
- catch (e) {
550
- log.error(`Failed to delete ${modelClazzName} with id ${id}`, e);
551
- throw e;
552
- }
553
- log.info(`deleted ${modelClazzName} with id ${id}`);
554
- ctx.toResponse(resp);
555
- return del;
456
+ catch (e) {
457
+ if (log)
458
+ log.error(`Custom query "${methodName}" failed`, e);
459
+ throw FromModelController.toDecafError(e, `Custom query "${methodName}" failed`);
556
460
  }
557
461
  };
558
- __decorate([
559
- (0, index_js_1.ApiOperationFromModel)(ModelConstr, "GET", "listBy/:key"),
560
- (0, swagger_1.ApiOperation)({ summary: `Retrieve ${modelClazzName} records by query.` }),
561
- (0, swagger_1.ApiParam)({ name: "key", description: "the model key to sort by" }),
562
- (0, swagger_1.ApiQuery)({ name: "direction", required: true, enum: core_1.OrderDirection }),
563
- (0, swagger_1.ApiOkResponse)({
564
- description: `${modelClazzName} listed successfully.`,
565
- }),
566
- __param(0, (0, common_1.Param)("key")),
567
- __param(1, (0, index_js_1.DecafQuery)()),
568
- __metadata("design:type", Function),
569
- __metadata("design:paramtypes", [String, Object]),
570
- __metadata("design:returntype", Promise)
571
- ], DynamicModelController.prototype, "listBy", null);
572
- __decorate([
573
- (0, index_js_1.ApiOperationFromModel)(ModelConstr, "GET", "paginateBy/:key/:page"),
574
- (0, swagger_1.ApiOperation)({ summary: `Retrieve ${modelClazzName} records by query.` }),
575
- (0, swagger_1.ApiParam)({ name: "key", description: "the model key to sort by" }),
576
- (0, swagger_1.ApiParam)({
577
- name: "page",
578
- description: "the page to retrieve or the bookmark",
579
- }),
580
- (0, swagger_1.ApiQuery)({
581
- name: "direction",
582
- required: true,
583
- enum: core_1.OrderDirection,
584
- description: "the sort order",
585
- }),
586
- (0, swagger_1.ApiQuery)({ name: "limit", required: true, description: "the page size" }),
587
- (0, swagger_1.ApiQuery)({ name: "offset", description: "the bookmark when necessary" }),
588
- (0, swagger_1.ApiOkResponse)({
589
- description: `${modelClazzName} listed paginated.`,
590
- }),
591
- __param(0, (0, common_1.Param)("key")),
592
- __param(1, (0, index_js_1.DecafQuery)()),
593
- __metadata("design:type", Function),
594
- __metadata("design:paramtypes", [String, Object]),
595
- __metadata("design:returntype", Promise)
596
- ], DynamicModelController.prototype, "paginateBy", null);
597
- __decorate([
598
- (0, index_js_1.ApiOperationFromModel)(ModelConstr, "GET", "find/:value"),
599
- (0, swagger_1.ApiOperation)({
600
- summary: `Find ${modelClazzName} records using the default query attributes.`,
601
- }),
602
- (0, swagger_1.ApiParam)({
603
- name: "value",
604
- description: "The string to match against the default query attributes",
605
- }),
606
- (0, swagger_1.ApiQuery)({
607
- name: "direction",
608
- required: false,
609
- enum: core_1.OrderDirection,
610
- description: "the sort order for the matching results",
611
- }),
612
- (0, swagger_1.ApiOkResponse)({
613
- description: `${modelClazzName} records matching the provided prefix.`,
614
- }),
615
- __param(0, (0, common_1.Param)("value")),
616
- __param(1, (0, index_js_1.DecafQuery)()),
617
- __metadata("design:type", Function),
618
- __metadata("design:paramtypes", [String, Object]),
619
- __metadata("design:returntype", Promise)
620
- ], DynamicModelController.prototype, "find", null);
621
- __decorate([
622
- (0, index_js_1.ApiOperationFromModel)(ModelConstr, "GET", "page/:value"),
623
- (0, swagger_1.ApiOperation)({
624
- summary: `Page ${modelClazzName} records using the default query attributes.`,
625
- }),
626
- (0, swagger_1.ApiParam)({
627
- name: "value",
628
- description: "The string to match against the default query attributes",
629
- }),
630
- (0, swagger_1.ApiQuery)({
631
- name: "direction",
632
- required: false,
633
- enum: core_1.OrderDirection,
634
- description: "the sort order for the paged results",
635
- }),
636
- (0, swagger_1.ApiQuery)({
637
- name: "limit",
638
- required: false,
639
- description: "page size",
640
- }),
641
- (0, swagger_1.ApiQuery)({
642
- name: "offset",
643
- required: false,
644
- description: "page number",
645
- }),
646
- (0, swagger_1.ApiQuery)({
647
- name: "bookmark",
648
- required: false,
649
- description: "bookmark for cursor pagination",
650
- }),
651
- (0, swagger_1.ApiOkResponse)({
652
- description: `${modelClazzName} records paged by the provided prefix.`,
653
- }),
654
- __param(0, (0, common_1.Param)("value")),
655
- __param(1, (0, index_js_1.DecafQuery)()),
656
- __metadata("design:type", Function),
657
- __metadata("design:paramtypes", [String, Object]),
658
- __metadata("design:returntype", Promise)
659
- ], DynamicModelController.prototype, "page", null);
660
- __decorate([
661
- (0, index_js_1.ApiOperationFromModel)(ModelConstr, "GET", "findOneBy/:key"),
662
- (0, swagger_1.ApiOperation)({ summary: `Retrieve ${modelClazzName} records by query.` }),
663
- (0, swagger_1.ApiParam)({ name: "key", description: "the model key to sort by" }),
664
- (0, swagger_1.ApiOkResponse)({
665
- description: `${modelClazzName} listed found.`,
666
- }),
667
- (0, swagger_1.ApiNotFoundResponse)({
668
- description: `No ${modelClazzName} record matches the provided identifier.`,
669
- }),
670
- __param(0, (0, common_1.Param)("key")),
671
- __param(1, (0, common_1.Param)("value")),
672
- __metadata("design:type", Function),
673
- __metadata("design:paramtypes", [String, Object]),
674
- __metadata("design:returntype", Promise)
675
- ], DynamicModelController.prototype, "findOneBy", null);
676
- __decorate([
677
- (0, index_js_1.ApiOperationFromModel)(ModelConstr, "GET", "findBy/:key/:value"),
678
- (0, swagger_1.ApiOperation)({ summary: `Retrieve ${modelClazzName} records by query.` }),
679
- (0, swagger_1.ApiParam)({ name: "key", description: "the model key to compare" }),
680
- (0, swagger_1.ApiParam)({ name: "value", description: "the value to match" }),
681
- (0, swagger_1.ApiQuery)({
682
- name: "direction",
683
- required: true,
684
- enum: core_1.OrderDirection,
685
- description: "the sort order when applicable",
686
- }),
687
- (0, swagger_1.ApiOkResponse)({
688
- description: `${modelClazzName} listed found.`,
689
- }),
690
- (0, swagger_1.ApiNotFoundResponse)({
691
- description: `No ${modelClazzName} record matches the provided identifier.`,
692
- }),
693
- __param(0, (0, common_1.Param)("key")),
694
- __param(1, (0, common_1.Param)("value")),
695
- __param(2, (0, index_js_1.DecafQuery)()),
696
- __metadata("design:type", Function),
697
- __metadata("design:paramtypes", [String, Object, Object]),
698
- __metadata("design:returntype", Promise)
699
- ], DynamicModelController.prototype, "findBy", null);
700
- __decorate([
701
- (0, index_js_1.ApiOperationFromModel)(ModelConstr, "GET", "statement/:method/*args"),
702
- (0, swagger_1.ApiOperation)({
703
- summary: `Executes a prepared statement on ${modelClazzName}.`,
704
- }),
705
- (0, swagger_1.ApiParam)({
706
- name: "method",
707
- description: "the prepared statement to execute",
708
- }),
709
- (0, swagger_1.ApiParam)({
710
- name: "args",
711
- description: "concatenated list of arguments the prepared statement can accept",
712
- }),
713
- (0, swagger_1.ApiQuery)({
714
- name: "direction",
715
- required: true,
716
- enum: core_1.OrderDirection,
717
- description: "the sort order when applicable",
718
- }),
719
- (0, swagger_1.ApiQuery)({
720
- name: "limit",
721
- required: true,
722
- description: "limit or page size when applicable",
723
- }),
724
- (0, swagger_1.ApiQuery)({
725
- name: "offset",
726
- required: true,
727
- description: "offset or bookmark when applicable",
728
- }),
729
- (0, swagger_1.ApiOkResponse)({
730
- description: `${modelClazzName} listed found.`,
731
- }),
732
- (0, swagger_1.ApiNotFoundResponse)({
733
- description: `No ${modelClazzName} record matches the provided identifier.`,
734
- }),
735
- __param(0, (0, common_1.Param)("method")),
736
- __param(1, (0, common_1.Param)("args")),
737
- __param(2, (0, index_js_1.DecafQuery)()),
738
- __metadata("design:type", Function),
739
- __metadata("design:paramtypes", [String, Array, Object]),
740
- __metadata("design:returntype", Promise)
741
- ], DynamicModelController.prototype, "statement", null);
742
- __decorate([
743
- (0, index_js_1.ApiOperationFromModel)(ModelConstr, "POST", "bulk"),
462
+ }
463
+ static extractQueryArgs(args) {
464
+ if (args.length === 0)
465
+ return args;
466
+ const last = args[args.length - 1];
467
+ if (last && typeof last === "object" && !Array.isArray(last)) {
468
+ const queryObj = args.pop();
469
+ const hasDirection = queryObj.direction !== undefined;
470
+ const hasLimit = queryObj.limit !== undefined;
471
+ const hasOffset = queryObj.offset !== undefined;
472
+ if (!hasDirection && !hasLimit && !hasOffset)
473
+ return args;
474
+ const extras = [];
475
+ if (hasDirection)
476
+ extras.push(queryObj.direction);
477
+ else if (hasLimit || hasOffset)
478
+ extras.push(undefined);
479
+ if (hasLimit)
480
+ extras.push(queryObj.limit);
481
+ if (hasOffset)
482
+ extras.push(queryObj.offset);
483
+ return [...args, ...extras];
484
+ }
485
+ return args;
486
+ }
487
+ static normalizeQueryArgs(args) {
488
+ const normalized = FromModelController.extractQueryArgs(args);
489
+ if (normalized.length === 0)
490
+ return normalized;
491
+ const last = normalized[normalized.length - 1];
492
+ if (!last || typeof last !== "object" || Array.isArray(last)) {
493
+ return normalized;
494
+ }
495
+ const queryObj = last;
496
+ const hasQueryFields = queryObj.direction !== undefined ||
497
+ queryObj.limit !== undefined ||
498
+ queryObj.offset !== undefined ||
499
+ queryObj.bookmark !== undefined;
500
+ if (!hasQueryFields) {
501
+ normalized.pop();
502
+ return normalized;
503
+ }
504
+ normalized.pop();
505
+ if (queryObj.direction !== undefined)
506
+ normalized.push(queryObj.direction);
507
+ if (queryObj.limit !== undefined)
508
+ normalized.push(queryObj.limit);
509
+ if (queryObj.offset !== undefined)
510
+ normalized.push(queryObj.offset);
511
+ if (queryObj.bookmark !== undefined)
512
+ normalized.push(queryObj.bookmark);
513
+ return normalized;
514
+ }
515
+ static createCreateDecorators(ModelConstr, modelClazzName) {
516
+ return [
517
+ (0, index_js_1.ApiOperationFromModel)(ModelConstr, "POST"),
744
518
  (0, swagger_1.ApiOperation)({ summary: `Create a new ${modelClazzName}.` }),
745
519
  (0, swagger_1.ApiBody)({
746
520
  description: `Payload for ${modelClazzName}`,
747
- schema: {
748
- type: "array",
749
- items: {
750
- $ref: (0, swagger_1.getSchemaPath)(ModelConstr),
751
- // $ref: getSchemaPath(DtoFor(OperationKeys.CREATE, ModelConstr)),
752
- },
753
- },
521
+ type: (0, DtoBuilder_js_1.DtoFor)(db_decorators_1.OperationKeys.CREATE, ModelConstr),
754
522
  }),
755
523
  (0, swagger_1.ApiCreatedResponse)({
756
524
  description: `${modelClazzName} created successfully.`,
757
- schema: {
758
- type: "array",
759
- items: {
760
- $ref: (0, swagger_1.getSchemaPath)(ModelConstr),
761
- },
762
- },
525
+ schema: { $ref: (0, swagger_1.getSchemaPath)(ModelConstr) },
763
526
  }),
764
527
  (0, swagger_1.ApiBadRequestResponse)({ description: "Payload validation failed." }),
765
528
  (0, swagger_1.ApiUnprocessableEntityResponse)({
766
529
  description: "Repository rejected the provided payload.",
767
530
  }),
768
- __param(0, (0, index_js_1.DecafBody)()),
769
- __param(1, (0, common_1.Response)({ passthrough: true })),
770
- __metadata("design:type", Function),
771
- __metadata("design:paramtypes", [Array, Object]),
772
- __metadata("design:returntype", Promise)
773
- ], DynamicModelController.prototype, "createAll", null);
774
- __decorate([
775
- (0, index_js_1.ApiOperationFromModel)(ModelConstr, "POST"),
531
+ ];
532
+ }
533
+ static bulkCreateDecorators(ModelConstr, modelClazzName) {
534
+ return [
535
+ (0, index_js_1.ApiOperationFromModel)(ModelConstr, "POST", "bulk"),
776
536
  (0, swagger_1.ApiOperation)({ summary: `Create a new ${modelClazzName}.` }),
777
537
  (0, swagger_1.ApiBody)({
778
538
  description: `Payload for ${modelClazzName}`,
779
- type: (0, DtoBuilder_js_1.DtoFor)(db_decorators_1.OperationKeys.CREATE, ModelConstr),
539
+ schema: {
540
+ type: "array",
541
+ items: { $ref: (0, swagger_1.getSchemaPath)(ModelConstr) },
542
+ },
780
543
  }),
781
544
  (0, swagger_1.ApiCreatedResponse)({
782
545
  description: `${modelClazzName} created successfully.`,
783
546
  schema: {
784
- $ref: (0, swagger_1.getSchemaPath)(ModelConstr),
547
+ type: "array",
548
+ items: { $ref: (0, swagger_1.getSchemaPath)(ModelConstr) },
785
549
  },
786
550
  }),
787
551
  (0, swagger_1.ApiBadRequestResponse)({ description: "Payload validation failed." }),
788
552
  (0, swagger_1.ApiUnprocessableEntityResponse)({
789
553
  description: "Repository rejected the provided payload.",
790
554
  }),
791
- __param(0, (0, index_js_1.DecafBody)()),
792
- __param(1, (0, common_1.Response)({ passthrough: true })),
793
- __metadata("design:type", Function),
794
- __metadata("design:paramtypes", [Object, Object]),
795
- __metadata("design:returntype", Promise)
796
- ], DynamicModelController.prototype, "create", null);
797
- __decorate([
555
+ ];
556
+ }
557
+ static bulkReadDecorators(ModelConstr, modelClazzName) {
558
+ return [
798
559
  (0, index_js_1.ApiOperationFromModel)(ModelConstr, "GET", "bulk"),
799
- (0, swagger_1.ApiOperation)({ summary: `Retrieve a ${modelClazzName} record by id.` }),
560
+ (0, swagger_1.ApiOperation)({ summary: `Retrieve ${modelClazzName} records by ids.` }),
800
561
  (0, swagger_1.ApiQuery)({ name: "ids", required: true, type: "array" }),
801
562
  (0, swagger_1.ApiOkResponse)({
802
563
  description: `${modelClazzName} retrieved successfully.`,
803
564
  schema: {
804
565
  type: "array",
805
- items: {
806
- $ref: (0, swagger_1.getSchemaPath)(ModelConstr),
807
- },
808
- },
809
- }),
810
- (0, swagger_1.ApiNotFoundResponse)({
811
- description: `No ${modelClazzName} record matches the provided identifier.`,
812
- }),
813
- __param(0, (0, common_1.Query)("ids")),
814
- __metadata("design:type", Function),
815
- __metadata("design:paramtypes", [Array]),
816
- __metadata("design:returntype", Promise)
817
- ], DynamicModelController.prototype, "readAll", null);
818
- __decorate([
819
- (0, index_js_1.ApiOperationFromModel)(ModelConstr, "GET", path),
820
- (0, index_js_1.ApiParamsFromModel)(apiProperties),
821
- (0, swagger_1.ApiOperation)({ summary: `Retrieve a ${modelClazzName} record by id.` }),
822
- (0, swagger_1.ApiOkResponse)({
823
- description: `${modelClazzName} retrieved successfully.`,
824
- schema: {
825
- $ref: (0, swagger_1.getSchemaPath)(ModelConstr),
566
+ items: { $ref: (0, swagger_1.getSchemaPath)(ModelConstr) },
826
567
  },
827
568
  }),
828
569
  (0, swagger_1.ApiNotFoundResponse)({
829
570
  description: `No ${modelClazzName} record matches the provided identifier.`,
830
571
  }),
831
- __param(0, (0, index_js_1.DecafParams)(apiProperties)),
832
- __metadata("design:type", Function),
833
- __metadata("design:paramtypes", [Object]),
834
- __metadata("design:returntype", Promise)
835
- ], DynamicModelController.prototype, "read", null);
836
- __decorate([
837
- (0, index_js_1.ApiOperationFromModel)(ModelConstr, "PUT", `bulk`),
572
+ ];
573
+ }
574
+ static bulkUpdateDecorators(ModelConstr, modelClazzName, apiProperties) {
575
+ return [
576
+ (0, index_js_1.ApiOperationFromModel)(ModelConstr, "PUT", "bulk"),
838
577
  (0, index_js_1.ApiParamsFromModel)(apiProperties),
839
578
  (0, swagger_1.ApiOperation)({
840
- summary: `Replace an existing ${modelClazzName} record with a new payload.`,
579
+ summary: `Replace existing ${modelClazzName} records with new payloads.`,
841
580
  }),
842
581
  (0, swagger_1.ApiBody)({
843
- description: `Payload for replace a existing record of ${modelClazzName}`,
582
+ description: `Payload for replacing existing records of ${modelClazzName}`,
844
583
  schema: {
845
584
  type: "array",
846
585
  $ref: (0, swagger_1.getSchemaPath)((0, DtoBuilder_js_1.DtoFor)(db_decorators_1.OperationKeys.UPDATE, ModelConstr)),
@@ -850,125 +589,212 @@ class FromModelController {
850
589
  description: `${modelClazzName} updated successfully.`,
851
590
  schema: {
852
591
  type: "array",
853
- items: {
854
- $ref: (0, swagger_1.getSchemaPath)(ModelConstr),
855
- },
592
+ items: { $ref: (0, swagger_1.getSchemaPath)(ModelConstr) },
856
593
  },
857
594
  }),
858
595
  (0, swagger_1.ApiNotFoundResponse)({
859
596
  description: `No ${modelClazzName} record matches the provided identifier.`,
860
597
  }),
861
598
  (0, swagger_1.ApiBadRequestResponse)({ description: "Payload validation failed." }),
862
- __param(0, (0, index_js_1.DecafBody)()),
863
- __param(1, (0, common_1.Response)({ passthrough: true })),
864
- __metadata("design:type", Function),
865
- __metadata("design:paramtypes", [Array, Object]),
866
- __metadata("design:returntype", Promise)
867
- ], DynamicModelController.prototype, "updateAll", null);
868
- __decorate([
869
- (0, index_js_1.ApiOperationFromModel)(ModelConstr, "PUT", path),
599
+ ];
600
+ }
601
+ static bulkDeleteDecorators(ModelConstr, modelClazzName, apiProperties) {
602
+ return [
603
+ (0, index_js_1.ApiOperationFromModel)(ModelConstr, "DELETE", "bulk"),
604
+ (0, index_js_1.ApiParamsFromModel)(apiProperties),
605
+ (0, swagger_1.ApiOperation)({ summary: `Delete ${modelClazzName} records by ids.` }),
606
+ (0, swagger_1.ApiQuery)({ name: "ids", required: true, type: "array" }),
607
+ (0, swagger_1.ApiOkResponse)({
608
+ description: `${modelClazzName} deleted successfully.`,
609
+ schema: {
610
+ type: "array",
611
+ items: { $ref: (0, swagger_1.getSchemaPath)(ModelConstr) },
612
+ },
613
+ }),
614
+ (0, swagger_1.ApiNotFoundResponse)({
615
+ description: `No ${modelClazzName} record matches the provided identifier.`,
616
+ }),
617
+ ];
618
+ }
619
+ static readDecorators(ModelConstr, modelClazzName, apiProperties, pkPath) {
620
+ return [
621
+ (0, index_js_1.ApiOperationFromModel)(ModelConstr, "GET", pkPath),
622
+ (0, index_js_1.ApiParamsFromModel)(apiProperties),
623
+ (0, swagger_1.ApiOperation)({ summary: `Retrieve a ${modelClazzName} record by id.` }),
624
+ (0, swagger_1.ApiOkResponse)({
625
+ description: `${modelClazzName} retrieved successfully.`,
626
+ schema: { $ref: (0, swagger_1.getSchemaPath)(ModelConstr) },
627
+ }),
628
+ (0, swagger_1.ApiNotFoundResponse)({
629
+ description: `No ${modelClazzName} record matches the provided identifier.`,
630
+ }),
631
+ ];
632
+ }
633
+ static updateDecorators(ModelConstr, modelClazzName, apiProperties, pkPath) {
634
+ return [
635
+ (0, index_js_1.ApiOperationFromModel)(ModelConstr, "PUT", pkPath),
870
636
  (0, index_js_1.ApiParamsFromModel)(apiProperties),
871
637
  (0, swagger_1.ApiOperation)({
872
638
  summary: `Replace an existing ${modelClazzName} record with a new payload.`,
873
639
  }),
874
640
  (0, swagger_1.ApiBody)({
875
- description: `Payload for replace a existing record of ${modelClazzName}`,
641
+ description: `Payload for replacing an existing record of ${modelClazzName}`,
876
642
  type: (0, DtoBuilder_js_1.DtoFor)(db_decorators_1.OperationKeys.UPDATE, ModelConstr),
877
643
  }),
878
644
  (0, swagger_1.ApiOkResponse)({
879
645
  description: `${modelClazzName} updated successfully.`,
880
- schema: {
881
- $ref: (0, swagger_1.getSchemaPath)(ModelConstr),
882
- },
646
+ schema: { $ref: (0, swagger_1.getSchemaPath)(ModelConstr) },
883
647
  }),
884
648
  (0, swagger_1.ApiNotFoundResponse)({
885
649
  description: `No ${modelClazzName} record matches the provided identifier.`,
886
650
  }),
887
651
  (0, swagger_1.ApiBadRequestResponse)({ description: "Payload validation failed." }),
888
- __param(0, (0, index_js_1.DecafParams)(apiProperties)),
889
- __param(1, (0, index_js_1.DecafBody)()),
890
- __param(2, (0, common_1.Response)({ passthrough: true })),
891
- __metadata("design:type", Function),
892
- __metadata("design:paramtypes", [Object, Object, Object]),
893
- __metadata("design:returntype", Promise)
894
- ], DynamicModelController.prototype, "update", null);
895
- __decorate([
896
- (0, index_js_1.ApiOperationFromModel)(ModelConstr, "DELETE", "bulk"),
652
+ ];
653
+ }
654
+ static deleteDecorators(ModelConstr, modelClazzName, apiProperties, pkPath) {
655
+ return [
656
+ (0, index_js_1.ApiOperationFromModel)(ModelConstr, "DELETE", pkPath),
897
657
  (0, index_js_1.ApiParamsFromModel)(apiProperties),
898
- (0, swagger_1.ApiOperation)({ summary: `Retrieve a ${modelClazzName} record by id.` }),
899
- (0, swagger_1.ApiQuery)({ name: "ids", required: true, type: "array" }),
658
+ (0, swagger_1.ApiOperation)({ summary: `Delete a ${modelClazzName} record by id.` }),
900
659
  (0, swagger_1.ApiOkResponse)({
901
660
  description: `${modelClazzName} deleted successfully.`,
902
- schema: {
903
- type: "array",
904
- items: {
905
- $ref: (0, swagger_1.getSchemaPath)(ModelConstr),
906
- },
907
- },
661
+ schema: { $ref: (0, swagger_1.getSchemaPath)(ModelConstr) },
908
662
  }),
909
663
  (0, swagger_1.ApiNotFoundResponse)({
910
664
  description: `No ${modelClazzName} record matches the provided identifier.`,
911
665
  }),
912
- __param(0, (0, common_1.Query)("ids")),
913
- __param(1, (0, common_1.Response)({ passthrough: true })),
914
- __metadata("design:type", Function),
915
- __metadata("design:paramtypes", [Array, Object]),
916
- __metadata("design:returntype", Promise)
917
- ], DynamicModelController.prototype, "deleteAll", null);
918
- __decorate([
919
- (0, index_js_1.ApiOperationFromModel)(ModelConstr, "DELETE", path),
920
- (0, index_js_1.ApiParamsFromModel)(apiProperties),
921
- (0, swagger_1.ApiOperation)({ summary: `Delete a ${modelClazzName} record by id.` }),
922
- (0, swagger_1.ApiOkResponse)({
923
- description: `${modelClazzName} deleted successfully.`,
924
- schema: {
925
- $ref: (0, swagger_1.getSchemaPath)(ModelConstr),
926
- },
666
+ ];
667
+ }
668
+ static statementDecorators(ModelConstr, modelClazzName) {
669
+ return [
670
+ (0, index_js_1.ApiOperationFromModel)(ModelConstr, "GET", "statement/:method/*args"),
671
+ (0, swagger_1.ApiOperation)({
672
+ summary: `Executes a prepared statement on ${modelClazzName}.`,
673
+ }),
674
+ (0, swagger_1.ApiParam)({
675
+ name: "method",
676
+ description: "the prepared statement to execute",
677
+ }),
678
+ (0, swagger_1.ApiParam)({
679
+ name: "args",
680
+ description: "concatenated list of arguments the prepared statement can accept",
681
+ }),
682
+ (0, swagger_1.ApiQuery)({
683
+ name: "direction",
684
+ required: true,
685
+ enum: core_1.OrderDirection,
686
+ description: "the sort order when applicable",
927
687
  }),
688
+ (0, swagger_1.ApiQuery)({
689
+ name: "limit",
690
+ required: true,
691
+ description: "limit or page size when applicable",
692
+ }),
693
+ (0, swagger_1.ApiQuery)({
694
+ name: "offset",
695
+ required: true,
696
+ description: "offset or bookmark when applicable",
697
+ }),
698
+ (0, swagger_1.ApiOkResponse)({ description: `${modelClazzName} listed found.` }),
928
699
  (0, swagger_1.ApiNotFoundResponse)({
929
700
  description: `No ${modelClazzName} record matches the provided identifier.`,
930
701
  }),
931
- __param(0, (0, index_js_1.DecafParams)(apiProperties)),
932
- __param(1, (0, common_1.Response)({ passthrough: true })),
933
- __metadata("design:type", Function),
934
- __metadata("design:paramtypes", [Object, Object]),
935
- __metadata("design:returntype", Promise)
936
- ], DynamicModelController.prototype, "delete", null);
937
- DynamicModelController = __decorate([
938
- (0, common_1.Controller)(routePath),
939
- (0, swagger_1.ApiTags)(modelClazzName),
940
- (0, swagger_1.ApiExtraModels)(ModelConstr),
941
- (0, decorators_js_1.Auth)(ModelConstr),
942
- __metadata("design:paramtypes", [index_js_2.DecafRequestContext])
943
- ], DynamicModelController);
944
- return DynamicModelController;
702
+ ];
945
703
  }
946
- static getRouteParametersFromModel(ModelClazz) {
947
- const pk = decorator_validation_1.Model.pk(ModelClazz);
948
- const composed = decoration_1.Metadata.get(ModelClazz, decoration_1.Metadata.key(db_decorators_1.DBKeys.COMPOSED, pk));
949
- const composedKeys = composed?.args ?? [];
950
- // remove duplicates while preserving order
951
- const uniqueKeys = Array.isArray(composedKeys) && composedKeys.length > 0
952
- ? Array.from(new Set([...composedKeys]))
953
- : Array.from(new Set([pk]));
954
- const description = decoration_1.Metadata.description(ModelClazz);
955
- const path = `:${uniqueKeys.join("/:")}`;
956
- const apiProperties = uniqueKeys.map((key) => {
957
- return {
958
- name: key,
959
- description: decoration_1.Metadata.description(ModelClazz, key),
960
- required: true,
961
- type: String,
962
- };
704
+ static statementShortcutDecorators(ModelConstr, modelClazzName, path, statementKey) {
705
+ const base = [
706
+ (0, index_js_1.ApiOperationFromModel)(ModelConstr, "GET", path),
707
+ (0, swagger_1.ApiOperation)({ summary: `Retrieve ${modelClazzName} records.` }),
708
+ (0, swagger_1.ApiOkResponse)({
709
+ description: `${modelClazzName} retrieved successfully.`,
710
+ }),
711
+ ];
712
+ const segments = path.split("/").filter((s) => s.startsWith(":"));
713
+ segments.forEach((seg) => {
714
+ const name = seg.replace(":", "");
715
+ base.push((0, swagger_1.ApiParam)({ name, description: `The ${name} parameter` }));
963
716
  });
964
- return {
965
- path,
966
- description,
967
- apiProperties,
968
- getPK: (...params) => composed?.separator ? params.join(composed.separator) : params.join(""),
969
- };
717
+ if (path.startsWith("listBy/") ||
718
+ path.startsWith("paginateBy/") ||
719
+ path.startsWith("find/") ||
720
+ path.startsWith("page/")) {
721
+ base.push((0, swagger_1.ApiQuery)({
722
+ name: "direction",
723
+ required: true,
724
+ enum: core_1.OrderDirection,
725
+ description: "the sort order",
726
+ }));
727
+ }
728
+ if (path.startsWith("paginateBy/") || path.startsWith("page/")) {
729
+ base.push((0, swagger_1.ApiQuery)({ name: "limit", required: false, description: "page size" }), (0, swagger_1.ApiQuery)({
730
+ name: "offset",
731
+ required: false,
732
+ description: "page number",
733
+ }), (0, swagger_1.ApiQuery)({
734
+ name: "bookmark",
735
+ required: false,
736
+ description: "bookmark for cursor pagination",
737
+ }));
738
+ }
739
+ if (path.startsWith("findOneBy/") || path.startsWith("findBy/")) {
740
+ base.push((0, swagger_1.ApiNotFoundResponse)({
741
+ description: `No ${modelClazzName} record matches the provided identifier.`,
742
+ }));
743
+ }
744
+ if (statementKey === core_1.PreparedStatementKeys.COUNT_OF ||
745
+ statementKey === core_1.PreparedStatementKeys.AVG_OF ||
746
+ statementKey === core_1.PreparedStatementKeys.SUM_OF) {
747
+ base.push((0, swagger_1.ApiOkResponse)({
748
+ description: `Result for ${modelClazzName}.`,
749
+ type: Number,
750
+ }));
751
+ }
752
+ if (statementKey === core_1.PreparedStatementKeys.DISTINCT_OF) {
753
+ base.push((0, swagger_1.ApiOkResponse)({
754
+ description: `Distinct values for ${modelClazzName}.`,
755
+ type: [String],
756
+ }));
757
+ }
758
+ return base;
759
+ }
760
+ static getQueryDecorators(methodName, routePath, httpVerb, includeQueryParams = false) {
761
+ const extractPathParams = (p) => p
762
+ .split("/")
763
+ .filter((s) => s.startsWith(":"))
764
+ .map((s) => s.slice(1));
765
+ const apiPathParams = extractPathParams(routePath).map((name) => ({
766
+ name,
767
+ description: `${name} parameter for the query`,
768
+ required: true,
769
+ type: String,
770
+ }));
771
+ const decorators = [
772
+ ...apiPathParams.map((p) => (0, swagger_1.ApiParam)(p)),
773
+ (0, swagger_1.ApiOperation)({ summary: `Retrieve records using "${methodName}".` }),
774
+ (0, swagger_1.ApiOkResponse)({ description: "Result successfully retrieved." }),
775
+ (0, swagger_1.ApiNoContentResponse)({
776
+ description: "No content returned by the method.",
777
+ }),
778
+ ];
779
+ if (httpVerb === "GET" && includeQueryParams) {
780
+ decorators.push((0, swagger_1.ApiQuery)({
781
+ name: "direction",
782
+ required: false,
783
+ enum: core_1.OrderDirection,
784
+ description: "the sort order when applicable",
785
+ }), (0, swagger_1.ApiQuery)({
786
+ name: "limit",
787
+ required: false,
788
+ description: "limit or page size",
789
+ }), (0, swagger_1.ApiQuery)({
790
+ name: "offset",
791
+ required: false,
792
+ description: "offset or bookmark",
793
+ }));
794
+ }
795
+ return decorators;
970
796
  }
971
797
  }
972
798
  exports.FromModelController = FromModelController;
973
799
  //# sourceMappingURL=FromModelController.js.map
974
- //# sourceMappingURL=FromModelController.js.map
800
+ //# sourceMappingURL=FromModelController.cjs.map