@budibase/server 1.0.48 → 1.0.49-alpha.3

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 (289) hide show
  1. package/builder/assets/{index.e083dcaf.js → index.21e57eb4.js} +234 -231
  2. package/builder/assets/index.83c0944f.css +1 -0
  3. package/builder/assets/{vendor.f9faf6ab.js → vendor.d526bfe8.js} +12 -12
  4. package/builder/index.html +3 -3
  5. package/coverage/clover.xml +2338 -2028
  6. package/coverage/coverage-final.json +100 -95
  7. package/coverage/lcov-report/api/controllers/analytics.js.html +1 -1
  8. package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
  9. package/coverage/lcov-report/api/controllers/application.js.html +210 -126
  10. package/coverage/lcov-report/api/controllers/auth.js.html +18 -6
  11. package/coverage/lcov-report/api/controllers/automation.js.html +21 -21
  12. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  13. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  14. package/coverage/lcov-report/api/controllers/component.js.html +4 -4
  15. package/coverage/lcov-report/api/controllers/datasource.js.html +72 -36
  16. package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
  17. package/coverage/lcov-report/api/controllers/index.html +53 -53
  18. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  19. package/coverage/lcov-report/api/controllers/layout.js.html +6 -6
  20. package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
  21. package/coverage/lcov-report/api/controllers/migrations.js.html +119 -0
  22. package/coverage/lcov-report/api/controllers/permission.js.html +15 -15
  23. package/coverage/lcov-report/api/controllers/query/index.html +1 -1
  24. package/coverage/lcov-report/api/controllers/query/index.js.html +19 -19
  25. package/coverage/lcov-report/api/controllers/query/validation.js.html +6 -6
  26. package/coverage/lcov-report/api/controllers/role.js.html +9 -9
  27. package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
  28. package/coverage/lcov-report/api/controllers/row/external.js.html +16 -16
  29. package/coverage/lcov-report/api/controllers/row/index.html +48 -33
  30. package/coverage/lcov-report/api/controllers/row/index.js.html +26 -26
  31. package/coverage/lcov-report/api/controllers/row/internal.js.html +95 -158
  32. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +41 -17
  33. package/coverage/lcov-report/api/controllers/row/staticFormula.js.html +551 -0
  34. package/coverage/lcov-report/api/controllers/row/utils.js.html +39 -57
  35. package/coverage/lcov-report/api/controllers/screen.js.html +7 -7
  36. package/coverage/lcov-report/api/controllers/script.js.html +4 -4
  37. package/coverage/lcov-report/api/controllers/table/bulkFormula.js.html +629 -0
  38. package/coverage/lcov-report/api/controllers/table/external.js.html +12 -12
  39. package/coverage/lcov-report/api/controllers/table/index.html +46 -31
  40. package/coverage/lcov-report/api/controllers/table/index.js.html +53 -77
  41. package/coverage/lcov-report/api/controllers/table/internal.js.html +59 -35
  42. package/coverage/lcov-report/api/controllers/table/utils.js.html +217 -73
  43. package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
  44. package/coverage/lcov-report/api/controllers/user.js.html +30 -24
  45. package/coverage/lcov-report/api/controllers/view/exporters.js.html +4 -4
  46. package/coverage/lcov-report/api/controllers/view/index.html +1 -1
  47. package/coverage/lcov-report/api/controllers/view/index.js.html +14 -14
  48. package/coverage/lcov-report/api/controllers/view/utils.js.html +11 -11
  49. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +7 -7
  50. package/coverage/lcov-report/api/controllers/webhook.js.html +15 -15
  51. package/coverage/lcov-report/api/index.html +1 -1
  52. package/coverage/lcov-report/api/index.js.html +8 -8
  53. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/application.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/dev.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/index.html +18 -18
  65. package/coverage/lcov-report/api/routes/index.js.html +3 -3
  66. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/{hosting.js.html → migrations.js.html} +17 -14
  70. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/query.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/row.js.html +1 -1
  75. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  76. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  77. package/coverage/lcov-report/api/routes/static.js.html +17 -2
  78. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  79. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  80. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  81. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  82. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  83. package/coverage/lcov-report/automations/actions.js.html +24 -24
  84. package/coverage/lcov-report/automations/automationUtils.js.html +5 -5
  85. package/coverage/lcov-report/automations/bullboard.js.html +16 -16
  86. package/coverage/lcov-report/automations/index.html +1 -1
  87. package/coverage/lcov-report/automations/index.js.html +1 -1
  88. package/coverage/lcov-report/automations/steps/bash.js.html +6 -6
  89. package/coverage/lcov-report/automations/steps/createRow.js.html +13 -19
  90. package/coverage/lcov-report/automations/steps/delay.js.html +4 -4
  91. package/coverage/lcov-report/automations/steps/deleteRow.js.html +11 -20
  92. package/coverage/lcov-report/automations/steps/discord.js.html +7 -7
  93. package/coverage/lcov-report/automations/steps/executeQuery.js.html +6 -6
  94. package/coverage/lcov-report/automations/steps/executeScript.js.html +6 -6
  95. package/coverage/lcov-report/automations/steps/filter.js.html +7 -7
  96. package/coverage/lcov-report/automations/steps/index.html +13 -13
  97. package/coverage/lcov-report/automations/steps/integromat.js.html +5 -5
  98. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +8 -8
  99. package/coverage/lcov-report/automations/steps/queryRows.js.html +10 -10
  100. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +5 -5
  101. package/coverage/lcov-report/automations/steps/serverLog.js.html +3 -3
  102. package/coverage/lcov-report/automations/steps/slack.js.html +5 -5
  103. package/coverage/lcov-report/automations/steps/updateRow.js.html +6 -6
  104. package/coverage/lcov-report/automations/steps/utils.js.html +3 -3
  105. package/coverage/lcov-report/automations/steps/zapier.js.html +5 -5
  106. package/coverage/lcov-report/automations/triggerInfo/app.js.html +2 -2
  107. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +2 -2
  108. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  109. package/coverage/lcov-report/automations/triggerInfo/index.js.html +8 -8
  110. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +2 -2
  111. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +2 -2
  112. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +2 -2
  113. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +2 -2
  114. package/coverage/lcov-report/automations/triggers.js.html +18 -18
  115. package/coverage/lcov-report/automations/utils.js.html +32 -32
  116. package/coverage/lcov-report/constants/index.html +5 -5
  117. package/coverage/lcov-report/constants/index.js.html +53 -26
  118. package/coverage/lcov-report/constants/layouts.js.html +5 -5
  119. package/coverage/lcov-report/constants/screens.js.html +4 -4
  120. package/coverage/lcov-report/db/client.js.html +18 -18
  121. package/coverage/lcov-report/db/inMemoryView.js.html +7 -7
  122. package/coverage/lcov-report/db/index.html +15 -15
  123. package/coverage/lcov-report/db/index.js.html +3 -3
  124. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +18 -18
  125. package/coverage/lcov-report/db/linkedRows/index.html +10 -10
  126. package/coverage/lcov-report/db/linkedRows/index.js.html +38 -41
  127. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +15 -15
  128. package/coverage/lcov-report/db/newid.js.html +4 -4
  129. package/coverage/lcov-report/db/utils.js.html +86 -86
  130. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  131. package/coverage/lcov-report/events/index.html +1 -1
  132. package/coverage/lcov-report/events/index.js.html +5 -5
  133. package/coverage/lcov-report/events/utils.js.html +3 -3
  134. package/coverage/lcov-report/index.html +114 -99
  135. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  136. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  137. package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
  138. package/coverage/lcov-report/middleware/authorized.js.html +166 -64
  139. package/coverage/lcov-report/middleware/builder.js.html +8 -8
  140. package/coverage/lcov-report/middleware/currentapp.js.html +29 -29
  141. package/coverage/lcov-report/middleware/index.html +30 -30
  142. package/coverage/lcov-report/middleware/joi-validator.js.html +3 -3
  143. package/coverage/lcov-report/middleware/resourceId.js.html +1 -1
  144. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  145. package/coverage/lcov-report/middleware/usageQuota.js.html +270 -42
  146. package/coverage/lcov-report/middleware/utils.js.html +2 -2
  147. package/coverage/lcov-report/threads/automation.js.html +1 -1
  148. package/coverage/lcov-report/threads/index.html +15 -15
  149. package/coverage/lcov-report/threads/index.js.html +10 -10
  150. package/coverage/lcov-report/threads/query.js.html +23 -8
  151. package/coverage/lcov-report/threads/utils.js.html +14 -14
  152. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  153. package/coverage/lcov-report/utilities/budibaseDir.js.html +7 -7
  154. package/coverage/lcov-report/utilities/centralPath.js.html +4 -4
  155. package/coverage/lcov-report/utilities/csvParser.js.html +6 -6
  156. package/coverage/lcov-report/utilities/global.js.html +26 -26
  157. package/coverage/lcov-report/utilities/index.html +19 -19
  158. package/coverage/lcov-report/utilities/index.js.html +25 -25
  159. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +8 -8
  160. package/coverage/lcov-report/utilities/queue/index.html +1 -1
  161. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  162. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  163. package/coverage/lcov-report/utilities/rowProcessor/index.html +27 -27
  164. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +115 -79
  165. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +75 -27
  166. package/coverage/lcov-report/utilities/scriptRunner.js.html +5 -5
  167. package/coverage/lcov-report/utilities/security.js.html +8 -8
  168. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  169. package/coverage/lcov-report/utilities/usageQuota/index.html +141 -0
  170. package/coverage/lcov-report/utilities/usageQuota/index.js.html +356 -0
  171. package/coverage/lcov-report/utilities/usageQuota/rows.js.html +302 -0
  172. package/coverage/lcov-report/{api/controllers/hosting.js.html → utilities/usageQuota/usageQuoteReset.js.html} +36 -48
  173. package/coverage/lcov-report/utilities/users.js.html +5 -5
  174. package/coverage/lcov-report/utilities/workerRequests.js.html +39 -108
  175. package/coverage/lcov.info +3656 -3057
  176. package/dist/api/controllers/application.js +61 -34
  177. package/dist/api/controllers/auth.js +4 -0
  178. package/dist/api/controllers/datasource.js +11 -0
  179. package/dist/api/controllers/migrations.js +21 -0
  180. package/dist/api/controllers/row/internal.js +30 -53
  181. package/dist/api/controllers/row/internalSearch.js +12 -4
  182. package/dist/api/controllers/row/staticFormula.js +147 -0
  183. package/dist/api/controllers/row/utils.js +6 -8
  184. package/dist/api/controllers/static/index.js +59 -10
  185. package/dist/api/controllers/table/bulkFormula.js +160 -0
  186. package/dist/api/controllers/table/index.js +12 -9
  187. package/dist/api/controllers/table/internal.js +9 -0
  188. package/dist/api/controllers/table/utils.js +41 -12
  189. package/dist/api/controllers/user.js +2 -0
  190. package/dist/api/routes/index.js +2 -2
  191. package/dist/api/routes/migrations.js +9 -0
  192. package/dist/api/routes/static.js +2 -1
  193. package/dist/app.js +35 -5
  194. package/dist/automations/steps/createRow.js +2 -4
  195. package/dist/automations/steps/deleteRow.js +1 -4
  196. package/dist/constants/index.js +7 -0
  197. package/dist/db/linkedRows/index.js +4 -5
  198. package/dist/definitions/datasource.js +1 -0
  199. package/dist/environment.js +2 -0
  200. package/dist/integrations/base/sql.js +13 -6
  201. package/dist/integrations/googlesheets.js +340 -0
  202. package/dist/integrations/index.js +3 -0
  203. package/dist/integrations/utils.js +57 -9
  204. package/dist/middleware/authorized.js +55 -30
  205. package/dist/middleware/usageQuota.js +80 -13
  206. package/dist/migrations/functions/appUrls.js +46 -0
  207. package/dist/migrations/functions/quotas1.js +50 -0
  208. package/dist/migrations/functions/usageQuotas/index.js +20 -0
  209. package/dist/migrations/functions/usageQuotas/syncApps.js +32 -0
  210. package/dist/migrations/functions/usageQuotas/syncRows.js +35 -0
  211. package/dist/migrations/functions/userEmailViewCasing.js +24 -0
  212. package/dist/migrations/index.js +62 -0
  213. package/dist/package.json +8 -5
  214. package/dist/tests/utilities/TestConfiguration.js +13 -6
  215. package/dist/threads/query.js +4 -0
  216. package/dist/tsconfig.tsbuildinfo +1 -1
  217. package/dist/utilities/rowProcessor/index.js +18 -11
  218. package/dist/utilities/rowProcessor/utils.js +14 -4
  219. package/dist/utilities/{usageQuota.js → usageQuota/index.js} +44 -31
  220. package/dist/utilities/usageQuota/rows.js +77 -0
  221. package/dist/utilities/usageQuota/usageQuoteReset.js +16 -0
  222. package/dist/utilities/workerRequests.js +0 -20
  223. package/nodemon.json +1 -1
  224. package/package.json +9 -6
  225. package/src/api/controllers/application.js +52 -24
  226. package/src/api/controllers/auth.js +4 -0
  227. package/src/api/controllers/datasource.js +12 -0
  228. package/src/api/controllers/migrations.js +13 -0
  229. package/src/api/controllers/row/internal.js +29 -50
  230. package/src/api/controllers/row/internalSearch.js +12 -4
  231. package/src/api/controllers/row/staticFormula.js +157 -0
  232. package/src/api/controllers/row/utils.js +6 -12
  233. package/src/api/controllers/static/index.js +63 -10
  234. package/src/api/controllers/table/bulkFormula.js +183 -0
  235. package/src/api/controllers/table/index.js +16 -24
  236. package/src/api/controllers/table/internal.js +9 -1
  237. package/src/api/controllers/table/utils.js +57 -9
  238. package/src/api/controllers/user.js +2 -0
  239. package/src/api/routes/index.js +2 -2
  240. package/src/api/routes/migrations.js +14 -0
  241. package/src/api/routes/static.js +5 -0
  242. package/src/api/routes/tests/application.spec.js +2 -2
  243. package/src/api/routes/tests/auth.spec.js +1 -2
  244. package/src/api/routes/tests/static.spec.js +98 -0
  245. package/src/app.ts +18 -5
  246. package/src/automations/steps/createRow.js +2 -4
  247. package/src/automations/steps/deleteRow.js +1 -4
  248. package/src/constants/index.js +9 -0
  249. package/src/db/linkedRows/index.js +4 -5
  250. package/src/definitions/common.ts +3 -0
  251. package/src/definitions/datasource.ts +3 -0
  252. package/src/environment.js +2 -0
  253. package/src/integrations/base/sql.ts +14 -7
  254. package/src/integrations/googlesheets.ts +353 -0
  255. package/src/integrations/index.ts +3 -0
  256. package/src/integrations/tests/sql.spec.js +16 -0
  257. package/src/integrations/utils.ts +71 -11
  258. package/src/middleware/authorized.js +64 -30
  259. package/src/middleware/tests/authorized.spec.js +6 -4
  260. package/src/middleware/tests/usageQuota.spec.js +18 -24
  261. package/src/middleware/usageQuota.js +91 -15
  262. package/src/migrations/functions/appUrls.ts +34 -0
  263. package/src/migrations/functions/quotas1.ts +20 -0
  264. package/src/migrations/functions/tests/appUrls.spec.js +29 -0
  265. package/src/migrations/functions/tests/quotas1.spec.js +27 -0
  266. package/src/migrations/functions/tests/userEmailViewCasing.spec.js +25 -0
  267. package/src/migrations/functions/usageQuotas/index.ts +8 -0
  268. package/src/migrations/functions/usageQuotas/syncApps.ts +18 -0
  269. package/src/migrations/functions/usageQuotas/syncRows.ts +21 -0
  270. package/src/migrations/functions/usageQuotas/tests/syncApps.spec.js +37 -0
  271. package/src/migrations/functions/usageQuotas/tests/syncRows.spec.js +43 -0
  272. package/src/migrations/functions/userEmailViewCasing.ts +13 -0
  273. package/src/migrations/index.ts +56 -0
  274. package/src/tests/utilities/TestConfiguration.js +14 -6
  275. package/src/threads/query.js +5 -0
  276. package/src/utilities/rowProcessor/index.js +24 -12
  277. package/src/utilities/rowProcessor/utils.js +23 -7
  278. package/src/utilities/tests/usageQuota/usageQuota.spec.js +72 -0
  279. package/src/utilities/usageQuota/index.js +92 -0
  280. package/src/utilities/usageQuota/rows.js +74 -0
  281. package/src/utilities/usageQuota/usageQuoteReset.js +18 -0
  282. package/src/utilities/workerRequests.js +0 -23
  283. package/builder/assets/index.39e01193.css +0 -1
  284. package/dist/api/controllers/hosting.js +0 -30
  285. package/dist/api/routes/hosting.js +0 -11
  286. package/src/api/controllers/hosting.js +0 -22
  287. package/src/api/routes/hosting.js +0 -13
  288. package/src/api/routes/tests/hosting.spec.js +0 -36
  289. package/src/utilities/usageQuota.js +0 -73
@@ -1,8 +1,8 @@
1
1
  const CouchDB = require("../../../db")
2
2
  const linkRows = require("../../../db/linkedRows")
3
3
  const {
4
- getRowParams,
5
4
  generateRowID,
5
+ getRowParams,
6
6
  DocumentTypes,
7
7
  InternalTables,
8
8
  } = require("../../../db/utils")
@@ -10,11 +10,9 @@ const userController = require("../user")
10
10
  const {
11
11
  inputProcessing,
12
12
  outputProcessing,
13
- processAutoColumn,
14
13
  cleanupAttachments,
15
14
  } = require("../../../utilities/rowProcessor")
16
15
  const { FieldTypes } = require("../../../constants")
17
- const { isEqual } = require("lodash")
18
16
  const { validate, findRow } = require("./utils")
19
17
  const { fullSearch, paginatedSearch } = require("./internalSearch")
20
18
  const { getGlobalUsersFromMetadata } = require("../../../utilities/global")
@@ -27,6 +25,7 @@ const {
27
25
  getFromMemoryDoc,
28
26
  } = require("../view/utils")
29
27
  const { cloneDeep } = require("lodash/fp")
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,6 +59,22 @@ 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
79
  const appId = ctx.appId
110
80
  const db = new CouchDB(appId)
@@ -162,7 +132,10 @@ exports.patch = async ctx => {
162
132
  return { row: ctx.body, table }
163
133
  }
164
134
 
165
- return storeResponse(ctx, db, row, dbTable, table)
135
+ return finaliseRow(ctx.appId, table, row, {
136
+ oldTable: dbTable,
137
+ updateFormula: true,
138
+ })
166
139
  }
167
140
 
168
141
  exports.save = async function (ctx) {
@@ -196,7 +169,10 @@ exports.save = async function (ctx) {
196
169
  table,
197
170
  })
198
171
 
199
- return storeResponse(ctx, db, row, dbTable, table)
172
+ return finaliseRow(ctx.appId, table, row, {
173
+ oldTable: dbTable,
174
+ updateFormula: true,
175
+ })
200
176
  }
201
177
 
202
178
  exports.fetchView = async ctx => {
@@ -302,6 +278,8 @@ exports.destroy = async function (ctx) {
302
278
  })
303
279
  // remove any attachments that were on the row from object storage
304
280
  await cleanupAttachments(appId, table, { row })
281
+ // remove any static formula
282
+ await updateRelatedFormula(appId, table, row)
305
283
 
306
284
  let response
307
285
  if (ctx.params.tableId === InternalTables.USER_METADATA) {
@@ -350,6 +328,7 @@ exports.bulkDestroy = async ctx => {
350
328
  }
351
329
  // remove any attachments that were on the rows from object storage
352
330
  await cleanupAttachments(appId, table, { rows })
331
+ await updateRelatedFormula(appId, table, rows)
353
332
  await Promise.all(updates)
354
333
  return { response: { ok: true }, rows }
355
334
  }
@@ -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
 
@@ -0,0 +1,157 @@
1
+ const CouchDB = require("../../../db")
2
+ const { getRowParams } = require("../../../db/utils")
3
+ const {
4
+ outputProcessing,
5
+ processAutoColumn,
6
+ processFormulas,
7
+ } = require("../../../utilities/rowProcessor")
8
+ const { FieldTypes, FormulaTypes } = require("../../../constants")
9
+ const { isEqual } = require("lodash")
10
+ const { cloneDeep } = require("lodash/fp")
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 (appId, table, enrichedRows) => {
19
+ const db = new CouchDB(appId)
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(appId, relatedTable, related, {
61
+ updateFormula: false,
62
+ })
63
+ )
64
+ )
65
+ }
66
+ }
67
+ }
68
+ }
69
+ await Promise.all(promises)
70
+ }
71
+
72
+ exports.updateAllFormulasInTable = async (appId, table) => {
73
+ const db = new CouchDB(appId)
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({ appId }, 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
+ appId,
113
+ table,
114
+ row,
115
+ { oldTable, updateFormula } = { updateFormula: true }
116
+ ) => {
117
+ const db = new CouchDB(appId)
118
+ row.type = "row"
119
+ // process the row before return, to include relationships
120
+ let enrichedRow = await outputProcessing({ appId }, table, cloneDeep(row), {
121
+ squash: false,
122
+ })
123
+ // use enriched row to generate formulas for saving, specifically only use as context
124
+ row = processFormulas(table, row, {
125
+ dynamic: false,
126
+ contextRows: enrichedRow,
127
+ })
128
+
129
+ // don't worry about rev, tables handle rev/lastID updates
130
+ // if another row has been written since processing this will
131
+ // handle the auto ID clash
132
+ if (oldTable && !isEqual(oldTable, table)) {
133
+ try {
134
+ await db.put(table)
135
+ } catch (err) {
136
+ if (err.status === 409) {
137
+ const updatedTable = await db.get(table._id)
138
+ let response = processAutoColumn(null, updatedTable, row, {
139
+ reprocessing: true,
140
+ })
141
+ await db.put(response.table)
142
+ row = response.row
143
+ } else {
144
+ throw err
145
+ }
146
+ }
147
+ }
148
+ const response = await db.put(row)
149
+ // for response, calculate the formulas for the enriched row
150
+ enrichedRow._rev = response.rev
151
+ enrichedRow = await processFormulas(table, enrichedRow, { dynamic: false })
152
+ // this updates the related formulas in other rows based on the relations to this row
153
+ if (updateFormula) {
154
+ await exports.updateRelatedFormula(appId, table, enrichedRow)
155
+ }
156
+ return { row: enrichedRow, table }
157
+ }
@@ -58,22 +58,16 @@ exports.validate = async ({ appId, tableId, row, table }) => {
58
58
  let res
59
59
 
60
60
  // Validate.js doesn't seem to handle array
61
- if (type === FieldTypes.ARRAY) {
62
- const hasValues =
63
- Array.isArray(row[fieldName]) && row[fieldName].length > 0
64
-
65
- // Check values are valid if values are specified
66
- if (hasValues) {
61
+ if (type === FieldTypes.ARRAY && row[fieldName]) {
62
+ if (row[fieldName].length) {
67
63
  row[fieldName].map(val => {
68
64
  if (!constraints.inclusion.includes(val)) {
69
- errors[fieldName] = "Value not in list"
65
+ errors[fieldName] = "Field not in list"
70
66
  }
71
67
  })
72
- }
73
-
74
- // Check for required constraint
75
- if (constraints.presence === true && !hasValues) {
76
- errors[fieldName] = "Required field"
68
+ } else if (constraints.presence && row[fieldName].length === 0) {
69
+ // non required MultiSelect creates an empty array, which should not throw errors
70
+ errors[fieldName] = [`${fieldName} is required`]
77
71
  }
78
72
  } else if (type === FieldTypes.JSON && typeof row[fieldName] === "string") {
79
73
  // this should only happen if there is an error
@@ -5,7 +5,7 @@ const { resolve, join } = require("../../../utilities/centralPath")
5
5
  const uuid = require("uuid")
6
6
  const { ObjectStoreBuckets } = require("../../../constants")
7
7
  const { processString } = require("@budibase/string-templates")
8
- const { getDeployedApps } = require("../../../utilities/workerRequests")
8
+ const { getAllApps } = require("@budibase/backend-core/db")
9
9
  const CouchDB = require("../../../db")
10
10
  const {
11
11
  loadHandlebarsFile,
@@ -17,6 +17,8 @@ const { clientLibraryPath } = require("../../../utilities")
17
17
  const { upload } = require("../../../utilities/fileSystem")
18
18
  const { attachmentsRelativeURL } = require("../../../utilities")
19
19
  const { DocumentTypes } = require("../../../db/utils")
20
+ const AWS = require("aws-sdk")
21
+ const AWS_REGION = env.AWS_REGION ? env.AWS_REGION : "eu-west-1"
20
22
 
21
23
  async function prepareUpload({ s3Key, bucket, metadata, file }) {
22
24
  const response = await upload({
@@ -37,12 +39,18 @@ async function prepareUpload({ s3Key, bucket, metadata, file }) {
37
39
  }
38
40
  }
39
41
 
40
- async function checkForSelfHostedURL(ctx) {
41
- // the "appId" component of the URL may actually be a specific self hosted URL
42
+ async function getAppIdFromUrl(ctx) {
43
+ // the "appId" component of the URL can be the id or the custom url
42
44
  let possibleAppUrl = `/${encodeURI(ctx.params.appId).toLowerCase()}`
43
- const apps = await getDeployedApps()
44
- if (apps[possibleAppUrl] && apps[possibleAppUrl].appId) {
45
- return apps[possibleAppUrl].appId
45
+
46
+ // search prod apps for a url that matches, exclude dev where id is always used
47
+ const apps = await getAllApps(CouchDB, { dev: false })
48
+ const app = apps.filter(
49
+ a => a.url && a.url.toLowerCase() === possibleAppUrl
50
+ )[0]
51
+
52
+ if (app && app.appId) {
53
+ return app.appId
46
54
  } else {
47
55
  return ctx.params.appId
48
56
  }
@@ -75,10 +83,7 @@ exports.uploadFile = async function (ctx) {
75
83
  }
76
84
 
77
85
  exports.serveApp = async function (ctx) {
78
- let appId = ctx.params.appId
79
- if (env.SELF_HOSTED) {
80
- appId = await checkForSelfHostedURL(ctx)
81
- }
86
+ let appId = await getAppIdFromUrl(ctx)
82
87
  const App = require("./templates/BudibaseApp.svelte").default
83
88
  const db = new CouchDB(appId, { skip_setup: true })
84
89
  const appInfo = await db.get(DocumentTypes.APP_METADATA)
@@ -104,3 +109,51 @@ exports.serveClientLibrary = async function (ctx) {
104
109
  root: join(NODE_MODULES_PATH, "@budibase", "client", "dist"),
105
110
  })
106
111
  }
112
+
113
+ exports.getSignedUploadURL = async function (ctx) {
114
+ const database = new CouchDB(ctx.appId)
115
+
116
+ // Ensure datasource is valid
117
+ let datasource
118
+ try {
119
+ const { datasourceId } = ctx.params
120
+ datasource = await database.get(datasourceId)
121
+ if (!datasource) {
122
+ ctx.throw(400, "The specified datasource could not be found")
123
+ }
124
+ } catch (error) {
125
+ ctx.throw(400, "The specified datasource could not be found")
126
+ }
127
+
128
+ // Ensure we aren't using a custom endpoint
129
+ if (datasource?.config?.endpoint) {
130
+ ctx.throw(400, "S3 datasources with custom endpoints are not supported")
131
+ }
132
+
133
+ // Determine type of datasource and generate signed URL
134
+ let signedUrl
135
+ let publicUrl
136
+ if (datasource.source === "S3") {
137
+ const { bucket, key } = ctx.request.body || {}
138
+ if (!bucket || !key) {
139
+ ctx.throw(400, "bucket and key values are required")
140
+ return
141
+ }
142
+ try {
143
+ const s3 = new AWS.S3({
144
+ region: AWS_REGION,
145
+ accessKeyId: datasource?.config?.accessKeyId,
146
+ secretAccessKey: datasource?.config?.secretAccessKey,
147
+ apiVersion: "2006-03-01",
148
+ signatureVersion: "v4",
149
+ })
150
+ const params = { Bucket: bucket, Key: key }
151
+ signedUrl = s3.getSignedUrl("putObject", params)
152
+ publicUrl = `https://${bucket}.s3.${AWS_REGION}.amazonaws.com/${key}`
153
+ } catch (error) {
154
+ ctx.throw(400, error)
155
+ }
156
+ }
157
+
158
+ ctx.body = { signedUrl, publicUrl }
159
+ }
@@ -0,0 +1,183 @@
1
+ const CouchDB = require("../../../db")
2
+ const { FieldTypes, FormulaTypes } = require("../../../constants")
3
+ const { getAllInternalTables, clearColumns } = require("./utils")
4
+ const { doesContainStrings } = require("@budibase/string-templates")
5
+ const { cloneDeep } = require("lodash/fp")
6
+ const { isEqual, uniq } = require("lodash")
7
+ const { updateAllFormulasInTable } = require("../row/staticFormula")
8
+
9
+ function isStaticFormula(column) {
10
+ return (
11
+ column.type === FieldTypes.FORMULA &&
12
+ column.formulaType === FormulaTypes.STATIC
13
+ )
14
+ }
15
+
16
+ /**
17
+ * This retrieves the formula columns from a table schema that use a specified column name
18
+ * in the formula.
19
+ */
20
+ function getFormulaThatUseColumn(table, columnNames) {
21
+ let formula = []
22
+ columnNames = Array.isArray(columnNames) ? columnNames : [columnNames]
23
+ for (let column of Object.values(table.schema)) {
24
+ // not a static formula, or doesn't contain a relationship
25
+ if (!isStaticFormula(column)) {
26
+ continue
27
+ }
28
+ if (!doesContainStrings(column.formula, columnNames)) {
29
+ continue
30
+ }
31
+ formula.push(column.name)
32
+ }
33
+ return formula
34
+ }
35
+
36
+ /**
37
+ * This functions checks for when a related table, column or related column is deleted, if any
38
+ * tables need to have the formula column removed.
39
+ */
40
+ async function checkIfFormulaNeedsCleared(
41
+ appId,
42
+ table,
43
+ { oldTable, deletion }
44
+ ) {
45
+ const db = new CouchDB(appId)
46
+ // start by retrieving all tables, remove the current table from the list
47
+ const tables = (await getAllInternalTables(appId)).filter(
48
+ tbl => tbl._id !== table._id
49
+ )
50
+ const schemaToUse = oldTable ? oldTable.schema : table.schema
51
+ let removedColumns = Object.values(schemaToUse).filter(
52
+ column => deletion || !table.schema[column.name]
53
+ )
54
+ // remove any formula columns that used related columns
55
+ for (let removed of removedColumns) {
56
+ let tableToUse = table
57
+ // if relationship, get the related table
58
+ if (removed.type === FieldTypes.LINK) {
59
+ tableToUse = tables.find(table => table._id === removed.tableId)
60
+ }
61
+ const columnsToDelete = getFormulaThatUseColumn(tableToUse, removed.name)
62
+ if (columnsToDelete.length > 0) {
63
+ await clearColumns(db, table, columnsToDelete)
64
+ }
65
+ // need a special case, where a column has been removed from this table, but was used
66
+ // in a different, related tables formula
67
+ if (!table.relatedFormula) {
68
+ continue
69
+ }
70
+ for (let relatedTableId of table.relatedFormula) {
71
+ const relatedColumns = Object.values(table.schema).filter(
72
+ column => column.tableId === relatedTableId
73
+ )
74
+ const relatedTable = tables.find(table => table._id === relatedTableId)
75
+ // look to see if the column was used in a relationship formula,
76
+ // relationships won't be used for this
77
+ if (relatedTable && relatedColumns && removed.type !== FieldTypes.LINK) {
78
+ let relatedFormulaToRemove = []
79
+ for (let column of relatedColumns) {
80
+ relatedFormulaToRemove = relatedFormulaToRemove.concat(
81
+ getFormulaThatUseColumn(relatedTable, [
82
+ column.fieldName,
83
+ removed.name,
84
+ ])
85
+ )
86
+ }
87
+ if (relatedFormulaToRemove.length > 0) {
88
+ await clearColumns(db, relatedTable, uniq(relatedFormulaToRemove))
89
+ }
90
+ }
91
+ }
92
+ }
93
+ }
94
+
95
+ /**
96
+ * This function adds a note to related tables that they are
97
+ * used in a static formula - so that the link controller
98
+ * can manage hydrating related rows formula fields. This is
99
+ * specifically only for static formula.
100
+ */
101
+ async function updateRelatedFormulaLinksOnTables(
102
+ appId,
103
+ table,
104
+ { deletion } = { deletion: false }
105
+ ) {
106
+ const db = new CouchDB(appId)
107
+ // start by retrieving all tables, remove the current table from the list
108
+ const tables = (await getAllInternalTables(appId)).filter(
109
+ tbl => tbl._id !== table._id
110
+ )
111
+ // clone the tables, so we can compare at end
112
+ const initialTables = cloneDeep(tables)
113
+ // first find the related column names
114
+ const relatedColumns = Object.values(table.schema).filter(
115
+ col => col.type === FieldTypes.LINK
116
+ )
117
+ // we start by removing the formula field from all tables
118
+ for (let otherTable of tables) {
119
+ if (!otherTable.relatedFormula) {
120
+ continue
121
+ }
122
+ const index = otherTable.relatedFormula.indexOf(table._id)
123
+ if (index !== -1) {
124
+ otherTable.relatedFormula.splice(index, 1)
125
+ }
126
+ }
127
+ // if deleting, just remove the table IDs, don't try add
128
+ if (!deletion) {
129
+ for (let relatedCol of relatedColumns) {
130
+ let columns = getFormulaThatUseColumn(table, relatedCol.name)
131
+ if (!columns || columns.length === 0) {
132
+ continue
133
+ }
134
+ const relatedTable = tables.find(
135
+ related => related._id === relatedCol.tableId
136
+ )
137
+ // check if the table is already in the list of related formula, if it isn't, then add it
138
+ if (
139
+ relatedTable &&
140
+ (!relatedTable.relatedFormula ||
141
+ !relatedTable.relatedFormula.includes(table._id))
142
+ ) {
143
+ relatedTable.relatedFormula = relatedTable.relatedFormula
144
+ ? [...relatedTable.relatedFormula, table._id]
145
+ : [table._id]
146
+ }
147
+ }
148
+ }
149
+ // now we just need to compare all the tables and see if any need saved
150
+ for (let initial of initialTables) {
151
+ const found = tables.find(tbl => initial._id === tbl._id)
152
+ if (found && !isEqual(initial, found)) {
153
+ await db.put(found)
154
+ }
155
+ }
156
+ }
157
+
158
+ async function checkIfFormulaUpdated(appId, table, { oldTable }) {
159
+ // look to see if any formula values have changed
160
+ const shouldUpdate = Object.values(table.schema).find(
161
+ column =>
162
+ isStaticFormula(column) &&
163
+ (!oldTable ||
164
+ !oldTable.schema[column.name] ||
165
+ !isEqual(oldTable.schema[column.name], column))
166
+ )
167
+ // if a static formula column has updated, then need to run the update
168
+ if (shouldUpdate != null) {
169
+ await updateAllFormulasInTable(appId, table)
170
+ }
171
+ }
172
+
173
+ exports.runStaticFormulaChecks = async (
174
+ appId,
175
+ table,
176
+ { oldTable, deletion }
177
+ ) => {
178
+ await updateRelatedFormulaLinksOnTables(appId, table, { deletion })
179
+ await checkIfFormulaNeedsCleared(appId, table, { oldTable, deletion })
180
+ if (!deletion) {
181
+ await checkIfFormulaUpdated(appId, table, { oldTable })
182
+ }
183
+ }