@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
@@ -180,6 +180,8 @@ function processAutoColumn(
180
180
  }
181
181
  exports.processAutoColumn = processAutoColumn
182
182
 
183
+ exports.processFormulas = processFormulas
184
+
183
185
  /**
184
186
  * This will coerce a value to the correct types based on the type transform map
185
187
  * @param {object} row The value to coerce
@@ -229,11 +231,12 @@ exports.inputProcessing = (
229
231
  }
230
232
  continue
231
233
  }
232
- // specific case to delete formula values if they get saved
233
- // type coercion cannot completely remove the field, so have to do it here
234
+ // remove any formula values, they are to be generated
234
235
  if (field.type === FieldTypes.FORMULA) {
235
236
  delete clonedRow[key]
236
- } else {
237
+ }
238
+ // otherwise coerce what is there to correct types
239
+ else {
237
240
  clonedRow[key] = exports.coerce(value, field.type)
238
241
  }
239
242
  }
@@ -250,7 +253,7 @@ exports.inputProcessing = (
250
253
  /**
251
254
  * This function enriches the input rows with anything they are supposed to contain, for example
252
255
  * link records or attachment links.
253
- * @param {object} ctx the request which is looking for enriched rows.
256
+ * @param {string} appId the app in which the request is looking for enriched rows.
254
257
  * @param {object} table the table from which these rows came from originally, this is used to determine
255
258
  * the schema of the rows and then enrich.
256
259
  * @param {object[]|object} rows the rows which are to be enriched.
@@ -258,22 +261,21 @@ exports.inputProcessing = (
258
261
  * @returns {object[]|object} the enriched rows will be returned.
259
262
  */
260
263
  exports.outputProcessing = async (
261
- ctx,
264
+ { appId },
262
265
  table,
263
266
  rows,
264
267
  opts = { squash: true }
265
268
  ) => {
266
- const appId = ctx.appId
267
269
  let wasArray = true
268
270
  if (!(rows instanceof Array)) {
269
271
  rows = [rows]
270
272
  wasArray = false
271
273
  }
272
274
  // attach any linked row information
273
- let enriched = await linkRows.attachFullLinkedDocs(ctx, table, rows)
275
+ let enriched = await linkRows.attachFullLinkedDocs(appId, table, rows)
274
276
 
275
277
  // process formulas
276
- enriched = processFormulas(table, enriched)
278
+ enriched = processFormulas(table, enriched, { dynamic: true })
277
279
 
278
280
  // update the attachments URL depending on hosting
279
281
  for (let [property, column] of Object.entries(table.schema)) {
@@ -305,9 +307,15 @@ exports.outputProcessing = async (
305
307
  * @param {any} row optional - the row being removed.
306
308
  * @param {any} rows optional - if multiple rows being deleted can do this in bulk.
307
309
  * @param {any} oldRow optional - if updating a row this will determine the difference.
310
+ * @param {any} oldTable optional - if updating a table, can supply the old table to look for
311
+ * deleted attachment columns.
308
312
  * @return {Promise<void>} When all attachments have been removed this will return.
309
313
  */
310
- exports.cleanupAttachments = async (appId, table, { row, rows, oldRow }) => {
314
+ exports.cleanupAttachments = async (
315
+ appId,
316
+ table,
317
+ { row, rows, oldRow, oldTable }
318
+ ) => {
311
319
  if (!isProdAppID(appId)) {
312
320
  const prodAppId = getDeployedAppID(appId)
313
321
  // if prod exists, then don't allow deleting
@@ -322,12 +330,16 @@ exports.cleanupAttachments = async (appId, table, { row, rows, oldRow }) => {
322
330
  files = files.concat(row[key].map(attachment => attachment.key))
323
331
  }
324
332
  }
325
- for (let [key, schema] of Object.entries(table.schema)) {
333
+ const schemaToUse = oldTable ? oldTable.schema : table.schema
334
+ for (let [key, schema] of Object.entries(schemaToUse)) {
326
335
  if (schema.type !== FieldTypes.ATTACHMENT) {
327
336
  continue
328
337
  }
329
- // if updating, need to manage the differences
330
- if (oldRow && row) {
338
+ // old table had this column, new table doesn't - delete it
339
+ if (oldTable && !table.schema[key]) {
340
+ rows.forEach(row => addFiles(row, key))
341
+ } else if (oldRow && row) {
342
+ // if updating, need to manage the differences
331
343
  files = files.concat(getRemovedAttachmentKeys(oldRow, row, key))
332
344
  } else if (row) {
333
345
  addFiles(row, key)
@@ -1,23 +1,39 @@
1
- const { FieldTypes } = require("../../constants")
1
+ const { FieldTypes, FormulaTypes } = require("../../constants")
2
2
  const { processStringSync } = require("@budibase/string-templates")
3
3
 
4
4
  /**
5
5
  * Looks through the rows provided and finds formulas - which it then processes.
6
6
  */
7
- exports.processFormulas = (table, rows) => {
7
+ exports.processFormulas = (
8
+ table,
9
+ rows,
10
+ { dynamic, contextRows } = { dynamic: true }
11
+ ) => {
8
12
  const single = !Array.isArray(rows)
9
13
  if (single) {
10
14
  rows = [rows]
15
+ contextRows = contextRows ? [contextRows] : contextRows
11
16
  }
12
17
  for (let [column, schema] of Object.entries(table.schema)) {
13
- if (schema.type !== FieldTypes.FORMULA) {
18
+ const isStatic = schema.formulaType === FormulaTypes.STATIC
19
+ if (
20
+ schema.type !== FieldTypes.FORMULA ||
21
+ (dynamic && isStatic) ||
22
+ (!dynamic && !isStatic)
23
+ ) {
14
24
  continue
15
25
  }
16
26
  // iterate through rows and process formula
17
- rows = rows.map(row => ({
18
- ...row,
19
- [column]: processStringSync(schema.formula, row),
20
- }))
27
+ for (let i = 0; i < rows.length; i++) {
28
+ if (schema.formula) {
29
+ let row = rows[i]
30
+ let context = contextRows ? contextRows[i] : row
31
+ rows[i] = {
32
+ ...row,
33
+ [column]: processStringSync(schema.formula, context),
34
+ }
35
+ }
36
+ }
21
37
  }
22
38
  return single ? rows[0] : rows
23
39
  }
@@ -0,0 +1,72 @@
1
+ const getTenantId = jest.fn()
2
+ jest.mock("@budibase/backend-core/tenancy", () => ({
3
+ getTenantId
4
+ }))
5
+ const usageQuota = require("../../usageQuota")
6
+ const env = require("../../../environment")
7
+
8
+ class TestConfiguration {
9
+ constructor() {
10
+ this.enableQuotas()
11
+ }
12
+
13
+ enableQuotas = () => {
14
+ env.USE_QUOTAS = 1
15
+ }
16
+
17
+ disableQuotas = () => {
18
+ env.USE_QUOTAS = null
19
+ }
20
+
21
+ setTenantId = (tenantId) => {
22
+ getTenantId.mockReturnValue(tenantId)
23
+ }
24
+
25
+ setExcludedTenants = (tenants) => {
26
+ env.EXCLUDE_QUOTAS_TENANTS = tenants
27
+ }
28
+
29
+ reset = () => {
30
+ this.disableQuotas()
31
+ this.setExcludedTenants(null)
32
+ }
33
+ }
34
+
35
+ describe("usageQuota", () => {
36
+ let config
37
+
38
+ beforeEach(() => {
39
+ config = new TestConfiguration()
40
+ })
41
+
42
+ afterEach(() => {
43
+ config.reset()
44
+ })
45
+
46
+ describe("useQuotas", () => {
47
+ it("works when no settings have been provided", () => {
48
+ config.reset()
49
+ expect(usageQuota.useQuotas()).toBe(false)
50
+ })
51
+ it("honours USE_QUOTAS setting", () => {
52
+ config.disableQuotas()
53
+ expect(usageQuota.useQuotas()).toBe(false)
54
+
55
+ config.enableQuotas()
56
+ expect(usageQuota.useQuotas()).toBe(true)
57
+ })
58
+ it("honours EXCLUDE_QUOTAS_TENANTS setting", () => {
59
+ config.setTenantId("test")
60
+
61
+ // tenantId is in the list
62
+ config.setExcludedTenants("test, test2, test2")
63
+ expect(usageQuota.useQuotas()).toBe(false)
64
+ config.setExcludedTenants("test,test2,test2")
65
+ expect(usageQuota.useQuotas()).toBe(false)
66
+
67
+ // tenantId is not in the list
68
+ config.setTenantId("other")
69
+ expect(usageQuota.useQuotas()).toBe(true)
70
+ })
71
+ })
72
+ })
@@ -0,0 +1,92 @@
1
+ const env = require("../../environment")
2
+ const { getGlobalDB, getTenantId } = require("@budibase/backend-core/tenancy")
3
+ const {
4
+ StaticDatabases,
5
+ generateNewUsageQuotaDoc,
6
+ } = require("@budibase/backend-core/db")
7
+
8
+ exports.useQuotas = () => {
9
+ // check if quotas are enabled
10
+ if (env.USE_QUOTAS) {
11
+ // check if there are any tenants without limits
12
+ if (env.EXCLUDE_QUOTAS_TENANTS) {
13
+ const excludedTenants = env.EXCLUDE_QUOTAS_TENANTS.replace(
14
+ /\s/g,
15
+ ""
16
+ ).split(",")
17
+ const tenantId = getTenantId()
18
+ if (excludedTenants.includes(tenantId)) {
19
+ return false
20
+ }
21
+ }
22
+ return true
23
+ }
24
+ return false
25
+ }
26
+
27
+ exports.Properties = {
28
+ ROW: "rows",
29
+ UPLOAD: "storage", // doesn't work yet
30
+ VIEW: "views", // doesn't work yet
31
+ USER: "users", // doesn't work yet
32
+ AUTOMATION: "automationRuns", // doesn't work yet
33
+ APPS: "apps",
34
+ EMAILS: "emails", // doesn't work yet
35
+ }
36
+
37
+ exports.getUsageQuotaDoc = async db => {
38
+ let quota
39
+ try {
40
+ quota = await db.get(StaticDatabases.GLOBAL.docs.usageQuota)
41
+ } catch (err) {
42
+ // doc doesn't exist. Create it
43
+ quota = generateNewUsageQuotaDoc()
44
+ const response = await db.put(quota)
45
+ quota._rev = response.rev
46
+ }
47
+
48
+ return quota
49
+ }
50
+
51
+ /**
52
+ * Given a specified tenantId this will add to the usage object for the specified property.
53
+ * @param {string} property The property which is to be added to (within the nested usageQuota object).
54
+ * @param {number} usage The amount (this can be negative) to adjust the number by.
55
+ * @returns {Promise<void>} When this completes the API key will now be up to date - the quota period may have
56
+ * also been reset after this call.
57
+ */
58
+ exports.update = async (property, usage, opts = { dryRun: false }) => {
59
+ if (!exports.useQuotas()) {
60
+ return
61
+ }
62
+
63
+ try {
64
+ const db = getGlobalDB()
65
+ const quota = await exports.getUsageQuotaDoc(db)
66
+
67
+ // increment the quota
68
+ quota.usageQuota[property] += usage
69
+
70
+ if (
71
+ quota.usageQuota[property] > quota.usageLimits[property] &&
72
+ usage > 0 // allow for decrementing usage when the quota is already exceeded
73
+ ) {
74
+ throw new Error(
75
+ `You have exceeded your usage quota of ${quota.usageLimits[property]} ${property}.`
76
+ )
77
+ }
78
+
79
+ if (quota.usageQuota[property] < 0) {
80
+ // never go negative if the quota has previously been exceeded
81
+ quota.usageQuota[property] = 0
82
+ }
83
+
84
+ // update the usage quotas
85
+ if (!opts.dryRun) {
86
+ await db.put(quota)
87
+ }
88
+ } catch (err) {
89
+ console.error(`Error updating usage quotas for ${property}`, err)
90
+ throw err
91
+ }
92
+ }
@@ -0,0 +1,74 @@
1
+ const { getRowParams, USER_METDATA_PREFIX } = require("../../db/utils")
2
+ const CouchDB = require("../../db")
3
+ const { isDevAppID, getDevelopmentAppID } = require("@budibase/backend-core/db")
4
+
5
+ const ROW_EXCLUSIONS = [USER_METDATA_PREFIX]
6
+
7
+ const getAppPairs = appIds => {
8
+ // collect the app ids into dev / prod pairs
9
+ // keyed by the dev app id
10
+ const pairs = {}
11
+ for (let appId of appIds) {
12
+ const devId = getDevelopmentAppID(appId)
13
+ if (!pairs[devId]) {
14
+ pairs[devId] = {}
15
+ }
16
+ if (isDevAppID(appId)) {
17
+ pairs[devId].devId = appId
18
+ } else {
19
+ pairs[devId].prodId = appId
20
+ }
21
+ }
22
+ return pairs
23
+ }
24
+
25
+ const getAppRows = async appId => {
26
+ const appDb = new CouchDB(appId)
27
+ const response = await appDb.allDocs(
28
+ getRowParams(null, null, {
29
+ include_docs: false,
30
+ })
31
+ )
32
+ return response.rows
33
+ .map(r => r.id)
34
+ .filter(id => {
35
+ for (let exclusion of ROW_EXCLUSIONS) {
36
+ if (id.startsWith(exclusion)) {
37
+ return false
38
+ }
39
+ }
40
+ return true
41
+ })
42
+ }
43
+
44
+ /**
45
+ * Return a set of all rows in the given app ids.
46
+ * The returned rows will be unique on a per dev/prod app basis.
47
+ * Rows duplicates may exist across apps due to data import so they are not filtered out.
48
+ */
49
+ exports.getUniqueRows = async appIds => {
50
+ let uniqueRows = []
51
+ const pairs = getAppPairs(appIds)
52
+
53
+ for (let pair of Object.values(pairs)) {
54
+ let appRows = []
55
+ for (let appId of [pair.devId, pair.prodId]) {
56
+ if (!appId) {
57
+ continue
58
+ }
59
+ try {
60
+ appRows.push(await getAppRows(appId))
61
+ } catch (e) {
62
+ console.error(e)
63
+ // don't error out if we can't count the app rows, just continue
64
+ }
65
+ }
66
+
67
+ // ensure uniqueness on a per app pair basis
68
+ // this can't be done on all rows because app import results in
69
+ // duplicate row ids across apps
70
+ uniqueRows = uniqueRows.concat(...new Set(appRows))
71
+ }
72
+
73
+ return uniqueRows
74
+ }
@@ -0,0 +1,18 @@
1
+ // UNUSED CODE
2
+ // Preserved for future use
3
+
4
+ /* eslint-disable no-unused-vars */
5
+
6
+ function getNewQuotaReset() {
7
+ return Date.now() + 2592000000
8
+ }
9
+
10
+ function resetQuotasIfRequired(quota) {
11
+ // Check if the quota needs reset
12
+ if (Date.now() >= quota.quotaReset) {
13
+ quota.quotaReset = getNewQuotaReset()
14
+ for (let prop of Object.keys(quota.usageQuota)) {
15
+ quota.usageQuota[prop] = 0
16
+ }
17
+ }
18
+ }
@@ -58,29 +58,6 @@ exports.sendSmtpEmail = async (to, from, subject, contents, automation) => {
58
58
  return response.json()
59
59
  }
60
60
 
61
- exports.getDeployedApps = async () => {
62
- try {
63
- const response = await fetch(
64
- checkSlashesInUrl(env.WORKER_URL + `/api/apps`),
65
- request(null, {
66
- method: "GET",
67
- })
68
- )
69
- const json = await response.json()
70
- const apps = {}
71
- for (let [key, value] of Object.entries(json)) {
72
- if (value.url) {
73
- value.url = value.url.toLowerCase()
74
- apps[key.toLowerCase()] = value
75
- }
76
- }
77
- return apps
78
- } catch (err) {
79
- // error, cannot determine deployed apps, don't stop app creation - sort this later
80
- return {}
81
- }
82
- }
83
-
84
61
  exports.getGlobalSelf = async (ctx, appId = null) => {
85
62
  const endpoint = `/api/global/users/self`
86
63
  const response = await fetch(