@resolveio/server-lib 22.2.33 → 22.2.34

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 (649) hide show
  1. package/.github/workflows/ai-assistant-nightly-eval.yml +224 -0
  2. package/.github/workflows/ai-assistant-pr-guardrails.yml +60 -0
  3. package/.nodemon.json +5 -0
  4. package/.vscode/settings.json +21 -0
  5. package/AGENTS.md +179 -0
  6. package/README.md +22 -0
  7. package/build_package.sh +5 -0
  8. package/compileDTS.pl +64 -0
  9. package/docs/ai-assistant-nightly-eval.md +65 -0
  10. package/docs/ai-assistant-preflight-checklist.md +23 -0
  11. package/docs/ai-assistant-report-builder-bridge-playbook.md +115 -0
  12. package/eslint-plugin-custom/index.js +7 -0
  13. package/eslint-plugin-custom/rules/no-filter-zero-index.js +44 -0
  14. package/eslint.config.js +103 -0
  15. package/gulpfile.js +216 -0
  16. package/methodAndPublicationListGenerator.py +319 -0
  17. package/mongodbensurers.js +2 -0
  18. package/mongostop.js +3 -0
  19. package/package.json +1 -1
  20. package/settings.development.json +25 -0
  21. package/settings.development.redacted.json +25 -0
  22. package/src/.env +12 -0
  23. package/src/ai/assistant-core-heuristics.ts +577 -0
  24. package/src/client-server-app.ts +12 -0
  25. package/src/collections/ai-terminal-conversation.collection.ts +91 -0
  26. package/src/collections/ai-terminal-issue-report.collection.ts +99 -0
  27. package/src/collections/ai-terminal-message.collection.ts +77 -0
  28. package/src/collections/app-setting.collection.ts +104 -0
  29. package/src/collections/app-status.collection.ts +58 -0
  30. package/src/collections/communication-metric.collection.ts +84 -0
  31. package/src/collections/counter.collection.ts +56 -0
  32. package/src/collections/cron-job-history.collection.ts +94 -0
  33. package/src/collections/cron-job.collection.ts +92 -0
  34. package/src/collections/customer-notification.collection.ts +131 -0
  35. package/src/collections/customer-portal-password.collection.ts +76 -0
  36. package/src/collections/email-history.collection.ts +121 -0
  37. package/src/collections/email-verified.collection.ts +61 -0
  38. package/src/collections/file.collection.ts +74 -0
  39. package/src/collections/flag-update.collection.ts +57 -0
  40. package/src/collections/flag.collection.ts +57 -0
  41. package/src/collections/log-method-latency.collection.ts +77 -0
  42. package/src/collections/log-subscription.collection.ts +80 -0
  43. package/src/collections/log.collection.ts +93 -0
  44. package/src/collections/logged-in-users.collection.ts +67 -0
  45. package/src/collections/monitor-cpu.collection.ts +65 -0
  46. package/src/collections/monitor-function.collection.ts +74 -0
  47. package/src/collections/monitor-memory.collection.ts +77 -0
  48. package/src/collections/monitor-mongo.collection.ts +71 -0
  49. package/src/collections/notification.collection.ts +57 -0
  50. package/src/collections/openai-usage-ledger.collection.ts +77 -0
  51. package/src/collections/report-builder-dashboard-builder.collection.ts +109 -0
  52. package/src/collections/report-builder-library.collection.ts +89 -0
  53. package/src/collections/report-builder-report.collection.ts +180 -0
  54. package/src/collections/user-group.collection.ts +89 -0
  55. package/src/collections/user-guide.collection.ts +57 -0
  56. package/src/collections/user.collection.ts +181 -0
  57. package/src/cron/cron.ts +117 -0
  58. package/src/fixtures/cron-jobs.ts +95 -0
  59. package/src/fixtures/init.ts +35 -0
  60. package/src/http/auth.ts +764 -0
  61. package/src/http/health.ts +7 -0
  62. package/src/http/home.ts +90 -0
  63. package/src/http/slow-query-publication.ts +49 -0
  64. package/src/index.ts +1 -0
  65. package/src/managers/communication-metric.manager.ts +82 -0
  66. package/src/managers/cron.manager.ts +333 -0
  67. package/src/managers/customer-notification-content.manager.ts +236 -0
  68. package/src/managers/diagnostic-manager-bootstrap.ts +165 -0
  69. package/src/managers/error-auto-fix.manager.ts +2767 -0
  70. package/src/managers/local-log.manager.ts +113 -0
  71. package/src/managers/method.manager.ts +1557 -0
  72. package/src/managers/mongo.manager.ts +4566 -0
  73. package/src/managers/monitor.manager.ts +489 -0
  74. package/src/managers/openai-usage-ledger.manager.ts +116 -0
  75. package/src/managers/slow-query-verifier.manager.ts +3590 -0
  76. package/src/managers/slow-query.manager.ts +519 -0
  77. package/src/managers/subscription.manager.ts +3120 -0
  78. package/src/managers/websocket.manager.ts +746 -0
  79. package/src/managers/worker-dispatcher.manager.ts +1318 -0
  80. package/src/managers/worker-server.manager.ts +468 -0
  81. package/src/methods/accounts.ts +532 -0
  82. package/src/methods/ai-terminal.ts +25505 -0
  83. package/src/methods/app-settings.ts +114 -0
  84. package/src/methods/aws.ts +646 -0
  85. package/src/methods/collections.ts +544 -0
  86. package/src/methods/counters.ts +67 -0
  87. package/src/methods/cron-jobs.ts +2610 -0
  88. package/src/methods/customer-notifications.ts +458 -0
  89. package/src/methods/diagnostics.ts +447 -0
  90. package/src/methods/flag-updates.ts +7 -0
  91. package/src/methods/flags.ts +7 -0
  92. package/src/methods/logs.ts +656 -0
  93. package/src/methods/mongo-explorer.ts +1883 -0
  94. package/src/methods/monitor.ts +540 -0
  95. package/src/methods/pdf.ts +1210 -0
  96. package/src/methods/publications.ts +128 -0
  97. package/src/methods/report-builder.ts +3305 -0
  98. package/src/methods/support.ts +210 -0
  99. package/src/models/ai-terminal-conversation.model.ts +19 -0
  100. package/src/models/ai-terminal-issue-report.model.ts +21 -0
  101. package/src/models/ai-terminal-message.model.ts +24 -0
  102. package/src/models/app-setting.model.ts +17 -0
  103. package/{models/app-status.model.d.ts → src/models/app-status.model.ts} +3 -2
  104. package/{models/billing-logged-in-users.model.d.ts → src/models/billing-logged-in-users.model.ts} +5 -4
  105. package/src/models/collection-document.model.ts +24 -0
  106. package/src/models/communication-metric.model.ts +23 -0
  107. package/{models/counter.model.d.ts → src/models/counter.model.ts} +4 -3
  108. package/src/models/cron-job-history.model.ts +16 -0
  109. package/src/models/cron-job.model.ts +15 -0
  110. package/src/models/customer-notification.model.ts +28 -0
  111. package/src/models/customer-portal-password.model.ts +12 -0
  112. package/src/models/dialog.model.ts +25 -0
  113. package/{models/email-history.model.js → src/models/email-history.model.ts} +34 -4
  114. package/{models/email-verified.model.d.ts → src/models/email-verified.model.ts} +6 -5
  115. package/{models/file.model.d.ts → src/models/file.model.ts} +8 -7
  116. package/{models/flag-update.model.d.ts → src/models/flag-update.model.ts} +4 -3
  117. package/{models/flag.model.d.ts → src/models/flag.model.ts} +4 -3
  118. package/src/models/log-method-latency.model.ts +11 -0
  119. package/{models/log-subscription.model.d.ts → src/models/log-subscription.model.ts} +11 -9
  120. package/src/models/log.model.ts +19 -0
  121. package/{models/logged-in-users.model.d.ts → src/models/logged-in-users.model.ts} +6 -5
  122. package/{models/method-response.model.d.ts → src/models/method-response.model.ts} +7 -6
  123. package/src/models/method.model.ts +23 -0
  124. package/{models/monitor-cpu.model.d.ts → src/models/monitor-cpu.model.ts} +9 -7
  125. package/src/models/monitor-function.model.ts +16 -0
  126. package/src/models/monitor-memory.model.ts +17 -0
  127. package/src/models/monitor-mongo.model.ts +15 -0
  128. package/{models/notification.model.d.ts → src/models/notification.model.ts} +6 -4
  129. package/src/models/openai-usage-ledger.model.ts +16 -0
  130. package/src/models/pagination.model.ts +35 -0
  131. package/src/models/permission.model.ts +14 -0
  132. package/src/models/report-builder-dashboard-builder.model.ts +29 -0
  133. package/src/models/report-builder-library.model.ts +20 -0
  134. package/src/models/report-builder-report.model.ts +135 -0
  135. package/src/models/report-builder.model.ts +68 -0
  136. package/src/models/select-data-label.model.ts +9 -0
  137. package/src/models/server-message.model.ts +31 -0
  138. package/src/models/slow-query-report.model.ts +23 -0
  139. package/src/models/subscription.model.ts +73 -0
  140. package/src/models/support-ticket.model.ts +96 -0
  141. package/src/models/user-group.model.ts +24 -0
  142. package/{models/user-guide.model.d.ts → src/models/user-guide.model.ts} +5 -4
  143. package/src/models/user.model.ts +96 -0
  144. package/src/private/images/ResolveIO.png +0 -0
  145. package/src/publications/ai-terminal.ts +73 -0
  146. package/src/publications/app-settings.ts +25 -0
  147. package/src/publications/app-status.ts +13 -0
  148. package/src/publications/cron-jobs.ts +29 -0
  149. package/src/publications/customer-notifications.ts +101 -0
  150. package/src/publications/files.ts +33 -0
  151. package/src/publications/flags-update.ts +19 -0
  152. package/src/publications/flags.ts +19 -0
  153. package/src/publications/logs.ts +163 -0
  154. package/src/publications/notifications.ts +13 -0
  155. package/src/publications/report-builder-dashboard-builders.ts +39 -0
  156. package/src/publications/report-builder-libraries.ts +41 -0
  157. package/src/publications/report-builder-reports.ts +47 -0
  158. package/src/publications/super-admin.ts +13 -0
  159. package/src/publications/user-groups.ts +12 -0
  160. package/src/publications/user-guides.ts +12 -0
  161. package/src/resolveio-server-app.ts +617 -0
  162. package/src/server-app.ts +2616 -0
  163. package/src/services/codex-client.ts +1117 -0
  164. package/src/services/openai-client.ts +265 -0
  165. package/src/types/error-report.ts +26 -0
  166. package/src/types/js-tiktoken.d.ts +11 -0
  167. package/src/types/slow-query-report.ts +28 -0
  168. package/src/util/common.ts +649 -0
  169. package/src/util/customer-portal-password.ts +183 -0
  170. package/src/util/error-reporter.ts +332 -0
  171. package/src/util/error-tracking.ts +79 -0
  172. package/src/util/report-builder-unwinds.ts +180 -0
  173. package/src/util/schema-report-builder.ts +448 -0
  174. package/src/util/slow-query-reporter.ts +216 -0
  175. package/src/util/subscription-dependency-context.ts +1096 -0
  176. package/src/util/tokenizer.ts +38 -0
  177. package/src/workers/codex-runner.worker.ts +142 -0
  178. package/start_server.sh +5 -0
  179. package/tests/ai-assistant-corpus-build.ts +484 -0
  180. package/tests/ai-assistant-corpus-replay-e2e.ts +773 -0
  181. package/tests/ai-assistant-data-parity-e2e.ts +2018 -0
  182. package/tests/ai-assistant-eval-triage.ts +831 -0
  183. package/tests/ai-assistant-openai-e2e.ts +1061 -0
  184. package/tests/ai-assistant-openai-git-e2e.ts +155 -0
  185. package/tests/ai-assistant-preflight-matrix.ts +215 -0
  186. package/tests/ai-assistant-routing-eval.test.ts +560 -0
  187. package/tests/ai-assistant-snf-live-eval.ts +921 -0
  188. package/tests/ai-assistant-utils.test.ts +2165 -0
  189. package/tests/error-reporter.test.ts +145 -0
  190. package/tests/report-builder-linking.test.ts +79 -0
  191. package/tests/subscription-connect-race.test.ts +157 -0
  192. package/tests/subscription-dependency-context.test.ts +324 -0
  193. package/tests/subscription-manager-collection-tracking.test.ts +86 -0
  194. package/tests/subscription-manager-invalidation.test.ts +85 -0
  195. package/tsconfig.json +34 -0
  196. package/ai/assistant-core-heuristics.d.ts +0 -11
  197. package/ai/assistant-core-heuristics.js +0 -531
  198. package/ai/assistant-core-heuristics.js.map +0 -1
  199. package/client-server-app.d.ts +0 -1
  200. package/client-server-app.js +0 -68
  201. package/client-server-app.js.map +0 -1
  202. package/collections/ai-terminal-conversation.collection.d.ts +0 -2
  203. package/collections/ai-terminal-conversation.collection.js +0 -140
  204. package/collections/ai-terminal-conversation.collection.js.map +0 -1
  205. package/collections/ai-terminal-issue-report.collection.d.ts +0 -2
  206. package/collections/ai-terminal-issue-report.collection.js +0 -148
  207. package/collections/ai-terminal-issue-report.collection.js.map +0 -1
  208. package/collections/ai-terminal-message.collection.d.ts +0 -2
  209. package/collections/ai-terminal-message.collection.js +0 -121
  210. package/collections/ai-terminal-message.collection.js.map +0 -1
  211. package/collections/app-setting.collection.d.ts +0 -3
  212. package/collections/app-setting.collection.js +0 -103
  213. package/collections/app-setting.collection.js.map +0 -1
  214. package/collections/app-status.collection.d.ts +0 -3
  215. package/collections/app-status.collection.js +0 -57
  216. package/collections/app-status.collection.js.map +0 -1
  217. package/collections/communication-metric.collection.d.ts +0 -2
  218. package/collections/communication-metric.collection.js +0 -133
  219. package/collections/communication-metric.collection.js.map +0 -1
  220. package/collections/counter.collection.d.ts +0 -3
  221. package/collections/counter.collection.js +0 -56
  222. package/collections/counter.collection.js.map +0 -1
  223. package/collections/cron-job-history.collection.d.ts +0 -3
  224. package/collections/cron-job-history.collection.js +0 -137
  225. package/collections/cron-job-history.collection.js.map +0 -1
  226. package/collections/cron-job.collection.d.ts +0 -3
  227. package/collections/cron-job.collection.js +0 -92
  228. package/collections/cron-job.collection.js.map +0 -1
  229. package/collections/customer-notification.collection.d.ts +0 -3
  230. package/collections/customer-notification.collection.js +0 -130
  231. package/collections/customer-notification.collection.js.map +0 -1
  232. package/collections/customer-portal-password.collection.d.ts +0 -3
  233. package/collections/customer-portal-password.collection.js +0 -75
  234. package/collections/customer-portal-password.collection.js.map +0 -1
  235. package/collections/email-history.collection.d.ts +0 -3
  236. package/collections/email-history.collection.js +0 -121
  237. package/collections/email-history.collection.js.map +0 -1
  238. package/collections/email-verified.collection.d.ts +0 -3
  239. package/collections/email-verified.collection.js +0 -61
  240. package/collections/email-verified.collection.js.map +0 -1
  241. package/collections/file.collection.d.ts +0 -3
  242. package/collections/file.collection.js +0 -74
  243. package/collections/file.collection.js.map +0 -1
  244. package/collections/flag-update.collection.d.ts +0 -3
  245. package/collections/flag-update.collection.js +0 -57
  246. package/collections/flag-update.collection.js.map +0 -1
  247. package/collections/flag.collection.d.ts +0 -3
  248. package/collections/flag.collection.js +0 -57
  249. package/collections/flag.collection.js.map +0 -1
  250. package/collections/log-method-latency.collection.d.ts +0 -3
  251. package/collections/log-method-latency.collection.js +0 -77
  252. package/collections/log-method-latency.collection.js.map +0 -1
  253. package/collections/log-subscription.collection.d.ts +0 -3
  254. package/collections/log-subscription.collection.js +0 -80
  255. package/collections/log-subscription.collection.js.map +0 -1
  256. package/collections/log.collection.d.ts +0 -3
  257. package/collections/log.collection.js +0 -93
  258. package/collections/log.collection.js.map +0 -1
  259. package/collections/logged-in-users.collection.d.ts +0 -3
  260. package/collections/logged-in-users.collection.js +0 -67
  261. package/collections/logged-in-users.collection.js.map +0 -1
  262. package/collections/monitor-cpu.collection.d.ts +0 -3
  263. package/collections/monitor-cpu.collection.js +0 -65
  264. package/collections/monitor-cpu.collection.js.map +0 -1
  265. package/collections/monitor-function.collection.d.ts +0 -3
  266. package/collections/monitor-function.collection.js +0 -74
  267. package/collections/monitor-function.collection.js.map +0 -1
  268. package/collections/monitor-memory.collection.d.ts +0 -3
  269. package/collections/monitor-memory.collection.js +0 -77
  270. package/collections/monitor-memory.collection.js.map +0 -1
  271. package/collections/monitor-mongo.collection.d.ts +0 -3
  272. package/collections/monitor-mongo.collection.js +0 -71
  273. package/collections/monitor-mongo.collection.js.map +0 -1
  274. package/collections/notification.collection.d.ts +0 -3
  275. package/collections/notification.collection.js +0 -57
  276. package/collections/notification.collection.js.map +0 -1
  277. package/collections/openai-usage-ledger.collection.d.ts +0 -2
  278. package/collections/openai-usage-ledger.collection.js +0 -124
  279. package/collections/openai-usage-ledger.collection.js.map +0 -1
  280. package/collections/report-builder-dashboard-builder.collection.d.ts +0 -3
  281. package/collections/report-builder-dashboard-builder.collection.js +0 -109
  282. package/collections/report-builder-dashboard-builder.collection.js.map +0 -1
  283. package/collections/report-builder-library.collection.d.ts +0 -3
  284. package/collections/report-builder-library.collection.js +0 -87
  285. package/collections/report-builder-library.collection.js.map +0 -1
  286. package/collections/report-builder-report.collection.d.ts +0 -4
  287. package/collections/report-builder-report.collection.js +0 -180
  288. package/collections/report-builder-report.collection.js.map +0 -1
  289. package/collections/user-group.collection.d.ts +0 -4
  290. package/collections/user-group.collection.js +0 -89
  291. package/collections/user-group.collection.js.map +0 -1
  292. package/collections/user-guide.collection.d.ts +0 -3
  293. package/collections/user-guide.collection.js +0 -57
  294. package/collections/user-guide.collection.js.map +0 -1
  295. package/collections/user.collection.d.ts +0 -4
  296. package/collections/user.collection.js +0 -180
  297. package/collections/user.collection.js.map +0 -1
  298. package/cron/cron.d.ts +0 -14
  299. package/cron/cron.js +0 -216
  300. package/cron/cron.js.map +0 -1
  301. package/fixtures/cron-jobs.d.ts +0 -1
  302. package/fixtures/cron-jobs.js +0 -150
  303. package/fixtures/cron-jobs.js.map +0 -1
  304. package/fixtures/init.d.ts +0 -1
  305. package/fixtures/init.js +0 -91
  306. package/fixtures/init.js.map +0 -1
  307. package/http/auth.d.ts +0 -2
  308. package/http/auth.js +0 -903
  309. package/http/auth.js.map +0 -1
  310. package/http/health.d.ts +0 -1
  311. package/http/health.js +0 -11
  312. package/http/health.js.map +0 -1
  313. package/http/home.d.ts +0 -1
  314. package/http/home.js +0 -134
  315. package/http/home.js.map +0 -1
  316. package/http/slow-query-publication.d.ts +0 -2
  317. package/http/slow-query-publication.js +0 -99
  318. package/http/slow-query-publication.js.map +0 -1
  319. package/index.d.ts +0 -1
  320. package/index.js +0 -19
  321. package/index.js.map +0 -1
  322. package/managers/communication-metric.manager.d.ts +0 -16
  323. package/managers/communication-metric.manager.js +0 -134
  324. package/managers/communication-metric.manager.js.map +0 -1
  325. package/managers/cron.manager.d.ts +0 -20
  326. package/managers/cron.manager.js +0 -534
  327. package/managers/cron.manager.js.map +0 -1
  328. package/managers/customer-notification-content.manager.d.ts +0 -55
  329. package/managers/customer-notification-content.manager.js +0 -158
  330. package/managers/customer-notification-content.manager.js.map +0 -1
  331. package/managers/diagnostic-manager-bootstrap.d.ts +0 -9
  332. package/managers/diagnostic-manager-bootstrap.js +0 -260
  333. package/managers/diagnostic-manager-bootstrap.js.map +0 -1
  334. package/managers/error-auto-fix.manager.d.ts +0 -149
  335. package/managers/error-auto-fix.manager.js +0 -3064
  336. package/managers/error-auto-fix.manager.js.map +0 -1
  337. package/managers/local-log.manager.d.ts +0 -18
  338. package/managers/local-log.manager.js +0 -88
  339. package/managers/local-log.manager.js.map +0 -1
  340. package/managers/method.manager.d.ts +0 -77
  341. package/managers/method.manager.js +0 -1701
  342. package/managers/method.manager.js.map +0 -1
  343. package/managers/mongo.manager.d.ts +0 -222
  344. package/managers/mongo.manager.js +0 -4984
  345. package/managers/mongo.manager.js.map +0 -1
  346. package/managers/monitor.manager.d.ts +0 -69
  347. package/managers/monitor.manager.js +0 -534
  348. package/managers/monitor.manager.js.map +0 -1
  349. package/managers/openai-usage-ledger.manager.d.ts +0 -15
  350. package/managers/openai-usage-ledger.manager.js +0 -144
  351. package/managers/openai-usage-ledger.manager.js.map +0 -1
  352. package/managers/slow-query-verifier.manager.d.ts +0 -144
  353. package/managers/slow-query-verifier.manager.js +0 -3857
  354. package/managers/slow-query-verifier.manager.js.map +0 -1
  355. package/managers/slow-query.manager.d.ts +0 -28
  356. package/managers/slow-query.manager.js +0 -468
  357. package/managers/slow-query.manager.js.map +0 -1
  358. package/managers/subscription.manager.d.ts +0 -169
  359. package/managers/subscription.manager.js +0 -3422
  360. package/managers/subscription.manager.js.map +0 -1
  361. package/managers/websocket.manager.d.ts +0 -73
  362. package/managers/websocket.manager.js +0 -673
  363. package/managers/websocket.manager.js.map +0 -1
  364. package/managers/worker-dispatcher.manager.d.ts +0 -117
  365. package/managers/worker-dispatcher.manager.js +0 -1210
  366. package/managers/worker-dispatcher.manager.js.map +0 -1
  367. package/managers/worker-server.manager.d.ts +0 -16
  368. package/managers/worker-server.manager.js +0 -530
  369. package/managers/worker-server.manager.js.map +0 -1
  370. package/methods/accounts.d.ts +0 -2
  371. package/methods/accounts.js +0 -624
  372. package/methods/accounts.js.map +0 -1
  373. package/methods/ai-terminal.d.ts +0 -304
  374. package/methods/ai-terminal.js +0 -25096
  375. package/methods/ai-terminal.js.map +0 -1
  376. package/methods/app-settings.d.ts +0 -2
  377. package/methods/app-settings.js +0 -169
  378. package/methods/app-settings.js.map +0 -1
  379. package/methods/aws.d.ts +0 -2
  380. package/methods/aws.js +0 -874
  381. package/methods/aws.js.map +0 -1
  382. package/methods/collections.d.ts +0 -2
  383. package/methods/collections.js +0 -626
  384. package/methods/collections.js.map +0 -1
  385. package/methods/counters.d.ts +0 -2
  386. package/methods/counters.js +0 -111
  387. package/methods/counters.js.map +0 -1
  388. package/methods/cron-jobs.d.ts +0 -2
  389. package/methods/cron-jobs.js +0 -2471
  390. package/methods/cron-jobs.js.map +0 -1
  391. package/methods/customer-notifications.d.ts +0 -2
  392. package/methods/customer-notifications.js +0 -528
  393. package/methods/customer-notifications.js.map +0 -1
  394. package/methods/diagnostics.d.ts +0 -2
  395. package/methods/diagnostics.js +0 -514
  396. package/methods/diagnostics.js.map +0 -1
  397. package/methods/flag-updates.d.ts +0 -2
  398. package/methods/flag-updates.js +0 -8
  399. package/methods/flag-updates.js.map +0 -1
  400. package/methods/flags.d.ts +0 -2
  401. package/methods/flags.js +0 -8
  402. package/methods/flags.js.map +0 -1
  403. package/methods/logs.d.ts +0 -2
  404. package/methods/logs.js +0 -750
  405. package/methods/logs.js.map +0 -1
  406. package/methods/mongo-explorer.d.ts +0 -2
  407. package/methods/mongo-explorer.js +0 -1811
  408. package/methods/mongo-explorer.js.map +0 -1
  409. package/methods/monitor.d.ts +0 -2
  410. package/methods/monitor.js +0 -543
  411. package/methods/monitor.js.map +0 -1
  412. package/methods/pdf.d.ts +0 -2
  413. package/methods/pdf.js +0 -1195
  414. package/methods/pdf.js.map +0 -1
  415. package/methods/publications.d.ts +0 -1
  416. package/methods/publications.js +0 -183
  417. package/methods/publications.js.map +0 -1
  418. package/methods/report-builder.d.ts +0 -2
  419. package/methods/report-builder.js +0 -2960
  420. package/methods/report-builder.js.map +0 -1
  421. package/methods/support.d.ts +0 -2
  422. package/methods/support.js +0 -313
  423. package/methods/support.js.map +0 -1
  424. package/models/ai-terminal-conversation.model.d.ts +0 -17
  425. package/models/ai-terminal-conversation.model.js +0 -4
  426. package/models/ai-terminal-conversation.model.js.map +0 -1
  427. package/models/ai-terminal-issue-report.model.d.ts +0 -19
  428. package/models/ai-terminal-issue-report.model.js +0 -4
  429. package/models/ai-terminal-issue-report.model.js.map +0 -1
  430. package/models/ai-terminal-message.model.d.ts +0 -22
  431. package/models/ai-terminal-message.model.js +0 -4
  432. package/models/ai-terminal-message.model.js.map +0 -1
  433. package/models/app-setting.model.d.ts +0 -16
  434. package/models/app-setting.model.js +0 -4
  435. package/models/app-setting.model.js.map +0 -1
  436. package/models/app-status.model.js +0 -4
  437. package/models/app-status.model.js.map +0 -1
  438. package/models/billing-logged-in-users.model.js +0 -4
  439. package/models/billing-logged-in-users.model.js.map +0 -1
  440. package/models/collection-document.model.d.ts +0 -21
  441. package/models/collection-document.model.js +0 -4
  442. package/models/collection-document.model.js.map +0 -1
  443. package/models/communication-metric.model.d.ts +0 -20
  444. package/models/communication-metric.model.js +0 -4
  445. package/models/communication-metric.model.js.map +0 -1
  446. package/models/counter.model.js +0 -4
  447. package/models/counter.model.js.map +0 -1
  448. package/models/cron-job-history.model.d.ts +0 -15
  449. package/models/cron-job-history.model.js +0 -4
  450. package/models/cron-job-history.model.js.map +0 -1
  451. package/models/cron-job.model.d.ts +0 -14
  452. package/models/cron-job.model.js +0 -4
  453. package/models/cron-job.model.js.map +0 -1
  454. package/models/customer-notification.model.d.ts +0 -26
  455. package/models/customer-notification.model.js +0 -4
  456. package/models/customer-notification.model.js.map +0 -1
  457. package/models/customer-portal-password.model.d.ts +0 -11
  458. package/models/customer-portal-password.model.js +0 -4
  459. package/models/customer-portal-password.model.js.map +0 -1
  460. package/models/dialog.model.d.ts +0 -23
  461. package/models/dialog.model.js +0 -4
  462. package/models/dialog.model.js.map +0 -1
  463. package/models/email-history.model.d.ts +0 -30
  464. package/models/email-history.model.js.map +0 -1
  465. package/models/email-verified.model.js +0 -4
  466. package/models/email-verified.model.js.map +0 -1
  467. package/models/file.model.js +0 -4
  468. package/models/file.model.js.map +0 -1
  469. package/models/flag-update.model.js +0 -4
  470. package/models/flag-update.model.js.map +0 -1
  471. package/models/flag.model.js +0 -4
  472. package/models/flag.model.js.map +0 -1
  473. package/models/log-method-latency.model.d.ts +0 -10
  474. package/models/log-method-latency.model.js +0 -4
  475. package/models/log-method-latency.model.js.map +0 -1
  476. package/models/log-subscription.model.js +0 -4
  477. package/models/log-subscription.model.js.map +0 -1
  478. package/models/log.model.d.ts +0 -17
  479. package/models/log.model.js +0 -4
  480. package/models/log.model.js.map +0 -1
  481. package/models/logged-in-users.model.js +0 -4
  482. package/models/logged-in-users.model.js.map +0 -1
  483. package/models/method-response.model.js +0 -4
  484. package/models/method-response.model.js.map +0 -1
  485. package/models/method.model.d.ts +0 -24
  486. package/models/method.model.js +0 -4
  487. package/models/method.model.js.map +0 -1
  488. package/models/monitor-cpu.model.js +0 -4
  489. package/models/monitor-cpu.model.js.map +0 -1
  490. package/models/monitor-function.model.d.ts +0 -14
  491. package/models/monitor-function.model.js +0 -4
  492. package/models/monitor-function.model.js.map +0 -1
  493. package/models/monitor-memory.model.d.ts +0 -15
  494. package/models/monitor-memory.model.js +0 -4
  495. package/models/monitor-memory.model.js.map +0 -1
  496. package/models/monitor-mongo.model.d.ts +0 -13
  497. package/models/monitor-mongo.model.js +0 -4
  498. package/models/monitor-mongo.model.js.map +0 -1
  499. package/models/notification.model.js +0 -4
  500. package/models/notification.model.js.map +0 -1
  501. package/models/openai-usage-ledger.model.d.ts +0 -15
  502. package/models/openai-usage-ledger.model.js +0 -4
  503. package/models/openai-usage-ledger.model.js.map +0 -1
  504. package/models/pagination.model.d.ts +0 -11
  505. package/models/pagination.model.js +0 -28
  506. package/models/pagination.model.js.map +0 -1
  507. package/models/permission.model.d.ts +0 -12
  508. package/models/permission.model.js +0 -4
  509. package/models/permission.model.js.map +0 -1
  510. package/models/report-builder-dashboard-builder.model.d.ts +0 -25
  511. package/models/report-builder-dashboard-builder.model.js +0 -4
  512. package/models/report-builder-dashboard-builder.model.js.map +0 -1
  513. package/models/report-builder-library.model.d.ts +0 -17
  514. package/models/report-builder-library.model.js +0 -4
  515. package/models/report-builder-library.model.js.map +0 -1
  516. package/models/report-builder-report.model.d.ts +0 -120
  517. package/models/report-builder-report.model.js +0 -4
  518. package/models/report-builder-report.model.js.map +0 -1
  519. package/models/report-builder.model.d.ts +0 -61
  520. package/models/report-builder.model.js +0 -4
  521. package/models/report-builder.model.js.map +0 -1
  522. package/models/select-data-label.model.d.ts +0 -9
  523. package/models/select-data-label.model.js +0 -4
  524. package/models/select-data-label.model.js.map +0 -1
  525. package/models/server-message.model.d.ts +0 -32
  526. package/models/server-message.model.js +0 -4
  527. package/models/server-message.model.js.map +0 -1
  528. package/models/slow-query-report.model.d.ts +0 -23
  529. package/models/slow-query-report.model.js +0 -4
  530. package/models/slow-query-report.model.js.map +0 -1
  531. package/models/subscription.model.d.ts +0 -31
  532. package/models/subscription.model.js +0 -4
  533. package/models/subscription.model.js.map +0 -1
  534. package/models/support-ticket.model.d.ts +0 -86
  535. package/models/support-ticket.model.js +0 -4
  536. package/models/support-ticket.model.js.map +0 -1
  537. package/models/user-group.model.d.ts +0 -20
  538. package/models/user-group.model.js +0 -4
  539. package/models/user-group.model.js.map +0 -1
  540. package/models/user-guide.model.js +0 -4
  541. package/models/user-guide.model.js.map +0 -1
  542. package/models/user.model.d.ts +0 -84
  543. package/models/user.model.js +0 -4
  544. package/models/user.model.js.map +0 -1
  545. package/private/images/ResolveIO.png +0 -0
  546. package/public_api.js +0 -107
  547. package/public_api.js.map +0 -1
  548. package/publications/ai-terminal.d.ts +0 -1
  549. package/publications/ai-terminal.js +0 -122
  550. package/publications/ai-terminal.js.map +0 -1
  551. package/publications/app-settings.d.ts +0 -2
  552. package/publications/app-settings.js +0 -28
  553. package/publications/app-settings.js.map +0 -1
  554. package/publications/app-status.d.ts +0 -2
  555. package/publications/app-status.js +0 -16
  556. package/publications/app-status.js.map +0 -1
  557. package/publications/cron-jobs.d.ts +0 -2
  558. package/publications/cron-jobs.js +0 -32
  559. package/publications/cron-jobs.js.map +0 -1
  560. package/publications/customer-notifications.d.ts +0 -2
  561. package/publications/customer-notifications.js +0 -161
  562. package/publications/customer-notifications.js.map +0 -1
  563. package/publications/files.d.ts +0 -2
  564. package/publications/files.js +0 -36
  565. package/publications/files.js.map +0 -1
  566. package/publications/flags-update.d.ts +0 -2
  567. package/publications/flags-update.js +0 -22
  568. package/publications/flags-update.js.map +0 -1
  569. package/publications/flags.d.ts +0 -2
  570. package/publications/flags.js +0 -22
  571. package/publications/flags.js.map +0 -1
  572. package/publications/logs.d.ts +0 -2
  573. package/publications/logs.js +0 -164
  574. package/publications/logs.js.map +0 -1
  575. package/publications/notifications.d.ts +0 -2
  576. package/publications/notifications.js +0 -16
  577. package/publications/notifications.js.map +0 -1
  578. package/publications/report-builder-dashboard-builders.d.ts +0 -2
  579. package/publications/report-builder-dashboard-builders.js +0 -42
  580. package/publications/report-builder-dashboard-builders.js.map +0 -1
  581. package/publications/report-builder-libraries.d.ts +0 -2
  582. package/publications/report-builder-libraries.js +0 -90
  583. package/publications/report-builder-libraries.js.map +0 -1
  584. package/publications/report-builder-reports.d.ts +0 -2
  585. package/publications/report-builder-reports.js +0 -50
  586. package/publications/report-builder-reports.js.map +0 -1
  587. package/publications/super-admin.d.ts +0 -2
  588. package/publications/super-admin.js +0 -16
  589. package/publications/super-admin.js.map +0 -1
  590. package/publications/user-groups.d.ts +0 -1
  591. package/publications/user-groups.js +0 -16
  592. package/publications/user-groups.js.map +0 -1
  593. package/publications/user-guides.d.ts +0 -1
  594. package/publications/user-guides.js +0 -16
  595. package/publications/user-guides.js.map +0 -1
  596. package/resolveio-server-app.d.ts +0 -70
  597. package/resolveio-server-app.js +0 -801
  598. package/resolveio-server-app.js.map +0 -1
  599. package/server-app.d.ts +0 -167
  600. package/server-app.js +0 -2784
  601. package/server-app.js.map +0 -1
  602. package/services/codex-client.d.ts +0 -119
  603. package/services/codex-client.js +0 -1470
  604. package/services/codex-client.js.map +0 -1
  605. package/services/openai-client.d.ts +0 -46
  606. package/services/openai-client.js +0 -318
  607. package/services/openai-client.js.map +0 -1
  608. package/types/error-report.d.ts +0 -25
  609. package/types/error-report.js +0 -4
  610. package/types/error-report.js.map +0 -1
  611. package/types/slow-query-report.d.ts +0 -27
  612. package/types/slow-query-report.js +0 -6
  613. package/types/slow-query-report.js.map +0 -1
  614. package/util/common.d.ts +0 -31
  615. package/util/common.js +0 -683
  616. package/util/common.js.map +0 -1
  617. package/util/customer-portal-password.d.ts +0 -13
  618. package/util/customer-portal-password.js +0 -209
  619. package/util/customer-portal-password.js.map +0 -1
  620. package/util/error-reporter.d.ts +0 -52
  621. package/util/error-reporter.js +0 -326
  622. package/util/error-reporter.js.map +0 -1
  623. package/util/error-tracking.d.ts +0 -13
  624. package/util/error-tracking.js +0 -120
  625. package/util/error-tracking.js.map +0 -1
  626. package/util/report-builder-unwinds.d.ts +0 -15
  627. package/util/report-builder-unwinds.js +0 -156
  628. package/util/report-builder-unwinds.js.map +0 -1
  629. package/util/schema-report-builder.d.ts +0 -6
  630. package/util/schema-report-builder.js +0 -481
  631. package/util/schema-report-builder.js.map +0 -1
  632. package/util/slow-query-reporter.d.ts +0 -28
  633. package/util/slow-query-reporter.js +0 -226
  634. package/util/slow-query-reporter.js.map +0 -1
  635. package/util/subscription-dependency-context.d.ts +0 -34
  636. package/util/subscription-dependency-context.js +0 -1283
  637. package/util/subscription-dependency-context.js.map +0 -1
  638. package/util/tokenizer.d.ts +0 -5
  639. package/util/tokenizer.js +0 -41
  640. package/util/tokenizer.js.map +0 -1
  641. package/workers/codex-runner.worker.d.ts +0 -1
  642. package/workers/codex-runner.worker.js +0 -192
  643. package/workers/codex-runner.worker.js.map +0 -1
  644. /package/{private → src/private}/email-templates/enrollment.html +0 -0
  645. /package/{private → src/private}/email-templates/forgot-password.html +0 -0
  646. /package/{private → src/private}/email-templates/support-ticket-deleted.html +0 -0
  647. /package/{private → src/private}/email-templates/support-ticket-modified.html +0 -0
  648. /package/{private → src/private}/email-templates/support-ticket.html +0 -0
  649. /package/{public_api.d.ts → src/public_api.ts} +0 -0
@@ -0,0 +1,4566 @@
1
+ import { AggregateOptions, AggregationCursor, BulkWriteOptions, BulkWriteResult, ChangeStream, ChangeStreamOptions, Collection, CollectionInfo, CommandOperationOptions, CountDocumentsOptions, CreateCollectionOptions, CreateIndexesOptions, CursorStreamOptions, DeleteOptions, DeleteResult, FindCursor, FindOneAndDeleteOptions, FindOneAndReplaceOptions, FindOneAndUpdateOptions, FindOptions, IndexDescription, IndexDescriptionCompact, IndexInformationOptions, InsertOneOptions, ListIndexesCursor, ListIndexesOptions, OptionalUnlessRequiredId, RenameOptions, ReplaceOptions, UpdateOptions, UpdateResult } from 'mongodb';
2
+ import * as NodeCache from 'node-cache';
3
+ import SimpleSchema from 'simpl-schema';
4
+ import { Users } from '../collections/user.collection';
5
+ import { CollectionDocument, Document } from '../models/collection-document.model';
6
+ import { LookupTables } from '../models/report-builder.model';
7
+ import { UserModel } from '../models/user.model';
8
+ import { ResolveIOServer } from '../resolveio-server-app';
9
+ import { buildRbLookups, buildRbSchema, dateReviver, deepCopy, getBinarySize, getMongoMergeUpdatedDoc, objectIdHexString } from '../util/common';
10
+ import { saveCustomerPortalPassword } from '../util/customer-portal-password';
11
+ import { SlowQueryReporter } from '../util/slow-query-reporter';
12
+ import { getPublicationContext, QueryMeta, recordAggregateDependencies, recordDependencyResult } from '../util/subscription-dependency-context';
13
+ import { MonitorMongo } from './monitor.manager';
14
+ const crypto = require('crypto');
15
+ const scmp = require('scmp');
16
+ const v8 = require('v8');
17
+ const { AsyncLocalStorage } = require('async_hooks');
18
+ const asyncLocalStorage = new AsyncLocalStorage();
19
+
20
+ function resolveHeapLimitBytes(): number {
21
+ const stats = v8.getHeapStatistics();
22
+ if (stats && typeof stats.heap_size_limit === 'number') {
23
+ return stats.heap_size_limit;
24
+ }
25
+
26
+ if (stats && typeof stats.total_available_size === 'number') {
27
+ return stats.total_available_size;
28
+ }
29
+
30
+ return 0;
31
+ }
32
+
33
+ function buildQueryMetaFromFindOptions(options?: FindOptions<any>): QueryMeta {
34
+ if (!options) {
35
+ return null;
36
+ }
37
+
38
+ const meta: QueryMeta = {};
39
+
40
+ if (typeof options.limit === 'number') {
41
+ meta.limit = options.limit;
42
+ }
43
+
44
+ if (typeof options.skip === 'number') {
45
+ meta.skip = options.skip;
46
+ }
47
+
48
+ if (options.sort) {
49
+ meta.sort = deepCopy(options.sort);
50
+ }
51
+
52
+ return Object.keys(meta).length ? meta : null;
53
+ }
54
+
55
+ // export declare interface MongoManagerFilterOperators<T> extends Document {
56
+ export declare interface MongoManagerFilterParamaterOperators<P> {
57
+ $eq?: P;
58
+ $gt?: P;
59
+ $gte?: P;
60
+ $in?: [P] extends [Array<any>] ? P : P[];
61
+ $lt?: P;
62
+ $lte?: P;
63
+ $ne?: P;
64
+ $nin?: [P] extends [Array<any>] ? P : P[];
65
+ $not?: P extends string ? MongoManagerFilterParamaterOperators<P> | RegExpConstructor : MongoManagerFilterParamaterOperators<P>;
66
+ $exists?: boolean;
67
+ $expr?: any[]
68
+ $regex?: P extends string ? RegExpConstructor | string : never;
69
+ $options?: P extends string ? string : never;
70
+ $geoIntersects?: {
71
+ $geometry: Document;
72
+ };
73
+ $geoWithin?: Document;
74
+ $near?: Document;
75
+ $nearSphere?: Document;
76
+ $maxDistance?: number;
77
+ $all?: any[];
78
+ $elemMatch?: Document;
79
+ $size?: P extends any[] ? number : never;
80
+ }
81
+
82
+ export declare interface MongoManagerFilterOperators<T> extends Document { //TODO: Remove extends document when I get answer on S.O.
83
+ $and?: MongoManagerFilter<T>[] | MongoManagerFilterOperators<T>;
84
+ $nor?: MongoManagerFilter<T>[] | MongoManagerFilterOperators<T>;
85
+ $or?: MongoManagerFilter<T>[] | MongoManagerFilterOperators<T>;
86
+ $text?: {
87
+ $search: string;
88
+ $language?: string;
89
+ $caseSensitive?: boolean;
90
+ $diacriticSensitive?: boolean;
91
+ };
92
+ // eslint-disable-next-line no-unused-vars
93
+ $where?: string | ((this: T) => boolean);
94
+ $comment?: string | Document;
95
+ }
96
+
97
+ // function isDotStringRegExp(obj: Object, dotKey: string): boolean {
98
+ // let keyData = dotKey.split('.');
99
+ // let objData = obj;
100
+
101
+ // for (let i = 0; i < keyData.length; i++) {
102
+ // let key = keyData[i];
103
+ // if (objData.hasOwnProperty(key)) {
104
+ // objData = objData[key];
105
+
106
+ // if (i === keyData.length - 1) {
107
+ // return true;
108
+ // }
109
+ // }
110
+ // else {
111
+ // return false;
112
+ // }
113
+ // }
114
+ // }
115
+
116
+ // export declare type MongoManagerDotStringFilter<T> = {
117
+ // [key: string]: (isDotStringRegExp(T, key) ? any : never);
118
+ // };
119
+
120
+ export declare type MongoManagerFilter<T> = {
121
+ [P in keyof T]?: T[P] | MongoManagerFilterParamaterOperators<T[P]>;
122
+ };
123
+
124
+ export declare type MongoManagerUnsetFilter<T> = {
125
+ // eslint-disable-next-line no-unused-vars
126
+ [P in keyof T]?: number | boolean;
127
+ };
128
+
129
+ export declare type MongoManagerIncFilter<T> = {
130
+ // eslint-disable-next-line no-unused-vars
131
+ [P in keyof T]?: number;
132
+ };
133
+
134
+ export declare type MongoManagerRenameFilter<T> = {
135
+ // eslint-disable-next-line no-unused-vars
136
+ [P in keyof T]?: string;
137
+ };
138
+
139
+ export declare type MongoManagerArrayFilter<T> = {
140
+ // eslint-disable-next-line no-unused-vars
141
+ [P in keyof T]?: any; // can refine if needed
142
+ };
143
+
144
+ export declare type MongoManagerCurrentDateFilter<T> = {
145
+ // eslint-disable-next-line no-unused-vars
146
+ [P in keyof T]?: true | { $type: 'date' | 'timestamp' };
147
+ };
148
+
149
+ export declare type MongoManagerUpdateFilter<T> = {
150
+ $set?: MongoManagerFilter<T> | Document;
151
+ $unset?: MongoManagerUnsetFilter<T> | Document;
152
+ $inc?: MongoManagerIncFilter<T> | Document;
153
+ $setOnInsert?: MongoManagerFilter<T> | Document;
154
+ $push?: MongoManagerArrayFilter<T> | Document;
155
+ $pull?: MongoManagerArrayFilter<T> | Document;
156
+ $addToSet?: MongoManagerArrayFilter<T> | Document;
157
+ $min?: MongoManagerIncFilter<T> | Document;
158
+ $max?: MongoManagerIncFilter<T> | Document;
159
+ $currentDate?: MongoManagerCurrentDateFilter<T> | Document;
160
+ $mul?: MongoManagerIncFilter<T> | Document;
161
+ $rename?: MongoManagerRenameFilter<T>;
162
+ };
163
+
164
+ export class MongoManager {
165
+ private _collections: MongoManagerCollection<CollectionDocument>[] = [];
166
+ private _nodeCache;
167
+ private _cacheMap: { collections: string[], key: string }[] = [];
168
+ private _operationInProgress: Map<string, { promise: Promise<any>, invalidatedDuringExecution: boolean, collections: string[] }> = new Map();
169
+ private _heapSize = resolveHeapLimitBytes();
170
+ private _heapLimit: number;
171
+ private _serverCollections: (CollectionInfo | Pick<CollectionInfo, 'name' | 'type'>)[] = [];
172
+ private _isWorkersEnabled = false;
173
+ private _isWorkerInstance = false;
174
+ private _watchedDatabases: string[] = [];
175
+ private _mainDatabaseName = '';
176
+ private _changeStream: ChangeStream | null = null;
177
+ private _changeStreamRestartTimeout: NodeJS.Timeout | null = null;
178
+
179
+ constructor() {}
180
+
181
+ static async create() {
182
+ const mongoManager = new MongoManager();
183
+ await mongoManager.initialize();
184
+ return mongoManager;
185
+ }
186
+
187
+ private async initialize() {
188
+ this._nodeCache = new NodeCache({ stdTTL: 0, checkperiod: 0 });
189
+
190
+ this._isWorkersEnabled = process.env.IS_WORKERS_ENABLED === 'true';
191
+ this._isWorkerInstance = process.env.IS_WORKER_INSTANCE === 'true';
192
+ this._mainDatabaseName = ResolveIOServer.getServerConfig() ? ResolveIOServer.getServerConfig()['DATABASE'] : '';
193
+ this._watchedDatabases = this.resolveWatchedDatabases();
194
+ this.setCacheLimit();
195
+
196
+ if (this._isWorkersEnabled && this._isWorkerInstance) {
197
+ await this.setupChangeStream();
198
+ }
199
+
200
+ let collections = await ResolveIOServer.getMainDB().listCollections().toArray();
201
+ this._serverCollections = collections;
202
+ }
203
+
204
+ private resolveWatchedDatabases(): string[] {
205
+ const config = ResolveIOServer.getServerConfig ? ResolveIOServer.getServerConfig() : null;
206
+ const watchSet = new Set<string>();
207
+
208
+ if (this._mainDatabaseName) {
209
+ watchSet.add(this._mainDatabaseName);
210
+ }
211
+
212
+ const configValue = config ? config['WATCH_DATABASES'] : null;
213
+
214
+ if (Array.isArray(configValue)) {
215
+ for (const value of configValue) {
216
+ if (typeof value === 'string' && value.trim()) {
217
+ watchSet.add(value.trim());
218
+ }
219
+ }
220
+ }
221
+ else if (typeof configValue === 'string' && configValue.trim()) {
222
+ configValue.split(',').map(a => a.trim()).filter(a => a.length).forEach(a => watchSet.add(a));
223
+ }
224
+
225
+ const envValue = process.env.MONGO_WATCH_DATABASES;
226
+ if (envValue) {
227
+ envValue.split(',').map(a => a.trim()).filter(a => a.length).forEach(a => watchSet.add(a));
228
+ }
229
+
230
+ return Array.from(watchSet.values());
231
+ }
232
+
233
+ public getWatchedDatabases(): string[] {
234
+ return this._watchedDatabases;
235
+ }
236
+
237
+ oneTimeTransaction<T>(fn: () => Promise<T>): Promise<T> {
238
+ return asyncLocalStorage.run({}, async () => {
239
+ let attempt = 0;
240
+ const maxRetries = 4;
241
+ while (true) {
242
+ const session = ResolveIOServer.getMongoConnection().startSession();
243
+ const store = asyncLocalStorage.getStore();
244
+ if (store) {
245
+ store.session = session;
246
+ }
247
+ try {
248
+ let result: T | undefined;
249
+ await session.withTransaction(async () => {
250
+ result = await fn();
251
+ return result;
252
+ });
253
+ return result as T;
254
+ }
255
+ catch (err) {
256
+ if (!this.shouldRetryTransactionError(err) || attempt >= maxRetries) {
257
+ throw err;
258
+ }
259
+ if (this.shouldRetryConnectionError(err)) {
260
+ try {
261
+ await ResolveIOServer.requestMongoReconnect('mongo-transaction-retry', err);
262
+ }
263
+ catch {}
264
+ }
265
+ attempt += 1;
266
+ const jitter = Math.floor(Math.random() * 60);
267
+ await this.delay((120 * attempt) + jitter);
268
+ }
269
+ finally {
270
+ try {
271
+ await session.endSession();
272
+ }
273
+ catch {}
274
+ const activeStore = asyncLocalStorage.getStore();
275
+ if (activeStore) {
276
+ delete activeStore.session;
277
+ }
278
+ }
279
+ }
280
+ });
281
+ }
282
+
283
+ getSession() {
284
+ const store = asyncLocalStorage.getStore();
285
+ return store ? store.session : null;
286
+ }
287
+
288
+ runWithoutSession<T>(fn: () => Promise<T> | T): Promise<T> {
289
+ return asyncLocalStorage.run({}, async () => {
290
+ return await fn();
291
+ });
292
+ }
293
+
294
+ private setCacheLimit() {
295
+ if (this._isWorkersEnabled) {
296
+ this._heapLimit = this._isWorkerInstance ? this._heapSize * 0.8 : this._heapSize * 0.4;
297
+ }
298
+ else {
299
+ this._heapLimit = this._heapSize * 0.3;
300
+ }
301
+ }
302
+
303
+ getServerCollections() {
304
+ return this._serverCollections;
305
+ }
306
+
307
+ async registerCollection(collection: MongoManagerCollection<CollectionDocument>) {
308
+ if (
309
+ collection.collectionOptions &&
310
+ collection.collectionOptions.timeseries &&
311
+ collection.collectionOptions.timeseries.timeField &&
312
+ this._serverCollections.some(a => a.name === collection.collectionName && a.type === 'collection')
313
+ ) {
314
+ await ResolveIOServer.getMainDB().dropCollection(collection.collectionName);
315
+ await this.createCollection(collection);
316
+ }
317
+
318
+ this._collections.push(collection);
319
+ }
320
+
321
+ async createCollection(collection: MongoManagerCollection<CollectionDocument>) {
322
+ await ResolveIOServer.getMainDB().createCollection(collection.collectionName, collection.collectionOptions);
323
+ }
324
+
325
+ collections() {
326
+ return this._collections;
327
+ }
328
+
329
+ collection(collectionName: string): MongoManagerCollection<CollectionDocument> | MongoManagerUserCollection<CollectionDocument> {
330
+ return this._collections.find(a => a.collectionName === collectionName);
331
+ }
332
+
333
+ private async delay(ms: number) {
334
+ // eslint-disable-next-line no-restricted-syntax
335
+ return new Promise(resolve => setTimeout(resolve, ms));
336
+ }
337
+
338
+ private hasMongoErrorLabel(err: any, label: string): boolean {
339
+ if (!err || !label) {
340
+ return false;
341
+ }
342
+ if (typeof err.hasErrorLabel === 'function') {
343
+ try {
344
+ if (err.hasErrorLabel(label)) {
345
+ return true;
346
+ }
347
+ }
348
+ catch {}
349
+ }
350
+ if (Array.isArray(err.errorLabels) && err.errorLabels.includes(label)) {
351
+ return true;
352
+ }
353
+ return false;
354
+ }
355
+
356
+ private shouldRetryWriteConflict(err: any): boolean {
357
+ if (!err) {
358
+ return false;
359
+ }
360
+ if (err.code === 112 || err.codeName === 'WriteConflict') {
361
+ return true;
362
+ }
363
+ const message = String(err.message || '');
364
+ return /write conflict/i.test(message) || /yielding is disabled/i.test(message);
365
+ }
366
+
367
+ private shouldRetryTransactionError(err: any): boolean {
368
+ if (!err) {
369
+ return false;
370
+ }
371
+ if (this.shouldRetryWriteConflict(err) || this.shouldRetryWaitQueueTimeout(err) || this.shouldRetryConnectionError(err)) {
372
+ return true;
373
+ }
374
+ return this.hasMongoErrorLabel(err, 'TransientTransactionError')
375
+ || this.hasMongoErrorLabel(err, 'UnknownTransactionCommitResult');
376
+ }
377
+
378
+ private shouldRetryWaitQueueTimeout(err: any): boolean {
379
+ if (!err) {
380
+ return false;
381
+ }
382
+
383
+ if (err.name === 'MongoWaitQueueTimeoutError' || err.codeName === 'MongoWaitQueueTimeoutError') {
384
+ return true;
385
+ }
386
+
387
+ return typeof err.message === 'string' && err.message.includes('Timed out while checking out a connection from connection pool');
388
+ }
389
+
390
+ private shouldRetryConnectionError(err: any): boolean {
391
+ if (!err) {
392
+ return false;
393
+ }
394
+
395
+ const name = String(err.name || err.codeName || '');
396
+ if (
397
+ name === 'MongoPoolClearedError'
398
+ || name === 'MongoNetworkError'
399
+ || name === 'MongoNetworkTimeoutError'
400
+ || name === 'MongoServerSelectionError'
401
+ || name === 'MongoTopologyClosedError'
402
+ || name === 'MongoNotConnectedError'
403
+ || name === 'MongoClientClosedError'
404
+ ) {
405
+ return true;
406
+ }
407
+
408
+ const message = String(err.message || '');
409
+ return /connection pool .* was cleared/i.test(message)
410
+ || /topology is closed/i.test(message)
411
+ || /client was closed/i.test(message)
412
+ || /not connected/i.test(message)
413
+ || /server selection timed out/i.test(message)
414
+ || /econnrefused|econnreset|etimedout|ehostunreach/i.test(message);
415
+ }
416
+
417
+ private async retryRead<TResult>(operation: () => Promise<TResult>, options?: { session?: any }): Promise<TResult> {
418
+ if (options && options.session && typeof options.session.inTransaction === 'function' && options.session.inTransaction()) {
419
+ return operation();
420
+ }
421
+
422
+ let attempt = 0;
423
+ const maxRetries = 2;
424
+
425
+ while (true) {
426
+ try {
427
+ return await operation();
428
+ }
429
+ catch (err) {
430
+ const shouldRetry = this.shouldRetryWaitQueueTimeout(err) || this.shouldRetryConnectionError(err);
431
+ if (!shouldRetry || attempt >= maxRetries) {
432
+ throw err;
433
+ }
434
+ if (this.shouldRetryConnectionError(err)) {
435
+ try {
436
+ await ResolveIOServer.requestMongoReconnect('mongo-read-retry', err);
437
+ }
438
+ catch {}
439
+ }
440
+
441
+ attempt += 1;
442
+ await this.delay(100 * attempt);
443
+ }
444
+ }
445
+ }
446
+
447
+ public async find<T extends CollectionDocument>(
448
+ collectionName: string,
449
+ filter: MongoManagerFilter<T> & MongoManagerFilterOperators<T> = {},
450
+ options?: FindOptions<T>
451
+ ): Promise<T[]> {
452
+ // eslint-disable-next-line no-unused-vars
453
+ let { session, ...safeOptions } = options || {};
454
+ const cacheKey = this.generateCacheKey([collectionName], 'find', [filter, safeOptions]);
455
+ let result = this.getFromCache(cacheKey);
456
+ if (result !== undefined) {
457
+ if (ResolveIOServer.getMainServer().getSubscriptionManager() && ResolveIOServer.getMainServer().getSubscriptionManager().getEnableDebug()) {
458
+ console.log(new Date(), 'Mongo Find - Cache', collectionName);
459
+ }
460
+ recordDependencyResult(collectionName, result, { filter, meta: buildQueryMetaFromFindOptions(safeOptions) });
461
+ return result;
462
+ }
463
+
464
+ if (this._operationInProgress.has(cacheKey)) {
465
+ if (ResolveIOServer.getMainServer().getSubscriptionManager() && ResolveIOServer.getMainServer().getSubscriptionManager().getEnableDebug()) {
466
+ console.log(new Date(), 'Mongo Find - Already running', collectionName);
467
+ }
468
+ return this._operationInProgress.get(cacheKey).promise;
469
+ }
470
+
471
+ if (ResolveIOServer.getMainServer().getSubscriptionManager() && ResolveIOServer.getMainServer().getSubscriptionManager().getEnableDebug()) {
472
+ console.log(new Date(), 'Mongo Find - Running', collectionName);
473
+ }
474
+
475
+ const operation = this.executeFind<T>(collectionName, filter, options, cacheKey);
476
+ this._operationInProgress.set(cacheKey, {
477
+ promise: operation,
478
+ invalidatedDuringExecution: false,
479
+ collections: [collectionName]
480
+ });
481
+
482
+ await operation.finally(() => {
483
+ if (ResolveIOServer.getMainServer().getSubscriptionManager() && ResolveIOServer.getMainServer().getSubscriptionManager().getEnableDebug()) {
484
+ console.log(new Date(), 'Mongo Find - Done', collectionName);
485
+ }
486
+
487
+ this._operationInProgress.delete(cacheKey);
488
+ });
489
+
490
+ return operation;
491
+ }
492
+
493
+ private async executeFind<T extends CollectionDocument>(
494
+ collectionName: string,
495
+ filter: MongoManagerFilter<T> & MongoManagerFilterOperators<T>,
496
+ options: FindOptions<T> | undefined,
497
+ cacheKey: string
498
+ ): Promise<T[]> {
499
+ let result;
500
+ const collection = ResolveIOServer.getMainDB().collection(collectionName);
501
+
502
+ // eslint-disable-next-line no-unused-vars
503
+ let { session, ...safeOptions } = options || {};
504
+ const pubContext = getPublicationContext();
505
+ const monitor = MonitorMongo.create('find', collectionName, JSON.stringify([filter, safeOptions]));
506
+ const queryStart = Date.now();
507
+
508
+ try {
509
+ result = await this.retryRead(() => collection.find<T>(<any>filter, options).toArray(), options);
510
+ }
511
+ catch (err) {
512
+ console.log(JSON.stringify([new Date(), 'Error Execute Find', collectionName, filter, safeOptions, {
513
+ code: err.code,
514
+ codeName: err.codeName,
515
+ message: err.message,
516
+ stack: err.stack
517
+ }], null, 2));
518
+
519
+ err.message = `Error in Execute Find: ${collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
520
+ throw err;
521
+ }
522
+ finally {
523
+ const durationMs = Date.now() - queryStart;
524
+ await monitor.finish();
525
+ SlowQueryReporter.reportSlowQueryFireAndForget({
526
+ collection: collectionName,
527
+ filter,
528
+ options: safeOptions,
529
+ durationMs,
530
+ queryType: 'find',
531
+ notes: 'MongoManager executeFind',
532
+ publication: pubContext.publication,
533
+ subscriptionData: pubContext.subscriptionData,
534
+ userId: pubContext.userId
535
+ });
536
+ }
537
+
538
+ recordDependencyResult(collectionName, result, { filter, meta: buildQueryMetaFromFindOptions(safeOptions) });
539
+
540
+ if (!this._operationInProgress.get(cacheKey).invalidatedDuringExecution) {
541
+ this.addToCache([collectionName], cacheKey, result);
542
+ }
543
+
544
+ return result;
545
+ }
546
+
547
+ // FindOne Operation
548
+ public async findOne<T extends CollectionDocument>(
549
+ collectionName: string,
550
+ filter: MongoManagerFilter<T> | MongoManagerFilterOperators<T> = {},
551
+ options?: FindOptions<T>
552
+ ): Promise<T> {
553
+ // eslint-disable-next-line no-unused-vars
554
+ let { session, ...safeOptions } = options || {};
555
+ const cacheKey = this.generateCacheKey([collectionName], 'findOne', [filter, safeOptions]);
556
+ let result = this.getFromCache(cacheKey);
557
+ if (result !== undefined) {
558
+ if (ResolveIOServer.getMainServer().getSubscriptionManager() && ResolveIOServer.getMainServer().getSubscriptionManager().getEnableDebug()) {
559
+ console.log(new Date(), 'Mongo FindOne - Cache', collectionName);
560
+ }
561
+
562
+ recordDependencyResult(collectionName, result, { filter, meta: buildQueryMetaFromFindOptions(safeOptions) });
563
+
564
+ return result;
565
+ }
566
+
567
+ if (this._operationInProgress.has(cacheKey)) {
568
+ if (ResolveIOServer.getMainServer().getSubscriptionManager() && ResolveIOServer.getMainServer().getSubscriptionManager().getEnableDebug()) {
569
+ console.log(new Date(), 'Mongo FindOne - Already running', collectionName);
570
+ }
571
+
572
+ return this._operationInProgress.get(cacheKey).promise;
573
+ }
574
+
575
+ if (ResolveIOServer.getMainServer().getSubscriptionManager() && ResolveIOServer.getMainServer().getSubscriptionManager().getEnableDebug()) {
576
+ console.log(new Date(), 'Mongo FindOne - Running', collectionName);
577
+ }
578
+
579
+ const operation = this.executeFindOne<T>(collectionName, filter, options, cacheKey);
580
+ this._operationInProgress.set(cacheKey, {
581
+ promise: operation,
582
+ invalidatedDuringExecution: false,
583
+ collections: [collectionName]
584
+ });
585
+
586
+ await operation.finally(() => {
587
+ if (ResolveIOServer.getMainServer().getSubscriptionManager() && ResolveIOServer.getMainServer().getSubscriptionManager().getEnableDebug()) {
588
+ console.log(new Date(), 'Mongo FindOne - Done', collectionName);
589
+ }
590
+
591
+ this._operationInProgress.delete(cacheKey);
592
+ });
593
+
594
+ return operation;
595
+ }
596
+
597
+ private async executeFindOne<T extends CollectionDocument>(
598
+ collectionName: string,
599
+ filter: MongoManagerFilter<T> | MongoManagerFilterOperators<T>,
600
+ options: FindOptions<T> | undefined,
601
+ cacheKey: string
602
+ ): Promise<T> {
603
+ let result;
604
+ const collection = ResolveIOServer.getMainDB().collection(collectionName);
605
+
606
+ // eslint-disable-next-line no-unused-vars
607
+ let { session, ...safeOptions } = options || {};
608
+ const pubContext = getPublicationContext();
609
+ const monitor = MonitorMongo.create('findOne', collectionName, JSON.stringify([filter, safeOptions]));
610
+ const queryStart = Date.now();
611
+
612
+ try {
613
+ result = await this.retryRead(() => collection.findOne<T>(<any>filter, options || undefined), options);
614
+ }
615
+ catch (err) {
616
+ console.log(JSON.stringify([new Date(), 'Error Execute Find One', collectionName, filter, safeOptions, {
617
+ code: err.code,
618
+ codeName: err.codeName,
619
+ message: err.message,
620
+ stack: err.stack
621
+ }], null, 2));
622
+
623
+ err.message = `Error in Execute Find One: ${collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
624
+ throw err;
625
+ }
626
+ finally {
627
+ const durationMs = Date.now() - queryStart;
628
+ await monitor.finish();
629
+ SlowQueryReporter.reportSlowQueryFireAndForget({
630
+ collection: collectionName,
631
+ filter,
632
+ options: safeOptions,
633
+ durationMs,
634
+ queryType: 'findOne',
635
+ notes: 'MongoManager executeFindOne',
636
+ publication: pubContext.publication,
637
+ subscriptionData: pubContext.subscriptionData,
638
+ userId: pubContext.userId
639
+ });
640
+ }
641
+
642
+ recordDependencyResult(collectionName, result, { filter, meta: buildQueryMetaFromFindOptions(safeOptions) });
643
+
644
+ if (!this._operationInProgress.get(cacheKey).invalidatedDuringExecution) {
645
+ this.addToCache([collectionName], cacheKey, result);
646
+ }
647
+
648
+ return result;
649
+ }
650
+
651
+ // Aggregate Operation
652
+ public async aggregate<T extends CollectionDocument>(
653
+ collectionName: string,
654
+ pipeline: any[],
655
+ options?: AggregateOptions
656
+ ): Promise<any[]> {
657
+ const collections = [collectionName, ...pipeline.flatMap(stage => stage.$lookup?.from ? [stage.$lookup.from] : [])];
658
+
659
+ // eslint-disable-next-line no-unused-vars
660
+ let { session, ...safeOptions } = options || {};
661
+ const cacheKey = this.generateCacheKey(collections, 'aggregate', [pipeline, safeOptions]);
662
+ let result = this.getFromCache(cacheKey);
663
+ if (result !== undefined) {
664
+ if (ResolveIOServer.getMainServer().getSubscriptionManager() && ResolveIOServer.getMainServer().getSubscriptionManager().getEnableDebug()) {
665
+ console.log(new Date(), 'Mongo Agg - Cache', collections);
666
+ }
667
+ recordAggregateDependencies(collectionName, pipeline, result);
668
+ return result;
669
+ }
670
+
671
+ if (this._operationInProgress.has(cacheKey)) {
672
+ if (ResolveIOServer.getMainServer().getSubscriptionManager() && ResolveIOServer.getMainServer().getSubscriptionManager().getEnableDebug()) {
673
+ console.log(new Date(), 'Mongo Agg - Already running', collections);
674
+ }
675
+
676
+ return this._operationInProgress.get(cacheKey).promise;
677
+ }
678
+
679
+ if (ResolveIOServer.getMainServer().getSubscriptionManager() && ResolveIOServer.getMainServer().getSubscriptionManager().getEnableDebug()) {
680
+ console.log(new Date(), 'Mongo Agg - Running', collections);
681
+ }
682
+
683
+ const operation = this.executeAggregate<T>(collectionName, pipeline, options, cacheKey, collections);
684
+ this._operationInProgress.set(cacheKey, {
685
+ promise: operation,
686
+ invalidatedDuringExecution: false,
687
+ collections: collections
688
+ });
689
+
690
+ await operation.finally(() => {
691
+ if (ResolveIOServer.getMainServer().getSubscriptionManager() && ResolveIOServer.getMainServer().getSubscriptionManager().getEnableDebug()) {
692
+ console.log(new Date(), 'Mongo Agg - Done', collections);
693
+ }
694
+
695
+ this._operationInProgress.delete(cacheKey);
696
+ });
697
+
698
+ return operation;
699
+ }
700
+
701
+ private async executeAggregate<T extends CollectionDocument>(
702
+ collectionName: string,
703
+ pipeline: any[],
704
+ options: AggregateOptions | undefined,
705
+ cacheKey: string,
706
+ collections: string[]
707
+ ): Promise<any[]> {
708
+ let result;
709
+ const collection = ResolveIOServer.getMainDB().collection(collectionName);
710
+
711
+ // eslint-disable-next-line no-unused-vars
712
+ let { session, ...safeOptions } = options || {};
713
+ const pubContext = getPublicationContext();
714
+ const monitor = MonitorMongo.create('aggregate', collectionName, JSON.stringify([pipeline, safeOptions]));
715
+ const queryStart = Date.now();
716
+
717
+ try {
718
+ result = await this.retryRead(() => collection.aggregate<T>(pipeline, options).toArray(), options);
719
+ }
720
+ catch (err) {
721
+ console.log(JSON.stringify([new Date(), 'Error Execute Aggregate', collectionName, pipeline, collections, safeOptions, {
722
+ code: err.code,
723
+ codeName: err.codeName,
724
+ message: err.message,
725
+ stack: err.stack
726
+ }], null, 2));
727
+
728
+ err.message = `Error in Execute Aggregate: ${collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
729
+ throw err;
730
+ }
731
+ finally {
732
+ const durationMs = Date.now() - queryStart;
733
+ await monitor.finish();
734
+ SlowQueryReporter.reportSlowQueryFireAndForget({
735
+ collection: collectionName,
736
+ pipeline,
737
+ options: safeOptions,
738
+ durationMs,
739
+ queryType: 'aggregate',
740
+ notes: 'MongoManager executeAggregate',
741
+ publication: pubContext.publication,
742
+ subscriptionData: pubContext.subscriptionData,
743
+ userId: pubContext.userId
744
+ });
745
+ }
746
+
747
+ recordAggregateDependencies(collectionName, pipeline, result);
748
+
749
+ if (!this._operationInProgress.get(cacheKey).invalidatedDuringExecution) {
750
+ this.addToCache(collections, cacheKey, result);
751
+ }
752
+
753
+ return result;
754
+ }
755
+
756
+ public async countDocuments<T extends CollectionDocument>(
757
+ collectionName: string,
758
+ filter: MongoManagerFilter<T> | MongoManagerFilterOperators<T> = {},
759
+ options?: CountDocumentsOptions
760
+ ): Promise<number> {
761
+ // eslint-disable-next-line no-unused-vars
762
+ let { session, ...safeOptions } = options || {};
763
+ const cacheKey = this.generateCacheKey([collectionName], 'countDocuments', [filter, safeOptions]);
764
+ let result = this.getFromCache(cacheKey);
765
+ if (result !== undefined) {
766
+ recordDependencyResult(collectionName, result, { filter });
767
+ return result;
768
+ }
769
+
770
+ if (this._operationInProgress.has(cacheKey)) {
771
+ return this._operationInProgress.get(cacheKey).promise;
772
+ }
773
+
774
+ const operation = this.executeCountDocuments<T>(collectionName, filter, options, cacheKey);
775
+ this._operationInProgress.set(cacheKey, {
776
+ promise: operation,
777
+ invalidatedDuringExecution: false,
778
+ collections: [collectionName]
779
+ });
780
+
781
+ await operation.finally(() => this._operationInProgress.delete(cacheKey));
782
+
783
+ return operation;
784
+ }
785
+
786
+ private async executeCountDocuments<T extends CollectionDocument>(
787
+ collectionName: string,
788
+ filter: MongoManagerFilter<T> | MongoManagerFilterOperators<T>,
789
+ options: CountDocumentsOptions,
790
+ cacheKey: string
791
+ ): Promise<number> {
792
+ let result;
793
+ const collection = ResolveIOServer.getMainDB().collection(collectionName);
794
+
795
+ // eslint-disable-next-line no-unused-vars
796
+ let { session, ...safeOptions } = options || {};
797
+ const pubContext = getPublicationContext();
798
+ const monitor = MonitorMongo.create('countDocuments', collectionName, JSON.stringify([filter, safeOptions]));
799
+ const queryStart = Date.now();
800
+
801
+ try {
802
+ result = await this.retryRead(() => collection.countDocuments(<any>filter, options || undefined), options);
803
+ }
804
+ catch (err) {
805
+ console.log(JSON.stringify([new Date(), 'Error Execute Count Documents', collectionName, filter, safeOptions, {
806
+ code: err.code,
807
+ codeName: err.codeName,
808
+ message: err.message,
809
+ stack: err.stack
810
+ }], null, 2));
811
+
812
+ err.message = `Error in Execute Count Documents: ${collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
813
+ throw err;
814
+ }
815
+ finally {
816
+ const durationMs = Date.now() - queryStart;
817
+ await monitor.finish();
818
+ SlowQueryReporter.reportSlowQueryFireAndForget({
819
+ collection: collectionName,
820
+ filter,
821
+ options: safeOptions,
822
+ durationMs,
823
+ queryType: 'countDocuments',
824
+ notes: 'MongoManager executeCountDocuments',
825
+ publication: pubContext.publication,
826
+ subscriptionData: pubContext.subscriptionData,
827
+ userId: pubContext.userId
828
+ });
829
+ }
830
+
831
+ recordDependencyResult(collectionName, result, { filter });
832
+
833
+ if (!this._operationInProgress.get(cacheKey).invalidatedDuringExecution) {
834
+ this.addToCache([collectionName], cacheKey, result);
835
+ }
836
+
837
+ return result;
838
+ }
839
+
840
+ // Cache and Invalidation Setup
841
+ private generateCacheKey(collections: string[], functionName: string, args: any[]): string {
842
+ const keyString = JSON.stringify({ collections, functionName, args });
843
+ return crypto.createHash('sha256').update(keyString).digest('hex');
844
+ }
845
+
846
+ private getFromCache(cacheKey: string): any | undefined {
847
+ try {
848
+ const cachedData = this._nodeCache.get(cacheKey);
849
+ if (cachedData) {
850
+ return JSON.parse(cachedData, dateReviver);
851
+ }
852
+ }
853
+ catch {
854
+ this._nodeCache.del(cacheKey);
855
+ }
856
+
857
+ return undefined;
858
+ }
859
+
860
+ private addToCache(collections: string[], cacheKey: string, data: any) {
861
+ if (getBinarySize(JSON.stringify(data)) < 1000000 &&
862
+ !collections.includes('logs') &&
863
+ !collections.find(a => a.endsWith('.versions')) &&
864
+ !collections.find(a => a.startsWith('monitor-'))
865
+ ) {
866
+ let nodeCacheSize = this._nodeCache.getStats().vsize;
867
+
868
+ if (nodeCacheSize > this._heapLimit) {
869
+ let deleteCount = 0;
870
+ const keys = this._nodeCache.keys();
871
+
872
+ for (const key of keys) {
873
+ this._nodeCache.del(key);
874
+ deleteCount += 1;
875
+
876
+ nodeCacheSize = this._nodeCache.getStats().vsize;
877
+
878
+ if (nodeCacheSize < this._heapLimit * 0.75) {
879
+ break;
880
+ }
881
+ }
882
+
883
+ console.log('Query Cache: ' + 'Too Big, - Deleted: ' + deleteCount + ' - ' + nodeCacheSize);
884
+ }
885
+
886
+ this._nodeCache.set(cacheKey, JSON.stringify(data));
887
+ this._cacheMap.push({ collections, key: cacheKey });
888
+ }
889
+ }
890
+
891
+ public invalidateQueryCache(collection: string, bypassLocalOplog = false) {
892
+ const collectionCacheMap = this._cacheMap.filter(a => a.collections.includes(collection));
893
+
894
+ if (ResolveIOServer.getMainServer().getSubscriptionManager() && ResolveIOServer.getMainServer().getSubscriptionManager().getEnableDebug()) {
895
+ console.log(new Date(), 'Mongo Invalidate Cache', collection);
896
+ }
897
+
898
+ for (const cacheMap of collectionCacheMap) {
899
+ this._nodeCache.del(cacheMap.key);
900
+
901
+ if (this._operationInProgress.has(cacheMap.key)) {
902
+ this._operationInProgress.get(cacheMap.key).invalidatedDuringExecution = true;
903
+
904
+ if (ResolveIOServer.getMainServer().getSubscriptionManager() && ResolveIOServer.getMainServer().getSubscriptionManager().getEnableDebug()) {
905
+ console.log(new Date(), 'Mongo Invalidated During Execution', cacheMap.collections);
906
+ }
907
+ }
908
+ }
909
+
910
+ // eslint-disable-next-line no-unused-vars
911
+ for (const [key, operation] of this._operationInProgress.entries()) {
912
+ if (operation.collections.includes(collection)) {
913
+ operation.invalidatedDuringExecution = true;
914
+
915
+ if (ResolveIOServer.getMainServer().getSubscriptionManager() && ResolveIOServer.getMainServer().getSubscriptionManager().getEnableDebug()) {
916
+ console.log(new Date(), 'Mongo Invalidated During Execution (No cache)', operation.collections);
917
+ }
918
+ }
919
+ }
920
+
921
+ this._cacheMap = this._cacheMap.filter(a => !a.collections.includes(collection));
922
+
923
+ if (!bypassLocalOplog && ResolveIOServer.getMainServer().getSubscriptionManager() && ResolveIOServer.getMainServer().getSubscriptionManager().getUseLocalOplog()) {
924
+ ResolveIOServer.getMainServer().getSubscriptionManager().notifyLocalOplog(collection, 'update');
925
+ }
926
+ }
927
+
928
+ public clearQueryCache(reason?: string) {
929
+ if (ResolveIOServer.getMainServer().getSubscriptionManager() && ResolveIOServer.getMainServer().getSubscriptionManager().getEnableDebug()) {
930
+ console.log(new Date(), 'Mongo Clear Cache', reason || '');
931
+ }
932
+
933
+ try {
934
+ this._nodeCache.flushAll();
935
+ }
936
+ catch {
937
+ // ignore flush errors
938
+ }
939
+
940
+ this._cacheMap = [];
941
+
942
+ // Mark all in-flight ops invalidated so they don't repopulate cache with stale data.
943
+ // eslint-disable-next-line no-unused-vars
944
+ for (const [key, operation] of this._operationInProgress.entries()) {
945
+ operation.invalidatedDuringExecution = true;
946
+ }
947
+ }
948
+
949
+ public async handleMongoReconnect(reason: string): Promise<void> {
950
+ this.clearQueryCache(`mongo-reconnect:${reason}`);
951
+
952
+ if (this._isWorkersEnabled && this._isWorkerInstance) {
953
+ try {
954
+ await this.setupChangeStream();
955
+ }
956
+ catch (error) {
957
+ console.log(new Date(), 'Mongo handle reconnect: change stream restart failed', reason, error);
958
+ this.scheduleChangeStreamRestart();
959
+ }
960
+ }
961
+ }
962
+
963
+ private async setupChangeStream() {
964
+ if (!this._isWorkersEnabled || !this._isWorkerInstance) {
965
+ return;
966
+ }
967
+
968
+ if (this._changeStreamRestartTimeout) {
969
+ clearTimeout(this._changeStreamRestartTimeout);
970
+ this._changeStreamRestartTimeout = null;
971
+ }
972
+
973
+ await this.teardownChangeStream(true);
974
+
975
+ const client = ResolveIOServer.getMongoConnection();
976
+
977
+ if (!client) {
978
+ this.scheduleChangeStreamRestart();
979
+ return;
980
+ }
981
+
982
+ const watchDatabases = this._watchedDatabases.length ? this._watchedDatabases : (this._mainDatabaseName ? [this._mainDatabaseName] : []);
983
+
984
+ const pipeline = [
985
+ {
986
+ $match: {
987
+ $and: [
988
+ ...(watchDatabases.length ? [{ 'ns.db': { $in: watchDatabases } }] : []),
989
+ {'ns.coll': { $nin: [
990
+ 'log-method-latencies',
991
+ 'log-subscriptions',
992
+ 'logs',
993
+ 'counters',
994
+ 'cron-job-histories',
995
+ 'email-histories',
996
+ 'qb-soap-request-histories',
997
+ 'qb-soap-request-responses',
998
+ 'qb-soap-requests',
999
+ 'qb-soap-retries',
1000
+ 'subscription-manager-resume-tokens'
1001
+ ] }},
1002
+ {'ns.coll': { $not: /.*\.versions$/ }},
1003
+ {'ns.coll': { $not: /^monitor-/ }},
1004
+ ]
1005
+ },
1006
+ },
1007
+ ];
1008
+
1009
+ let changeStream: ChangeStream;
1010
+
1011
+ try {
1012
+ changeStream = client.watch(pipeline, { fullDocument: 'updateLookup' });
1013
+ }
1014
+ catch (err) {
1015
+ console.log(new Date(), 'Mongo change stream setup failed. Restarting...', err);
1016
+ this.scheduleChangeStreamRestart();
1017
+ return;
1018
+ }
1019
+
1020
+ this._changeStream = changeStream;
1021
+
1022
+ changeStream.on('change', (change: any) => {
1023
+ if (change['ns'] && change['ns'].coll) {
1024
+ const collectionName = change['ns'].coll;
1025
+ const collection = this.collection(collectionName);
1026
+
1027
+ if (collection) {
1028
+ this.invalidateQueryCache(collectionName);
1029
+ }
1030
+ }
1031
+ });
1032
+
1033
+ changeStream.on('error', async error => {
1034
+ await this.handleChangeStreamFailure('error', error);
1035
+ });
1036
+
1037
+ changeStream.on('close', async () => {
1038
+ await this.handleChangeStreamFailure('close');
1039
+ });
1040
+ }
1041
+
1042
+ private async teardownChangeStream(forceClose: boolean) {
1043
+ if (!this._changeStream) {
1044
+ return;
1045
+ }
1046
+
1047
+ const currentStream = this._changeStream;
1048
+ this._changeStream = null;
1049
+
1050
+ currentStream.removeAllListeners();
1051
+
1052
+ if (forceClose) {
1053
+ try {
1054
+ await currentStream.close();
1055
+ }
1056
+ catch (err) {
1057
+ console.log(new Date(), 'Mongo change stream close failed', err);
1058
+ }
1059
+ }
1060
+ }
1061
+
1062
+ private async handleChangeStreamFailure(reason: 'error' | 'close', error?: unknown) {
1063
+ if (reason === 'error') {
1064
+ console.log(new Date(), 'Mongo change stream error. Restart...', error);
1065
+ await this.teardownChangeStream(true);
1066
+ }
1067
+ else {
1068
+ console.log(new Date(), 'Mongo change stream closed. Restarting...');
1069
+ await this.teardownChangeStream(false);
1070
+ }
1071
+
1072
+ this.scheduleChangeStreamRestart();
1073
+ }
1074
+
1075
+ private scheduleChangeStreamRestart(delayMs = 5000) {
1076
+ if (this._changeStreamRestartTimeout) {
1077
+ return;
1078
+ }
1079
+
1080
+ this._changeStreamRestartTimeout = setTimeout(async () => {
1081
+ this._changeStreamRestartTimeout = null;
1082
+
1083
+ try {
1084
+ await this.setupChangeStream();
1085
+ }
1086
+ catch (err) {
1087
+ console.log(new Date(), 'Mongo change stream restart failed. Retrying...', err);
1088
+ this.scheduleChangeStreamRestart(Math.min(delayMs * 2, 60000));
1089
+ }
1090
+ }, delayMs);
1091
+ }
1092
+ }
1093
+
1094
+ export class MongoManagerModel<T extends CollectionDocument> {
1095
+ collection_main: MongoManagerCollection<T> | MongoManagerUserCollection<T> = null;
1096
+ collection_version: MongoManagerCollection<T> | MongoManagerUserCollection<T> = null;
1097
+
1098
+ constructor() {}
1099
+
1100
+ static create<T>(options: MongoManagerCollectionOptions): MongoManagerModel<T> {
1101
+ const mongoManagerModel = new MongoManagerModel<T>();
1102
+ mongoManagerModel.initialize(options);
1103
+ return mongoManagerModel;
1104
+ }
1105
+
1106
+ private initialize(options: MongoManagerCollectionOptions) {
1107
+ if (options.collectionName === 'users') {
1108
+ this.collection_main = MongoManagerUserCollection.create(options) as MongoManagerUserCollection<T>;
1109
+ }
1110
+ else {
1111
+ this.collection_main = MongoManagerCollection.create(options) as MongoManagerCollection<T>;
1112
+ }
1113
+
1114
+ if (options.useVersionCollection) {
1115
+ this.collection_main.useVersions = true;
1116
+
1117
+ let versionSchema = deepCopy(options.schema);
1118
+ versionSchema._id.type = 'Object';
1119
+ versionSchema._id.blackbox = true;
1120
+
1121
+ let versionOptions: MongoManagerCollectionOptions = {
1122
+ collectionName: options.collectionName + '.versions',
1123
+ schema: versionSchema,
1124
+ useVersionCollection: false,
1125
+ useReportBuilder: false,
1126
+ reportBuilderLookupTables: [],
1127
+ timestamps: true,
1128
+ createLogs: false,
1129
+ checkSchema: false,
1130
+ collectionOptions: null,
1131
+ skipCache: true,
1132
+ bypassSession: true
1133
+ };
1134
+
1135
+ if (options.collectionName === 'users') {
1136
+ this.collection_version = MongoManagerUserCollection.create(versionOptions) as MongoManagerUserCollection<T>;
1137
+ }
1138
+ else {
1139
+ this.collection_version = MongoManagerCollection.create(versionOptions) as MongoManagerCollection<T>;
1140
+ }
1141
+
1142
+ setImmediate(async () => {
1143
+ await this.collection_version.createIndex({'_id._id': 1, '_id.__v': 1});
1144
+ });
1145
+
1146
+ this.collection_main.versionCollection = options.collectionName + '.versions';
1147
+ }
1148
+ }
1149
+ }
1150
+
1151
+ export interface MongoManagerCollectionOptions {
1152
+ collectionName: string;
1153
+ schema: object;
1154
+ useVersionCollection: boolean;
1155
+ useReportBuilder: boolean;
1156
+ reportBuilderLookupTables: LookupTables[]
1157
+ timestamps: boolean;
1158
+ createLogs: boolean;
1159
+ checkSchema: boolean;
1160
+ collectionOptions: CreateCollectionOptions;
1161
+ skipCache?: boolean;
1162
+ bypassSession?: boolean;
1163
+ searchFields?: string[];
1164
+ }
1165
+
1166
+ export class MongoManagerCollection<T extends CollectionDocument> {
1167
+ collectionName = '';
1168
+ checkSchema = false;
1169
+ simplschema = null;
1170
+ rbSchema = null;
1171
+ timestamps = false;
1172
+ useVersions = false;
1173
+ versionCollection = '';
1174
+ createLogs = true;
1175
+ useRB = false;
1176
+ collectionOptions: CreateCollectionOptions;
1177
+ skipCache = false;
1178
+ bypassSession = false;
1179
+
1180
+ constructor() {}
1181
+
1182
+ static create(options: MongoManagerCollectionOptions) {
1183
+ const mongoManagerCollection = new MongoManagerCollection();
1184
+ mongoManagerCollection.initialize(options);
1185
+ return mongoManagerCollection;
1186
+ }
1187
+
1188
+ initialize(options: MongoManagerCollectionOptions) {
1189
+ this.collectionName = options.collectionName;
1190
+ this.simplschema = new SimpleSchema(options.schema);
1191
+ this.timestamps = options.timestamps;
1192
+ this.createLogs = options.createLogs;
1193
+ this.checkSchema = options.checkSchema;
1194
+ this.collectionOptions = options.collectionOptions;
1195
+ this.skipCache = options.skipCache || false;
1196
+ this.bypassSession = options.bypassSession || false;
1197
+
1198
+ if (options.useReportBuilder) {
1199
+ this.useRB = true;
1200
+ let schemaCopy = deepCopy(options.schema);
1201
+ let rbSchema = buildRbLookups(options.reportBuilderLookupTables, schemaCopy, []);
1202
+ this.rbSchema = buildRbSchema(rbSchema);
1203
+ }
1204
+
1205
+ const retryDelayMs = 250;
1206
+ const registerWhenReady = async () => {
1207
+ if (ResolveIOServer && ResolveIOServer.getMainServer() && ResolveIOServer.getMongoManager() && ResolveIOServer.getMongoManager().getServerCollections().length) {
1208
+ await ResolveIOServer.getMongoManager().registerCollection(this);
1209
+ return;
1210
+ }
1211
+ setTimeout(registerWhenReady, retryDelayMs);
1212
+ };
1213
+ setTimeout(registerWhenReady, retryDelayMs);
1214
+ }
1215
+
1216
+ // Method to extract index name from error message
1217
+ extractIndexNameFromError(errorMessage) {
1218
+ let regexPattern = /Index already exists with a different name: (\S+)/;
1219
+ let matches = errorMessage.match(regexPattern);
1220
+
1221
+ if (!matches || matches.length <= 1) {
1222
+ // If the first pattern doesn't match, try the second pattern
1223
+ regexPattern = /existing index:.*name: "([^"]+)"/;
1224
+ matches = errorMessage.match(regexPattern);
1225
+ }
1226
+
1227
+ return matches && matches.length > 1 ? matches[1] : null;
1228
+ }
1229
+
1230
+ private shouldRetryWriteConflict(err: any): boolean {
1231
+ if (!err) {
1232
+ return false;
1233
+ }
1234
+
1235
+ if (err.code === 112 || err.codeName === 'WriteConflict') {
1236
+ return true;
1237
+ }
1238
+
1239
+ const message = String(err.message || '');
1240
+ return /write conflict/i.test(message) || /yielding is disabled/i.test(message);
1241
+ }
1242
+
1243
+ private shouldRetryWaitQueueTimeout(err: any): boolean {
1244
+ if (!err) {
1245
+ return false;
1246
+ }
1247
+
1248
+ if (err.name === 'MongoWaitQueueTimeoutError' || err.codeName === 'MongoWaitQueueTimeoutError') {
1249
+ return true;
1250
+ }
1251
+
1252
+ return typeof err.message === 'string' && err.message.includes('Timed out while checking out a connection from connection pool');
1253
+ }
1254
+
1255
+ private shouldRetryConnectionError(err: any): boolean {
1256
+ if (!err) {
1257
+ return false;
1258
+ }
1259
+
1260
+ const name = String(err.name || err.codeName || '');
1261
+ if (
1262
+ name === 'MongoPoolClearedError'
1263
+ || name === 'MongoNetworkError'
1264
+ || name === 'MongoNetworkTimeoutError'
1265
+ || name === 'MongoServerSelectionError'
1266
+ || name === 'MongoTopologyClosedError'
1267
+ || name === 'MongoNotConnectedError'
1268
+ || name === 'MongoClientClosedError'
1269
+ ) {
1270
+ return true;
1271
+ }
1272
+
1273
+ const message = String(err.message || '');
1274
+ return /connection pool .* was cleared/i.test(message)
1275
+ || /topology is closed/i.test(message)
1276
+ || /client was closed/i.test(message)
1277
+ || /not connected/i.test(message)
1278
+ || /server selection timed out/i.test(message)
1279
+ || /econnrefused|econnreset|etimedout|ehostunreach/i.test(message);
1280
+ }
1281
+
1282
+ private async delay(ms: number) {
1283
+ // eslint-disable-next-line no-restricted-syntax
1284
+ return new Promise(resolve => setTimeout(resolve, ms));
1285
+ }
1286
+
1287
+ private async retryWrite<TResult>(operation: () => Promise<TResult>, options?: { session?: any }): Promise<TResult> {
1288
+ if (options && options.session && typeof options.session.inTransaction === 'function' && options.session.inTransaction()) {
1289
+ return operation();
1290
+ }
1291
+
1292
+ let attempt = 0;
1293
+ const configuredMaxRetries = Number(process.env.MONGO_WRITE_RETRY_MAX || 8);
1294
+ const maxRetries = Number.isFinite(configuredMaxRetries) && configuredMaxRetries >= 0
1295
+ ? Math.min(Math.floor(configuredMaxRetries), 20)
1296
+ : 8;
1297
+ const configuredBaseDelay = Number(process.env.MONGO_WRITE_RETRY_BASE_MS || 120);
1298
+ const baseDelayMs = Number.isFinite(configuredBaseDelay) && configuredBaseDelay > 0
1299
+ ? Math.min(Math.floor(configuredBaseDelay), 5000)
1300
+ : 120;
1301
+
1302
+ while (true) {
1303
+ try {
1304
+ return await operation();
1305
+ }
1306
+ catch (err) {
1307
+ const shouldRetry = this.shouldRetryWriteConflict(err) || this.shouldRetryWaitQueueTimeout(err) || this.shouldRetryConnectionError(err);
1308
+ if (!shouldRetry || attempt >= maxRetries) {
1309
+ throw err;
1310
+ }
1311
+ if (this.shouldRetryConnectionError(err)) {
1312
+ try {
1313
+ await ResolveIOServer.requestMongoReconnect(`mongo-collection-write-retry:${this.collectionName}`, err);
1314
+ }
1315
+ catch {}
1316
+ }
1317
+
1318
+ attempt += 1;
1319
+ const backoff = Math.min(5000, baseDelayMs * (2 ** (attempt - 1)));
1320
+ const jitter = Math.floor(Math.random() * 90);
1321
+ await this.delay(backoff + jitter);
1322
+ }
1323
+ }
1324
+ }
1325
+
1326
+ private async retryRead<TResult>(operation: () => Promise<TResult>, options?: { session?: any }): Promise<TResult> {
1327
+ if (options && options.session && typeof options.session.inTransaction === 'function' && options.session.inTransaction()) {
1328
+ return operation();
1329
+ }
1330
+
1331
+ let attempt = 0;
1332
+ const maxRetries = 2;
1333
+
1334
+ while (true) {
1335
+ try {
1336
+ return await operation();
1337
+ }
1338
+ catch (err) {
1339
+ const shouldRetry = this.shouldRetryWaitQueueTimeout(err) || this.shouldRetryConnectionError(err);
1340
+ if (!shouldRetry || attempt >= maxRetries) {
1341
+ throw err;
1342
+ }
1343
+ if (this.shouldRetryConnectionError(err)) {
1344
+ try {
1345
+ await ResolveIOServer.requestMongoReconnect(`mongo-collection-read-retry:${this.collectionName}`, err);
1346
+ }
1347
+ catch {}
1348
+ }
1349
+
1350
+ attempt += 1;
1351
+ await this.delay(100 * attempt);
1352
+ }
1353
+ }
1354
+ }
1355
+
1356
+ async aggregate(pipeline: object[], options?: AggregateOptions, skipCache = false, bypassSession = false) : Promise<any[]> {
1357
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
1358
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
1359
+ if (!options) {
1360
+ options = {
1361
+ session: mongoSession
1362
+ };
1363
+ }
1364
+ else {
1365
+ options.session = mongoSession;
1366
+ }
1367
+ }
1368
+
1369
+ if (!skipCache && !this.skipCache) {
1370
+ return ResolveIOServer.getMongoManager().aggregate(this.collectionName, pipeline, options || undefined);
1371
+ }
1372
+ else {
1373
+ // eslint-disable-next-line no-unused-vars
1374
+ let { session, ...safeOptions } = options || {};
1375
+ let monitor = MonitorMongo.create('aggregate', this.collectionName, JSON.stringify([pipeline, safeOptions]));
1376
+
1377
+ try {
1378
+ let res = await ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).aggregate(pipeline, options).toArray();
1379
+ recordAggregateDependencies(this.collectionName, pipeline, res);
1380
+ return res;
1381
+ }
1382
+ catch (err) {
1383
+ console.log(JSON.stringify([new Date(), 'Error Aggregate', this.collectionName, pipeline, safeOptions, {
1384
+ code: err.code,
1385
+ codeName: err.codeName,
1386
+ message: err.message,
1387
+ stack: err.stack
1388
+ }], null, 2));
1389
+
1390
+ err.message = `Error in Aggregate: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
1391
+ throw err;
1392
+ }
1393
+ finally {
1394
+ await monitor.finish();
1395
+ }
1396
+ }
1397
+ }
1398
+
1399
+ async aggregateCount(pipeline: object[], options?: AggregateOptions, bypassSession = false) : Promise<number> {
1400
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
1401
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
1402
+ if (!options) {
1403
+ options = {
1404
+ session: mongoSession
1405
+ };
1406
+ }
1407
+ else {
1408
+ options.session = mongoSession;
1409
+ }
1410
+ }
1411
+
1412
+ // eslint-disable-next-line no-unused-vars
1413
+ let { session, ...safeOptions } = options || {};
1414
+ let monitor = MonitorMongo.create('aggregateCount', this.collectionName, JSON.stringify([pipeline, safeOptions]));
1415
+
1416
+ try {
1417
+ let res = await ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).aggregate(pipeline, options).toArray();
1418
+ recordAggregateDependencies(this.collectionName, pipeline, res);
1419
+ return res.length;
1420
+ }
1421
+ catch (err) {
1422
+ console.log(JSON.stringify([new Date(), 'Error Aggregate Count', this.collectionName, pipeline, safeOptions, {
1423
+ code: err.code,
1424
+ codeName: err.codeName,
1425
+ message: err.message,
1426
+ stack: err.stack
1427
+ }], null, 2));
1428
+
1429
+ err.message = `Error in Aggregate Count: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
1430
+ throw err;
1431
+ }
1432
+ finally {
1433
+ await monitor.finish();
1434
+ }
1435
+ }
1436
+
1437
+ aggregateCursor(pipeline: object[], options?: AggregateOptions, bypassSession = false): AggregationCursor {
1438
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
1439
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
1440
+ if (!options) {
1441
+ options = {
1442
+ session: mongoSession
1443
+ };
1444
+ }
1445
+ else {
1446
+ options.session = mongoSession;
1447
+ }
1448
+ }
1449
+
1450
+ // eslint-disable-next-line no-unused-vars
1451
+ let { session, ...safeOptions } = options || {};
1452
+ let monitor = MonitorMongo.create('aggregateCursor', this.collectionName, JSON.stringify([pipeline, safeOptions]));
1453
+ let cursor = ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).aggregate(pipeline, options || undefined);
1454
+ cursor.on('close', async ev => {
1455
+ await monitor.finish();
1456
+ return ev;
1457
+ });
1458
+
1459
+ return cursor;
1460
+ }
1461
+
1462
+ aggregateStream(pipeline: object[], options?: AggregateOptions, streamOptions?: CursorStreamOptions, bypassSession = false) {
1463
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
1464
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
1465
+ if (!options) {
1466
+ options = {
1467
+ session: mongoSession
1468
+ };
1469
+ }
1470
+ else {
1471
+ options.session = mongoSession;
1472
+ }
1473
+ }
1474
+
1475
+ // eslint-disable-next-line no-unused-vars
1476
+ let { session, ...safeOptions } = options || {};
1477
+ let monitor = MonitorMongo.create('aggregateStream', this.collectionName, JSON.stringify([pipeline, safeOptions, streamOptions]));
1478
+ let stream = ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).aggregate(pipeline, options).stream(streamOptions).on('close', async ev => {
1479
+ await monitor.finish();
1480
+ return ev;
1481
+ });
1482
+
1483
+ return stream;
1484
+ }
1485
+
1486
+ async bulkWrite(
1487
+ operations: any[],
1488
+ options?: BulkWriteOptions,
1489
+ bypassCheckSchema = false,
1490
+ bypassLogs = false,
1491
+ bypassVersions = false,
1492
+ bypassSession = false
1493
+ ): Promise<BulkWriteResult> {
1494
+ if (!operations.length) {
1495
+ return null;
1496
+ }
1497
+
1498
+ const BATCH_SIZE = 1000;
1499
+ let opIndex = 0;
1500
+
1501
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
1502
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
1503
+ if (!options) {
1504
+ options = {session: mongoSession};
1505
+ }
1506
+ else {
1507
+ options.session = mongoSession;
1508
+ }
1509
+ }
1510
+
1511
+ let { session, ...safeOptions } = options || {};
1512
+
1513
+ while (opIndex < operations.length) {
1514
+ const batchOps = operations.slice(opIndex, opIndex + BATCH_SIZE);
1515
+
1516
+ // Initialize arrays for logs and version operations
1517
+ const logs = [];
1518
+ const versionOps = [];
1519
+
1520
+ for (const op of batchOps) {
1521
+ const opType = Object.keys(op)[0];
1522
+ const doc = op[opType];
1523
+
1524
+ // Skip validation if bypassCheckSchema is true
1525
+ if (this.checkSchema && !bypassCheckSchema) {
1526
+ const validationContext = this.simplschema.newContext();
1527
+ let isValid: boolean;
1528
+
1529
+ if (opType === 'insertOne') {
1530
+ isValid = validationContext.validate(doc.document);
1531
+ }
1532
+ else if (opType === 'replaceOne') {
1533
+ isValid = validationContext.validate(doc.replacement);
1534
+ }
1535
+ else if (opType === 'updateOne' || opType === 'updateMany') {
1536
+ // For updates, validate the update modifier
1537
+ isValid = validationContext.validate(doc.update, { modifier: true });
1538
+ }
1539
+ else if (opType === 'deleteOne' || opType === 'deleteMany') {
1540
+ // No validation needed for deletes
1541
+ isValid = true;
1542
+ }
1543
+ else {
1544
+ throw new Error(`Unsupported operation type: ${opType}`);
1545
+ }
1546
+
1547
+ if (!isValid) {
1548
+ throw new Error(
1549
+ `Schema validation failed for ${opType}: ${JSON.stringify(validationContext.validationErrors(), null, 2)}`
1550
+ );
1551
+ }
1552
+ }
1553
+
1554
+ // Now proceed to process the operations
1555
+
1556
+ if (opType === 'insertOne') {
1557
+ // If this.timestamps is true, set createdAt and updatedAt
1558
+ if (this.timestamps) {
1559
+ if (!doc.document.createdAt) {
1560
+ doc.document.createdAt = new Date();
1561
+ }
1562
+
1563
+ if (!doc.document.updatedAt) {
1564
+ doc.document.updatedAt = new Date();
1565
+ }
1566
+ }
1567
+
1568
+ // Prepare log entry
1569
+ if (!bypassLogs && this.createLogs) {
1570
+ logs.push({
1571
+ _id: objectIdHexString(),
1572
+ type: 'document',
1573
+ collection: this.collectionName,
1574
+ id_document: doc.document._id || objectIdHexString(),
1575
+ payload:
1576
+ getBinarySize(JSON.stringify(doc.document)) < 1000000
1577
+ ? JSON.stringify(doc.document, null, 2)
1578
+ : 'Too Big',
1579
+ method: 'insertOne',
1580
+ id_user: '', // Add user info if available
1581
+ user: '',
1582
+ messageId: 0,
1583
+ route: '',
1584
+ createdAt: new Date(),
1585
+ });
1586
+ }
1587
+
1588
+ // Since we might have modified doc.document, update the operation
1589
+ op[opType].document = doc.document;
1590
+ }
1591
+ else if (opType === 'updateOne' || opType === 'updateMany') {
1592
+ // If this.timestamps is true, add updatedAt
1593
+ if (this.timestamps) {
1594
+ if (!doc.update.$set) {
1595
+ doc.update.$set = {};
1596
+ }
1597
+ doc.update.$set.updatedAt = new Date();
1598
+ }
1599
+
1600
+ // If this.useVersions is true, increment __v
1601
+ if (!bypassVersions && this.useVersions) {
1602
+ if (!doc.update.$inc) {
1603
+ doc.update.$inc = {};
1604
+ }
1605
+ doc.update.$inc.__v = 1;
1606
+ }
1607
+
1608
+ // Prepare log entry
1609
+ if (!bypassLogs && this.createLogs) {
1610
+ logs.push({
1611
+ _id: objectIdHexString(),
1612
+ type: 'document',
1613
+ collection: this.collectionName,
1614
+ id_document: doc.filter._id || null,
1615
+ payload:
1616
+ getBinarySize(JSON.stringify(doc.update)) < 1000000
1617
+ ? JSON.stringify(doc.update, null, 2)
1618
+ : 'Too Big',
1619
+ method: opType,
1620
+ id_user: '', // Add user info if available
1621
+ user: '',
1622
+ messageId: 0,
1623
+ route: '',
1624
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
1625
+ createdAt: new Date()
1626
+ });
1627
+ }
1628
+
1629
+ // For versioning, store the filter to fetch the document later
1630
+ if (!bypassVersions && this.useVersions) {
1631
+ versionOps.push({
1632
+ filter: doc.filter,
1633
+ });
1634
+ }
1635
+
1636
+ // No need to modify the operation further
1637
+ }
1638
+ else if (opType === 'replaceOne') {
1639
+ // If this.timestamps is true, set updatedAt
1640
+ if (this.timestamps) {
1641
+ doc.replacement.updatedAt = new Date();
1642
+ }
1643
+
1644
+ // If this.useVersions is true, increment __v
1645
+ if (!bypassVersions && this.useVersions) {
1646
+ doc.replacement.__v = (doc.replacement.__v || 0) + 1;
1647
+ }
1648
+
1649
+ // Prepare log entry
1650
+ if (!bypassLogs && this.createLogs) {
1651
+ logs.push({
1652
+ _id: objectIdHexString(),
1653
+ type: 'document',
1654
+ collection: this.collectionName,
1655
+ id_document: doc.filter._id || null,
1656
+ payload:
1657
+ getBinarySize(JSON.stringify(doc.replacement)) < 1000000
1658
+ ? JSON.stringify(doc.replacement, null, 2)
1659
+ : 'Too Big',
1660
+ method: 'replaceOne',
1661
+ id_user: '', // Add user info if available
1662
+ user: '',
1663
+ messageId: 0,
1664
+ route: '',
1665
+ createdAt: new Date(),
1666
+ });
1667
+ }
1668
+
1669
+ // For versioning, store the filter to fetch the document later
1670
+ if (!bypassVersions && this.useVersions) {
1671
+ versionOps.push({
1672
+ filter: doc.filter,
1673
+ });
1674
+ }
1675
+
1676
+ // Since we might have modified doc.replacement, update the operation
1677
+ op[opType].replacement = doc.replacement;
1678
+ }
1679
+ else if (opType === 'deleteOne' || opType === 'deleteMany') {
1680
+ // Prepare log entry
1681
+ if (!bypassLogs && this.createLogs) {
1682
+ logs.push({
1683
+ _id: objectIdHexString(),
1684
+ type: 'document',
1685
+ collection: this.collectionName,
1686
+ id_document: doc.filter._id || null,
1687
+ payload:
1688
+ getBinarySize(JSON.stringify(doc.filter)) < 1000000
1689
+ ? JSON.stringify(doc.filter, null, 2)
1690
+ : 'Too Big',
1691
+ method: opType,
1692
+ id_user: '', // Add user info if available
1693
+ user: '',
1694
+ messageId: 0,
1695
+ route: '',
1696
+ createdAt: new Date(),
1697
+ });
1698
+ }
1699
+
1700
+ // For versioning, store the filter to fetch the document later
1701
+ if (!bypassVersions && this.useVersions) {
1702
+ versionOps.push({
1703
+ filter: doc.filter,
1704
+ });
1705
+ }
1706
+
1707
+ // No need to modify the operation
1708
+ }
1709
+ else {
1710
+ throw new Error(`Unsupported operation type: ${opType}`);
1711
+ }
1712
+ }
1713
+
1714
+ let affectedDocs = [];
1715
+
1716
+ // Handle versioning
1717
+ if (!bypassVersions && this.useVersions && versionOps.length > 0) {
1718
+ try {
1719
+ // Fetch the affected documents
1720
+ const versionFilters = versionOps.map((v) => v.filter);
1721
+
1722
+ if (versionFilters.length) {
1723
+ affectedDocs = await ResolveIOServer.getMainDB().collection(this.collectionName).find({ $or: versionFilters }, {session}).toArray();
1724
+ }
1725
+
1726
+ // Prepare version documents
1727
+ const versionInserts = affectedDocs.map((doc) => ({
1728
+ insertOne: {
1729
+ document: {
1730
+ ...doc,
1731
+ _id: { _id: doc._id, __v: doc.__v },
1732
+ },
1733
+ },
1734
+ }));
1735
+
1736
+ // Insert versions in bulk
1737
+ if (versionInserts.length > 0) {
1738
+ try {
1739
+ await ResolveIOServer.getMainDB().collection(this.versionCollection).bulkWrite(<any>versionInserts);
1740
+ }
1741
+ catch {}
1742
+ }
1743
+ }
1744
+ catch {}
1745
+ }
1746
+
1747
+ // Execute bulkWrite
1748
+ const monitor = MonitorMongo.create('bulkWrite', this.collectionName, JSON.stringify([batchOps, safeOptions]));
1749
+
1750
+ try {
1751
+ await this.retryWrite(
1752
+ () => ResolveIOServer.getMainDB().collection(this.collectionName).bulkWrite(batchOps, options || undefined),
1753
+ options
1754
+ );
1755
+ }
1756
+ catch (err) {
1757
+ console.log(JSON.stringify([new Date(), 'Error Bulk Write', this.collectionName, operations, safeOptions, {
1758
+ code: err.code,
1759
+ codeName: err.codeName,
1760
+ message: err.message,
1761
+ stack: err.stack
1762
+ }], null, 2));
1763
+
1764
+ err.message = `Error in Bulk Write: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
1765
+ throw err;
1766
+ }
1767
+ finally {
1768
+ await monitor.finish();
1769
+ }
1770
+
1771
+ // Insert logs in bulk
1772
+ if (!bypassLogs && this.createLogs && logs.length > 0) {
1773
+ if (
1774
+ ResolveIOServer.shouldWriteLogsOffline()
1775
+ ) {
1776
+ ResolveIOServer.getLocalLogManager().writeLogs(logs.map(a => {
1777
+ return {
1778
+ type: 'log',
1779
+ data: a
1780
+ };
1781
+ }));
1782
+ }
1783
+ else {
1784
+ let bwOptions = null;
1785
+ bwOptions = {session, ordered: false};
1786
+
1787
+ await ResolveIOServer.getMainDB().collection('logs').bulkWrite(logs.map(a => {
1788
+ a.client = 'ResolveIO';
1789
+ a.instance = ResolveIOServer.getInstanceHost();
1790
+
1791
+ return {
1792
+ insertOne: {
1793
+ document: a
1794
+ }
1795
+ };
1796
+ }), bwOptions);
1797
+ }
1798
+ }
1799
+
1800
+ if (!bypassVersions && this.useVersions) {
1801
+ try {
1802
+ // Delete old versions beyond the latest 5
1803
+ const affectedDocIds = affectedDocs.map((doc) => doc._id);
1804
+
1805
+ const pipeline = [
1806
+ {
1807
+ $match: { '_id._id': { $in: affectedDocIds } },
1808
+ },
1809
+ {
1810
+ $sort: { '_id._id': 1, '_id.__v': -1 },
1811
+ },
1812
+ {
1813
+ $group: {
1814
+ _id: '$_id._id',
1815
+ versions: { $push: '$_id' },
1816
+ },
1817
+ },
1818
+ {
1819
+ $project: {
1820
+ versionsToDelete: { $slice: ['$versions', 5, { $size: '$versions' }] },
1821
+ },
1822
+ },
1823
+ { $unwind: '$versionsToDelete' },
1824
+ {
1825
+ $replaceRoot: { newRoot: { _id: '$versionsToDelete' } },
1826
+ },
1827
+ ];
1828
+
1829
+ const oldVersions = await ResolveIOServer.getMainDB().collection(this.versionCollection).aggregate(pipeline).toArray();
1830
+
1831
+ if (oldVersions.length > 0) {
1832
+ await ResolveIOServer.getMainDB().collection(this.versionCollection).deleteMany({ _id: { $in: oldVersions.map((v) => v._id) } });
1833
+ }
1834
+ }
1835
+ catch {}
1836
+ }
1837
+
1838
+ opIndex += BATCH_SIZE;
1839
+
1840
+ // Yield to the event loop
1841
+ // eslint-disable-next-line no-restricted-syntax
1842
+ await new Promise((resolve) => setImmediate(resolve));
1843
+
1844
+ // Invalidate cache
1845
+ if (!this.skipCache) {
1846
+ ResolveIOServer.getMongoManager().invalidateQueryCache(this.collectionName);
1847
+ }
1848
+ }
1849
+
1850
+ return { ok: 1 } as BulkWriteResult; // Adjust return value as per your requirements
1851
+ }
1852
+
1853
+ async countDocuments(
1854
+ filter: MongoManagerFilter<T> & MongoManagerFilterOperators<T> = {},
1855
+ options?: CountDocumentsOptions,
1856
+ skipCache = false
1857
+ ): Promise<number> {
1858
+
1859
+ // ********* COUNTS ARE NOT SUPPORTED IN MONGO SESSIONS! ***************
1860
+
1861
+ // eslint-disable-next-line no-unused-vars
1862
+ let { session, ...safeOptions } = options || {};
1863
+
1864
+ // Check if the filter is empty
1865
+ const isUnfiltered = Object.keys(filter).length === 0;
1866
+
1867
+ if (isUnfiltered) {
1868
+ // Use collection stats for an unfiltered count
1869
+ return ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).estimatedDocumentCount(safeOptions);
1870
+ }
1871
+ else if (!skipCache && !this.skipCache) {
1872
+ // Use cached count if skipCache is false
1873
+ return ResolveIOServer.getMongoManager().countDocuments(this.collectionName, filter, safeOptions);
1874
+ }
1875
+ else {
1876
+ // Fallback to direct countDocuments call with monitoring if skipCache is true
1877
+
1878
+ let monitor = MonitorMongo.create('countDocuments', this.collectionName, JSON.stringify([filter, safeOptions]));
1879
+
1880
+ try {
1881
+ let res = await ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).countDocuments(<any>filter, safeOptions);
1882
+ return res;
1883
+ }
1884
+ catch (err) {
1885
+ console.log(JSON.stringify([new Date(), 'Error Count Documents', this.collectionName, filter, safeOptions, {
1886
+ code: err.code,
1887
+ codeName: err.codeName,
1888
+ message: err.message,
1889
+ stack: err.stack
1890
+ }], null, 2));
1891
+
1892
+ err.message = `Error in Count Documents: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
1893
+ throw err;
1894
+ }
1895
+ finally {
1896
+ await monitor.finish();
1897
+ }
1898
+ }
1899
+ }
1900
+
1901
+ //Helper Function (1 or Many) - Not Real Mongo Function
1902
+ create(f_docs: T | T[], options?: InsertOneOptions | BulkWriteOptions): Promise<T | T[]> {
1903
+ if (!Array.isArray(f_docs)) {
1904
+ return this.insertOne(f_docs, <InsertOneOptions>options);
1905
+ }
1906
+ else if (f_docs.length === 1) {
1907
+ return this.insertOne(f_docs[0], <InsertOneOptions>options);
1908
+ }
1909
+
1910
+ return this.insertMany(f_docs, <BulkWriteOptions>options);
1911
+ }
1912
+
1913
+ async createIndex(fieldOrSpec: any, options?: CreateIndexesOptions): Promise<string> {
1914
+ // Check if index already exists based on the key
1915
+
1916
+ try {
1917
+ const dbCollection = ResolveIOServer.getMainDB().collection(this.collectionName, this.collectionOptions);
1918
+ const existingIndexes = await dbCollection.indexes();
1919
+
1920
+ // Check if an index with the same key already exists
1921
+ const indexExists = existingIndexes.some(index =>
1922
+ JSON.stringify(index.key) === JSON.stringify(fieldOrSpec)
1923
+ );
1924
+
1925
+ if (indexExists) {
1926
+ return 'Index already exists'; // Skip creation if index exists
1927
+ }
1928
+
1929
+ return await dbCollection.createIndex(fieldOrSpec, options || undefined);
1930
+ }
1931
+ catch {}
1932
+
1933
+ return 'Index creation queued';
1934
+ }
1935
+
1936
+ async createIndexes(indexSpecs: IndexDescription[], options?: CreateIndexesOptions): Promise<string[]> {
1937
+ try {
1938
+ const dbCollection = ResolveIOServer.getMainDB().collection(this.collectionName, this.collectionOptions);
1939
+ const existingIndexes = await dbCollection.indexes();
1940
+
1941
+ // Filter out indexes that already exist based on the key
1942
+ const indexesToCreate = indexSpecs.filter(indexSpec =>
1943
+ !existingIndexes.some(existingIndex =>
1944
+ JSON.stringify(existingIndex.key) === JSON.stringify(indexSpec.key)
1945
+ )
1946
+ );
1947
+
1948
+ // If no new indexes to create, resolve early
1949
+ if (indexesToCreate.length === 0) {
1950
+ return ['All indexes already exist'];
1951
+ }
1952
+
1953
+ return await dbCollection.createIndexes(indexesToCreate, options || undefined);
1954
+ }
1955
+ catch {}
1956
+
1957
+ return ['Index creation queued'];
1958
+ }
1959
+
1960
+ async deleteMany(filter: MongoManagerFilter<T> & MongoManagerFilterOperators<T> = {}, options?: DeleteOptions, bypassLogs = false, bypassSession = false): Promise<DeleteResult> {
1961
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
1962
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
1963
+ if (!options) {
1964
+ options = {session: mongoSession};
1965
+ }
1966
+ else {
1967
+ options.session = mongoSession;
1968
+ }
1969
+ }
1970
+
1971
+ let { session, ...safeOptions } = options || {};
1972
+
1973
+ if ((this.createLogs && !bypassLogs) || this.useVersions) {
1974
+ let docs = await this.find(filter, {session});
1975
+
1976
+ for (let i = 0; i < docs.length; i++) {
1977
+ let doc = docs[i];
1978
+
1979
+ if (this.createLogs && !bypassLogs) {
1980
+ if (
1981
+ ResolveIOServer.shouldWriteLogsOffline()
1982
+ ) {
1983
+ ResolveIOServer.getLocalLogManager().writeLog({
1984
+ type: 'log',
1985
+ data: {
1986
+ _id: objectIdHexString(),
1987
+ type: 'document',
1988
+ collection: this.collectionName,
1989
+ id_document: doc._id,
1990
+ payload: getBinarySize(JSON.stringify([doc, filter, safeOptions])) < 1000000 ? JSON.stringify([doc, filter, safeOptions], null, 2) : 'Too Big',
1991
+ method: 'deleteMany',
1992
+ id_user: '',
1993
+ user: '',
1994
+ messageId: 0,
1995
+ route: '',
1996
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
1997
+ createdAt: new Date()
1998
+ }
1999
+ });
2000
+ }
2001
+ else {
2002
+ await ResolveIOServer.getMainDB().collection('logs').insertOne(<any>{
2003
+ _id: objectIdHexString(),
2004
+ type: 'document',
2005
+ collection: this.collectionName,
2006
+ id_document: doc._id,
2007
+ payload: getBinarySize(JSON.stringify([doc, filter, safeOptions])) < 1000000 ? JSON.stringify([doc, filter, safeOptions], null, 2) : 'Too Big',
2008
+ method: 'deleteMany',
2009
+ id_user: '',
2010
+ user: '',
2011
+ messageId: 0,
2012
+ route: '',
2013
+ client: 'ResolveIO',
2014
+ instance: ResolveIOServer.getInstanceHost(),
2015
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
2016
+ createdAt: new Date()
2017
+ }, {session});
2018
+ }
2019
+ }
2020
+
2021
+ if (this.useVersions) {
2022
+ try {
2023
+ let versionDoc = deepCopy(doc);
2024
+ versionDoc._id = {_id: doc._id, __v: doc.__v};
2025
+
2026
+ await ResolveIOServer.getMongoManager().collection(this.versionCollection).replaceOne(<any>{_id: { _id: doc._id, __v: doc.__v}}, versionDoc, {upsert: true});
2027
+ await ResolveIOServer.getMongoManager().collection(this.versionCollection).deleteMany(<any>{$and: [{'_id._id': doc._id}, {'_id.__v': {$lt: doc.__v - 1}}]});
2028
+ }
2029
+ catch {}
2030
+ }
2031
+ }
2032
+ }
2033
+
2034
+ let monitor = MonitorMongo.create('deleteMany', this.collectionName, JSON.stringify([filter, safeOptions]));
2035
+
2036
+ try {
2037
+ let res = await ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).deleteMany(<any>filter, options || undefined);
2038
+ if (!this.skipCache) {
2039
+ ResolveIOServer.getMongoManager().invalidateQueryCache(this.collectionName);
2040
+ }
2041
+
2042
+ return res;
2043
+ }
2044
+ catch (err) {
2045
+ console.log(JSON.stringify([new Date(), 'Error Delete Many', this.collectionName, filter, safeOptions, {
2046
+ code: err.code,
2047
+ codeName: err.codeName,
2048
+ message: err.message,
2049
+ stack: err.stack
2050
+ }], null, 2));
2051
+
2052
+ err.message = `Error in Delete Many: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
2053
+ throw err;
2054
+ }
2055
+ finally {
2056
+ await monitor.finish();
2057
+ }
2058
+ }
2059
+
2060
+ async deleteOne(filter: MongoManagerFilter<T> & MongoManagerFilterOperators<T> = {}, options?: FindOneAndDeleteOptions, bypassLogs = false, bypassSession = false): Promise<DeleteResult> {
2061
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
2062
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
2063
+ if (!options) {
2064
+ options = {session: mongoSession};
2065
+ }
2066
+ else {
2067
+ options.session = mongoSession;
2068
+ }
2069
+ }
2070
+
2071
+ let { session, ...safeOptions } = options || {};
2072
+
2073
+ if (this.createLogs && !bypassLogs) {
2074
+ let monitor = MonitorMongo.create('findOneAndDelete', this.collectionName, JSON.stringify([filter, safeOptions]));
2075
+
2076
+ try {
2077
+ let res = await ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).deleteOne(<any>filter, options || undefined);
2078
+ let doc = await this.findOne(filter, options || undefined);
2079
+
2080
+ if (doc) {
2081
+ if (this.createLogs && !bypassLogs) {
2082
+ if (
2083
+ ResolveIOServer.shouldWriteLogsOffline()
2084
+ ) {
2085
+ ResolveIOServer.getLocalLogManager().writeLog({
2086
+ type: 'log',
2087
+ data: {
2088
+ _id: objectIdHexString(),
2089
+ type: 'document',
2090
+ collection: this.collectionName,
2091
+ id_document: doc._id as string,
2092
+ payload: getBinarySize(JSON.stringify([doc, filter, safeOptions])) < 1000000 ? JSON.stringify([doc, filter, safeOptions], null, 2) : 'Too Big',
2093
+ method: 'deleteOne',
2094
+ id_user: '',
2095
+ user: '',
2096
+ messageId: 0,
2097
+ route: '',
2098
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
2099
+ createdAt: new Date()
2100
+ }
2101
+ });
2102
+ }
2103
+ else {
2104
+ await ResolveIOServer.getMainDB().collection('logs').insertOne(<any>{
2105
+ _id: objectIdHexString(),
2106
+ type: 'document',
2107
+ collection: this.collectionName,
2108
+ id_document: doc._id as string,
2109
+ payload: getBinarySize(JSON.stringify([doc, filter, safeOptions])) < 1000000 ? JSON.stringify([doc, filter, safeOptions], null, 2) : 'Too Big',
2110
+ method: 'deleteOne',
2111
+ id_user: '',
2112
+ user: '',
2113
+ messageId: 0,
2114
+ route: '',
2115
+ client: 'ResolveIO',
2116
+ instance: ResolveIOServer.getInstanceHost(),
2117
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
2118
+ createdAt: new Date()
2119
+ }, {session});
2120
+ }
2121
+ }
2122
+
2123
+ if (this.useVersions) {
2124
+ try {
2125
+ let versionDoc = deepCopy(doc);
2126
+ versionDoc._id = {_id: doc._id, __v: doc.__v};
2127
+
2128
+ await ResolveIOServer.getMongoManager().collection(this.versionCollection).replaceOne(<any>{_id: { _id: doc._id, __v: doc.__v}}, versionDoc, {upsert: true});
2129
+ await ResolveIOServer.getMongoManager().collection(this.versionCollection).deleteMany(<any>{$and: [{'_id._id': doc._id}, {'_id.__v': {$lt: doc.__v - 1}}]});
2130
+ }
2131
+ catch {}
2132
+ }
2133
+
2134
+ if (!this.skipCache) {
2135
+ ResolveIOServer.getMongoManager().invalidateQueryCache(this.collectionName);
2136
+ }
2137
+ }
2138
+
2139
+ return res;
2140
+ }
2141
+ catch (err) {
2142
+ console.log(JSON.stringify([new Date(), 'Error Delete One (Find One And Delete)', this.collectionName, filter, safeOptions, {
2143
+ code: err.code,
2144
+ codeName: err.codeName,
2145
+ message: err.message,
2146
+ stack: err.stack
2147
+ }], null, 2));
2148
+
2149
+ err.message = `Error in Delete One (Find One And Delete): ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
2150
+ throw err;
2151
+ }
2152
+ finally {
2153
+ await monitor.finish();
2154
+ }
2155
+ }
2156
+ else {
2157
+ let monitor = MonitorMongo.create('deleteOne', this.collectionName, JSON.stringify([filter, safeOptions]));
2158
+
2159
+ try {
2160
+ let res = await ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).deleteOne(<any>filter, options || undefined);
2161
+ if (!this.skipCache) {
2162
+ ResolveIOServer.getMongoManager().invalidateQueryCache(this.collectionName);
2163
+ }
2164
+ return res;
2165
+ }
2166
+ catch (err) {
2167
+ console.log(JSON.stringify([new Date(), 'Error Delete One', this.collectionName, filter, safeOptions, {
2168
+ code: err.code,
2169
+ codeName: err.codeName,
2170
+ message: err.message,
2171
+ stack: err.stack
2172
+ }], null, 2));
2173
+
2174
+ err.message = `Error in Delete One: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
2175
+ throw err;
2176
+ }
2177
+ finally {
2178
+ await monitor.finish();
2179
+ }
2180
+ }
2181
+ }
2182
+
2183
+ async distinct(key: string, filter?: MongoManagerFilter<T> & MongoManagerFilterOperators<T>, options?: CommandOperationOptions, bypassSession = false): Promise<T[]> {
2184
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
2185
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
2186
+ if (!options) {
2187
+ options = {
2188
+ session: mongoSession
2189
+ };
2190
+ }
2191
+ else {
2192
+ options.session = mongoSession;
2193
+ }
2194
+ }
2195
+
2196
+ // eslint-disable-next-line no-unused-vars
2197
+ let { session, ...safeOptions } = options || {};
2198
+
2199
+ let monitor = MonitorMongo.create('distinct', this.collectionName, JSON.stringify([key, filter, safeOptions]));
2200
+
2201
+ try {
2202
+ let res = await ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).distinct(key, <any>filter, options || undefined);
2203
+ return res;
2204
+ }
2205
+ catch (err) {
2206
+ console.log(JSON.stringify([new Date(), 'Error Distinct', this.collectionName, filter, safeOptions, {
2207
+ code: err.code,
2208
+ codeName: err.codeName,
2209
+ message: err.message,
2210
+ stack: err.stack
2211
+ }], null, 2));
2212
+
2213
+ err.message = `Error in Distinct: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
2214
+ throw err;
2215
+ }
2216
+ finally {
2217
+ await monitor.finish();
2218
+ }
2219
+ }
2220
+
2221
+ async drop(options?: CommandOperationOptions, bypassSession = false): Promise<boolean> {
2222
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
2223
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
2224
+ if (!options) {
2225
+ options = {
2226
+ session: mongoSession
2227
+ };
2228
+ }
2229
+ else {
2230
+ options.session = mongoSession;
2231
+ }
2232
+ }
2233
+
2234
+ // eslint-disable-next-line no-unused-vars
2235
+ let { session, ...safeOptions } = options || {};
2236
+
2237
+ let monitor = MonitorMongo.create('drop', this.collectionName, JSON.stringify([safeOptions]));
2238
+
2239
+ try {
2240
+ let res = await ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).drop(options);
2241
+ return res;
2242
+ }
2243
+ catch (err) {
2244
+ console.log(JSON.stringify([new Date(), 'Error Drop Collection', this.collectionName, safeOptions, {
2245
+ code: err.code,
2246
+ codeName: err.codeName,
2247
+ message: err.message,
2248
+ stack: err.stack
2249
+ }], null, 2));
2250
+
2251
+ err.message = `Error in Drop Collection: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
2252
+ throw err;
2253
+ }
2254
+ finally {
2255
+ await monitor.finish();
2256
+ }
2257
+ }
2258
+
2259
+ async dropIndex(indexName: string, options?: CommandOperationOptions, bypassSession = false): Promise<Document> {
2260
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
2261
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
2262
+ if (!options) {
2263
+ options = {
2264
+ session: mongoSession
2265
+ };
2266
+ }
2267
+ else {
2268
+ options.session = mongoSession;
2269
+ }
2270
+ }
2271
+
2272
+ // eslint-disable-next-line no-unused-vars
2273
+ let { session, ...safeOptions } = options || {};
2274
+
2275
+ let monitor = MonitorMongo.create('dropIndex', this.collectionName, JSON.stringify([indexName, safeOptions]));
2276
+
2277
+ try {
2278
+ let res = await ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).dropIndex(indexName, options || undefined);
2279
+ return <Document>res;
2280
+ }
2281
+ catch (err) {
2282
+ console.log(JSON.stringify([new Date(), 'Error Drop Index', this.collectionName, indexName, safeOptions, {
2283
+ code: err.code,
2284
+ codeName: err.codeName,
2285
+ message: err.message,
2286
+ stack: err.stack
2287
+ }], null, 2));
2288
+
2289
+ err.message = `Error in Drop Index: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
2290
+ throw err;
2291
+ }
2292
+ finally {
2293
+ await monitor.finish();
2294
+ }
2295
+ }
2296
+
2297
+ async dropIndexes(options?: CommandOperationOptions, bypassSession = false): Promise<boolean> {
2298
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
2299
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
2300
+ if (!options) {
2301
+ options = {
2302
+ session: mongoSession
2303
+ };
2304
+ }
2305
+ else {
2306
+ options.session = mongoSession;
2307
+ }
2308
+ }
2309
+
2310
+ // eslint-disable-next-line no-unused-vars
2311
+ let { session, ...safeOptions } = options || {};
2312
+
2313
+ let monitor = MonitorMongo.create('dropIndexes', this.collectionName, JSON.stringify([safeOptions]));
2314
+
2315
+ try {
2316
+ let res = await ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).dropIndexes(options);
2317
+ return res;
2318
+ }
2319
+ catch (err) {
2320
+ console.log(JSON.stringify([new Date(), 'Error Drop Indexes', this.collectionName, safeOptions, {
2321
+ code: err.code,
2322
+ codeName: err.codeName,
2323
+ message: err.message,
2324
+ stack: err.stack
2325
+ }], null, 2));
2326
+
2327
+ err.message = `Error in Drop Indexes: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
2328
+ throw err;
2329
+ }
2330
+ finally {
2331
+ await monitor.finish();
2332
+ }
2333
+ }
2334
+
2335
+ async find(filter: MongoManagerFilter<T> & MongoManagerFilterOperators<T> = {}, options?: FindOptions<T>, skipCache = false, bypassSession = false): Promise<T[]> {
2336
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
2337
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
2338
+ if (!options) {
2339
+ options = {
2340
+ session: mongoSession
2341
+ };
2342
+ }
2343
+ else {
2344
+ options.session = mongoSession;
2345
+ }
2346
+ }
2347
+
2348
+ // eslint-disable-next-line no-unused-vars
2349
+ let { session, ...safeOptions } = options || {};
2350
+
2351
+ if (!skipCache && !this.skipCache) {
2352
+ return ResolveIOServer.getMongoManager().find(this.collectionName, filter, options || undefined);
2353
+ }
2354
+ else {
2355
+ let monitor = MonitorMongo.create('find', this.collectionName, JSON.stringify([filter, safeOptions]));
2356
+
2357
+ try {
2358
+ let res = await ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).find<T>(<any>filter, options).toArray();
2359
+ recordDependencyResult(this.collectionName, res, { filter, meta: buildQueryMetaFromFindOptions(safeOptions) });
2360
+ return res;
2361
+ }
2362
+ catch (err) {
2363
+ console.log(JSON.stringify([new Date(), 'Error Find', this.collectionName, filter, safeOptions, {
2364
+ code: err.code,
2365
+ codeName: err.codeName,
2366
+ message: err.message,
2367
+ stack: err.stack
2368
+ }], null, 2));
2369
+
2370
+ err.message = `Error in Find: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
2371
+ throw err;
2372
+ }
2373
+ finally {
2374
+ await monitor.finish();
2375
+ }
2376
+ }
2377
+ }
2378
+
2379
+ async findById(id: string, options?: FindOptions<T>, skipCache = false, bypassSession = false): Promise<T> {
2380
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
2381
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
2382
+ if (!options) {
2383
+ options = {
2384
+ session: mongoSession
2385
+ };
2386
+ }
2387
+ else {
2388
+ options.session = mongoSession;
2389
+ }
2390
+ }
2391
+
2392
+ // eslint-disable-next-line no-unused-vars
2393
+ let { session, ...safeOptions } = options || {};
2394
+
2395
+ if (!skipCache && !this.skipCache) {
2396
+ return ResolveIOServer.getMongoManager().findOne(this.collectionName, {_id: id}, options || undefined);
2397
+ }
2398
+ else {
2399
+ let monitor = MonitorMongo.create('findById', this.collectionName, JSON.stringify([{_id: id}, safeOptions]));
2400
+
2401
+ try {
2402
+ let res = await ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).findOne<T>(<any>{_id: id}, options || undefined);
2403
+ recordDependencyResult(this.collectionName, res, { filter: { _id: id }, meta: buildQueryMetaFromFindOptions(safeOptions) });
2404
+ return res;
2405
+ }
2406
+ catch (err) {
2407
+ console.log(JSON.stringify([new Date(), 'Error Find By ID', this.collectionName, id, safeOptions, {
2408
+ code: err.code,
2409
+ codeName: err.codeName,
2410
+ message: err.message,
2411
+ stack: err.stack
2412
+ }], null, 2));
2413
+
2414
+ err.message = `Error in Find By ID: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
2415
+ throw err;
2416
+ }
2417
+ finally {
2418
+ await monitor.finish();
2419
+ }
2420
+ }
2421
+ }
2422
+
2423
+ async findCount(filter: MongoManagerFilter<T> & MongoManagerFilterOperators<T> = {}, options?: FindOptions<T>, bypassSession = false): Promise<Number> {
2424
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
2425
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
2426
+ if (!options) {
2427
+ options = {
2428
+ session: mongoSession
2429
+ };
2430
+ }
2431
+ else {
2432
+ options.session = mongoSession;
2433
+ }
2434
+ }
2435
+
2436
+ // eslint-disable-next-line no-unused-vars
2437
+ let { session, ...safeOptions } = options || {};
2438
+
2439
+ let monitor = MonitorMongo.create('findCount', this.collectionName, JSON.stringify([filter, safeOptions]));
2440
+
2441
+ try {
2442
+ let res = await ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).find(<any>filter, options).count();
2443
+ recordDependencyResult(this.collectionName, res, { filter });
2444
+ return res;
2445
+ }
2446
+ catch (err) {
2447
+ console.log(JSON.stringify([new Date(), 'Error Find Count', this.collectionName, filter, safeOptions, {
2448
+ code: err.code,
2449
+ codeName: err.codeName,
2450
+ message: err.message,
2451
+ stack: err.stack
2452
+ }], null, 2));
2453
+
2454
+ err.message = `Error in Find Count: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
2455
+ throw err;
2456
+ }
2457
+ finally {
2458
+ await monitor.finish();
2459
+ }
2460
+ }
2461
+
2462
+ findCursor(filter: MongoManagerFilter<T> & MongoManagerFilterOperators<T> = {}, options?: FindOptions<T>, bypassSession = false): FindCursor {
2463
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
2464
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
2465
+ if (!options) {
2466
+ options = {session: mongoSession};
2467
+ }
2468
+ else {
2469
+ options.session = mongoSession;
2470
+ }
2471
+ }
2472
+
2473
+ // eslint-disable-next-line no-unused-vars
2474
+ let { session, ...safeOptions } = options || {};
2475
+
2476
+ let monitor = MonitorMongo.create('findCursor', this.collectionName, JSON.stringify([filter, safeOptions]));
2477
+ let cursor = ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).find(<any>filter, options || undefined);
2478
+ cursor.on('close', async ev => {
2479
+ await monitor.finish();
2480
+ cursor.removeAllListeners();
2481
+ return ev;
2482
+ });
2483
+
2484
+ return cursor;
2485
+ }
2486
+
2487
+ findStream(filter: MongoManagerFilter<T> & MongoManagerFilterOperators<T> = {}, options?: FindOptions<T>, streamOptions?: CursorStreamOptions, bypassSession = false) {
2488
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
2489
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
2490
+ if (!options) {
2491
+ options = {session: mongoSession};
2492
+ }
2493
+ else {
2494
+ options.session = mongoSession;
2495
+ }
2496
+ }
2497
+
2498
+ // eslint-disable-next-line no-unused-vars
2499
+ let { session, ...safeOptions } = options || {};
2500
+
2501
+ let monitor = MonitorMongo.create('findStream', this.collectionName, JSON.stringify([filter, safeOptions, streamOptions]));
2502
+ let stream = ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).find(<any>filter, options).stream(streamOptions).on('close', async ev => {
2503
+ await monitor.finish();
2504
+ return ev;
2505
+ });
2506
+
2507
+ return stream;
2508
+ }
2509
+
2510
+ async findOne(filter: MongoManagerFilter<T> | MongoManagerFilterOperators<T> = {}, options?: FindOptions<T>, skipCache = false, bypassSession = false): Promise<T> {
2511
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
2512
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
2513
+ if (!options) {
2514
+ options = {
2515
+ session: mongoSession
2516
+ };
2517
+ }
2518
+ else {
2519
+ options.session = mongoSession;
2520
+ }
2521
+ }
2522
+
2523
+ if (!skipCache && !this.skipCache) {
2524
+ return ResolveIOServer.getMongoManager().findOne(this.collectionName, filter, options || undefined);
2525
+ }
2526
+ else {
2527
+ // eslint-disable-next-line no-unused-vars
2528
+ let { session, ...safeOptions } = options || {};
2529
+
2530
+ let monitor = MonitorMongo.create('findOne', this.collectionName, JSON.stringify([filter, safeOptions]));
2531
+
2532
+ try {
2533
+ let res = await ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).findOne<T>(<any>filter, options || undefined);
2534
+ recordDependencyResult(this.collectionName, res, { filter });
2535
+ return res;
2536
+ }
2537
+ catch (err) {
2538
+ console.log(JSON.stringify([new Date(), 'Error Find One', this.collectionName, filter, safeOptions, {
2539
+ code: err.code,
2540
+ codeName: err.codeName,
2541
+ message: err.message,
2542
+ stack: err.stack
2543
+ }], null, 2));
2544
+
2545
+ err.message = `Error in Find One: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
2546
+ throw err;
2547
+ }
2548
+ finally {
2549
+ await monitor.finish();
2550
+ }
2551
+ }
2552
+ }
2553
+
2554
+ async findOneAndDelete(filter: MongoManagerFilter<T> & MongoManagerFilterOperators<T> = {}, options?: FindOneAndDeleteOptions, bypassLogs = false, bypassSession = false): Promise<T> {
2555
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
2556
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
2557
+ if (!options) {
2558
+ options = {session: mongoSession};
2559
+ }
2560
+ else {
2561
+ options.session = mongoSession;
2562
+ }
2563
+ }
2564
+
2565
+ let { session, ...safeOptions } = options || {};
2566
+
2567
+ let monitor = MonitorMongo.create('findOneAndDelete', this.collectionName, JSON.stringify([filter, safeOptions]));
2568
+
2569
+ try {
2570
+ let doc = await ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).findOneAndDelete(<any>filter, options) as T;
2571
+
2572
+ if (doc) {
2573
+ if (this.createLogs && !bypassLogs) {
2574
+ if (
2575
+ ResolveIOServer.shouldWriteLogsOffline()
2576
+ ) {
2577
+ ResolveIOServer.getLocalLogManager().writeLog({
2578
+ type: 'log',
2579
+ data: {
2580
+ _id: objectIdHexString(),
2581
+ type: 'document',
2582
+ collection: this.collectionName,
2583
+ id_document: doc._id as string,
2584
+ payload: getBinarySize(JSON.stringify([doc, filter, safeOptions])) < 1000000 ? JSON.stringify([doc, filter, safeOptions], null, 2) : 'Too Big',
2585
+ method: 'findOneAndDelete',
2586
+ id_user: '',
2587
+ user: '',
2588
+ messageId: 0,
2589
+ route: '',
2590
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
2591
+ createdAt: new Date()
2592
+ }
2593
+ });
2594
+ }
2595
+ else {
2596
+ await ResolveIOServer.getMainDB().collection('logs').insertOne(<any>{
2597
+ _id: objectIdHexString(),
2598
+ type: 'document',
2599
+ collection: this.collectionName,
2600
+ id_document: doc._id as string,
2601
+ payload: getBinarySize(JSON.stringify([doc, filter, safeOptions])) < 1000000 ? JSON.stringify([doc, filter, safeOptions], null, 2) : 'Too Big',
2602
+ method: 'findOneAndDelete',
2603
+ id_user: '',
2604
+ user: '',
2605
+ messageId: 0,
2606
+ route: '',
2607
+ client: 'ResolveIO',
2608
+ instance: ResolveIOServer.getInstanceHost(),
2609
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
2610
+ createdAt: new Date()
2611
+ }, {session});
2612
+ }
2613
+ }
2614
+
2615
+ if (this.useVersions) {
2616
+ try {
2617
+ let versionDoc = deepCopy(doc);
2618
+ versionDoc._id = {_id: doc._id, __v: doc.__v};
2619
+
2620
+ await ResolveIOServer.getMongoManager().collection(this.versionCollection).replaceOne(<any>{_id: { _id: doc._id, __v: doc.__v}}, versionDoc, {upsert: true});
2621
+ }
2622
+ catch {}
2623
+ }
2624
+
2625
+ if (!this.skipCache) {
2626
+ ResolveIOServer.getMongoManager().invalidateQueryCache(this.collectionName);
2627
+ }
2628
+
2629
+ return <T>doc;
2630
+ }
2631
+ else {
2632
+ return null;
2633
+ }
2634
+ }
2635
+ catch (err) {
2636
+ console.log(JSON.stringify([new Date(), 'Error Find One And Delete', this.collectionName, filter, safeOptions, {
2637
+ code: err.code,
2638
+ codeName: err.codeName,
2639
+ message: err.message,
2640
+ stack: err.stack
2641
+ }], null, 2));
2642
+
2643
+ err.message = `Error in Find One And Delete: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
2644
+ throw err;
2645
+ }
2646
+ finally {
2647
+ await monitor.finish();
2648
+ }
2649
+ }
2650
+
2651
+ async findOneAndReplace(filter: MongoManagerFilter<T> & MongoManagerFilterOperators<T> = {}, replacement: T, options?: FindOneAndReplaceOptions, bypassLogs = false, bypassCheckSchema = false, bypassSession = false): Promise<T> {
2652
+ if (this.checkSchema && !bypassCheckSchema) {
2653
+ const validation = this.simplschema.newContext();
2654
+ const isValid = validation.validate(replacement);
2655
+
2656
+ if (!isValid) {
2657
+ console.log(new Date(), this.collectionName, 'Schema Errors - findOneAndReplace', validation.validationErrors());
2658
+ await ResolveIOServer.getMainServer().getMethodManager().callMethod('insertErrorLog', 'Schema Failed on findOneAndReplace - ' + this.collectionName, JSON.stringify([validation.validationErrors(), replacement], null, 2));
2659
+ throw new Error(JSON.stringify(validation.validationErrors(), null, 2));
2660
+ }
2661
+ }
2662
+
2663
+ let date = new Date();
2664
+
2665
+ if (this.timestamps) {
2666
+ replacement.updatedAt = date;
2667
+ }
2668
+
2669
+ if (options && options.upsert) {
2670
+ if (!replacement._id) {
2671
+ replacement._id = objectIdHexString();
2672
+ }
2673
+
2674
+ if (this.useVersions) {
2675
+ replacement.__v = 0;
2676
+ }
2677
+
2678
+ if (this.timestamps && !replacement.createdAt) {
2679
+ replacement.createdAt = date;
2680
+ }
2681
+ }
2682
+
2683
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
2684
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
2685
+ if (!options) {
2686
+ options = {session: mongoSession};
2687
+ }
2688
+ else {
2689
+ options.session = mongoSession;
2690
+ }
2691
+ }
2692
+
2693
+ let { session, ...safeOptions } = options || {};
2694
+
2695
+ let monitor = MonitorMongo.create('findOneAndReplace', this.collectionName, JSON.stringify([filter, replacement, safeOptions]));
2696
+
2697
+ try {
2698
+ let doc = await ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).findOneAndReplace(<any>filter, replacement, options) as T;
2699
+
2700
+ if (doc) {
2701
+ if (this.createLogs && !bypassLogs) {
2702
+ if (
2703
+ ResolveIOServer.shouldWriteLogsOffline()
2704
+ ) {
2705
+ ResolveIOServer.getLocalLogManager().writeLog({
2706
+ type: 'log',
2707
+ data: {
2708
+ _id: objectIdHexString(),
2709
+ type: 'document',
2710
+ collection: this.collectionName,
2711
+ id_document: doc._id as string,
2712
+ payload: getBinarySize(JSON.stringify([doc, filter, replacement, safeOptions])) < 1000000 ? JSON.stringify([doc, filter, replacement, safeOptions], null, 2) : 'Too Big',
2713
+ method: 'findOneAndReplace',
2714
+ id_user: '',
2715
+ user: '',
2716
+ messageId: 0,
2717
+ route: '',
2718
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
2719
+ createdAt: new Date()
2720
+ }
2721
+ });
2722
+ }
2723
+ else {
2724
+ await ResolveIOServer.getMainDB().collection('logs').insertOne(<any>{
2725
+ _id: objectIdHexString(),
2726
+ type: 'document',
2727
+ collection: this.collectionName,
2728
+ id_document: doc._id as string,
2729
+ payload: getBinarySize(JSON.stringify([doc, filter, replacement, safeOptions])) < 1000000 ? JSON.stringify([doc, filter, replacement, safeOptions], null, 2) : 'Too Big',
2730
+ method: 'findOneAndReplace',
2731
+ id_user: '',
2732
+ user: '',
2733
+ messageId: 0,
2734
+ route: '',
2735
+ client: 'ResolveIO',
2736
+ instance: ResolveIOServer.getInstanceHost(),
2737
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
2738
+ createdAt: new Date()
2739
+ }, {session});
2740
+ }
2741
+ }
2742
+
2743
+ if (this.useVersions) {
2744
+ try {
2745
+ let versionDoc = deepCopy(doc);
2746
+ versionDoc._id = {_id: doc._id, __v: doc.__v};
2747
+
2748
+ await ResolveIOServer.getMongoManager().collection(this.versionCollection).replaceOne(<any>{_id: { _id: doc._id, __v: doc.__v}}, versionDoc, {upsert: true});
2749
+ }
2750
+ catch {}
2751
+ }
2752
+
2753
+ if (!this.skipCache) {
2754
+ ResolveIOServer.getMongoManager().invalidateQueryCache(this.collectionName);
2755
+ }
2756
+
2757
+ return <T>doc;
2758
+ }
2759
+ else {
2760
+ return null;
2761
+ }
2762
+ }
2763
+ catch (err) {
2764
+ console.log(JSON.stringify([new Date(), 'Error Find One And Replace', this.collectionName, filter, safeOptions, {
2765
+ code: err.code,
2766
+ codeName: err.codeName,
2767
+ message: err.message,
2768
+ stack: err.stack
2769
+ }], null, 2));
2770
+
2771
+ err.message = `Error in Find One And Replace: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
2772
+ throw err;
2773
+ }
2774
+ finally {
2775
+ await monitor.finish();
2776
+ }
2777
+ }
2778
+
2779
+ async findOneAndUpdate(filter: MongoManagerFilter<T> & MongoManagerFilterOperators<T> = {}, update: MongoManagerUpdateFilter<T>, options?: FindOneAndUpdateOptions, bypassLogs = false, bypassCheckSchema = false, bypassSession = false): Promise<T> {
2780
+ if (this.checkSchema && !bypassCheckSchema) {
2781
+ const validation = this.simplschema.newContext();
2782
+ const isValid = validation.validate(update, {modifier: true});
2783
+
2784
+ if (!isValid) {
2785
+ console.log(new Date(), this.collectionName, 'Schema Errors - findOneAndUpdate', validation.validationErrors());
2786
+ await ResolveIOServer.getMainServer().getMethodManager().callMethod('insertErrorLog', 'Schema Failed on findOneAndUpdate - ' + this.collectionName, JSON.stringify([validation.validationErrors(), update], null, 2));
2787
+ throw new Error(JSON.stringify(validation.validationErrors(), null, 2));
2788
+ }
2789
+ }
2790
+
2791
+ if (this.timestamps) {
2792
+ let date = new Date();
2793
+
2794
+ if (!update.$set) {
2795
+ update.$set = <any>{updatedAt: date};
2796
+ }
2797
+ else {
2798
+ update.$set.updatedAt = date;
2799
+ }
2800
+ }
2801
+
2802
+ if (options && options.upsert) {
2803
+ if (!update.$setOnInsert) {
2804
+ if (this.timestamps) {
2805
+ update.$setOnInsert = <any>{
2806
+ _id: objectIdHexString(),
2807
+ createdAt: new Date()
2808
+ };
2809
+ }
2810
+ else {
2811
+ update.$setOnInsert = <any>{
2812
+ _id: objectIdHexString()
2813
+ };
2814
+ }
2815
+ }
2816
+ else {
2817
+ if (!update.$setOnInsert._id) {
2818
+ update.$setOnInsert._id = objectIdHexString();
2819
+ }
2820
+
2821
+ if (this.timestamps && !update.$setOnInsert.createdAt) {
2822
+ update.$setOnInsert.createdAt = new Date();
2823
+ }
2824
+ }
2825
+ }
2826
+
2827
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
2828
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
2829
+ if (!options) {
2830
+ options = {session: mongoSession};
2831
+ }
2832
+ else {
2833
+ options.session = mongoSession;
2834
+ }
2835
+ }
2836
+
2837
+
2838
+ let { session, ...safeOptions } = options || {};
2839
+
2840
+ let monitor = MonitorMongo.create('findOneAndUpdate', this.collectionName, JSON.stringify([filter, update, safeOptions]));
2841
+
2842
+ try {
2843
+ let doc = await ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).findOneAndUpdate(<any>filter, <any>update, options) as T;
2844
+
2845
+ if (doc) {
2846
+ if (this.createLogs && !bypassLogs) {
2847
+ if (
2848
+ ResolveIOServer.shouldWriteLogsOffline()
2849
+ ) {
2850
+ ResolveIOServer.getLocalLogManager().writeLog({
2851
+ type: 'log',
2852
+ data: {
2853
+ _id: objectIdHexString(),
2854
+ type: 'document',
2855
+ collection: this.collectionName,
2856
+ id_document: <string>doc._id,
2857
+ payload: getBinarySize(JSON.stringify([doc, filter, update, safeOptions])) < 1000000 ? JSON.stringify([doc, filter, update, safeOptions], null, 2) : 'Too Big',
2858
+ method: 'findOneAndUpdate',
2859
+ id_user: '',
2860
+ user: '',
2861
+ messageId: 0,
2862
+ route: '',
2863
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
2864
+ createdAt: new Date()
2865
+ }
2866
+ });
2867
+ }
2868
+ else {
2869
+ await ResolveIOServer.getMainDB().collection('logs').insertOne(<any>{
2870
+ _id: objectIdHexString(),
2871
+ type: 'document',
2872
+ collection: this.collectionName,
2873
+ id_document: <string>doc._id,
2874
+ payload: getBinarySize(JSON.stringify([doc, filter, update, safeOptions])) < 1000000 ? JSON.stringify([doc, filter, update, safeOptions], null, 2) : 'Too Big',
2875
+ method: 'findOneAndUpdate',
2876
+ id_user: '',
2877
+ user: '',
2878
+ messageId: 0,
2879
+ route: '',
2880
+ client: 'ResolveIO',
2881
+ instance: ResolveIOServer.getInstanceHost(),
2882
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
2883
+ createdAt: new Date()
2884
+ }, {session});
2885
+ }
2886
+ }
2887
+
2888
+ if (this.useVersions) {
2889
+ try {
2890
+ let versionDoc = deepCopy(doc);
2891
+ versionDoc._id = {_id: doc._id, __v: doc.__v};
2892
+
2893
+ await ResolveIOServer.getMongoManager().collection(this.versionCollection).replaceOne(<any>{_id: { _id: doc._id, __v: doc.__v}}, versionDoc, {upsert: true});
2894
+ }
2895
+ catch {}
2896
+ }
2897
+
2898
+ if (!this.skipCache) {
2899
+ ResolveIOServer.getMongoManager().invalidateQueryCache(this.collectionName);
2900
+ }
2901
+
2902
+ return <T>doc;
2903
+ }
2904
+ else {
2905
+ return null;
2906
+ }
2907
+ }
2908
+ catch (err) {
2909
+ console.log(JSON.stringify([new Date(), 'Error Find One And Update', this.collectionName, filter, safeOptions, {
2910
+ code: err.code,
2911
+ codeName: err.codeName,
2912
+ message: err.message,
2913
+ stack: err.stack
2914
+ }], null, 2));
2915
+
2916
+ err.message = `Error in Find One And Update: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
2917
+ throw err;
2918
+ }
2919
+ finally {
2920
+ await monitor.finish();
2921
+ }
2922
+ }
2923
+
2924
+ async indexes(options: IndexInformationOptions): Promise<IndexDescriptionCompact | IndexDescriptionCompact[]> {
2925
+ // eslint-disable-next-line no-unused-vars
2926
+ let { session, ...safeOptions } = options || {};
2927
+
2928
+ let monitor = MonitorMongo.create('indexes', this.collectionName, JSON.stringify([safeOptions]));
2929
+
2930
+ try {
2931
+ let res = await ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).indexes(options);
2932
+ return res;
2933
+ }
2934
+ catch (err) {
2935
+ console.log(JSON.stringify([new Date(), 'Error Indexes', this.collectionName, safeOptions, {
2936
+ code: err.code,
2937
+ codeName: err.codeName,
2938
+ message: err.message,
2939
+ stack: err.stack
2940
+ }], null, 2));
2941
+
2942
+ err.message = `Error in Indexes: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
2943
+ throw err;
2944
+ }
2945
+ finally {
2946
+ await monitor.finish();
2947
+ }
2948
+ }
2949
+
2950
+ async indexExists(indexes: string | string[], options?: IndexInformationOptions): Promise<boolean> {
2951
+ // eslint-disable-next-line no-unused-vars
2952
+ let { session, ...safeOptions } = options || {};
2953
+
2954
+ let monitor = MonitorMongo.create('indexExists', this.collectionName, JSON.stringify([indexes, safeOptions]));
2955
+
2956
+ try {
2957
+ let res = await ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).indexExists(indexes, options || undefined);
2958
+ return res;
2959
+ }
2960
+ catch (err) {
2961
+ console.log(JSON.stringify([new Date(), 'Error Index Exists', this.collectionName, indexes, safeOptions, {
2962
+ code: err.code,
2963
+ codeName: err.codeName,
2964
+ message: err.message,
2965
+ stack: err.stack
2966
+ }], null, 2));
2967
+
2968
+ err.message = `Error in Index Exists: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
2969
+ throw err;
2970
+ }
2971
+ finally {
2972
+ await monitor.finish();
2973
+ }
2974
+ }
2975
+
2976
+ async insertMany(docs: T[], options?: BulkWriteOptions, bypassLogs = false, bypassCheckSchema = false, bypassMonitor = false, bypassSession = false): Promise<T[]> {
2977
+ if (!docs.length) {
2978
+ return [];
2979
+ }
2980
+
2981
+ let validationResults = [];
2982
+
2983
+ if (this.checkSchema && !bypassCheckSchema) {
2984
+ for (let doc of docs) {
2985
+ const validation = this.simplschema.newContext();
2986
+ const isValid = validation.validate(doc);
2987
+
2988
+ if (!isValid) {
2989
+ console.log(new Date(), this.collectionName, 'Schema Errors - insertMany', validation.validationErrors());
2990
+ await ResolveIOServer.getMainServer().getMethodManager().callMethod('insertErrorLog', 'Schema Failed on insertMany - ' + this.collectionName, JSON.stringify([validation.validationErrors(), doc], null, 2));
2991
+ validationResults.push(false);
2992
+ }
2993
+ else {
2994
+ validationResults.push(true);
2995
+ }
2996
+ }
2997
+ }
2998
+
2999
+ let validDocs = this.checkSchema ? docs.filter((a, idx) => validationResults[idx]) : docs;
3000
+
3001
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
3002
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
3003
+ if (!options) {
3004
+ options = {session: mongoSession};
3005
+ }
3006
+ else {
3007
+ options.session = mongoSession;
3008
+ }
3009
+ }
3010
+
3011
+
3012
+ let { session, ...safeOptions } = options || {};
3013
+
3014
+ for (let i = 0; i < validDocs.length; i++) {
3015
+ let doc = validDocs[i];
3016
+
3017
+ if (!doc._id) {
3018
+ doc._id = objectIdHexString();
3019
+ }
3020
+
3021
+ if (this.timestamps) {
3022
+ let date = new Date();
3023
+
3024
+ if (!doc.createdAt) {
3025
+ doc.createdAt = date;
3026
+ }
3027
+
3028
+ if (!doc.updatedAt) {
3029
+ doc.updatedAt = date;
3030
+ }
3031
+ }
3032
+
3033
+ if (this.useVersions && !doc.hasOwnProperty('__v')) {
3034
+ doc.__v = 0;
3035
+ }
3036
+
3037
+ if (this.createLogs && !bypassLogs) {
3038
+ if (
3039
+ ResolveIOServer.shouldWriteLogsOffline()
3040
+ ) {
3041
+ ResolveIOServer.getLocalLogManager().writeLog({
3042
+ type: 'log',
3043
+ data: {
3044
+ _id: objectIdHexString(),
3045
+ type: 'document',
3046
+ collection: this.collectionName,
3047
+ id_document: doc._id,
3048
+ payload: getBinarySize(JSON.stringify([doc, safeOptions])) < 1000000 ? JSON.stringify([doc, safeOptions], null, 2) : 'Too Big',
3049
+ method: 'insertMany',
3050
+ id_user: '',
3051
+ user: '',
3052
+ messageId: 0,
3053
+ route: '',
3054
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
3055
+ createdAt: new Date()
3056
+ }
3057
+ });
3058
+ }
3059
+ else {
3060
+ await ResolveIOServer.getMainDB().collection('logs').insertOne(<any>{
3061
+ _id: objectIdHexString(),
3062
+ type: 'document',
3063
+ collection: this.collectionName,
3064
+ id_document: doc._id,
3065
+ payload: getBinarySize(JSON.stringify([doc, safeOptions])) < 1000000 ? JSON.stringify([doc, safeOptions], null, 2) : 'Too Big',
3066
+ method: 'insertMany',
3067
+ id_user: '',
3068
+ user: '',
3069
+ messageId: 0,
3070
+ route: '',
3071
+ client: 'ResolveIO',
3072
+ instance: ResolveIOServer.getInstanceHost(),
3073
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
3074
+ createdAt: new Date()
3075
+ }, {session});
3076
+ }
3077
+ }
3078
+ }
3079
+
3080
+ if (validDocs.length) {
3081
+ let monitor = null;
3082
+
3083
+ if (!bypassMonitor) {
3084
+ monitor = MonitorMongo.create('insertMany', this.collectionName, JSON.stringify([validDocs, safeOptions]));
3085
+ }
3086
+
3087
+ try {
3088
+ await this.retryWrite(() => ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).insertMany(<OptionalUnlessRequiredId<T>[]>validDocs, options || undefined), options);
3089
+ if (!this.skipCache) {
3090
+ ResolveIOServer.getMongoManager().invalidateQueryCache(this.collectionName);
3091
+ }
3092
+ return validDocs;
3093
+ }
3094
+ catch (err) {
3095
+ console.log(JSON.stringify([new Date(), 'Error Insert Many', this.collectionName, docs, safeOptions, {
3096
+ code: err.code,
3097
+ codeName: err.codeName,
3098
+ message: err.message,
3099
+ stack: err.stack
3100
+ }], null, 2));
3101
+
3102
+ err.message = `Error in Insert Many: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
3103
+ throw err;
3104
+ }
3105
+ finally {
3106
+ if (monitor) {
3107
+ monitor.finish();
3108
+ }
3109
+ }
3110
+ }
3111
+
3112
+ return [];
3113
+ }
3114
+
3115
+ async insertOne(doc: T, options?: InsertOneOptions, bypassLogs = false, bypassCheckSchema = false, bypassSession = false): Promise<T> {
3116
+ if (!doc._id) {
3117
+ doc._id = objectIdHexString();
3118
+ }
3119
+
3120
+ if (this.checkSchema && !bypassCheckSchema) {
3121
+ const validation = this.simplschema.newContext();
3122
+ const isValid = validation.validate(doc);
3123
+
3124
+ if (!isValid) {
3125
+ console.log(new Date(), this.collectionName, 'Schema Errors - insertOne', validation.validationErrors());
3126
+ await ResolveIOServer.getMainServer().getMethodManager().callMethod('insertErrorLog', 'Schema Failed on insertOne - ' + this.collectionName, JSON.stringify([validation.validationErrors(), doc], null, 2));
3127
+ throw new Error(JSON.stringify(validation.validationErrors(), null, 2));
3128
+ }
3129
+ }
3130
+
3131
+ if (this.timestamps) {
3132
+ let date = new Date();
3133
+
3134
+ if (!doc.createdAt) {
3135
+ doc.createdAt = date;
3136
+ }
3137
+
3138
+ if (!doc.updatedAt) {
3139
+ doc.updatedAt = date;
3140
+ }
3141
+ }
3142
+
3143
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
3144
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
3145
+ if (!options) {
3146
+ options = {session: mongoSession};
3147
+ }
3148
+ else {
3149
+ options.session = mongoSession;
3150
+ }
3151
+ }
3152
+
3153
+
3154
+ let { session, ...safeOptions } = options || {};
3155
+
3156
+ if (this.createLogs && !bypassLogs) {
3157
+ if (
3158
+ ResolveIOServer.shouldWriteLogsOffline()
3159
+ ) {
3160
+ ResolveIOServer.getLocalLogManager().writeLog({
3161
+ type: 'log',
3162
+ data: {
3163
+ _id: objectIdHexString(),
3164
+ type: 'document',
3165
+ collection: this.collectionName,
3166
+ id_document: doc._id,
3167
+ payload: getBinarySize(JSON.stringify([doc, safeOptions])) < 1000000 ? JSON.stringify([doc, safeOptions], null, 2) : 'Too Big',
3168
+ method: 'insertOne',
3169
+ id_user: '',
3170
+ user: '',
3171
+ messageId: 0,
3172
+ route: '',
3173
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
3174
+ createdAt: new Date()
3175
+ }
3176
+ });
3177
+ }
3178
+ else {
3179
+ await ResolveIOServer.getMainDB().collection('logs').insertOne(<any>{
3180
+ _id: objectIdHexString(),
3181
+ type: 'document',
3182
+ collection: this.collectionName,
3183
+ id_document: doc._id,
3184
+ payload: getBinarySize(JSON.stringify([doc, safeOptions])) < 1000000 ? JSON.stringify([doc, safeOptions], null, 2) : 'Too Big',
3185
+ method: 'insertOne',
3186
+ id_user: '',
3187
+ user: '',
3188
+ messageId: 0,
3189
+ route: '',
3190
+ client: 'ResolveIO',
3191
+ instance: ResolveIOServer.getInstanceHost(),
3192
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
3193
+ createdAt: new Date()
3194
+ }, {session});
3195
+ }
3196
+ }
3197
+
3198
+ if (this.useVersions && !doc.hasOwnProperty('__v')) {
3199
+ doc.__v = 0;
3200
+ }
3201
+
3202
+ let monitor = MonitorMongo.create('insertOne', this.collectionName, JSON.stringify([doc, safeOptions]));
3203
+
3204
+ try {
3205
+ await this.retryWrite(() => ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).insertOne(<OptionalUnlessRequiredId<T>>doc, options || undefined), options);
3206
+ if (!this.skipCache) {
3207
+ ResolveIOServer.getMongoManager().invalidateQueryCache(this.collectionName);
3208
+ }
3209
+
3210
+ return doc;
3211
+ }
3212
+ catch(err) {
3213
+ console.log(JSON.stringify([new Date(), 'Error Insert One', this.collectionName, doc, safeOptions, {
3214
+ code: err.code,
3215
+ codeName: err.codeName,
3216
+ message: err.message,
3217
+ stack: err.stack
3218
+ }], null, 2));
3219
+
3220
+ err.message = `Error in Insert One: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
3221
+ throw err;
3222
+ }
3223
+ finally {
3224
+ await monitor.finish();
3225
+ }
3226
+ }
3227
+
3228
+ listIndexes(options?: ListIndexesOptions, bypassSession = false): ListIndexesCursor {
3229
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
3230
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
3231
+ if (!options) {
3232
+ options = {session: mongoSession};
3233
+ }
3234
+ else {
3235
+ options.session = mongoSession;
3236
+ }
3237
+ }
3238
+
3239
+ // eslint-disable-next-line no-unused-vars
3240
+ let { session, ...safeOptions } = options || {};
3241
+
3242
+ let monitor = MonitorMongo.create('listIndexes', this.collectionName, JSON.stringify([safeOptions]));
3243
+ let cursor = ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).listIndexes(options);
3244
+ cursor.on('close', async ev => {
3245
+ await monitor.finish();
3246
+ return ev;
3247
+ });
3248
+
3249
+ return cursor;
3250
+ }
3251
+
3252
+ async rename(newName: string, options?: RenameOptions, bypassSession = false): Promise<Collection<Document>> {
3253
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
3254
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
3255
+ if (!options) {
3256
+ options = {
3257
+ session: mongoSession
3258
+ };
3259
+ }
3260
+ else {
3261
+ options.session = mongoSession;
3262
+ }
3263
+ }
3264
+
3265
+ // eslint-disable-next-line no-unused-vars
3266
+ let { session, ...safeOptions } = options || {};
3267
+
3268
+ let monitor = MonitorMongo.create('rename', this.collectionName, JSON.stringify([newName, safeOptions]));
3269
+ try {
3270
+ let res = await ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).rename(newName, options || undefined);
3271
+ return res;
3272
+ }
3273
+ catch (err) {
3274
+ console.log(JSON.stringify([new Date(), 'Error Rename', this.collectionName, newName, safeOptions, {
3275
+ code: err.code,
3276
+ codeName: err.codeName,
3277
+ message: err.message,
3278
+ stack: err.stack
3279
+ }], null, 2));
3280
+
3281
+ err.message = `Error in Rename: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
3282
+ throw err;
3283
+ }
3284
+ finally {
3285
+ await monitor.finish();
3286
+ }
3287
+ }
3288
+
3289
+ async replaceOne(filter: MongoManagerFilter<T> & MongoManagerFilterOperators<T>, replacement: T, options?: ReplaceOptions, bypassLogs = false, bypassCheckSchema = false, doc: T = null, bypassSession = false) : Promise<UpdateResult> {
3290
+ if (this.checkSchema && !bypassCheckSchema) {
3291
+ const validation = this.simplschema.newContext();
3292
+ const isValid = validation.validate(replacement);
3293
+
3294
+ if (!isValid) {
3295
+ console.log(new Date(), this.collectionName, 'Schema Errors - replaceOne', validation.validationErrors());
3296
+ await ResolveIOServer.getMainServer().getMethodManager().callMethod('insertErrorLog', 'Schema Failed on replaceOne - ' + this.collectionName, JSON.stringify([validation.validationErrors(), replacement], null, 2));
3297
+ throw new Error(JSON.stringify(validation.validationErrors(), null, 2));
3298
+ }
3299
+ }
3300
+
3301
+ let date = new Date();
3302
+
3303
+ if (this.timestamps) {
3304
+ replacement.updatedAt = date;
3305
+ }
3306
+
3307
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
3308
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
3309
+ if (!options) {
3310
+ options = {session: mongoSession};
3311
+ }
3312
+ else {
3313
+ options.session = mongoSession;
3314
+ }
3315
+
3316
+ // console.log('Replace One', 'Has Session', this.collectionName, JSON.stringify(filter, null, 2), JSON.stringify(replacement, null, 2));
3317
+ }
3318
+ else {
3319
+ // console.log('Replace One', 'No Session', this.collectionName, JSON.stringify(filter, null, 2), JSON.stringify(replacement, null, 2));
3320
+ }
3321
+
3322
+
3323
+ let { session, ...safeOptions } = options || {};
3324
+
3325
+ if (this.useVersions) {
3326
+ if (!doc) {
3327
+ doc = await this.findOne(<any>filter, {session}, true);
3328
+ }
3329
+
3330
+ if (doc) {
3331
+ if (this.timestamps && !replacement.createdAt && doc.createdAt) {
3332
+ replacement.createdAt = doc.createdAt;
3333
+ }
3334
+
3335
+ if (doc.__v === replacement.__v) {
3336
+ try {
3337
+ replacement.__v += 1;
3338
+
3339
+ let versionDoc = deepCopy(doc);
3340
+ versionDoc._id = {_id: doc._id, __v: doc.__v};
3341
+
3342
+ await ResolveIOServer.getMongoManager().collection(this.versionCollection).replaceOne(<any>{_id: { _id: doc._id, __v: doc.__v}}, versionDoc, {upsert: true});
3343
+
3344
+ if (doc.__v >= 4) {
3345
+ await ResolveIOServer.getMongoManager().collection(this.versionCollection).deleteMany(<any>{$and: [{'_id._id': doc._id}, {'_id.__v': {$lt: doc.__v - 4}}]});
3346
+ }
3347
+ }
3348
+ catch {}
3349
+
3350
+ if (this.createLogs && !bypassLogs) {
3351
+ if (
3352
+ ResolveIOServer.shouldWriteLogsOffline()
3353
+ ) {
3354
+ ResolveIOServer.getLocalLogManager().writeLog({
3355
+ type: 'log',
3356
+ data: {
3357
+ _id: objectIdHexString(),
3358
+ type: 'document',
3359
+ collection: this.collectionName,
3360
+ id_document: doc._id,
3361
+ payload: getBinarySize(JSON.stringify([doc, filter, replacement, safeOptions])) < 1000000 ? JSON.stringify([doc, filter, replacement, safeOptions], null, 2) : 'Too Big',
3362
+ method: 'replaceOne',
3363
+ id_user: '',
3364
+ user: '',
3365
+ messageId: 0,
3366
+ route: '',
3367
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
3368
+ createdAt: new Date()
3369
+ }
3370
+ });
3371
+ }
3372
+ else {
3373
+ await ResolveIOServer.getMainDB().collection('logs').insertOne(<any>{
3374
+ _id: objectIdHexString(),
3375
+ type: 'document',
3376
+ collection: this.collectionName,
3377
+ id_document: doc._id,
3378
+ payload: getBinarySize(JSON.stringify([doc, filter, replacement, safeOptions])) < 1000000 ? JSON.stringify([doc, filter, replacement, safeOptions], null, 2) : 'Too Big',
3379
+ method: 'replaceOne',
3380
+ id_user: '',
3381
+ user: '',
3382
+ messageId: 0,
3383
+ route: '',
3384
+ client: 'ResolveIO',
3385
+ instance: ResolveIOServer.getInstanceHost(),
3386
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
3387
+ createdAt: new Date()
3388
+ }, {session});
3389
+ }
3390
+ }
3391
+
3392
+ let monitor = MonitorMongo.create('replaceOne', this.collectionName, JSON.stringify([filter, replacement, safeOptions]));
3393
+
3394
+ try {
3395
+ let res = await this.retryWrite(() => ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).replaceOne(<any>filter, replacement, options), options);
3396
+ if (!this.skipCache) {
3397
+ ResolveIOServer.getMongoManager().invalidateQueryCache(this.collectionName);
3398
+ }
3399
+ return res;
3400
+ }
3401
+ catch (err) {
3402
+ console.log(JSON.stringify([new Date(), 'Error Replace One', this.collectionName, filter, replacement, safeOptions, {
3403
+ code: err.code,
3404
+ codeName: err.codeName,
3405
+ message: err.message,
3406
+ stack: err.stack
3407
+ }], null, 2));
3408
+
3409
+ err.message = `Error in Replace One: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
3410
+ throw err;
3411
+ }
3412
+ finally {
3413
+ await monitor.finish();
3414
+ }
3415
+ }
3416
+ else {
3417
+ console.log('invalid version - ' + this.collectionName, doc.__v, replacement.__v);
3418
+
3419
+ let prevDoc = await ResolveIOServer.getMongoManager().collection(this.versionCollection).findOne({
3420
+ _id: <any>{
3421
+ _id: doc._id,
3422
+ __v: replacement.__v
3423
+ }
3424
+ }, null, true);
3425
+
3426
+ if (prevDoc) {
3427
+ let docId = doc._id;
3428
+ let docVersion = doc.__v;
3429
+ let updatedDoc: T = getMongoMergeUpdatedDoc(replacement, doc, prevDoc);
3430
+ updatedDoc._id = docId;
3431
+ updatedDoc.__v = docVersion + 1;
3432
+
3433
+ if (this.createLogs && !bypassLogs) {
3434
+ if (
3435
+ ResolveIOServer.shouldWriteLogsOffline()
3436
+ ) {
3437
+ ResolveIOServer.getLocalLogManager().writeLog({
3438
+ type: 'log',
3439
+ data: {
3440
+ _id: objectIdHexString(),
3441
+ type: 'document',
3442
+ collection: this.collectionName,
3443
+ id_document: docId,
3444
+ payload: getBinarySize(JSON.stringify(['invalidVersion - merge', doc, filter, updatedDoc, safeOptions])) < 1000000 ? JSON.stringify(['invalidVersion - merge', doc, filter, updatedDoc, safeOptions], null, 2) : 'Too Big',
3445
+ method: 'replaceOne',
3446
+ id_user: '',
3447
+ user: '',
3448
+ messageId: 0,
3449
+ route: '',
3450
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
3451
+ createdAt: new Date()
3452
+ }
3453
+ });
3454
+ }
3455
+ else {
3456
+ await ResolveIOServer.getMainDB().collection('logs').insertOne(<any>{
3457
+ _id: objectIdHexString(),
3458
+ type: 'document',
3459
+ collection: this.collectionName,
3460
+ id_document: docId,
3461
+ payload: getBinarySize(JSON.stringify(['invalidVersion - merge', doc, filter, updatedDoc, safeOptions])) < 1000000 ? JSON.stringify(['invalidVersion - merge', doc, filter, updatedDoc, safeOptions], null, 2) : 'Too Big',
3462
+ method: 'replaceOne',
3463
+ id_user: '',
3464
+ user: '',
3465
+ messageId: 0,
3466
+ route: '',
3467
+ client: 'ResolveIO',
3468
+ instance: ResolveIOServer.getInstanceHost(),
3469
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
3470
+ createdAt: new Date()
3471
+ }, {session});
3472
+ }
3473
+ }
3474
+
3475
+ let monitor = MonitorMongo.create('replaceOne', this.collectionName, JSON.stringify([filter, updatedDoc, safeOptions]));
3476
+
3477
+ try {
3478
+ let res = await this.retryWrite(() => ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).replaceOne(<any>filter, updatedDoc, options), options);
3479
+ if (!this.skipCache) {
3480
+ ResolveIOServer.getMongoManager().invalidateQueryCache(this.collectionName);
3481
+ }
3482
+ return res;
3483
+ }
3484
+ catch (err) {
3485
+ console.log(JSON.stringify([new Date(), 'Error Replace One', this.collectionName, filter, replacement, safeOptions, {
3486
+ code: err.code,
3487
+ codeName: err.codeName,
3488
+ message: err.message,
3489
+ stack: err.stack
3490
+ }], null, 2));
3491
+
3492
+ err.message = `Error in Replace One: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
3493
+ throw err;
3494
+ }
3495
+ finally {
3496
+ await monitor.finish();
3497
+ }
3498
+ }
3499
+ else {
3500
+ throw new Error('Error in Replace One: Invalid Version And Could Not Find History - DB: ' + doc.__v + ', Trying to update with :' + replacement.__v);
3501
+ }
3502
+ }
3503
+ }
3504
+ else if (options && options.upsert) {
3505
+ if (this.timestamps) {
3506
+ replacement.createdAt = date;
3507
+ }
3508
+
3509
+ if (!replacement._id) {
3510
+ replacement._id = objectIdHexString();
3511
+ }
3512
+
3513
+ replacement.__v = 0;
3514
+
3515
+ if (this.createLogs && !bypassLogs) {
3516
+ if (
3517
+ ResolveIOServer.shouldWriteLogsOffline()
3518
+ ) {
3519
+ ResolveIOServer.getLocalLogManager().writeLog({
3520
+ type: 'log',
3521
+ data: {
3522
+ _id: objectIdHexString(),
3523
+ type: 'document',
3524
+ collection: this.collectionName,
3525
+ id_document: replacement._id,
3526
+ payload: getBinarySize(JSON.stringify(['upsert', filter, replacement, safeOptions])) < 1000000 ? JSON.stringify(['upsert', filter, replacement, safeOptions], null, 2) : 'Too Big',
3527
+ method: 'replaceOne',
3528
+ id_user: '',
3529
+ user: '',
3530
+ messageId: 0,
3531
+ route: '',
3532
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
3533
+ createdAt: new Date()
3534
+ }
3535
+ });
3536
+ }
3537
+ else {
3538
+ await ResolveIOServer.getMainDB().collection('logs').insertOne(<any>{
3539
+ _id: objectIdHexString(),
3540
+ type: 'document',
3541
+ collection: this.collectionName,
3542
+ id_document: replacement._id,
3543
+ payload: getBinarySize(JSON.stringify(['upsert', filter, replacement, safeOptions])) < 1000000 ? JSON.stringify(['upsert', filter, replacement, safeOptions], null, 2) : 'Too Big',
3544
+ method: 'replaceOne',
3545
+ id_user: '',
3546
+ user: '',
3547
+ messageId: 0,
3548
+ route: '',
3549
+ client: 'ResolveIO',
3550
+ instance: ResolveIOServer.getInstanceHost(),
3551
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
3552
+ createdAt: new Date()
3553
+ }, {session});
3554
+ }
3555
+ }
3556
+
3557
+ let monitor = MonitorMongo.create('replaceOne', this.collectionName, JSON.stringify([filter, replacement, safeOptions]));
3558
+
3559
+ try {
3560
+ let res = await this.retryWrite(() => ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).replaceOne(<any>filter, replacement, options), options);
3561
+ if (!this.skipCache) {
3562
+ ResolveIOServer.getMongoManager().invalidateQueryCache(this.collectionName);
3563
+ }
3564
+ return res;
3565
+ }
3566
+ catch (err) {
3567
+ console.log(JSON.stringify([new Date(), 'Error Replace One', this.collectionName, filter, replacement, safeOptions, {
3568
+ code: err.code,
3569
+ codeName: err.codeName,
3570
+ message: err.message,
3571
+ stack: err.stack
3572
+ }], null, 2));
3573
+
3574
+ err.message = `Error in Replace One: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
3575
+ throw err;
3576
+ }
3577
+ finally {
3578
+ await monitor.finish();
3579
+ }
3580
+ }
3581
+ else {
3582
+ return {
3583
+ acknowledged: false,
3584
+ matchedCount: 0,
3585
+ modifiedCount: 0,
3586
+ upsertedCount: 0,
3587
+ upsertedId: null
3588
+ };
3589
+ }
3590
+ }
3591
+ else {
3592
+ if (options && options.upsert) {
3593
+ if (this.timestamps && !replacement.createdAt) {
3594
+ replacement.createdAt = date;
3595
+ }
3596
+
3597
+ if (!replacement._id) {
3598
+ replacement._id = objectIdHexString();
3599
+ }
3600
+
3601
+ replacement.__v = 0;
3602
+ }
3603
+
3604
+ if (this.createLogs && !bypassLogs) {
3605
+ if (!options) {
3606
+ options = <FindOneAndReplaceOptions>{returnDocument: 'before'};
3607
+ }
3608
+ else {
3609
+ (<FindOneAndReplaceOptions>options).returnDocument = 'before';
3610
+ }
3611
+
3612
+
3613
+ let { session, ...safeOptions } = options || {};
3614
+
3615
+ let monitor = MonitorMongo.create('findOneAndReplace', this.collectionName, JSON.stringify([filter, replacement, safeOptions]));
3616
+
3617
+ try {
3618
+ let res = await this.retryWrite(() => ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).replaceOne(<any>filter, replacement, options), options);
3619
+
3620
+ let doc = await this.findOne(filter, options || undefined);
3621
+
3622
+ if (doc) {
3623
+ if (
3624
+ ResolveIOServer.shouldWriteLogsOffline()
3625
+ ) {
3626
+ ResolveIOServer.getLocalLogManager().writeLog({
3627
+ type: 'log',
3628
+ data: {
3629
+ _id: objectIdHexString(),
3630
+ type: 'document',
3631
+ collection: this.collectionName,
3632
+ id_document: <string>doc._id,
3633
+ payload: getBinarySize(JSON.stringify([doc, filter, replacement, safeOptions])) < 1000000 ? JSON.stringify([doc, filter, replacement, safeOptions], null, 2) : 'Too Big',
3634
+ method: 'replaceOne',
3635
+ id_user: '',
3636
+ user: '',
3637
+ messageId: 0,
3638
+ route: '',
3639
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
3640
+ createdAt: new Date()
3641
+ }
3642
+ });
3643
+ }
3644
+ else {
3645
+ await ResolveIOServer.getMainDB().collection('logs').insertOne(<any>{
3646
+ _id: objectIdHexString(),
3647
+ type: 'document',
3648
+ collection: this.collectionName,
3649
+ id_document: <string>doc._id,
3650
+ payload: getBinarySize(JSON.stringify([doc, filter, replacement, safeOptions])) < 1000000 ? JSON.stringify([doc, filter, replacement, safeOptions], null, 2) : 'Too Big',
3651
+ method: 'replaceOne',
3652
+ id_user: '',
3653
+ user: '',
3654
+ messageId: 0,
3655
+ route: '',
3656
+ client: 'ResolveIO',
3657
+ instance: ResolveIOServer.getInstanceHost(),
3658
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
3659
+ createdAt: new Date()
3660
+ }, {session});
3661
+ }
3662
+ }
3663
+ else {
3664
+ if (
3665
+ ResolveIOServer.shouldWriteLogsOffline()
3666
+ ) {
3667
+ ResolveIOServer.getLocalLogManager().writeLog({
3668
+ type: 'log',
3669
+ data: {
3670
+ _id: objectIdHexString(),
3671
+ type: 'document',
3672
+ collection: this.collectionName,
3673
+ id_document: replacement._id,
3674
+ payload: getBinarySize(JSON.stringify(['upsert', filter, replacement, safeOptions])) < 1000000 ? JSON.stringify(['upsert', filter, replacement, safeOptions], null, 2) : 'Too Big',
3675
+ method: 'replaceOne',
3676
+ id_user: '',
3677
+ user: '',
3678
+ messageId: 0,
3679
+ route: '',
3680
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
3681
+ createdAt: new Date()
3682
+ }
3683
+ });
3684
+ }
3685
+ else {
3686
+ await ResolveIOServer.getMainDB().collection('logs').insertOne(<any>{
3687
+ _id: objectIdHexString(),
3688
+ type: 'document',
3689
+ collection: this.collectionName,
3690
+ id_document: replacement._id,
3691
+ payload: getBinarySize(JSON.stringify(['upsert', filter, replacement, safeOptions])) < 1000000 ? JSON.stringify(['upsert', filter, replacement, safeOptions], null, 2) : 'Too Big',
3692
+ method: 'replaceOne',
3693
+ id_user: '',
3694
+ user: '',
3695
+ messageId: 0,
3696
+ route: '',
3697
+ client: 'ResolveIO',
3698
+ instance: ResolveIOServer.getInstanceHost(),
3699
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
3700
+ createdAt: new Date()
3701
+ }, {session});
3702
+ }
3703
+ }
3704
+
3705
+ if (!this.skipCache) {
3706
+ ResolveIOServer.getMongoManager().invalidateQueryCache(this.collectionName);
3707
+ }
3708
+
3709
+ return res;
3710
+ }
3711
+ catch (err) {
3712
+ console.log(JSON.stringify([new Date(), 'Error Replace One (Find One And Replace)', this.collectionName, filter, safeOptions, {
3713
+ code: err.code,
3714
+ codeName: err.codeName,
3715
+ message: err.message,
3716
+ stack: err.stack
3717
+ }], null, 2));
3718
+
3719
+ err.message = `Error in Replace One (Find One And Replace): ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
3720
+ throw err;
3721
+ }
3722
+ finally {
3723
+ await monitor.finish();
3724
+ }
3725
+ }
3726
+ else {
3727
+ let monitor = MonitorMongo.create('replaceOne', this.collectionName, JSON.stringify([filter, replacement, safeOptions]));
3728
+
3729
+ try {
3730
+ let res = await this.retryWrite(() => ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).replaceOne(<any>filter, replacement, options), options);
3731
+ if (!this.skipCache) {
3732
+ ResolveIOServer.getMongoManager().invalidateQueryCache(this.collectionName);
3733
+ }
3734
+ return res;
3735
+ }
3736
+ catch (err) {
3737
+ console.log(JSON.stringify([new Date(), 'Error Replace One', this.collectionName, filter, replacement, safeOptions, {
3738
+ code: err.code,
3739
+ codeName: err.codeName,
3740
+ message: err.message,
3741
+ stack: err.stack
3742
+ }], null, 2));
3743
+
3744
+ err.message = `Error in Replace One: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
3745
+ throw err;
3746
+ }
3747
+ finally {
3748
+ await monitor.finish();
3749
+ }
3750
+ }
3751
+ }
3752
+ }
3753
+
3754
+ async updateMany(filter: MongoManagerFilter<T> & MongoManagerFilterOperators<T>, update: MongoManagerUpdateFilter<T>, options?: UpdateOptions, bypassLogs = false, bypassCheckSchema = false, bypassVersions = false, bypassSession = false) : Promise<UpdateResult> {
3755
+ const isEmptyUpdate = !update.$set && !update.$unset && !update.$inc && !update.$setOnInsert && !update.$push && !update.$pull && !update.$addToSet && !update.$min && !update.$max && !update.$currentDate && !update.$mul && !update.$rename;
3756
+
3757
+ const allEmpty =
3758
+ isEmptyUpdate ||
3759
+ Object.values(update).every(op => {
3760
+ if (!op) {
3761
+ return true;
3762
+ }
3763
+
3764
+ for (let key in op) {
3765
+ if (Object.prototype.hasOwnProperty.call(op, key)) {
3766
+ return false;
3767
+ }
3768
+ }
3769
+
3770
+ return true;
3771
+ });
3772
+
3773
+ if (allEmpty) {
3774
+ return {
3775
+ acknowledged: false,
3776
+ matchedCount: 0,
3777
+ modifiedCount: 0,
3778
+ upsertedCount: 0,
3779
+ upsertedId: null
3780
+ };
3781
+ }
3782
+
3783
+ if (this.timestamps) {
3784
+ let date = new Date();
3785
+
3786
+ if (!update.$set) {
3787
+ update.$set = <any>{updatedAt: date};
3788
+ }
3789
+ else {
3790
+ update.$set.updatedAt = date;
3791
+ }
3792
+ }
3793
+
3794
+ if (this.checkSchema && !bypassCheckSchema) {
3795
+ const validation = this.simplschema.newContext();
3796
+ const isValid = validation.validate(update, {modifier: true});
3797
+
3798
+ if (!isValid) {
3799
+ console.log(new Date(), this.collectionName, 'Schema Errors - updateMany', validation.validationErrors());
3800
+ await ResolveIOServer.getMainServer().getMethodManager().callMethod('insertErrorLog', 'Schema Failed on updateMany - ' + this.collectionName, JSON.stringify([validation.validationErrors(), update], null, 2));
3801
+ throw new Error(JSON.stringify(validation.validationErrors(), null, 2));
3802
+ }
3803
+ }
3804
+
3805
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
3806
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
3807
+ if (!options) {
3808
+ options = {session: mongoSession};
3809
+ }
3810
+ else {
3811
+ options.session = mongoSession;
3812
+ }
3813
+ }
3814
+
3815
+
3816
+ let { session, ...safeOptions } = options || {};
3817
+
3818
+ if ((this.useVersions && !bypassVersions) || (this.createLogs && !bypassLogs)) {
3819
+ let docs = await this.find(filter, {session});
3820
+
3821
+ for (let i = 0; i < docs.length; i++) {
3822
+ let doc = docs[i];
3823
+
3824
+ if (this.createLogs && !bypassLogs) {
3825
+ if (
3826
+ ResolveIOServer.shouldWriteLogsOffline()
3827
+ ) {
3828
+ ResolveIOServer.getLocalLogManager().writeLog({
3829
+ type: 'log',
3830
+ data: {
3831
+ _id: objectIdHexString(),
3832
+ type: 'document',
3833
+ collection: this.collectionName,
3834
+ id_document: doc._id,
3835
+ payload: getBinarySize(JSON.stringify([doc, filter, update, safeOptions])) < 1000000 ? JSON.stringify([doc, filter, update, safeOptions], null, 2) : 'Too Big',
3836
+ method: 'updateMany',
3837
+ id_user: '',
3838
+ user: '',
3839
+ messageId: 0,
3840
+ route: '',
3841
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
3842
+ createdAt: new Date()
3843
+ }
3844
+ });
3845
+ }
3846
+ else {
3847
+ await ResolveIOServer.getMainDB().collection('logs').insertOne(<any>{
3848
+ _id: objectIdHexString(),
3849
+ type: 'document',
3850
+ collection: this.collectionName,
3851
+ id_document: doc._id,
3852
+ payload: getBinarySize(JSON.stringify([doc, filter, update, safeOptions])) < 1000000 ? JSON.stringify([doc, filter, update, safeOptions], null, 2) : 'Too Big',
3853
+ method: 'updateMany',
3854
+ id_user: '',
3855
+ user: '',
3856
+ messageId: 0,
3857
+ route: '',
3858
+ client: 'ResolveIO',
3859
+ instance: ResolveIOServer.getInstanceHost(),
3860
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
3861
+ createdAt: new Date()
3862
+ }, {session});
3863
+ }
3864
+ }
3865
+
3866
+ if (this.useVersions) {
3867
+ try {
3868
+ let versionDoc = deepCopy(doc);
3869
+ versionDoc._id = {_id: doc._id, __v: doc.__v};
3870
+
3871
+ await ResolveIOServer.getMongoManager().collection(this.versionCollection).replaceOne(<any>{_id: { _id: doc._id, __v: doc.__v}}, versionDoc, {upsert: true});
3872
+
3873
+ if (doc.__v >= 4) {
3874
+ await ResolveIOServer.getMongoManager().collection(this.versionCollection).deleteMany(<any>{$and: [{'_id._id': doc._id}, {'_id.__v': {$lt: doc.__v - 4}}]});
3875
+ }
3876
+ }
3877
+ catch {}
3878
+
3879
+ if (!update.$inc) {
3880
+ update.$inc = {};
3881
+ }
3882
+
3883
+ if (typeof update.$inc.__v !== 'number') {
3884
+ update.$inc.__v = 1;
3885
+ }
3886
+
3887
+ // If someone mistakenly put __v in $set, remove it
3888
+ if (update.$set && '__v' in update.$set) {
3889
+ delete update.$set.__v;
3890
+ if (Object.keys(update.$set).length === 0) {
3891
+ delete update.$set;
3892
+ }
3893
+ }
3894
+ }
3895
+ }
3896
+ }
3897
+
3898
+ let monitor = MonitorMongo.create('updateMany', this.collectionName, JSON.stringify([filter, update, safeOptions]));
3899
+
3900
+ try {
3901
+ let res = await this.retryWrite<UpdateResult>(() => ResolveIOServer.getMainDB().collection(this.collectionName, this.collectionOptions).updateMany(<any>filter, <any>update, options), options);
3902
+ if (!this.skipCache) {
3903
+ ResolveIOServer.getMongoManager().invalidateQueryCache(this.collectionName);
3904
+ }
3905
+
3906
+ return res;
3907
+ }
3908
+ catch (err) {
3909
+ console.log(JSON.stringify([new Date(), 'Error Update Many', this.collectionName, filter, update, safeOptions, {
3910
+ code: err.code,
3911
+ codeName: err.codeName,
3912
+ message: err.message,
3913
+ stack: err.stack
3914
+ }], null, 2));
3915
+
3916
+ err.message = `Error in Update Many: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
3917
+ throw err;
3918
+ }
3919
+ finally {
3920
+ await monitor.finish();
3921
+ }
3922
+ }
3923
+
3924
+ async updateOne(filter: MongoManagerFilter<T> & MongoManagerFilterOperators<T>, update: MongoManagerUpdateFilter<T>, options?: UpdateOptions, bypassLogs = false, bypassCheckSchema = false, bypassSession = false) : Promise<UpdateResult> {
3925
+ if (this.checkSchema && !bypassCheckSchema) {
3926
+ const validation = this.simplschema.newContext();
3927
+ const isValid = validation.validate(update, {modifier: true});
3928
+
3929
+ if (!isValid) {
3930
+ console.log(new Date(), this.collectionName, 'Schema Errors - updateOne', validation.validationErrors());
3931
+ await ResolveIOServer.getMainServer().getMethodManager().callMethod('insertErrorLog', 'Schema Failed on updateOne - ' + this.collectionName, JSON.stringify([validation.validationErrors(), update], null, 2));
3932
+ throw new Error(JSON.stringify(validation.validationErrors(), null, 2));
3933
+ }
3934
+ }
3935
+
3936
+ const isEmptyUpdate = !update.$set && !update.$unset && !update.$inc && !update.$setOnInsert && !update.$push && !update.$pull && !update.$addToSet && !update.$min && !update.$max && !update.$currentDate && !update.$mul && !update.$rename;
3937
+
3938
+ const allEmpty =
3939
+ isEmptyUpdate ||
3940
+ Object.values(update).every(op => {
3941
+ if (!op) {
3942
+ return true;
3943
+ }
3944
+
3945
+ for (let key in op) {
3946
+ if (Object.prototype.hasOwnProperty.call(op, key)) {
3947
+ return false;
3948
+ }
3949
+ }
3950
+
3951
+ return true;
3952
+ });
3953
+
3954
+ if (allEmpty) {
3955
+ return {
3956
+ acknowledged: false,
3957
+ matchedCount: 0,
3958
+ modifiedCount: 0,
3959
+ upsertedCount: 0,
3960
+ upsertedId: null
3961
+ };
3962
+ }
3963
+
3964
+
3965
+ let date = new Date();
3966
+
3967
+ if (this.timestamps) {
3968
+ if (!update.$set) {
3969
+ update.$set = <any>{updatedAt: date};
3970
+ }
3971
+ else {
3972
+ update.$set.updatedAt = date;
3973
+ }
3974
+ }
3975
+
3976
+ const mongoSession = ResolveIOServer.getMongoManager().getSession();
3977
+
3978
+ if (mongoSession && !bypassSession && !this.bypassSession && (!options || ((!options.readPreference || options.readPreference === 'primary') && !options.session))) {
3979
+ if (!options) {
3980
+ options = {session: mongoSession};
3981
+ }
3982
+ else {
3983
+ options.session = mongoSession;
3984
+ }
3985
+ // console.log('Update One', 'Has Session', this.collectionName, JSON.stringify(filter, null, 2), JSON.stringify(update, null, 2));
3986
+ }
3987
+ else {
3988
+ // console.log('Update One', 'No Session', this.collectionName, JSON.stringify(filter, null, 2), JSON.stringify(update, null, 2));
3989
+ }
3990
+
3991
+ let { session, ...safeOptions } = options || {};
3992
+
3993
+ if (this.useVersions) {
3994
+ let doc = await this.findOne(filter, {session}, true);
3995
+
3996
+ if (doc) {
3997
+ if (this.createLogs && !bypassLogs) {
3998
+ if (
3999
+ ResolveIOServer.shouldWriteLogsOffline()
4000
+ ) {
4001
+ ResolveIOServer.getLocalLogManager().writeLog({
4002
+ type: 'log',
4003
+ data: {
4004
+ _id: objectIdHexString(),
4005
+ type: 'document',
4006
+ collection: this.collectionName,
4007
+ id_document: doc._id,
4008
+ payload: getBinarySize(JSON.stringify([doc, filter, update, safeOptions])) < 1000000 ? JSON.stringify([doc, filter, update, safeOptions], null, 2) : 'Too Big',
4009
+ method: 'updateOne',
4010
+ id_user: '',
4011
+ user: '',
4012
+ messageId: 0,
4013
+ route: '',
4014
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
4015
+ createdAt: new Date()
4016
+ }
4017
+ });
4018
+ }
4019
+ else {
4020
+ await ResolveIOServer.getMainDB().collection('logs').insertOne(<any>{
4021
+ _id: objectIdHexString(),
4022
+ type: 'document',
4023
+ collection: this.collectionName,
4024
+ id_document: doc._id,
4025
+ payload: getBinarySize(JSON.stringify([doc, filter, update, safeOptions])) < 1000000 ? JSON.stringify([doc, filter, update, safeOptions], null, 2) : 'Too Big',
4026
+ method: 'updateOne',
4027
+ id_user: '',
4028
+ user: '',
4029
+ messageId: 0,
4030
+ route: '',
4031
+ client: 'ResolveIO',
4032
+ instance: ResolveIOServer.getInstanceHost(),
4033
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
4034
+ createdAt: new Date()
4035
+ }, {session});
4036
+ }
4037
+ }
4038
+
4039
+ try {
4040
+ let versionDoc = deepCopy(doc);
4041
+ versionDoc._id = {_id: doc._id, __v: doc.__v};
4042
+
4043
+ await ResolveIOServer.getMongoManager().collection(this.versionCollection).replaceOne(<any>{_id: { _id: doc._id, __v: doc.__v}}, versionDoc, {upsert: true});
4044
+
4045
+ if (doc.__v >= 4) {
4046
+ await ResolveIOServer.getMongoManager().collection(this.versionCollection).deleteMany(<any>{$and: [{'_id._id': doc._id}, {'_id.__v': {$lt: doc.__v - 4}}]});
4047
+ }
4048
+ }
4049
+ catch {}
4050
+
4051
+ if (!update.$inc) {
4052
+ update.$inc = {};
4053
+ }
4054
+
4055
+ if (typeof update.$inc.__v !== 'number') {
4056
+ update.$inc.__v = 1;
4057
+ }
4058
+
4059
+ // If someone mistakenly put __v in $set, remove it
4060
+ if (update.$set && '__v' in update.$set) {
4061
+ delete update.$set.__v;
4062
+ if (Object.keys(update.$set).length === 0) {
4063
+ delete update.$set;
4064
+ }
4065
+ }
4066
+
4067
+ let monitor = MonitorMongo.create('updateOne', this.collectionName, JSON.stringify([filter, update, safeOptions]));
4068
+
4069
+ try {
4070
+ let res = await this.retryWrite(() => ResolveIOServer.getMainDB().collection(this.collectionName, this.collectionOptions).updateOne(<any>filter, <any>update, options || undefined), options);
4071
+ if (!this.skipCache) {
4072
+ ResolveIOServer.getMongoManager().invalidateQueryCache(this.collectionName);
4073
+ }
4074
+ return res;
4075
+ }
4076
+ catch (err) {
4077
+ console.log(JSON.stringify([new Date(), 'Error Update One', this.collectionName, filter, update, safeOptions, {
4078
+ code: err.code,
4079
+ codeName: err.codeName,
4080
+ message: err.message,
4081
+ stack: err.stack
4082
+ }], null, 2));
4083
+
4084
+ err.message = `Error in Update One: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
4085
+ throw err;
4086
+ }
4087
+ finally {
4088
+ await monitor.finish();
4089
+ }
4090
+ }
4091
+ else if (options && options.upsert) {
4092
+ if (!update.$setOnInsert) {
4093
+ if (this.timestamps) {
4094
+ update.$setOnInsert = <any>{
4095
+ _id: objectIdHexString(),
4096
+ __v: 0,
4097
+ createdAt: new Date()
4098
+ };
4099
+ }
4100
+ else {
4101
+ update.$setOnInsert = <any>{
4102
+ _id: objectIdHexString(),
4103
+ __v: 0
4104
+ };
4105
+ }
4106
+ }
4107
+ else {
4108
+ if (!update.$setOnInsert._id) {
4109
+ update.$setOnInsert._id = objectIdHexString();
4110
+ }
4111
+
4112
+ if (this.timestamps && !update.$setOnInsert.createdAt) {
4113
+ update.$setOnInsert.createdAt = new Date();
4114
+ }
4115
+ }
4116
+
4117
+ if (this.createLogs && !bypassLogs) {
4118
+ if (
4119
+ ResolveIOServer.shouldWriteLogsOffline()
4120
+ ) {
4121
+ ResolveIOServer.getLocalLogManager().writeLog({
4122
+ type: 'log',
4123
+ data: {
4124
+ _id: objectIdHexString(),
4125
+ type: 'document',
4126
+ collection: this.collectionName,
4127
+ id_document: update.$setOnInsert._id,
4128
+ payload: getBinarySize(JSON.stringify(['upsert', filter, update, safeOptions])) < 1000000 ? JSON.stringify(['upsert', filter, update, safeOptions], null, 2) : 'Too Big',
4129
+ method: 'updateOne',
4130
+ id_user: '',
4131
+ user: '',
4132
+ messageId: 0,
4133
+ route: '',
4134
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
4135
+ createdAt: new Date()
4136
+ }
4137
+ });
4138
+ }
4139
+ else {
4140
+ await ResolveIOServer.getMainDB().collection('logs').insertOne(<any>{
4141
+ _id: objectIdHexString(),
4142
+ type: 'document',
4143
+ collection: this.collectionName,
4144
+ id_document: update.$setOnInsert._id,
4145
+ payload: getBinarySize(JSON.stringify(['upsert', filter, update, safeOptions])) < 1000000 ? JSON.stringify(['upsert', filter, update, safeOptions], null, 2) : 'Too Big',
4146
+ method: 'updateOne',
4147
+ id_user: '',
4148
+ user: '',
4149
+ messageId: 0,
4150
+ route: '',
4151
+ client: 'ResolveIO',
4152
+ instance: ResolveIOServer.getInstanceHost(),
4153
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
4154
+ createdAt: new Date()
4155
+ }, {session});
4156
+ }
4157
+ }
4158
+
4159
+ let monitor = MonitorMongo.create('updateOne', this.collectionName, JSON.stringify([filter, update, safeOptions]));
4160
+
4161
+ try {
4162
+ let res = await this.retryWrite(() => ResolveIOServer.getMainDB().collection(this.collectionName, this.collectionOptions).updateOne(<any>filter, <any>update, options || undefined), options);
4163
+ if (!this.skipCache) {
4164
+ ResolveIOServer.getMongoManager().invalidateQueryCache(this.collectionName);
4165
+ }
4166
+ return res;
4167
+ }
4168
+ catch (err) {
4169
+ console.log(JSON.stringify([new Date(), 'Error Update One', this.collectionName, filter, safeOptions, {
4170
+ code: err.code,
4171
+ codeName: err.codeName,
4172
+ message: err.message,
4173
+ stack: err.stack
4174
+ }], null, 2));
4175
+
4176
+ err.message = `Error in Update One: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
4177
+ throw err;
4178
+ }
4179
+ finally {
4180
+ await monitor.finish();
4181
+ }
4182
+ }
4183
+ else {
4184
+ return {
4185
+ acknowledged: false,
4186
+ matchedCount: 0,
4187
+ modifiedCount: 0,
4188
+ upsertedCount: 0,
4189
+ upsertedId: null
4190
+ };
4191
+ }
4192
+ }
4193
+ else {
4194
+ if (options && options.upsert) {
4195
+ if (!update.$setOnInsert) {
4196
+ if (this.timestamps) {
4197
+ update.$setOnInsert = <any>{
4198
+ _id: objectIdHexString(),
4199
+ createdAt: new Date()
4200
+ };
4201
+ }
4202
+ else {
4203
+ update.$setOnInsert = <any>{
4204
+ _id: objectIdHexString()
4205
+ };
4206
+ }
4207
+ }
4208
+ else {
4209
+ if (!update.$setOnInsert._id) {
4210
+ update.$setOnInsert._id = objectIdHexString();
4211
+ }
4212
+
4213
+ if (this.timestamps && !update.$setOnInsert.createdAt) {
4214
+ update.$setOnInsert.createdAt = new Date();
4215
+ }
4216
+ }
4217
+ }
4218
+
4219
+ if (this.createLogs && !bypassLogs) {
4220
+ if (!options) {
4221
+ options = <FindOneAndUpdateOptions>{returnDocument: 'before'};
4222
+ }
4223
+ else {
4224
+ (<FindOneAndUpdateOptions>options).returnDocument = 'before';
4225
+ }
4226
+
4227
+ let monitor = MonitorMongo.create('findOneAndUpdate', this.collectionName, JSON.stringify([filter, update, safeOptions]));
4228
+
4229
+ try {
4230
+ let res = await this.retryWrite(() => ResolveIOServer.getMainDB().collection(this.collectionName, this.collectionOptions).updateOne(<any>filter, <any>update, options || undefined), options);
4231
+
4232
+ let doc = await this.findOne(filter, options || undefined);
4233
+
4234
+ if (doc) {
4235
+ if (
4236
+ ResolveIOServer.shouldWriteLogsOffline()
4237
+ ) {
4238
+ ResolveIOServer.getLocalLogManager().writeLog({
4239
+ type: 'log',
4240
+ data: {
4241
+ _id: objectIdHexString(),
4242
+ type: 'document',
4243
+ collection: this.collectionName,
4244
+ id_document: <any>doc._id,
4245
+ payload: getBinarySize(JSON.stringify([doc, filter, update, safeOptions])) < 1000000 ? JSON.stringify([doc, filter, update, safeOptions], null, 2) : 'Too Big',
4246
+ method: 'updateOne',
4247
+ id_user: '',
4248
+ user: '',
4249
+ messageId: 0,
4250
+ route: '',
4251
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
4252
+ createdAt: new Date()
4253
+ }
4254
+ });
4255
+ }
4256
+ else {
4257
+ await ResolveIOServer.getMainDB().collection('logs').insertOne(<any>{
4258
+ _id: objectIdHexString(),
4259
+ type: 'document',
4260
+ collection: this.collectionName,
4261
+ id_document: <any>doc._id,
4262
+ payload: getBinarySize(JSON.stringify([doc, filter, update, safeOptions])) < 1000000 ? JSON.stringify([doc, filter, update, safeOptions], null, 2) : 'Too Big',
4263
+ method: 'updateOne',
4264
+ id_user: '',
4265
+ user: '',
4266
+ messageId: 0,
4267
+ route: '',
4268
+ client: 'ResolveIO',
4269
+ instance: ResolveIOServer.getInstanceHost(),
4270
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
4271
+ createdAt: new Date()
4272
+ }, {session});
4273
+ }
4274
+
4275
+ return res;
4276
+ }
4277
+ else if (update.$setOnInsert) {
4278
+ if (
4279
+ ResolveIOServer.shouldWriteLogsOffline()
4280
+ ) {
4281
+ ResolveIOServer.getLocalLogManager().writeLog({
4282
+ type: 'log',
4283
+ data: {
4284
+ _id: objectIdHexString(),
4285
+ type: 'document',
4286
+ collection: this.collectionName,
4287
+ id_document: update.$setOnInsert._id,
4288
+ payload: getBinarySize(JSON.stringify(['upsert', filter, update, safeOptions])) < 1000000 ? JSON.stringify(['upsert', filter, update, safeOptions], null, 2) : 'Too Big',
4289
+ method: 'updateOne',
4290
+ id_user: '',
4291
+ user: '',
4292
+ messageId: 0,
4293
+ route: '',
4294
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
4295
+ createdAt: new Date()
4296
+ }
4297
+ });
4298
+ }
4299
+ else {
4300
+ await ResolveIOServer.getMainDB().collection('logs').insertOne(<any>{
4301
+ _id: objectIdHexString(),
4302
+ type: 'document',
4303
+ collection: this.collectionName,
4304
+ id_document: update.$setOnInsert._id,
4305
+ payload: getBinarySize(JSON.stringify(['upsert', filter, update, safeOptions])) < 1000000 ? JSON.stringify(['upsert', filter, update, safeOptions], null, 2) : 'Too Big',
4306
+ method: 'updateOne',
4307
+ id_user: '',
4308
+ user: '',
4309
+ messageId: 0,
4310
+ route: '',
4311
+ client: 'ResolveIO',
4312
+ instance: ResolveIOServer.getInstanceHost(),
4313
+ instance_index: process.env.NODE_APP_INSTANCE || '0',
4314
+ createdAt: new Date()
4315
+ });
4316
+ }
4317
+
4318
+ if (!this.skipCache) {
4319
+ ResolveIOServer.getMongoManager().invalidateQueryCache(this.collectionName);
4320
+ }
4321
+ }
4322
+
4323
+ return res;
4324
+ }
4325
+ catch (err) {
4326
+ console.log(JSON.stringify([new Date(), 'Error Update One (Find One And Update)', this.collectionName, filter, safeOptions, {
4327
+ code: err.code,
4328
+ codeName: err.codeName,
4329
+ message: err.message,
4330
+ stack: err.stack
4331
+ }], null, 2));
4332
+
4333
+ err.message = `Error in Update One (Find One And Update): ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
4334
+ throw err;
4335
+ }
4336
+ finally {
4337
+ await monitor.finish();
4338
+ }
4339
+ }
4340
+ else {
4341
+ let monitor = MonitorMongo.create('updateOne', this.collectionName, JSON.stringify([filter, update, safeOptions]));
4342
+
4343
+ try {
4344
+ let res = await this.retryWrite(() => ResolveIOServer.getMainDB().collection(this.collectionName, this.collectionOptions).updateOne(<any>filter, <any>update, options || undefined), options);
4345
+ if (!this.skipCache) {
4346
+ ResolveIOServer.getMongoManager().invalidateQueryCache(this.collectionName);
4347
+ }
4348
+ return res;
4349
+ }
4350
+ catch (err) {
4351
+ console.log(JSON.stringify([new Date(), 'Error Update One', this.collectionName, filter, update, safeOptions, {
4352
+ code: err.code,
4353
+ codeName: err.codeName,
4354
+ message: err.message,
4355
+ stack: err.stack
4356
+ }], null, 2));
4357
+
4358
+ err.message = `Error in Update One: ${this.collectionName} => ${err.codeName || 'NoCodeName'} => ${err.message}`;
4359
+ throw err;
4360
+ }
4361
+ finally {
4362
+ await monitor.finish();
4363
+ }
4364
+ }
4365
+ }
4366
+ }
4367
+
4368
+ watchCollection(pipeline = [], options?: ChangeStreamOptions, bypassSession = false): ChangeStream<T> {
4369
+ const session = ResolveIOServer.getMongoManager().getSession();
4370
+ if (session && !bypassSession && !this.bypassSession) {
4371
+ if (!options) {
4372
+ options = {session};
4373
+ }
4374
+ else {
4375
+ options.session = session;
4376
+ }
4377
+ }
4378
+
4379
+ let stream = ResolveIOServer.getMainDB().collection<T>(this.collectionName, this.collectionOptions).watch(pipeline, options || undefined);
4380
+ return <ChangeStream<T>>stream;
4381
+ }
4382
+ }
4383
+
4384
+ export class MongoManagerUserCollection<T extends CollectionDocument> extends MongoManagerCollection<T> {
4385
+ static create(options: MongoManagerCollectionOptions) {
4386
+ const mongoManagerCollection = new MongoManagerUserCollection();
4387
+ mongoManagerCollection.initialize(options);
4388
+ return mongoManagerCollection;
4389
+ }
4390
+
4391
+ async authenticate(user: UserModel, password: string): Promise<{data: UserModel, error: string}> {
4392
+ const attemptsInterval = Math.pow(100, Math.log(user.attempts + 1));
4393
+ const calculatedInterval = attemptsInterval < 300000 ? attemptsInterval : 300000;
4394
+
4395
+ if (user.last) {
4396
+ if (Date.now() - user.last.getTime() < calculatedInterval) {
4397
+ user.last = new Date();
4398
+ await Users.updateOne({_id: user._id}, {$set: {last: user.last}});
4399
+ return {
4400
+ data: null,
4401
+ error: 'Attempt Too Soon'
4402
+ };
4403
+ }
4404
+ }
4405
+ else {
4406
+ user.last = new Date();
4407
+ }
4408
+
4409
+ if (user.attempts >= 5) {
4410
+ return {
4411
+ data: null,
4412
+ error: 'Too Many Attempts'
4413
+ };
4414
+ }
4415
+
4416
+ if (!user.salt) {
4417
+ return {
4418
+ data: null,
4419
+ error: 'No Salt Value Stored'
4420
+ };
4421
+ }
4422
+
4423
+ try {
4424
+ let hashBuffer = await pbkdf2Promisified(password, user.salt, {
4425
+ iterations: 25000,
4426
+ keylen: 512,
4427
+ digestAlgorithm: 'sha256'
4428
+ });
4429
+
4430
+ if (scmp(hashBuffer, Buffer.from(user.hash, 'hex'))) {
4431
+ user.last = new Date();
4432
+ user.attempts = 0;
4433
+ await Users.updateOne({_id: user._id}, {$set: {last: user.last, attempts: user.attempts}});
4434
+
4435
+ return {
4436
+ data: user,
4437
+ error: ''
4438
+ };
4439
+ }
4440
+ else {
4441
+ user.last = new Date();
4442
+ user.attempts = user.attempts + 1;
4443
+ await Users.updateOne({_id: user._id}, {$set: {last: user.last, attempts: user.attempts}});
4444
+
4445
+ if (user.attempts >= 5) {
4446
+ return {
4447
+ data: null,
4448
+ error: 'Too Many Attempts'
4449
+ };
4450
+ }
4451
+ else {
4452
+ return {
4453
+ data: null,
4454
+ error: 'Invalid Username And Password'
4455
+ };
4456
+ }
4457
+ }
4458
+ }
4459
+ catch (err) {
4460
+ err.message = `Error in User Authenticate: ${JSON.stringify(user, null, 2)} - ${err.message}`;
4461
+ throw err;
4462
+ }
4463
+ }
4464
+
4465
+ serializeUser() {
4466
+ return (user, cb) => {
4467
+ cb(null, user.username);
4468
+ };
4469
+ }
4470
+
4471
+ deserializeUser() {
4472
+ return async (username) => {
4473
+ return Users.findOne({username: username});
4474
+ };
4475
+ }
4476
+
4477
+ async setPassword(user: UserModel, password: string) {
4478
+ if (!user) {
4479
+ throw new Error('Error In User Set Password: No User');
4480
+ }
4481
+ else if (!password) {
4482
+ throw new Error('Error In User Set Password: No Password');
4483
+ }
4484
+ else {
4485
+ let saltBuffer = await randomBytes(32);
4486
+ let salt = saltBuffer.toString('hex');
4487
+ try {
4488
+ let hashRaw = await pbkdf2Promisified(password, salt, {
4489
+ iterations: 25000,
4490
+ keylen: 512,
4491
+ digestAlgorithm: 'sha256'
4492
+ });
4493
+ let hash = Buffer.from(hashRaw, 'binary').toString('hex');
4494
+
4495
+ const res = await Users.updateOne({_id: user._id}, {$set: {hash: hash, salt: salt, services: {}, attempts: 0}});
4496
+ await saveCustomerPortalPassword(user, password);
4497
+ return res;
4498
+ }
4499
+ catch (err) {
4500
+ err.message = `Error in User Set Password: ${JSON.stringify(user, null, 2)} - ${err.message}`;
4501
+ throw err;
4502
+ }
4503
+ }
4504
+ };
4505
+
4506
+ async changePassword(user, oldPassword, newPassword) {
4507
+ if (!user) {
4508
+ throw new Error('Error in User Change Password: Missing User');
4509
+ }
4510
+ else if (!oldPassword || !newPassword) {
4511
+ throw new Error('Error in User Change Password: Missing Password');
4512
+ }
4513
+ else {
4514
+ let authUser = await this.authenticate(user, oldPassword);
4515
+
4516
+ if (!authUser['data']) {
4517
+ throw authUser['error'];
4518
+ }
4519
+ else {
4520
+ return this.setPassword(user, newPassword);
4521
+ }
4522
+ }
4523
+ }
4524
+
4525
+ async register(user, password) {
4526
+ if (!user.username) {
4527
+ throw new Error('Error in User Register: Missing Username');
4528
+ }
4529
+ else {
4530
+ let dbUser = await Users.findOne({username: user.username});
4531
+
4532
+ if (dbUser) {
4533
+ throw new Error('Error in User Register: Username Exists');
4534
+ }
4535
+ else {
4536
+ dbUser = await Users.findOne({email: user.email});
4537
+
4538
+ if (dbUser) {
4539
+ throw new Error('Error in User Register: Email Exists');
4540
+ }
4541
+ else {
4542
+ user.setPassword(password);
4543
+ return user;
4544
+ }
4545
+ }
4546
+ }
4547
+ }
4548
+
4549
+ resetAttempts(user: UserModel) {
4550
+ return Users.updateOne({_id: user._id}, {$set: {attempts: 0}});
4551
+ }
4552
+ }
4553
+
4554
+ function pbkdf2(password, salt, options, callback) {
4555
+ crypto.pbkdf2(password, salt, options.iterations, options.keylen, options.digestAlgorithm, callback);
4556
+ };
4557
+
4558
+ function pbkdf2Promisified(password, salt, options): Promise<any> {
4559
+ // eslint-disable-next-line no-restricted-syntax
4560
+ return new Promise((resolve, reject) => pbkdf2(password, salt, options, (err, hashRaw) => (err ? reject(err) : resolve(hashRaw))));
4561
+ }
4562
+
4563
+ function randomBytes(saltlen): Promise<Buffer> {
4564
+ // eslint-disable-next-line no-restricted-syntax
4565
+ return new Promise((resolve, reject) => crypto.randomBytes(saltlen, (err, saltBuffer) => (err ? reject(err) : resolve(saltBuffer))));
4566
+ }