@budibase/server 1.0.46 → 1.0.49-alpha.1

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 (277) hide show
  1. package/builder/assets/index.ba78c49d.css +1 -0
  2. package/builder/assets/{index.e9bff1c6.js → index.ed1cf508.js} +237 -234
  3. package/builder/assets/{vendor.f9faf6ab.js → vendor.d526bfe8.js} +12 -12
  4. package/builder/index.html +3 -3
  5. package/coverage/clover.xml +2368 -2016
  6. package/coverage/coverage-final.json +101 -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 +38 -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/permission.js.html +15 -15
  22. package/coverage/lcov-report/api/controllers/query/index.html +1 -1
  23. package/coverage/lcov-report/api/controllers/query/index.js.html +19 -19
  24. package/coverage/lcov-report/api/controllers/query/validation.js.html +6 -6
  25. package/coverage/lcov-report/api/controllers/role.js.html +9 -9
  26. package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
  27. package/coverage/lcov-report/api/controllers/row/external.js.html +16 -16
  28. package/coverage/lcov-report/api/controllers/row/index.html +48 -33
  29. package/coverage/lcov-report/api/controllers/row/index.js.html +26 -26
  30. package/coverage/lcov-report/api/controllers/row/internal.js.html +95 -158
  31. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +41 -17
  32. package/coverage/lcov-report/api/controllers/row/staticFormula.js.html +551 -0
  33. package/coverage/lcov-report/api/controllers/row/utils.js.html +39 -57
  34. package/coverage/lcov-report/api/controllers/screen.js.html +7 -7
  35. package/coverage/lcov-report/api/controllers/script.js.html +4 -4
  36. package/coverage/lcov-report/api/controllers/table/bulkFormula.js.html +629 -0
  37. package/coverage/lcov-report/api/controllers/table/external.js.html +12 -12
  38. package/coverage/lcov-report/api/controllers/table/index.html +49 -34
  39. package/coverage/lcov-report/api/controllers/table/index.js.html +53 -77
  40. package/coverage/lcov-report/api/controllers/table/internal.js.html +59 -35
  41. package/coverage/lcov-report/api/controllers/table/utils.js.html +283 -76
  42. package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
  43. package/coverage/lcov-report/api/controllers/user.js.html +30 -24
  44. package/coverage/lcov-report/api/controllers/view/exporters.js.html +4 -4
  45. package/coverage/lcov-report/api/controllers/view/index.html +1 -1
  46. package/coverage/lcov-report/api/controllers/view/index.js.html +14 -14
  47. package/coverage/lcov-report/api/controllers/view/utils.js.html +11 -11
  48. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +7 -7
  49. package/coverage/lcov-report/api/controllers/webhook.js.html +15 -15
  50. package/coverage/lcov-report/api/index.html +1 -1
  51. package/coverage/lcov-report/api/index.js.html +10 -10
  52. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  53. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/application.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/dev.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/index.html +7 -22
  64. package/coverage/lcov-report/api/routes/index.js.html +4 -10
  65. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/query.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/row.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  75. package/coverage/lcov-report/api/routes/static.js.html +17 -2
  76. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  77. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  78. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  79. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  80. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  81. package/coverage/lcov-report/automations/actions.js.html +24 -24
  82. package/coverage/lcov-report/automations/automationUtils.js.html +5 -5
  83. package/coverage/lcov-report/automations/bullboard.js.html +16 -16
  84. package/coverage/lcov-report/automations/index.html +1 -1
  85. package/coverage/lcov-report/automations/index.js.html +1 -1
  86. package/coverage/lcov-report/automations/steps/bash.js.html +6 -6
  87. package/coverage/lcov-report/automations/steps/createRow.js.html +13 -19
  88. package/coverage/lcov-report/automations/steps/delay.js.html +4 -4
  89. package/coverage/lcov-report/automations/steps/deleteRow.js.html +11 -20
  90. package/coverage/lcov-report/automations/steps/discord.js.html +7 -7
  91. package/coverage/lcov-report/automations/steps/executeQuery.js.html +6 -6
  92. package/coverage/lcov-report/automations/steps/executeScript.js.html +6 -6
  93. package/coverage/lcov-report/automations/steps/filter.js.html +7 -7
  94. package/coverage/lcov-report/automations/steps/index.html +13 -13
  95. package/coverage/lcov-report/automations/steps/integromat.js.html +5 -5
  96. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +8 -8
  97. package/coverage/lcov-report/automations/steps/queryRows.js.html +10 -10
  98. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +5 -5
  99. package/coverage/lcov-report/automations/steps/serverLog.js.html +3 -3
  100. package/coverage/lcov-report/automations/steps/slack.js.html +5 -5
  101. package/coverage/lcov-report/automations/steps/updateRow.js.html +6 -6
  102. package/coverage/lcov-report/automations/steps/utils.js.html +3 -3
  103. package/coverage/lcov-report/automations/steps/zapier.js.html +5 -5
  104. package/coverage/lcov-report/automations/triggerInfo/app.js.html +2 -2
  105. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +2 -2
  106. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  107. package/coverage/lcov-report/automations/triggerInfo/index.js.html +8 -8
  108. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +2 -2
  109. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +2 -2
  110. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +2 -2
  111. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +2 -2
  112. package/coverage/lcov-report/automations/triggers.js.html +18 -18
  113. package/coverage/lcov-report/automations/utils.js.html +32 -32
  114. package/coverage/lcov-report/constants/index.html +11 -11
  115. package/coverage/lcov-report/constants/index.js.html +68 -32
  116. package/coverage/lcov-report/constants/layouts.js.html +5 -5
  117. package/coverage/lcov-report/constants/screens.js.html +4 -4
  118. package/coverage/lcov-report/db/client.js.html +18 -18
  119. package/coverage/lcov-report/db/inMemoryView.js.html +7 -7
  120. package/coverage/lcov-report/db/index.html +15 -15
  121. package/coverage/lcov-report/db/index.js.html +3 -3
  122. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +18 -18
  123. package/coverage/lcov-report/db/linkedRows/index.html +10 -10
  124. package/coverage/lcov-report/db/linkedRows/index.js.html +38 -41
  125. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +15 -15
  126. package/coverage/lcov-report/db/newid.js.html +4 -4
  127. package/coverage/lcov-report/db/utils.js.html +86 -86
  128. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  129. package/coverage/lcov-report/events/index.html +1 -1
  130. package/coverage/lcov-report/events/index.js.html +5 -5
  131. package/coverage/lcov-report/events/utils.js.html +3 -3
  132. package/coverage/lcov-report/index.html +131 -101
  133. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  134. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  135. package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
  136. package/coverage/lcov-report/middleware/authorized.js.html +166 -64
  137. package/coverage/lcov-report/middleware/builder.js.html +8 -8
  138. package/coverage/lcov-report/middleware/currentapp.js.html +29 -29
  139. package/coverage/lcov-report/middleware/index.html +30 -30
  140. package/coverage/lcov-report/middleware/joi-validator.js.html +3 -3
  141. package/coverage/lcov-report/middleware/resourceId.js.html +1 -1
  142. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  143. package/coverage/lcov-report/middleware/usageQuota.js.html +275 -41
  144. package/coverage/lcov-report/middleware/utils.js.html +2 -2
  145. package/coverage/lcov-report/migrations/usageQuotas/index.html +141 -0
  146. package/coverage/lcov-report/migrations/usageQuotas/index.js.html +152 -0
  147. package/coverage/lcov-report/{api/routes/hosting.js.html → migrations/usageQuotas/syncApps.js.html} +42 -27
  148. package/coverage/lcov-report/{api/controllers/hosting.js.html → migrations/usageQuotas/syncRows.js.html} +41 -44
  149. package/coverage/lcov-report/threads/automation.js.html +1 -1
  150. package/coverage/lcov-report/threads/index.html +15 -15
  151. package/coverage/lcov-report/threads/index.js.html +10 -10
  152. package/coverage/lcov-report/threads/query.js.html +23 -8
  153. package/coverage/lcov-report/threads/utils.js.html +14 -14
  154. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  155. package/coverage/lcov-report/utilities/budibaseDir.js.html +7 -7
  156. package/coverage/lcov-report/utilities/centralPath.js.html +4 -4
  157. package/coverage/lcov-report/utilities/csvParser.js.html +6 -6
  158. package/coverage/lcov-report/utilities/global.js.html +26 -26
  159. package/coverage/lcov-report/utilities/index.html +19 -19
  160. package/coverage/lcov-report/utilities/index.js.html +25 -25
  161. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +8 -8
  162. package/coverage/lcov-report/utilities/queue/index.html +1 -1
  163. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  164. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  165. package/coverage/lcov-report/utilities/rowProcessor/index.html +27 -27
  166. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +115 -79
  167. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +75 -27
  168. package/coverage/lcov-report/utilities/scriptRunner.js.html +5 -5
  169. package/coverage/lcov-report/utilities/security.js.html +8 -8
  170. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  171. package/coverage/lcov-report/utilities/usageQuota/index.html +141 -0
  172. package/coverage/lcov-report/utilities/usageQuota/index.js.html +356 -0
  173. package/coverage/lcov-report/utilities/usageQuota/rows.js.html +302 -0
  174. package/coverage/lcov-report/utilities/usageQuota/usageQuoteReset.js.html +134 -0
  175. package/coverage/lcov-report/utilities/users.js.html +5 -5
  176. package/coverage/lcov-report/utilities/workerRequests.js.html +39 -108
  177. package/coverage/lcov.info +3719 -3046
  178. package/dist/api/controllers/application.js +49 -20
  179. package/dist/api/controllers/auth.js +4 -0
  180. package/dist/api/controllers/datasource.js +11 -0
  181. package/dist/api/controllers/row/ExternalRequest.js +2 -1
  182. package/dist/api/controllers/row/internal.js +30 -53
  183. package/dist/api/controllers/row/internalSearch.js +12 -4
  184. package/dist/api/controllers/row/staticFormula.js +147 -0
  185. package/dist/api/controllers/row/utils.js +6 -8
  186. package/dist/api/controllers/static/index.js +59 -10
  187. package/dist/api/controllers/table/bulkFormula.js +160 -0
  188. package/dist/api/controllers/table/index.js +12 -9
  189. package/dist/api/controllers/table/internal.js +9 -0
  190. package/dist/api/controllers/table/utils.js +56 -14
  191. package/dist/api/controllers/user.js +2 -0
  192. package/dist/api/routes/index.js +0 -2
  193. package/dist/api/routes/static.js +2 -1
  194. package/dist/automations/steps/createRow.js +2 -4
  195. package/dist/automations/steps/deleteRow.js +1 -4
  196. package/dist/constants/index.js +12 -5
  197. package/dist/db/linkedRows/index.js +4 -5
  198. package/dist/definitions/datasource.js +1 -0
  199. package/dist/environment.js +1 -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 -8
  204. package/dist/middleware/authorized.js +55 -30
  205. package/dist/middleware/usageQuota.js +82 -13
  206. package/dist/migrations/usageQuotas/index.js +24 -0
  207. package/dist/migrations/usageQuotas/syncApps.js +26 -0
  208. package/dist/migrations/usageQuotas/syncRows.js +29 -0
  209. package/dist/package.json +8 -5
  210. package/dist/tests/utilities/TestConfiguration.js +10 -6
  211. package/dist/threads/query.js +4 -0
  212. package/dist/tsconfig.tsbuildinfo +1 -1
  213. package/dist/utilities/rowProcessor/index.js +18 -11
  214. package/dist/utilities/rowProcessor/utils.js +14 -4
  215. package/dist/utilities/{usageQuota.js → usageQuota/index.js} +44 -31
  216. package/dist/utilities/usageQuota/rows.js +77 -0
  217. package/dist/utilities/usageQuota/usageQuoteReset.js +16 -0
  218. package/dist/utilities/workerRequests.js +0 -20
  219. package/nodemon.json +1 -1
  220. package/package.json +9 -6
  221. package/src/api/controllers/application.js +52 -24
  222. package/src/api/controllers/auth.js +4 -0
  223. package/src/api/controllers/datasource.js +12 -0
  224. package/src/api/controllers/row/ExternalRequest.ts +1 -1
  225. package/src/api/controllers/row/internal.js +29 -50
  226. package/src/api/controllers/row/internalSearch.js +12 -4
  227. package/src/api/controllers/row/staticFormula.js +157 -0
  228. package/src/api/controllers/row/utils.js +6 -12
  229. package/src/api/controllers/static/index.js +63 -10
  230. package/src/api/controllers/table/bulkFormula.js +183 -0
  231. package/src/api/controllers/table/index.js +16 -24
  232. package/src/api/controllers/table/internal.js +9 -1
  233. package/src/api/controllers/table/utils.js +80 -11
  234. package/src/api/controllers/user.js +2 -0
  235. package/src/api/routes/index.js +0 -2
  236. package/src/api/routes/static.js +5 -0
  237. package/src/api/routes/tests/application.spec.js +2 -2
  238. package/src/api/routes/tests/auth.spec.js +1 -2
  239. package/src/api/routes/tests/static.spec.js +98 -0
  240. package/src/automations/steps/createRow.js +2 -4
  241. package/src/automations/steps/deleteRow.js +1 -4
  242. package/src/constants/index.js +17 -5
  243. package/src/db/linkedRows/index.js +4 -5
  244. package/src/definitions/common.ts +3 -0
  245. package/src/definitions/datasource.ts +3 -0
  246. package/src/environment.js +1 -0
  247. package/src/integrations/base/sql.ts +14 -7
  248. package/src/integrations/googlesheets.ts +353 -0
  249. package/src/integrations/index.ts +3 -0
  250. package/src/integrations/tests/sql.spec.js +16 -0
  251. package/src/integrations/utils.ts +71 -10
  252. package/src/middleware/authorized.js +64 -30
  253. package/src/middleware/tests/authorized.spec.js +6 -4
  254. package/src/middleware/tests/usageQuota.spec.js +18 -24
  255. package/src/middleware/usageQuota.js +93 -15
  256. package/src/migrations/tests/usageQuotas/index.spec.js +27 -0
  257. package/src/migrations/tests/usageQuotas/syncApps.spec.js +37 -0
  258. package/src/migrations/tests/usageQuotas/syncRows.spec.js +43 -0
  259. package/src/migrations/usageQuotas/index.js +24 -0
  260. package/src/migrations/usageQuotas/syncApps.js +18 -0
  261. package/src/migrations/usageQuotas/syncRows.js +21 -0
  262. package/src/tests/utilities/TestConfiguration.js +10 -6
  263. package/src/threads/query.js +5 -0
  264. package/src/utilities/rowProcessor/index.js +24 -12
  265. package/src/utilities/rowProcessor/utils.js +23 -7
  266. package/src/utilities/tests/usageQuota/usageQuota.spec.js +72 -0
  267. package/src/utilities/usageQuota/index.js +92 -0
  268. package/src/utilities/usageQuota/rows.js +74 -0
  269. package/src/utilities/usageQuota/usageQuoteReset.js +18 -0
  270. package/src/utilities/workerRequests.js +0 -23
  271. package/builder/assets/index.39e01193.css +0 -1
  272. package/dist/api/controllers/hosting.js +0 -30
  273. package/dist/api/routes/hosting.js +0 -11
  274. package/src/api/controllers/hosting.js +0 -22
  275. package/src/api/routes/hosting.js +0 -13
  276. package/src/api/routes/tests/hosting.spec.js +0 -36
  277. package/src/utilities/usageQuota.js +0 -73
@@ -1,11 +1,5 @@
1
1
  jest.mock("../../db")
2
2
  jest.mock("../../utilities/usageQuota")
3
- jest.mock("../../environment", () => ({
4
- isTest: () => true,
5
- isProd: () => false,
6
- isDev: () => true,
7
- _set: () => {},
8
- }))
9
3
  jest.mock("@budibase/backend-core/tenancy", () => ({
10
4
  getTenantId: () => "testing123"
11
5
  }))
@@ -29,9 +23,10 @@ class TestConfiguration {
29
23
  },
30
24
  req: {
31
25
  method: "POST",
32
- url: "/rows"
26
+ url: "/applications"
33
27
  }
34
28
  }
29
+ usageQuota.useQuotas = () => true
35
30
  }
36
31
 
37
32
  executeMiddleware() {
@@ -113,7 +108,6 @@ describe("usageQuota middleware", () => {
113
108
 
114
109
  it("calculates and persists the correct usage quota for the relevant action", async () => {
115
110
  config.setUrl("/rows")
116
- config.setProd(true)
117
111
 
118
112
  await config.executeMiddleware()
119
113
 
@@ -121,20 +115,20 @@ describe("usageQuota middleware", () => {
121
115
  expect(config.next).toHaveBeenCalled()
122
116
  })
123
117
 
124
- it("calculates the correct file size from a file upload call and adds it to quota", async () => {
125
- config.setUrl("/upload")
126
- config.setProd(true)
127
- config.setFiles([
128
- {
129
- size: 100
130
- },
131
- {
132
- size: 10000
133
- },
134
- ])
135
- await config.executeMiddleware()
136
-
137
- expect(usageQuota.update).toHaveBeenCalledWith("storage", 10100)
138
- expect(config.next).toHaveBeenCalled()
139
- })
118
+ // it("calculates the correct file size from a file upload call and adds it to quota", async () => {
119
+ // config.setUrl("/upload")
120
+ // config.setProd(true)
121
+ // config.setFiles([
122
+ // {
123
+ // size: 100
124
+ // },
125
+ // {
126
+ // size: 10000
127
+ // },
128
+ // ])
129
+ // await config.executeMiddleware()
130
+
131
+ // expect(usageQuota.update).toHaveBeenCalledWith("storage", 10100)
132
+ // expect(config.next).toHaveBeenCalled()
133
+ // })
140
134
  })
@@ -1,14 +1,11 @@
1
1
  const CouchDB = require("../db")
2
2
  const usageQuota = require("../utilities/usageQuota")
3
- const env = require("../environment")
4
- const { getTenantId } = require("@budibase/backend-core/tenancy")
3
+ const { getUniqueRows } = require("../utilities/usageQuota/rows")
5
4
  const {
6
5
  isExternalTable,
7
6
  isRowId: isExternalRowId,
8
7
  } = require("../integrations/utils")
9
-
10
- // tenants without limits
11
- const EXCLUDED_TENANTS = ["bb", "default", "bbtest", "bbstaging"]
8
+ const migration = require("../migrations/usageQuotas")
12
9
 
13
10
  // currently only counting new writes and deletes
14
11
  const METHOD_MAP = {
@@ -18,13 +15,13 @@ const METHOD_MAP = {
18
15
 
19
16
  const DOMAIN_MAP = {
20
17
  rows: usageQuota.Properties.ROW,
21
- upload: usageQuota.Properties.UPLOAD,
22
- views: usageQuota.Properties.VIEW,
23
- users: usageQuota.Properties.USER,
18
+ // upload: usageQuota.Properties.UPLOAD, // doesn't work yet
19
+ // views: usageQuota.Properties.VIEW, // doesn't work yet
20
+ // users: usageQuota.Properties.USER, // doesn't work yet
24
21
  applications: usageQuota.Properties.APPS,
25
22
  // this will not be updated by endpoint calls
26
23
  // instead it will be updated by triggerInfo
27
- automationRuns: usageQuota.Properties.AUTOMATION,
24
+ // automationRuns: usageQuota.Properties.AUTOMATION, // doesn't work yet
28
25
  }
29
26
 
30
27
  function getProperty(url) {
@@ -36,10 +33,7 @@ function getProperty(url) {
36
33
  }
37
34
 
38
35
  module.exports = async (ctx, next) => {
39
- const tenantId = getTenantId()
40
-
41
- // if in development or a self hosted cloud usage quotas should not be executed
42
- if (env.isDev() || env.SELF_HOSTED || EXCLUDED_TENANTS.includes(tenantId)) {
36
+ if (!usageQuota.useQuotas()) {
43
37
  return next()
44
38
  }
45
39
 
@@ -80,9 +74,93 @@ module.exports = async (ctx, next) => {
80
74
  usage = files.map(file => file.size).reduce((total, size) => total + size)
81
75
  }
82
76
  try {
83
- await usageQuota.update(property, usage)
84
- return next()
77
+ await migration.run()
78
+ await performRequest(ctx, next, property, usage)
85
79
  } catch (err) {
86
80
  ctx.throw(400, err)
87
81
  }
88
82
  }
83
+
84
+ const performRequest = async (ctx, next, property, usage) => {
85
+ const usageContext = {
86
+ skipNext: false,
87
+ skipUsage: false,
88
+ [usageQuota.Properties.APPS]: {},
89
+ }
90
+
91
+ if (usage === -1) {
92
+ if (PRE_DELETE[property]) {
93
+ await PRE_DELETE[property](ctx, usageContext)
94
+ }
95
+ } else {
96
+ if (PRE_CREATE[property]) {
97
+ await PRE_CREATE[property](ctx, usageContext)
98
+ }
99
+ }
100
+
101
+ // run the request
102
+ if (!usageContext.skipNext) {
103
+ await usageQuota.update(property, usage, { dryRun: true })
104
+ await next()
105
+ }
106
+
107
+ if (usage === -1) {
108
+ if (POST_DELETE[property]) {
109
+ await POST_DELETE[property](ctx, usageContext)
110
+ }
111
+ } else {
112
+ if (POST_CREATE[property]) {
113
+ await POST_CREATE[property](ctx, usageContext)
114
+ }
115
+ }
116
+
117
+ // update the usage
118
+ if (!usageContext.skipUsage) {
119
+ await usageQuota.update(property, usage)
120
+ }
121
+ }
122
+
123
+ const appPreDelete = async (ctx, usageContext) => {
124
+ if (ctx.query.unpublish) {
125
+ // don't run usage decrement for unpublish
126
+ usageContext.skipUsage = true
127
+ return
128
+ }
129
+
130
+ // store the row count to delete
131
+ const rows = await getUniqueRows([ctx.appId])
132
+ if (rows.length) {
133
+ usageContext[usageQuota.Properties.APPS] = { rowCount: rows.length }
134
+ }
135
+ }
136
+
137
+ const appPostDelete = async (ctx, usageContext) => {
138
+ // delete the app rows from usage
139
+ const rowCount = usageContext[usageQuota.Properties.APPS].rowCount
140
+ if (rowCount) {
141
+ await usageQuota.update(usageQuota.Properties.ROW, -rowCount)
142
+ }
143
+ }
144
+
145
+ const appPostCreate = async ctx => {
146
+ // app import & template creation
147
+ if (ctx.request.body.useTemplate === "true") {
148
+ const rows = await getUniqueRows([ctx.response.body.appId])
149
+ const rowCount = rows ? rows.length : 0
150
+ await usageQuota.update(usageQuota.Properties.ROW, rowCount)
151
+ }
152
+ }
153
+
154
+ const PRE_DELETE = {
155
+ [usageQuota.Properties.APPS]: appPreDelete,
156
+ }
157
+
158
+ const POST_DELETE = {
159
+ [usageQuota.Properties.APPS]: appPostDelete,
160
+ }
161
+
162
+ const PRE_CREATE = {}
163
+
164
+ const POST_CREATE = {
165
+ [usageQuota.Properties.APPS]: appPostCreate,
166
+ }
@@ -0,0 +1,27 @@
1
+ const env = require("../../../environment")
2
+ const TestConfig = require("../../../tests/utilities/TestConfiguration")
3
+
4
+ const syncApps = jest.fn()
5
+ const syncRows = jest.fn()
6
+
7
+ jest.mock("../../usageQuotas/syncApps", () => ({ run: syncApps }) )
8
+ jest.mock("../../usageQuotas/syncRows", () => ({ run: syncRows }) )
9
+
10
+ const migrations = require("../../usageQuotas")
11
+
12
+ describe("run", () => {
13
+ let config = new TestConfig(false)
14
+
15
+ beforeEach(async () => {
16
+ await config.init()
17
+ env._set("USE_QUOTAS", 1)
18
+ })
19
+
20
+ afterAll(config.end)
21
+
22
+ it("runs the required migrations", async () => {
23
+ await migrations.run()
24
+ expect(syncApps).toHaveBeenCalledTimes(1)
25
+ expect(syncRows).toHaveBeenCalledTimes(1)
26
+ })
27
+ })
@@ -0,0 +1,37 @@
1
+ const { getGlobalDB } = require("@budibase/backend-core/tenancy")
2
+ const TestConfig = require("../../../tests/utilities/TestConfiguration")
3
+ const { getUsageQuotaDoc, update, Properties } = require("../../../utilities/usageQuota")
4
+ const syncApps = require("../../usageQuotas/syncApps")
5
+ const env = require("../../../environment")
6
+
7
+ describe("syncApps", () => {
8
+ let config = new TestConfig(false)
9
+
10
+ beforeEach(async () => {
11
+ await config.init()
12
+ env._set("USE_QUOTAS", 1)
13
+ })
14
+
15
+ afterAll(config.end)
16
+
17
+ it("runs successfully", async () => {
18
+ // create the usage quota doc and mock usages
19
+ const db = getGlobalDB()
20
+ await getUsageQuotaDoc(db)
21
+ await update(Properties.APPS, 3)
22
+
23
+ let usageDoc = await getUsageQuotaDoc(db)
24
+ expect(usageDoc.usageQuota.apps).toEqual(3)
25
+
26
+ // create an extra app to test the migration
27
+ await config.createApp("quota-test")
28
+
29
+ // migrate
30
+ await syncApps.run()
31
+
32
+ // assert the migration worked
33
+ usageDoc = await getUsageQuotaDoc(db)
34
+ expect(usageDoc.usageQuota.apps).toEqual(2)
35
+ })
36
+ })
37
+
@@ -0,0 +1,43 @@
1
+ const { getGlobalDB } = require("@budibase/backend-core/tenancy")
2
+ const TestConfig = require("../../../tests/utilities/TestConfiguration")
3
+ const { getUsageQuotaDoc, update, Properties } = require("../../../utilities/usageQuota")
4
+ const syncRows = require("../../usageQuotas/syncRows")
5
+ const env = require("../../../environment")
6
+
7
+ describe("syncRows", () => {
8
+ let config = new TestConfig(false)
9
+
10
+ beforeEach(async () => {
11
+ await config.init()
12
+ env._set("USE_QUOTAS", 1)
13
+ })
14
+
15
+ afterAll(config.end)
16
+
17
+ it("runs successfully", async () => {
18
+ // create the usage quota doc and mock usages
19
+ const db = getGlobalDB()
20
+ await getUsageQuotaDoc(db)
21
+ await update(Properties.ROW, 300)
22
+
23
+ let usageDoc = await getUsageQuotaDoc(db)
24
+ expect(usageDoc.usageQuota.rows).toEqual(300)
25
+
26
+ // app 1
27
+ await config.createTable()
28
+ await config.createRow()
29
+ // app 2
30
+ await config.createApp("second-app")
31
+ await config.createTable()
32
+ await config.createRow()
33
+ await config.createRow()
34
+
35
+ // migrate
36
+ await syncRows.run()
37
+
38
+ // assert the migration worked
39
+ usageDoc = await getUsageQuotaDoc(db)
40
+ expect(usageDoc.usageQuota.rows).toEqual(3)
41
+ })
42
+ })
43
+
@@ -0,0 +1,24 @@
1
+ const {
2
+ MIGRATIONS,
3
+ MIGRATION_DBS,
4
+ migrateIfRequired,
5
+ } = require("@budibase/backend-core/migrations")
6
+ const { useQuotas } = require("../../utilities/usageQuota")
7
+ const syncApps = require("./syncApps")
8
+ const syncRows = require("./syncRows")
9
+
10
+ exports.run = async () => {
11
+ if (!useQuotas()) {
12
+ return
13
+ }
14
+
15
+ // Jan 2022
16
+ await migrateIfRequired(
17
+ MIGRATION_DBS.GLOBAL_DB,
18
+ MIGRATIONS.QUOTAS_1,
19
+ async () => {
20
+ await syncApps.run()
21
+ await syncRows.run()
22
+ }
23
+ )
24
+ }
@@ -0,0 +1,18 @@
1
+ const { getGlobalDB, getTenantId } = require("@budibase/backend-core/tenancy")
2
+ const { getAllApps } = require("@budibase/backend-core/db")
3
+ const CouchDB = require("../../db")
4
+ const { getUsageQuotaDoc } = require("../../utilities/usageQuota")
5
+
6
+ exports.run = async () => {
7
+ const db = getGlobalDB()
8
+ // get app count
9
+ const devApps = await getAllApps(CouchDB, { dev: true })
10
+ const appCount = devApps ? devApps.length : 0
11
+
12
+ // sync app count
13
+ const tenantId = getTenantId()
14
+ console.log(`[Tenant: ${tenantId}] Syncing app count: ${appCount}`)
15
+ const usageDoc = await getUsageQuotaDoc(db)
16
+ usageDoc.usageQuota.apps = appCount
17
+ await db.put(usageDoc)
18
+ }
@@ -0,0 +1,21 @@
1
+ const { getGlobalDB, getTenantId } = require("@budibase/backend-core/tenancy")
2
+ const { getAllApps } = require("@budibase/backend-core/db")
3
+ const CouchDB = require("../../db")
4
+ const { getUsageQuotaDoc } = require("../../utilities/usageQuota")
5
+ const { getUniqueRows } = require("../../utilities/usageQuota/rows")
6
+
7
+ exports.run = async () => {
8
+ const db = getGlobalDB()
9
+ // get all rows in all apps
10
+ const allApps = await getAllApps(CouchDB, { all: true })
11
+ const appIds = allApps ? allApps.map(app => app.appId) : []
12
+ const rows = await getUniqueRows(appIds)
13
+ const rowCount = rows ? rows.length : 0
14
+
15
+ // sync row count
16
+ const tenantId = getTenantId()
17
+ console.log(`[Tenant: ${tenantId}] Syncing row count: ${rowCount}`)
18
+ const usageDoc = await getUsageQuotaDoc(db)
19
+ usageDoc.usageQuota.rows = rowCount
20
+ await db.put(usageDoc)
21
+ }
@@ -22,10 +22,12 @@ const { getGlobalDB } = require("@budibase/backend-core/tenancy")
22
22
  const { createASession } = require("@budibase/backend-core/sessions")
23
23
  const { user: userCache } = require("@budibase/backend-core/cache")
24
24
  const CouchDB = require("../../db")
25
+ const newid = require("../../db/newid")
25
26
  core.init(CouchDB)
26
27
 
27
28
  const GLOBAL_USER_ID = "us_uuid1"
28
29
  const EMAIL = "babs@babs.com"
30
+ const CSRF_TOKEN = "e3727778-7af0-4226-b5eb-f43cbe60a306"
29
31
 
30
32
  class TestConfiguration {
31
33
  constructor(openServer = true) {
@@ -85,7 +87,11 @@ class TestConfiguration {
85
87
  roles: roles || {},
86
88
  tenantId: TENANT_ID,
87
89
  }
88
- await createASession(id, { sessionId: "sessionid", tenantId: TENANT_ID })
90
+ await createASession(id, {
91
+ sessionId: "sessionid",
92
+ tenantId: TENANT_ID,
93
+ csrfToken: CSRF_TOKEN,
94
+ })
89
95
  if (builder) {
90
96
  user.builder = { global: true }
91
97
  } else {
@@ -98,7 +104,8 @@ class TestConfiguration {
98
104
  }
99
105
  }
100
106
 
101
- async init(appName = "test_application") {
107
+ // use a new id as the name to avoid name collisions
108
+ async init(appName = newid()) {
102
109
  await this.globalUser()
103
110
  return this.createApp(appName)
104
111
  }
@@ -131,6 +138,7 @@ class TestConfiguration {
131
138
  `${Cookies.Auth}=${authToken}`,
132
139
  `${Cookies.CurrentApp}=${appToken}`,
133
140
  ],
141
+ [Headers.CSRF_TOKEN]: CSRF_TOKEN,
134
142
  }
135
143
  if (this.appId) {
136
144
  headers[Headers.APP_ID] = this.appId
@@ -424,10 +432,6 @@ class TestConfiguration {
424
432
  roles: { [this.prodAppId]: roleId },
425
433
  })
426
434
  }
427
- await createASession(userId, {
428
- sessionId: "sessionid",
429
- tenantId: TENANT_ID,
430
- })
431
435
  // have to fake this
432
436
  const auth = {
433
437
  userId,
@@ -78,6 +78,11 @@ class QueryRunner {
78
78
  return this.execute()
79
79
  }
80
80
 
81
+ // check for undefined response
82
+ if (!rows) {
83
+ rows = []
84
+ }
85
+
81
86
  // needs to an array for next step
82
87
  if (!Array.isArray(rows)) {
83
88
  rows = [rows]
@@ -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
+ })