@budibase/server 1.0.49 → 1.0.50-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (381) hide show
  1. package/__mocks__/arangojs.ts +1 -1
  2. package/__mocks__/aws-sdk.ts +4 -4
  3. package/__mocks__/mongodb.ts +4 -5
  4. package/__mocks__/mssql.ts +3 -3
  5. package/__mocks__/node-fetch.ts +2 -1
  6. package/__mocks__/pg.ts +3 -5
  7. package/builder/assets/index.c0cc369d.css +6 -0
  8. package/builder/assets/{index.2c37968e.js → index.ff2bad21.js} +252 -249
  9. package/builder/assets/{vendor.f9faf6ab.js → vendor.5ad9695a.js} +202 -106
  10. package/builder/index.html +3 -3
  11. package/coverage/clover.xml +3105 -2814
  12. package/coverage/coverage-final.json +106 -101
  13. package/coverage/lcov-report/api/controllers/analytics.js.html +1 -1
  14. package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
  15. package/coverage/lcov-report/api/controllers/application.js.html +257 -152
  16. package/coverage/lcov-report/api/controllers/auth.js.html +22 -13
  17. package/coverage/lcov-report/api/controllers/automation.js.html +39 -69
  18. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  19. package/coverage/lcov-report/api/controllers/cloud.js.html +8 -11
  20. package/coverage/lcov-report/api/controllers/component.js.html +13 -16
  21. package/coverage/lcov-report/api/controllers/datasource.js.html +81 -51
  22. package/coverage/lcov-report/api/controllers/dev.js.html +6 -6
  23. package/coverage/lcov-report/api/controllers/index.html +78 -78
  24. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  25. package/coverage/lcov-report/api/controllers/layout.js.html +9 -9
  26. package/coverage/lcov-report/api/controllers/metadata.js.html +6 -21
  27. package/coverage/lcov-report/api/controllers/migrations.js.html +119 -0
  28. package/coverage/lcov-report/api/controllers/permission.js.html +19 -19
  29. package/coverage/lcov-report/api/controllers/query/index.html +10 -10
  30. package/coverage/lcov-report/api/controllers/query/index.js.html +38 -35
  31. package/coverage/lcov-report/api/controllers/query/validation.js.html +6 -6
  32. package/coverage/lcov-report/api/controllers/role.js.html +16 -16
  33. package/coverage/lcov-report/api/controllers/routing.js.html +7 -10
  34. package/coverage/lcov-report/api/controllers/row/external.js.html +48 -141
  35. package/coverage/lcov-report/api/controllers/row/index.html +55 -40
  36. package/coverage/lcov-report/api/controllers/row/index.js.html +26 -26
  37. package/coverage/lcov-report/api/controllers/row/internal.js.html +118 -226
  38. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +52 -34
  39. package/coverage/lcov-report/api/controllers/row/staticFormula.js.html +548 -0
  40. package/coverage/lcov-report/api/controllers/row/utils.js.html +47 -62
  41. package/coverage/lcov-report/api/controllers/screen.js.html +15 -21
  42. package/coverage/lcov-report/api/controllers/script.js.html +4 -4
  43. package/coverage/lcov-report/api/controllers/table/bulkFormula.js.html +599 -0
  44. package/coverage/lcov-report/api/controllers/table/external.js.html +25 -34
  45. package/coverage/lcov-report/api/controllers/table/index.html +58 -43
  46. package/coverage/lcov-report/api/controllers/table/index.js.html +58 -82
  47. package/coverage/lcov-report/api/controllers/table/internal.js.html +81 -66
  48. package/coverage/lcov-report/api/controllers/table/utils.js.html +218 -110
  49. package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
  50. package/coverage/lcov-report/api/controllers/user.js.html +79 -76
  51. package/coverage/lcov-report/api/controllers/view/exporters.js.html +4 -4
  52. package/coverage/lcov-report/api/controllers/view/index.html +16 -16
  53. package/coverage/lcov-report/api/controllers/view/index.js.html +27 -33
  54. package/coverage/lcov-report/api/controllers/view/utils.js.html +35 -20
  55. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +7 -7
  56. package/coverage/lcov-report/api/controllers/webhook.js.html +34 -28
  57. package/coverage/lcov-report/api/index.html +1 -1
  58. package/coverage/lcov-report/api/index.js.html +8 -8
  59. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/application.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/dev.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/index.html +18 -18
  71. package/coverage/lcov-report/api/routes/index.js.html +3 -3
  72. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  75. package/coverage/lcov-report/api/routes/{hosting.js.html → migrations.js.html} +17 -14
  76. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  77. package/coverage/lcov-report/api/routes/query.js.html +1 -1
  78. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  79. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  80. package/coverage/lcov-report/api/routes/row.js.html +1 -1
  81. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  82. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  83. package/coverage/lcov-report/api/routes/static.js.html +17 -2
  84. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  85. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  86. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  87. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  88. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  89. package/coverage/lcov-report/automations/actions.js.html +24 -24
  90. package/coverage/lcov-report/automations/automationUtils.js.html +7 -10
  91. package/coverage/lcov-report/automations/bullboard.js.html +16 -16
  92. package/coverage/lcov-report/automations/index.html +18 -18
  93. package/coverage/lcov-report/automations/index.js.html +1 -1
  94. package/coverage/lcov-report/automations/steps/bash.js.html +6 -6
  95. package/coverage/lcov-report/automations/steps/createRow.js.html +13 -22
  96. package/coverage/lcov-report/automations/steps/delay.js.html +4 -4
  97. package/coverage/lcov-report/automations/steps/deleteRow.js.html +11 -20
  98. package/coverage/lcov-report/automations/steps/discord.js.html +7 -7
  99. package/coverage/lcov-report/automations/steps/executeQuery.js.html +6 -6
  100. package/coverage/lcov-report/automations/steps/executeScript.js.html +6 -6
  101. package/coverage/lcov-report/automations/steps/filter.js.html +7 -7
  102. package/coverage/lcov-report/automations/steps/index.html +13 -13
  103. package/coverage/lcov-report/automations/steps/integromat.js.html +5 -5
  104. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +8 -8
  105. package/coverage/lcov-report/automations/steps/queryRows.js.html +10 -10
  106. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +5 -5
  107. package/coverage/lcov-report/automations/steps/serverLog.js.html +3 -3
  108. package/coverage/lcov-report/automations/steps/slack.js.html +5 -5
  109. package/coverage/lcov-report/automations/steps/updateRow.js.html +7 -7
  110. package/coverage/lcov-report/automations/steps/utils.js.html +3 -3
  111. package/coverage/lcov-report/automations/steps/zapier.js.html +5 -5
  112. package/coverage/lcov-report/automations/triggerInfo/app.js.html +2 -2
  113. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +2 -2
  114. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  115. package/coverage/lcov-report/automations/triggerInfo/index.js.html +8 -8
  116. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +2 -2
  117. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +2 -2
  118. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +2 -2
  119. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +2 -2
  120. package/coverage/lcov-report/automations/triggers.js.html +61 -55
  121. package/coverage/lcov-report/automations/utils.js.html +54 -42
  122. package/coverage/lcov-report/constants/index.html +5 -5
  123. package/coverage/lcov-report/constants/index.js.html +53 -26
  124. package/coverage/lcov-report/constants/layouts.js.html +5 -5
  125. package/coverage/lcov-report/constants/screens.js.html +4 -4
  126. package/coverage/lcov-report/db/client.js.html +18 -18
  127. package/coverage/lcov-report/db/inMemoryView.js.html +7 -7
  128. package/coverage/lcov-report/db/index.html +15 -15
  129. package/coverage/lcov-report/db/index.js.html +3 -3
  130. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +25 -37
  131. package/coverage/lcov-report/db/linkedRows/index.html +27 -27
  132. package/coverage/lcov-report/db/linkedRows/index.js.html +51 -96
  133. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +42 -42
  134. package/coverage/lcov-report/db/newid.js.html +4 -4
  135. package/coverage/lcov-report/db/utils.js.html +87 -87
  136. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  137. package/coverage/lcov-report/events/index.html +1 -1
  138. package/coverage/lcov-report/events/index.js.html +5 -5
  139. package/coverage/lcov-report/events/utils.js.html +3 -3
  140. package/coverage/lcov-report/index.html +135 -120
  141. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  142. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  143. package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
  144. package/coverage/lcov-report/middleware/authorized.js.html +173 -65
  145. package/coverage/lcov-report/middleware/builder.js.html +10 -10
  146. package/coverage/lcov-report/middleware/currentapp.js.html +95 -83
  147. package/coverage/lcov-report/middleware/index.html +38 -38
  148. package/coverage/lcov-report/middleware/joi-validator.js.html +3 -3
  149. package/coverage/lcov-report/middleware/resourceId.js.html +1 -1
  150. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  151. package/coverage/lcov-report/middleware/usageQuota.js.html +281 -53
  152. package/coverage/lcov-report/middleware/utils.js.html +2 -2
  153. package/coverage/lcov-report/threads/automation.js.html +30 -24
  154. package/coverage/lcov-report/threads/index.html +26 -26
  155. package/coverage/lcov-report/threads/index.js.html +10 -10
  156. package/coverage/lcov-report/threads/query.js.html +39 -24
  157. package/coverage/lcov-report/threads/utils.js.html +14 -14
  158. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  159. package/coverage/lcov-report/utilities/budibaseDir.js.html +7 -7
  160. package/coverage/lcov-report/utilities/centralPath.js.html +4 -4
  161. package/coverage/lcov-report/utilities/csvParser.js.html +6 -6
  162. package/coverage/lcov-report/utilities/global.js.html +58 -49
  163. package/coverage/lcov-report/utilities/index.html +37 -37
  164. package/coverage/lcov-report/utilities/index.js.html +43 -73
  165. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +8 -8
  166. package/coverage/lcov-report/utilities/queue/index.html +1 -1
  167. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  168. package/coverage/lcov-report/utilities/routing/index.js.html +7 -7
  169. package/coverage/lcov-report/utilities/rowProcessor/index.html +27 -27
  170. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +97 -103
  171. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +75 -27
  172. package/coverage/lcov-report/utilities/scriptRunner.js.html +5 -5
  173. package/coverage/lcov-report/utilities/security.js.html +8 -8
  174. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  175. package/coverage/lcov-report/utilities/usageQuota/index.html +141 -0
  176. package/coverage/lcov-report/utilities/usageQuota/index.js.html +359 -0
  177. package/coverage/lcov-report/utilities/usageQuota/rows.js.html +305 -0
  178. package/coverage/lcov-report/{api/controllers/hosting.js.html → utilities/usageQuota/usageQuoteReset.js.html} +36 -48
  179. package/coverage/lcov-report/utilities/users.js.html +9 -9
  180. package/coverage/lcov-report/utilities/workerRequests.js.html +43 -112
  181. package/coverage/lcov.info +5010 -4425
  182. package/dist/api/controllers/application.js +90 -60
  183. package/dist/api/controllers/auth.js +8 -5
  184. package/dist/api/controllers/automation.js +14 -19
  185. package/dist/api/controllers/cloud.js +2 -3
  186. package/dist/api/controllers/component.js +3 -4
  187. package/dist/api/controllers/datasource.js +20 -10
  188. package/dist/api/controllers/deploy/Deployment.js +3 -6
  189. package/dist/api/controllers/deploy/index.js +12 -13
  190. package/dist/api/controllers/dev.js +5 -5
  191. package/dist/api/controllers/layout.js +3 -3
  192. package/dist/api/controllers/metadata.js +4 -4
  193. package/dist/api/controllers/migrations.js +21 -0
  194. package/dist/api/controllers/permission.js +4 -4
  195. package/dist/api/controllers/query/import/index.js +4 -6
  196. package/dist/api/controllers/query/index.js +11 -10
  197. package/dist/api/controllers/role.js +5 -5
  198. package/dist/api/controllers/routing.js +5 -6
  199. package/dist/api/controllers/row/ExternalRequest.js +90 -67
  200. package/dist/api/controllers/row/external.js +14 -22
  201. package/dist/api/controllers/row/internal.js +55 -93
  202. package/dist/api/controllers/row/internalSearch.js +23 -17
  203. package/dist/api/controllers/row/staticFormula.js +147 -0
  204. package/dist/api/controllers/row/utils.js +13 -14
  205. package/dist/api/controllers/screen.js +5 -7
  206. package/dist/api/controllers/static/index.js +62 -16
  207. package/dist/api/controllers/table/bulkFormula.js +159 -0
  208. package/dist/api/controllers/table/external.js +7 -10
  209. package/dist/api/controllers/table/index.js +16 -13
  210. package/dist/api/controllers/table/internal.js +22 -16
  211. package/dist/api/controllers/table/utils.js +62 -35
  212. package/dist/api/controllers/user.js +43 -43
  213. package/dist/api/controllers/view/index.js +8 -10
  214. package/dist/api/controllers/view/utils.js +9 -4
  215. package/dist/api/controllers/webhook.js +10 -8
  216. package/dist/api/routes/index.js +2 -2
  217. package/dist/api/routes/migrations.js +9 -0
  218. package/dist/api/routes/static.js +2 -1
  219. package/dist/api/routes/tests/utilities/TestFunctions.js +37 -17
  220. package/dist/app.js +35 -5
  221. package/dist/automations/automationUtils.js +2 -3
  222. package/dist/automations/steps/createRow.js +3 -5
  223. package/dist/automations/steps/deleteRow.js +1 -4
  224. package/dist/automations/steps/updateRow.js +1 -1
  225. package/dist/automations/triggers.js +27 -25
  226. package/dist/automations/utils.js +10 -5
  227. package/dist/constants/index.js +7 -0
  228. package/dist/db/linkedRows/LinkController.js +3 -7
  229. package/dist/db/linkedRows/index.js +16 -27
  230. package/dist/db/linkedRows/linkUtils.js +6 -6
  231. package/dist/db/views/staticViews.js +9 -10
  232. package/dist/definitions/datasource.js +1 -0
  233. package/dist/environment.js +4 -1
  234. package/dist/integrations/base/sql.js +62 -21
  235. package/dist/integrations/googlesheets.js +340 -0
  236. package/dist/integrations/index.js +3 -0
  237. package/dist/integrations/utils.js +60 -9
  238. package/dist/middleware/authorized.js +57 -30
  239. package/dist/middleware/builder.js +2 -2
  240. package/dist/middleware/currentapp.js +41 -37
  241. package/dist/middleware/usageQuota.js +82 -15
  242. package/dist/migrations/functions/appUrls.js +46 -0
  243. package/dist/migrations/functions/quotas1.js +50 -0
  244. package/dist/migrations/functions/usageQuotas/index.js +20 -0
  245. package/dist/migrations/functions/usageQuotas/syncApps.js +29 -0
  246. package/dist/migrations/functions/usageQuotas/syncRows.js +33 -0
  247. package/dist/migrations/functions/userEmailViewCasing.js +24 -0
  248. package/dist/migrations/index.js +62 -0
  249. package/dist/package.json +8 -5
  250. package/dist/tests/utilities/TestConfiguration.js +80 -50
  251. package/dist/threads/automation.js +13 -11
  252. package/dist/threads/query.js +15 -11
  253. package/dist/tsconfig.tsbuildinfo +1 -1
  254. package/dist/utilities/fileSystem/index.js +4 -3
  255. package/dist/utilities/global.js +15 -12
  256. package/dist/utilities/index.js +7 -17
  257. package/dist/utilities/routing/index.js +5 -5
  258. package/dist/utilities/rowProcessor/index.js +23 -17
  259. package/dist/utilities/rowProcessor/utils.js +14 -4
  260. package/dist/utilities/{usageQuota.js → usageQuota/index.js} +45 -31
  261. package/dist/utilities/usageQuota/rows.js +78 -0
  262. package/dist/utilities/usageQuota/usageQuoteReset.js +16 -0
  263. package/dist/utilities/users.js +3 -3
  264. package/dist/utilities/workerRequests.js +4 -24
  265. package/nodemon.json +1 -1
  266. package/package.json +9 -6
  267. package/scripts/integrations/mssql/data/Dockerfile +1 -1
  268. package/scripts/integrations/mssql/data/setup.sql +17 -4
  269. package/scripts/integrations/postgres/init.sql +11 -6
  270. package/scripts/likeCypress.ts +1 -1
  271. package/src/api/controllers/application.js +85 -50
  272. package/src/api/controllers/auth.js +8 -5
  273. package/src/api/controllers/automation.js +13 -23
  274. package/src/api/controllers/cloud.js +2 -3
  275. package/src/api/controllers/component.js +3 -4
  276. package/src/api/controllers/datasource.js +21 -11
  277. package/src/api/controllers/deploy/Deployment.js +3 -7
  278. package/src/api/controllers/deploy/index.js +20 -13
  279. package/src/api/controllers/dev.js +5 -5
  280. package/src/api/controllers/layout.js +3 -3
  281. package/src/api/controllers/metadata.js +4 -9
  282. package/src/api/controllers/migrations.js +13 -0
  283. package/src/api/controllers/permission.js +4 -4
  284. package/src/api/controllers/query/import/index.ts +4 -6
  285. package/src/api/controllers/query/import/tests/index.spec.js +2 -1
  286. package/src/api/controllers/query/index.js +11 -10
  287. package/src/api/controllers/role.js +5 -5
  288. package/src/api/controllers/routing.js +5 -6
  289. package/src/api/controllers/row/ExternalRequest.ts +67 -43
  290. package/src/api/controllers/row/external.js +27 -58
  291. package/src/api/controllers/row/internal.js +54 -90
  292. package/src/api/controllers/row/internalSearch.js +23 -17
  293. package/src/api/controllers/row/staticFormula.js +156 -0
  294. package/src/api/controllers/row/utils.js +13 -18
  295. package/src/api/controllers/screen.js +5 -7
  296. package/src/api/controllers/static/index.js +66 -15
  297. package/src/api/controllers/table/bulkFormula.js +173 -0
  298. package/src/api/controllers/table/external.js +7 -10
  299. package/src/api/controllers/table/index.js +20 -28
  300. package/src/api/controllers/table/internal.js +24 -19
  301. package/src/api/controllers/table/utils.js +78 -42
  302. package/src/api/controllers/user.js +46 -45
  303. package/src/api/controllers/view/index.js +8 -10
  304. package/src/api/controllers/view/utils.js +9 -4
  305. package/src/api/controllers/webhook.js +10 -8
  306. package/src/api/routes/index.js +2 -2
  307. package/src/api/routes/migrations.js +14 -0
  308. package/src/api/routes/static.js +5 -0
  309. package/src/api/routes/tests/application.spec.js +2 -2
  310. package/src/api/routes/tests/auth.spec.js +1 -2
  311. package/src/api/routes/tests/automation.spec.js +1 -0
  312. package/src/api/routes/tests/misc.spec.js +0 -1
  313. package/src/api/routes/tests/routing.spec.js +21 -12
  314. package/src/api/routes/tests/row.spec.js +7 -4
  315. package/src/api/routes/tests/static.spec.js +98 -0
  316. package/src/api/routes/tests/utilities/TestFunctions.js +39 -17
  317. package/src/app.ts +18 -5
  318. package/src/automations/automationUtils.js +2 -3
  319. package/src/automations/steps/createRow.js +2 -5
  320. package/src/automations/steps/deleteRow.js +1 -4
  321. package/src/automations/steps/updateRow.js +1 -1
  322. package/src/automations/triggers.js +33 -31
  323. package/src/automations/utils.js +9 -5
  324. package/src/constants/index.js +9 -0
  325. package/src/db/linkedRows/LinkController.js +3 -7
  326. package/src/db/linkedRows/index.js +16 -31
  327. package/src/db/linkedRows/linkUtils.js +6 -6
  328. package/src/db/tests/linkController.spec.js +0 -1
  329. package/src/db/tests/linkTests.spec.js +5 -6
  330. package/src/db/views/staticViews.js +8 -10
  331. package/src/definitions/common.ts +3 -0
  332. package/src/definitions/datasource.ts +3 -0
  333. package/src/environment.js +4 -1
  334. package/src/integrations/base/sql.ts +75 -29
  335. package/src/integrations/googlesheets.ts +353 -0
  336. package/src/integrations/index.ts +3 -0
  337. package/src/integrations/tests/sql.spec.js +16 -0
  338. package/src/integrations/utils.ts +75 -12
  339. package/src/middleware/authorized.js +66 -30
  340. package/src/middleware/builder.js +2 -2
  341. package/src/middleware/currentapp.js +50 -46
  342. package/src/middleware/tests/authorized.spec.js +13 -6
  343. package/src/middleware/tests/currentapp.spec.js +6 -0
  344. package/src/middleware/tests/usageQuota.spec.js +18 -24
  345. package/src/middleware/usageQuota.js +93 -17
  346. package/src/migrations/functions/appUrls.ts +34 -0
  347. package/src/migrations/functions/quotas1.ts +20 -0
  348. package/src/migrations/functions/tests/appUrls.spec.js +29 -0
  349. package/src/migrations/functions/tests/quotas1.spec.js +27 -0
  350. package/src/migrations/functions/tests/userEmailViewCasing.spec.js +25 -0
  351. package/src/migrations/functions/usageQuotas/index.ts +8 -0
  352. package/src/migrations/functions/usageQuotas/syncApps.ts +18 -0
  353. package/src/migrations/functions/usageQuotas/syncRows.ts +22 -0
  354. package/src/migrations/functions/usageQuotas/tests/syncApps.spec.js +37 -0
  355. package/src/migrations/functions/usageQuotas/tests/syncRows.spec.js +43 -0
  356. package/src/migrations/functions/userEmailViewCasing.ts +13 -0
  357. package/src/migrations/index.ts +56 -0
  358. package/src/module.d.ts +3 -0
  359. package/src/tests/utilities/TestConfiguration.js +82 -55
  360. package/src/threads/automation.js +17 -15
  361. package/src/threads/query.js +17 -12
  362. package/src/utilities/fileSystem/index.js +4 -3
  363. package/src/utilities/global.js +15 -12
  364. package/src/utilities/index.js +7 -17
  365. package/src/utilities/routing/index.js +5 -5
  366. package/src/utilities/rowProcessor/index.js +25 -27
  367. package/src/utilities/rowProcessor/utils.js +23 -7
  368. package/src/utilities/tests/usageQuota/usageQuota.spec.js +72 -0
  369. package/src/utilities/usageQuota/index.js +93 -0
  370. package/src/utilities/usageQuota/rows.js +75 -0
  371. package/src/utilities/usageQuota/usageQuoteReset.js +18 -0
  372. package/src/utilities/users.js +3 -3
  373. package/src/utilities/workerRequests.js +4 -27
  374. package/tsconfig.json +2 -1
  375. package/builder/assets/index.39e01193.css +0 -1
  376. package/dist/api/controllers/hosting.js +0 -30
  377. package/dist/api/routes/hosting.js +0 -11
  378. package/src/api/controllers/hosting.js +0 -22
  379. package/src/api/routes/hosting.js +0 -13
  380. package/src/api/routes/tests/hosting.spec.js +0 -36
  381. package/src/utilities/usageQuota.js +0 -73
@@ -1,8 +1,7 @@
1
- const CouchDB = require("../../../db")
2
1
  const linkRows = require("../../../db/linkedRows")
3
2
  const {
4
- getRowParams,
5
3
  generateRowID,
4
+ getRowParams,
6
5
  DocumentTypes,
7
6
  InternalTables,
8
7
  } = require("../../../db/utils")
@@ -10,11 +9,9 @@ const userController = require("../user")
10
9
  const {
11
10
  inputProcessing,
12
11
  outputProcessing,
13
- processAutoColumn,
14
12
  cleanupAttachments,
15
13
  } = require("../../../utilities/rowProcessor")
16
14
  const { FieldTypes } = require("../../../constants")
17
- const { isEqual } = require("lodash")
18
15
  const { validate, findRow } = require("./utils")
19
16
  const { fullSearch, paginatedSearch } = require("./internalSearch")
20
17
  const { getGlobalUsersFromMetadata } = require("../../../utilities/global")
@@ -27,6 +24,8 @@ const {
27
24
  getFromMemoryDoc,
28
25
  } = require("../view/utils")
29
26
  const { cloneDeep } = require("lodash/fp")
27
+ const { getAppDB } = require("@budibase/backend-core/context")
28
+ const { finaliseRow, updateRelatedFormula } = require("./staticFormula")
30
29
 
31
30
  const CALCULATION_TYPES = {
32
31
  SUM: "sum",
@@ -34,51 +33,6 @@ const CALCULATION_TYPES = {
34
33
  STATS: "stats",
35
34
  }
36
35
 
37
- async function storeResponse(ctx, db, row, oldTable, table) {
38
- row.type = "row"
39
- // don't worry about rev, tables handle rev/lastID updates
40
- // if another row has been written since processing this will
41
- // handle the auto ID clash
42
- if (!isEqual(oldTable, table)) {
43
- try {
44
- await db.put(table)
45
- } catch (err) {
46
- if (err.status === 409) {
47
- const updatedTable = await db.get(table._id)
48
- let response = processAutoColumn(null, updatedTable, row, {
49
- reprocessing: true,
50
- })
51
- await db.put(response.table)
52
- row = response.row
53
- } else {
54
- throw err
55
- }
56
- }
57
- }
58
- const response = await db.put(row)
59
- row._rev = response.rev
60
- // process the row before return, to include relationships
61
- row = await outputProcessing(ctx, table, row, { squash: false })
62
- return { row, table }
63
- }
64
-
65
- // doesn't do the outputProcessing
66
- async function getRawTableData(ctx, db, tableId) {
67
- let rows
68
- if (tableId === InternalTables.USER_METADATA) {
69
- await userController.fetchMetadata(ctx)
70
- rows = ctx.body
71
- } else {
72
- const response = await db.allDocs(
73
- getRowParams(tableId, null, {
74
- include_docs: true,
75
- })
76
- )
77
- rows = response.rows.map(row => row.doc)
78
- }
79
- return rows
80
- }
81
-
82
36
  async function getView(db, viewName) {
83
37
  let mainGetter = env.SELF_HOSTED ? getFromDesignDoc : getFromMemoryDoc
84
38
  let secondaryGetter = env.SELF_HOSTED ? getFromMemoryDoc : getFromDesignDoc
@@ -105,9 +59,24 @@ async function getView(db, viewName) {
105
59
  return viewInfo
106
60
  }
107
61
 
62
+ async function getRawTableData(ctx, db, tableId) {
63
+ let rows
64
+ if (tableId === InternalTables.USER_METADATA) {
65
+ await userController.fetchMetadata(ctx)
66
+ rows = ctx.body
67
+ } else {
68
+ const response = await db.allDocs(
69
+ getRowParams(tableId, null, {
70
+ include_docs: true,
71
+ })
72
+ )
73
+ rows = response.rows.map(row => row.doc)
74
+ }
75
+ return rows
76
+ }
77
+
108
78
  exports.patch = async ctx => {
109
- const appId = ctx.appId
110
- const db = new CouchDB(appId)
79
+ const db = getAppDB()
111
80
  const inputs = ctx.request.body
112
81
  const tableId = inputs.tableId
113
82
  const isUserTable = tableId === InternalTables.USER_METADATA
@@ -146,14 +115,13 @@ exports.patch = async ctx => {
146
115
 
147
116
  // returned row is cleaned and prepared for writing to DB
148
117
  row = await linkRows.updateLinks({
149
- appId,
150
118
  eventType: linkRows.EventType.ROW_UPDATE,
151
119
  row,
152
120
  tableId: row.tableId,
153
121
  table,
154
122
  })
155
123
  // check if any attachments removed
156
- await cleanupAttachments(appId, table, { oldRow, row })
124
+ await cleanupAttachments(table, { oldRow, row })
157
125
 
158
126
  if (isUserTable) {
159
127
  // the row has been updated, need to put it into the ctx
@@ -162,12 +130,14 @@ exports.patch = async ctx => {
162
130
  return { row: ctx.body, table }
163
131
  }
164
132
 
165
- return storeResponse(ctx, db, row, dbTable, table)
133
+ return finaliseRow(table, row, {
134
+ oldTable: dbTable,
135
+ updateFormula: true,
136
+ })
166
137
  }
167
138
 
168
139
  exports.save = async function (ctx) {
169
- const appId = ctx.appId
170
- const db = new CouchDB(appId)
140
+ const db = getAppDB()
171
141
  let inputs = ctx.request.body
172
142
  inputs.tableId = ctx.params.tableId
173
143
 
@@ -189,18 +159,19 @@ exports.save = async function (ctx) {
189
159
 
190
160
  // make sure link rows are up to date
191
161
  row = await linkRows.updateLinks({
192
- appId,
193
162
  eventType: linkRows.EventType.ROW_SAVE,
194
163
  row,
195
164
  tableId: row.tableId,
196
165
  table,
197
166
  })
198
167
 
199
- return storeResponse(ctx, db, row, dbTable, table)
168
+ return finaliseRow(table, row, {
169
+ oldTable: dbTable,
170
+ updateFormula: true,
171
+ })
200
172
  }
201
173
 
202
174
  exports.fetchView = async ctx => {
203
- const appId = ctx.appId
204
175
  const viewName = ctx.params.viewName
205
176
 
206
177
  // if this is a table view being looked for just transfer to that
@@ -209,7 +180,7 @@ exports.fetchView = async ctx => {
209
180
  return exports.fetch(ctx)
210
181
  }
211
182
 
212
- const db = new CouchDB(appId)
183
+ const db = getAppDB()
213
184
  const { calculation, group, field } = ctx.query
214
185
  const viewInfo = await getView(db, viewName)
215
186
  let response
@@ -236,7 +207,7 @@ exports.fetchView = async ctx => {
236
207
  schema: {},
237
208
  }
238
209
  }
239
- rows = await outputProcessing(ctx, table, response.rows)
210
+ rows = await outputProcessing(table, response.rows)
240
211
  }
241
212
 
242
213
  if (calculation === CALCULATION_TYPES.STATS) {
@@ -263,27 +234,24 @@ exports.fetchView = async ctx => {
263
234
  }
264
235
 
265
236
  exports.fetch = async ctx => {
266
- const appId = ctx.appId
267
- const db = new CouchDB(appId)
237
+ const db = getAppDB()
268
238
 
269
239
  const tableId = ctx.params.tableId
270
240
  let table = await db.get(tableId)
271
241
  let rows = await getRawTableData(ctx, db, tableId)
272
- return outputProcessing(ctx, table, rows)
242
+ return outputProcessing(table, rows)
273
243
  }
274
244
 
275
245
  exports.find = async ctx => {
276
- const appId = ctx.appId
277
- const db = new CouchDB(appId)
246
+ const db = getAppDB()
278
247
  const table = await db.get(ctx.params.tableId)
279
- let row = await findRow(ctx, db, ctx.params.tableId, ctx.params.rowId)
280
- row = await outputProcessing(ctx, table, row)
248
+ let row = await findRow(ctx, ctx.params.tableId, ctx.params.rowId)
249
+ row = await outputProcessing(table, row)
281
250
  return row
282
251
  }
283
252
 
284
253
  exports.destroy = async function (ctx) {
285
- const appId = ctx.appId
286
- const db = new CouchDB(appId)
254
+ const db = getAppDB()
287
255
  const { _id, _rev } = ctx.request.body
288
256
  let row = await db.get(_id)
289
257
 
@@ -292,16 +260,17 @@ exports.destroy = async function (ctx) {
292
260
  }
293
261
  const table = await db.get(row.tableId)
294
262
  // update the row to include full relationships before deleting them
295
- row = await outputProcessing(ctx, table, row, { squash: false })
263
+ row = await outputProcessing(table, row, { squash: false })
296
264
  // now remove the relationships
297
265
  await linkRows.updateLinks({
298
- appId,
299
266
  eventType: linkRows.EventType.ROW_DELETE,
300
267
  row,
301
268
  tableId: row.tableId,
302
269
  })
303
270
  // remove any attachments that were on the row from object storage
304
- await cleanupAttachments(appId, table, { row })
271
+ await cleanupAttachments(table, { row })
272
+ // remove any static formula
273
+ await updateRelatedFormula(table, row)
305
274
 
306
275
  let response
307
276
  if (ctx.params.tableId === InternalTables.USER_METADATA) {
@@ -317,20 +286,18 @@ exports.destroy = async function (ctx) {
317
286
  }
318
287
 
319
288
  exports.bulkDestroy = async ctx => {
320
- const appId = ctx.appId
321
- const db = new CouchDB(appId)
289
+ const db = getAppDB()
322
290
  const tableId = ctx.params.tableId
323
291
  const table = await db.get(tableId)
324
292
  let { rows } = ctx.request.body
325
293
 
326
294
  // before carrying out any updates, make sure the rows are ready to be returned
327
295
  // they need to be the full rows (including previous relationships) for automations
328
- rows = await outputProcessing(ctx, table, rows, { squash: false })
296
+ rows = await outputProcessing(table, rows, { squash: false })
329
297
 
330
298
  // remove the relationships first
331
299
  let updates = rows.map(row =>
332
300
  linkRows.updateLinks({
333
- appId,
334
301
  eventType: linkRows.EventType.ROW_DELETE,
335
302
  row,
336
303
  tableId: row.tableId,
@@ -349,7 +316,8 @@ exports.bulkDestroy = async ctx => {
349
316
  await db.bulkDocs(rows.map(row => ({ ...row, _deleted: true })))
350
317
  }
351
318
  // remove any attachments that were on the rows from object storage
352
- await cleanupAttachments(appId, table, { rows })
319
+ await cleanupAttachments(table, { rows })
320
+ await updateRelatedFormula(table, rows)
353
321
  await Promise.all(updates)
354
322
  return { response: { ok: true }, rows }
355
323
  }
@@ -360,28 +328,27 @@ exports.search = async ctx => {
360
328
  return { rows: await exports.fetch(ctx) }
361
329
  }
362
330
 
363
- const appId = ctx.appId
364
331
  const { tableId } = ctx.params
365
- const db = new CouchDB(appId)
332
+ const db = getAppDB()
366
333
  const { paginate, query, ...params } = ctx.request.body
367
334
  params.version = ctx.version
368
335
  params.tableId = tableId
369
336
 
370
337
  let response
371
338
  if (paginate) {
372
- response = await paginatedSearch(appId, query, params)
339
+ response = await paginatedSearch(query, params)
373
340
  } else {
374
- response = await fullSearch(appId, query, params)
341
+ response = await fullSearch(query, params)
375
342
  }
376
343
 
377
344
  // Enrich search results with relationships
378
345
  if (response.rows && response.rows.length) {
379
346
  // enrich with global users if from users table
380
347
  if (tableId === InternalTables.USER_METADATA) {
381
- response.rows = await getGlobalUsersFromMetadata(appId, response.rows)
348
+ response.rows = await getGlobalUsersFromMetadata(response.rows)
382
349
  }
383
350
  const table = await db.get(tableId)
384
- response.rows = await outputProcessing(ctx, table, response.rows)
351
+ response.rows = await outputProcessing(table, response.rows)
385
352
  }
386
353
 
387
354
  return response
@@ -389,25 +356,22 @@ exports.search = async ctx => {
389
356
 
390
357
  exports.validate = async ctx => {
391
358
  return validate({
392
- appId: ctx.appId,
393
359
  tableId: ctx.params.tableId,
394
360
  row: ctx.request.body,
395
361
  })
396
362
  }
397
363
 
398
364
  exports.fetchEnrichedRow = async ctx => {
399
- const appId = ctx.appId
400
- const db = new CouchDB(appId)
365
+ const db = getAppDB()
401
366
  const tableId = ctx.params.tableId
402
367
  const rowId = ctx.params.rowId
403
368
  // need table to work out where links go in row
404
369
  let [table, row] = await Promise.all([
405
370
  db.get(tableId),
406
- findRow(ctx, db, tableId, rowId),
371
+ findRow(ctx, tableId, rowId),
407
372
  ])
408
373
  // get the link docs
409
374
  const linkVals = await linkRows.getLinkDocuments({
410
- appId,
411
375
  tableId,
412
376
  rowId,
413
377
  })
@@ -434,7 +398,7 @@ exports.fetchEnrichedRow = async ctx => {
434
398
  for (let [tableId, rows] of Object.entries(groups)) {
435
399
  // need to include the IDs in these rows for any links they may have
436
400
  linkedRows = linkedRows.concat(
437
- await outputProcessing(ctx, tables[tableId], rows)
401
+ await outputProcessing(tables[tableId], rows)
438
402
  )
439
403
  }
440
404
 
@@ -1,14 +1,14 @@
1
1
  const { SearchIndexes } = require("../../../db/utils")
2
2
  const fetch = require("node-fetch")
3
3
  const { getCouchUrl } = require("@budibase/backend-core/db")
4
+ const { getAppId } = require("@budibase/backend-core/context")
4
5
 
5
6
  /**
6
7
  * Class to build lucene query URLs.
7
8
  * Optionally takes a base lucene query object.
8
9
  */
9
10
  class QueryBuilder {
10
- constructor(appId, base) {
11
- this.appId = appId
11
+ constructor(base) {
12
12
  this.query = {
13
13
  string: {},
14
14
  fuzzy: {},
@@ -37,22 +37,30 @@ class QueryBuilder {
37
37
  }
38
38
 
39
39
  setLimit(limit) {
40
- this.limit = limit
40
+ if (limit != null) {
41
+ this.limit = limit
42
+ }
41
43
  return this
42
44
  }
43
45
 
44
46
  setSort(sort) {
45
- this.sort = sort
47
+ if (sort != null) {
48
+ this.sort = sort
49
+ }
46
50
  return this
47
51
  }
48
52
 
49
53
  setSortOrder(sortOrder) {
50
- this.sortOrder = sortOrder
54
+ if (sortOrder != null) {
55
+ this.sortOrder = sortOrder
56
+ }
51
57
  return this
52
58
  }
53
59
 
54
60
  setSortType(sortType) {
55
- this.sortType = sortType
61
+ if (sortType != null) {
62
+ this.sortType = sortType
63
+ }
56
64
  return this
57
65
  }
58
66
 
@@ -233,7 +241,8 @@ class QueryBuilder {
233
241
  }
234
242
 
235
243
  async run() {
236
- const url = `${getCouchUrl()}/${this.appId}/_design/database/_search/${
244
+ const appId = getAppId()
245
+ const url = `${getCouchUrl()}/${appId}/_design/database/_search/${
237
246
  SearchIndexes.ROWS
238
247
  }`
239
248
  const body = this.buildSearchBody()
@@ -270,7 +279,6 @@ const runQuery = async (url, body) => {
270
279
  * Gets round the fixed limit of 200 results from a query by fetching as many
271
280
  * pages as required and concatenating the results. This recursively operates
272
281
  * until enough results have been found.
273
- * @param appId {string} The app ID to search
274
282
  * @param query {object} The JSON query structure
275
283
  * @param params {object} The search params including:
276
284
  * tableId {string} The table ID to search
@@ -283,7 +291,7 @@ const runQuery = async (url, body) => {
283
291
  * rows {array|null} Current results in the recursive search
284
292
  * @returns {Promise<*[]|*>}
285
293
  */
286
- const recursiveSearch = async (appId, query, params) => {
294
+ const recursiveSearch = async (query, params) => {
287
295
  const bookmark = params.bookmark
288
296
  const rows = params.rows || []
289
297
  if (rows.length >= params.limit) {
@@ -293,7 +301,7 @@ const recursiveSearch = async (appId, query, params) => {
293
301
  if (rows.length > params.limit - 200) {
294
302
  pageSize = params.limit - rows.length
295
303
  }
296
- const page = await new QueryBuilder(appId, query)
304
+ const page = await new QueryBuilder(query)
297
305
  .setVersion(params.version)
298
306
  .setTable(params.tableId)
299
307
  .setBookmark(bookmark)
@@ -313,14 +321,13 @@ const recursiveSearch = async (appId, query, params) => {
313
321
  bookmark: page.bookmark,
314
322
  rows: [...rows, ...page.rows],
315
323
  }
316
- return await recursiveSearch(appId, query, newParams)
324
+ return await recursiveSearch(query, newParams)
317
325
  }
318
326
 
319
327
  /**
320
328
  * Performs a paginated search. A bookmark will be returned to allow the next
321
329
  * page to be fetched. There is a max limit off 200 results per page in a
322
330
  * paginated search.
323
- * @param appId {string} The app ID to search
324
331
  * @param query {object} The JSON query structure
325
332
  * @param params {object} The search params including:
326
333
  * tableId {string} The table ID to search
@@ -332,13 +339,13 @@ const recursiveSearch = async (appId, query, params) => {
332
339
  * bookmark {string} The bookmark to resume from
333
340
  * @returns {Promise<{hasNextPage: boolean, rows: *[]}>}
334
341
  */
335
- exports.paginatedSearch = async (appId, query, params) => {
342
+ exports.paginatedSearch = async (query, params) => {
336
343
  let limit = params.limit
337
344
  if (limit == null || isNaN(limit) || limit < 0) {
338
345
  limit = 50
339
346
  }
340
347
  limit = Math.min(limit, 200)
341
- const search = new QueryBuilder(appId, query)
348
+ const search = new QueryBuilder(query)
342
349
  .setVersion(params.version)
343
350
  .setTable(params.tableId)
344
351
  .setSort(params.sort)
@@ -367,7 +374,6 @@ exports.paginatedSearch = async (appId, query, params) => {
367
374
  * desired amount of results. There is a limit of 1000 results to avoid
368
375
  * heavy performance hits, and to avoid client components breaking from
369
376
  * handling too much data.
370
- * @param appId {string} The app ID to search
371
377
  * @param query {object} The JSON query structure
372
378
  * @param params {object} The search params including:
373
379
  * tableId {string} The table ID to search
@@ -378,12 +384,12 @@ exports.paginatedSearch = async (appId, query, params) => {
378
384
  * limit {number} The desired number of results
379
385
  * @returns {Promise<{rows: *}>}
380
386
  */
381
- exports.fullSearch = async (appId, query, params) => {
387
+ exports.fullSearch = async (query, params) => {
382
388
  let limit = params.limit
383
389
  if (limit == null || isNaN(limit) || limit < 0) {
384
390
  limit = 1000
385
391
  }
386
392
  params.limit = Math.min(limit, 1000)
387
- const rows = await recursiveSearch(appId, query, params)
393
+ const rows = await recursiveSearch(query, params)
388
394
  return { rows }
389
395
  }
@@ -0,0 +1,156 @@
1
+ const { getRowParams } = require("../../../db/utils")
2
+ const {
3
+ outputProcessing,
4
+ processAutoColumn,
5
+ processFormulas,
6
+ } = require("../../../utilities/rowProcessor")
7
+ const { FieldTypes, FormulaTypes } = require("../../../constants")
8
+ const { isEqual } = require("lodash")
9
+ const { cloneDeep } = require("lodash/fp")
10
+ const { getAppDB } = require("@budibase/backend-core/context")
11
+
12
+ /**
13
+ * This function runs through a list of enriched rows, looks at the rows which
14
+ * are related and then checks if they need the state of their formulas
15
+ * updated.
16
+ * NOTE: this will only for affect static formulas.
17
+ */
18
+ exports.updateRelatedFormula = async (table, enrichedRows) => {
19
+ const db = getAppDB()
20
+ // no formula to update, we're done
21
+ if (!table.relatedFormula) {
22
+ return
23
+ }
24
+ let promises = []
25
+ for (let enrichedRow of Array.isArray(enrichedRows)
26
+ ? enrichedRows
27
+ : [enrichedRows]) {
28
+ // the related rows by tableId
29
+ let relatedRows = {}
30
+ for (let [key, field] of Object.entries(enrichedRow)) {
31
+ const columnDefinition = table.schema[key]
32
+ if (columnDefinition && columnDefinition.type === FieldTypes.LINK) {
33
+ const relatedTableId = columnDefinition.tableId
34
+ if (!relatedRows[relatedTableId]) {
35
+ relatedRows[relatedTableId] = []
36
+ }
37
+ relatedRows[relatedTableId] = relatedRows[relatedTableId].concat(field)
38
+ }
39
+ }
40
+ for (let tableId of table.relatedFormula) {
41
+ let relatedTable
42
+ try {
43
+ // no rows to update, skip
44
+ if (!relatedRows[tableId] || relatedRows[tableId].length === 0) {
45
+ continue
46
+ }
47
+ relatedTable = await db.get(tableId)
48
+ } catch (err) {
49
+ // no error scenario, table doesn't seem to exist anymore, ignore
50
+ }
51
+ for (let column of Object.values(relatedTable.schema)) {
52
+ // needs updated in related rows
53
+ if (
54
+ column.type === FieldTypes.FORMULA &&
55
+ column.formulaType === FormulaTypes.STATIC
56
+ ) {
57
+ // re-enrich rows for all the related, don't update the related formula for them
58
+ promises = promises.concat(
59
+ relatedRows[tableId].map(related =>
60
+ exports.finaliseRow(relatedTable, related, {
61
+ updateFormula: false,
62
+ })
63
+ )
64
+ )
65
+ }
66
+ }
67
+ }
68
+ }
69
+ await Promise.all(promises)
70
+ }
71
+
72
+ exports.updateAllFormulasInTable = async table => {
73
+ const db = getAppDB()
74
+ // start by getting the raw rows (which will be written back to DB after update)
75
+ let rows = (
76
+ await db.allDocs(
77
+ getRowParams(table._id, null, {
78
+ include_docs: true,
79
+ })
80
+ )
81
+ ).rows.map(row => row.doc)
82
+ // now enrich the rows, note the clone so that we have the base state of the
83
+ // rows so that we don't write any of the enriched information back
84
+ let enrichedRows = await outputProcessing(table, cloneDeep(rows), {
85
+ squash: false,
86
+ })
87
+ const updatedRows = []
88
+ for (let row of rows) {
89
+ // find the enriched row, if found process the formulas
90
+ const enrichedRow = enrichedRows.find(enriched => enriched._id === row._id)
91
+ if (enrichedRow) {
92
+ const processed = processFormulas(table, cloneDeep(row), {
93
+ dynamic: false,
94
+ contextRows: enrichedRow,
95
+ })
96
+ // values have changed, need to add to bulk docs to update
97
+ if (!isEqual(processed, row)) {
98
+ updatedRows.push(processed)
99
+ }
100
+ }
101
+ }
102
+ await db.bulkDocs(updatedRows)
103
+ }
104
+
105
+ /**
106
+ * This function runs at the end of the save/patch functions of the row controller, all this
107
+ * really does is enrich the row, handle any static formula processing, then return the enriched
108
+ * row. The reason we need to return the enriched row is that the automation row created trigger
109
+ * expects the row to be totally enriched/contain all relationships.
110
+ */
111
+ exports.finaliseRow = async (
112
+ table,
113
+ row,
114
+ { oldTable, updateFormula } = { updateFormula: true }
115
+ ) => {
116
+ const db = getAppDB()
117
+ row.type = "row"
118
+ // process the row before return, to include relationships
119
+ let enrichedRow = await outputProcessing(table, cloneDeep(row), {
120
+ squash: false,
121
+ })
122
+ // use enriched row to generate formulas for saving, specifically only use as context
123
+ row = processFormulas(table, row, {
124
+ dynamic: false,
125
+ contextRows: enrichedRow,
126
+ })
127
+
128
+ // don't worry about rev, tables handle rev/lastID updates
129
+ // if another row has been written since processing this will
130
+ // handle the auto ID clash
131
+ if (oldTable && !isEqual(oldTable, table)) {
132
+ try {
133
+ await db.put(table)
134
+ } catch (err) {
135
+ if (err.status === 409) {
136
+ const updatedTable = await db.get(table._id)
137
+ let response = processAutoColumn(null, updatedTable, row, {
138
+ reprocessing: true,
139
+ })
140
+ await db.put(response.table)
141
+ row = response.row
142
+ } else {
143
+ throw err
144
+ }
145
+ }
146
+ }
147
+ const response = await db.put(row)
148
+ // for response, calculate the formulas for the enriched row
149
+ enrichedRow._rev = response.rev
150
+ enrichedRow = await processFormulas(table, enrichedRow, { dynamic: false })
151
+ // this updates the related formulas in other rows based on the relations to this row
152
+ if (updateFormula) {
153
+ await exports.updateRelatedFormula(table, enrichedRow)
154
+ }
155
+ return { row: enrichedRow, table }
156
+ }