@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
@@ -2,13 +2,9 @@ const CouchDB = require("../../../db")
2
2
  const internal = require("./internal")
3
3
  const external = require("./external")
4
4
  const csvParser = require("../../../utilities/csvParser")
5
- const { isExternalTable } = require("../../../integrations/utils")
6
- const {
7
- getTableParams,
8
- getDatasourceParams,
9
- BudibaseInternalDB,
10
- } = require("../../../db/utils")
11
- const { getTable } = require("./utils")
5
+ const { isExternalTable, isSQL } = require("../../../integrations/utils")
6
+ const { getDatasourceParams } = require("../../../db/utils")
7
+ const { getTable, getAllInternalTables } = require("./utils")
12
8
 
13
9
  function pickApi({ tableId, table }) {
14
10
  if (table && !tableId) {
@@ -26,17 +22,7 @@ function pickApi({ tableId, table }) {
26
22
  exports.fetch = async function (ctx) {
27
23
  const db = new CouchDB(ctx.appId)
28
24
 
29
- const internalTables = await db.allDocs(
30
- getTableParams(null, {
31
- include_docs: true,
32
- })
33
- )
34
-
35
- const internal = internalTables.rows.map(row => ({
36
- ...row.doc,
37
- type: "internal",
38
- sourceId: BudibaseInternalDB._id,
39
- }))
25
+ const internal = await getAllInternalTables(ctx.appId)
40
26
 
41
27
  const externalTables = await db.allDocs(
42
28
  getDatasourceParams("plus", {
@@ -44,12 +30,18 @@ exports.fetch = async function (ctx) {
44
30
  })
45
31
  )
46
32
 
47
- const external = externalTables.rows.flatMap(row => {
48
- return Object.values(row.doc.entities || {}).map(entity => ({
49
- ...entity,
50
- type: "external",
51
- sourceId: row.doc._id,
52
- }))
33
+ const external = externalTables.rows.flatMap(tableDoc => {
34
+ let entities = tableDoc.doc.entities
35
+ if (entities) {
36
+ return Object.values(entities).map(entity => ({
37
+ ...entity,
38
+ type: "external",
39
+ sourceId: tableDoc.doc._id,
40
+ sql: isSQL(tableDoc.doc),
41
+ }))
42
+ } else {
43
+ return []
44
+ }
53
45
  })
54
46
 
55
47
  ctx.body = [...internal, ...external]
@@ -8,6 +8,9 @@ const {
8
8
  getTable,
9
9
  handleDataImport,
10
10
  } = require("./utils")
11
+ const usageQuota = require("../../../utilities/usageQuota")
12
+ const { cleanupAttachments } = require("../../../utilities/rowProcessor")
13
+ const { runStaticFormulaChecks } = require("./bulkFormula")
11
14
 
12
15
  exports.save = async function (ctx) {
13
16
  const appId = ctx.appId
@@ -103,7 +106,8 @@ exports.save = async function (ctx) {
103
106
  tableToSave._rev = result.rev
104
107
 
105
108
  tableToSave = await tableSaveFunctions.after(tableToSave)
106
-
109
+ // has to run after, make sure it has _id
110
+ await runStaticFormulaChecks(appId, tableToSave, { oldTable })
107
111
  return tableToSave
108
112
  }
109
113
 
@@ -119,6 +123,7 @@ exports.destroy = async function (ctx) {
119
123
  })
120
124
  )
121
125
  await db.bulkDocs(rows.rows.map(row => ({ ...row.doc, _deleted: true })))
126
+ await usageQuota.update(usageQuota.Properties.ROW, -rows.rows.length)
122
127
 
123
128
  // update linked rows
124
129
  await linkRows.updateLinks({
@@ -139,6 +144,9 @@ exports.destroy = async function (ctx) {
139
144
  await db.deleteIndex(existingIndex)
140
145
  }
141
146
 
147
+ // has to run after, make sure it has _id
148
+ await runStaticFormulaChecks(appId, tableToDelete, { deletion: true })
149
+ await cleanupAttachments(appId, tableToDelete, { rows })
142
150
  return tableToDelete
143
151
  }
144
152
 
@@ -4,10 +4,15 @@ const {
4
4
  getRowParams,
5
5
  generateRowID,
6
6
  InternalTables,
7
+ getTableParams,
8
+ BudibaseInternalDB,
7
9
  } = require("../../../db/utils")
8
- const { isEqual } = require("lodash/fp")
10
+ const { isEqual } = require("lodash")
9
11
  const { AutoFieldSubTypes, FieldTypes } = require("../../../constants")
10
- const { inputProcessing } = require("../../../utilities/rowProcessor")
12
+ const {
13
+ inputProcessing,
14
+ cleanupAttachments,
15
+ } = require("../../../utilities/rowProcessor")
11
16
  const {
12
17
  USERS_TABLE_SCHEMA,
13
18
  SwitchableTypes,
@@ -16,11 +21,29 @@ const {
16
21
  const {
17
22
  isExternalTable,
18
23
  breakExternalTableId,
24
+ isSQL,
19
25
  } = require("../../../integrations/utils")
20
26
  const { getViews, saveView } = require("../view/utils")
21
27
  const viewTemplate = require("../view/viewBuilder")
28
+ const usageQuota = require("../../../utilities/usageQuota")
29
+ const { cloneDeep } = require("lodash/fp")
30
+
31
+ exports.clearColumns = async (appId, table, columnNames) => {
32
+ const db = new CouchDB(appId)
33
+ const rows = await db.allDocs(
34
+ getRowParams(table._id, null, {
35
+ include_docs: true,
36
+ })
37
+ )
38
+ return db.bulkDocs(
39
+ rows.rows.map(({ doc }) => {
40
+ columnNames.forEach(colName => delete doc[colName])
41
+ return doc
42
+ })
43
+ )
44
+ }
22
45
 
23
- exports.checkForColumnUpdates = async (db, oldTable, updatedTable) => {
46
+ exports.checkForColumnUpdates = async (appId, db, oldTable, updatedTable) => {
24
47
  let updatedRows = []
25
48
  const rename = updatedTable._rename
26
49
  let deletedColumns = []
@@ -37,16 +60,20 @@ exports.checkForColumnUpdates = async (db, oldTable, updatedTable) => {
37
60
  include_docs: true,
38
61
  })
39
62
  )
40
- updatedRows = rows.rows.map(({ doc }) => {
63
+ const rawRows = rows.rows.map(({ doc }) => doc)
64
+ updatedRows = rawRows.map(row => {
65
+ row = cloneDeep(row)
41
66
  if (rename) {
42
- doc[rename.updated] = doc[rename.old]
43
- delete doc[rename.old]
67
+ row[rename.updated] = row[rename.old]
68
+ delete row[rename.old]
44
69
  } else if (deletedColumns.length !== 0) {
45
- deletedColumns.forEach(colName => delete doc[colName])
70
+ deletedColumns.forEach(colName => delete row[colName])
46
71
  }
47
- return doc
72
+ return row
48
73
  })
49
74
 
75
+ // cleanup any attachments from object storage for deleted attachment columns
76
+ await cleanupAttachments(appId, updatedTable, { oldTable, rows: rawRows })
50
77
  // Update views
51
78
  await exports.checkForViewUpdates(db, updatedTable, rename, deletedColumns)
52
79
  delete updatedTable._rename
@@ -115,7 +142,11 @@ exports.handleDataImport = async (appId, user, table, dataImport) => {
115
142
  finalData.push(row)
116
143
  }
117
144
 
145
+ await usageQuota.update(usageQuota.Properties.ROW, finalData.length, {
146
+ dryRun: true,
147
+ })
118
148
  await db.bulkDocs(finalData)
149
+ await usageQuota.update(usageQuota.Properties.ROW, finalData.length)
119
150
  let response = await db.put(table)
120
151
  table._rev = response._rev
121
152
  return table
@@ -203,6 +234,7 @@ class TableSaveFunctions {
203
234
  // when confirmed valid
204
235
  async mid(table) {
205
236
  let response = await exports.checkForColumnUpdates(
237
+ this.appId,
206
238
  this.db,
207
239
  this.oldTable,
208
240
  table
@@ -228,6 +260,20 @@ class TableSaveFunctions {
228
260
  }
229
261
  }
230
262
 
263
+ exports.getAllInternalTables = async appId => {
264
+ const db = new CouchDB(appId)
265
+ const internalTables = await db.allDocs(
266
+ getTableParams(null, {
267
+ include_docs: true,
268
+ })
269
+ )
270
+ return internalTables.rows.map(tableDoc => ({
271
+ ...tableDoc.doc,
272
+ type: "internal",
273
+ sourceId: BudibaseInternalDB._id,
274
+ }))
275
+ }
276
+
231
277
  exports.getAllExternalTables = async (appId, datasourceId) => {
232
278
  const db = new CouchDB(appId)
233
279
  const datasource = await db.get(datasourceId)
@@ -246,7 +292,9 @@ exports.getTable = async (appId, tableId) => {
246
292
  const db = new CouchDB(appId)
247
293
  if (isExternalTable(tableId)) {
248
294
  let { datasourceId, tableName } = breakExternalTableId(tableId)
249
- return exports.getExternalTable(appId, datasourceId, tableName)
295
+ const datasource = await db.get(datasourceId)
296
+ const table = await exports.getExternalTable(appId, datasourceId, tableName)
297
+ return { ...table, sql: isSQL(datasource) }
250
298
  } else {
251
299
  return db.get(tableId)
252
300
  }
@@ -167,6 +167,8 @@ exports.updateSelfMetadata = async function (ctx) {
167
167
  ctx.request.body._id = ctx.user._id
168
168
  // make sure no stale rev
169
169
  delete ctx.request.body._rev
170
+ // make sure no csrf token
171
+ delete ctx.request.body.csrfToken
170
172
  await exports.updateMetadata(ctx)
171
173
  }
172
174
 
@@ -20,11 +20,11 @@ const integrationRoutes = require("./integration")
20
20
  const permissionRoutes = require("./permission")
21
21
  const datasourceRoutes = require("./datasource")
22
22
  const queryRoutes = require("./query")
23
- const hostingRoutes = require("./hosting")
24
23
  const backupRoutes = require("./backup")
25
24
  const metadataRoutes = require("./metadata")
26
25
  const devRoutes = require("./dev")
27
26
  const cloudRoutes = require("./cloud")
27
+ const migrationRoutes = require("./migrations")
28
28
 
29
29
  exports.mainRoutes = [
30
30
  authRoutes,
@@ -46,7 +46,6 @@ exports.mainRoutes = [
46
46
  permissionRoutes,
47
47
  datasourceRoutes,
48
48
  queryRoutes,
49
- hostingRoutes,
50
49
  backupRoutes,
51
50
  metadataRoutes,
52
51
  devRoutes,
@@ -55,6 +54,7 @@ exports.mainRoutes = [
55
54
  // this could be breaking as koa may recognise other routes as this
56
55
  tableRoutes,
57
56
  rowRoutes,
57
+ migrationRoutes,
58
58
  ]
59
59
 
60
60
  exports.staticRoutes = staticRoutes
@@ -0,0 +1,14 @@
1
+ const Router = require("@koa/router")
2
+ const migrationsController = require("../controllers/migrations")
3
+ const router = Router()
4
+ const { internalApi } = require("@budibase/backend-core/auth")
5
+
6
+ router
7
+ .post("/api/migrations/run", internalApi, migrationsController.migrate)
8
+ .get(
9
+ "/api/migrations/definitions",
10
+ internalApi,
11
+ migrationsController.fetchDefinitions
12
+ )
13
+
14
+ module.exports = router
@@ -46,5 +46,10 @@ router
46
46
  )
47
47
  // TODO: this likely needs to be secured in some way
48
48
  .get("/:appId/:path*", controller.serveApp)
49
+ .post(
50
+ "/api/attachments/:datasourceId/url",
51
+ authorized(PermissionTypes.TABLE, PermissionLevels.READ),
52
+ controller.getSignedUploadURL
53
+ )
49
54
 
50
55
  module.exports = router
@@ -53,8 +53,8 @@ describe("/applications", () => {
53
53
 
54
54
  describe("fetch", () => {
55
55
  it("lists all applications", async () => {
56
- await config.createApp(request, "app1")
57
- await config.createApp(request, "app2")
56
+ await config.createApp("app1")
57
+ await config.createApp("app2")
58
58
 
59
59
  const res = await request
60
60
  .get(`/api/applications?status=${AppStatus.DEV}`)
@@ -13,10 +13,9 @@ describe("/authenticate", () => {
13
13
 
14
14
  describe("fetch self", () => {
15
15
  it("should be able to fetch self", async () => {
16
- const headers = await config.login()
17
16
  const res = await request
18
17
  .get(`/api/self`)
19
- .set(headers)
18
+ .set(config.defaultHeaders())
20
19
  .expect("Content-Type", /json/)
21
20
  .expect(200)
22
21
  expect(res.body._id).toEqual(generateUserMetadataID("us_uuid1"))
@@ -0,0 +1,98 @@
1
+ jest.mock("node-fetch")
2
+ jest.mock("aws-sdk", () => ({
3
+ config: {
4
+ update: jest.fn(),
5
+ },
6
+ DynamoDB: {
7
+ DocumentClient: jest.fn(),
8
+ },
9
+ S3: jest.fn(() => ({
10
+ getSignedUrl: jest.fn(() => {
11
+ return "my-url"
12
+ }),
13
+ })),
14
+ }))
15
+
16
+ const setup = require("./utilities")
17
+
18
+ describe("/attachments", () => {
19
+ let request = setup.getRequest()
20
+ let config = setup.getConfig()
21
+ let app
22
+
23
+ afterAll(setup.afterAll)
24
+
25
+ beforeEach(async () => {
26
+ app = await config.init()
27
+ })
28
+
29
+ describe("generateSignedUrls", () => {
30
+ let datasource
31
+
32
+ beforeEach(async () => {
33
+ datasource = await config.createDatasource({
34
+ datasource: {
35
+ type: "datasource",
36
+ name: "Test",
37
+ source: "S3",
38
+ config: {},
39
+ },
40
+ })
41
+ })
42
+
43
+ it("should be able to generate a signed upload URL", async () => {
44
+ const bucket = "foo"
45
+ const key = "bar"
46
+ const res = await request
47
+ .post(`/api/attachments/${datasource._id}/url`)
48
+ .send({ bucket, key })
49
+ .set(config.defaultHeaders())
50
+ .expect("Content-Type", /json/)
51
+ .expect(200)
52
+ expect(res.body.signedUrl).toEqual("my-url")
53
+ expect(res.body.publicUrl).toEqual(
54
+ `https://${bucket}.s3.eu-west-1.amazonaws.com/${key}`
55
+ )
56
+ })
57
+
58
+ it("should handle an invalid datasource ID", async () => {
59
+ const res = await request
60
+ .post(`/api/attachments/foo/url`)
61
+ .send({
62
+ bucket: "foo",
63
+ key: "bar",
64
+ })
65
+ .set(config.defaultHeaders())
66
+ .expect("Content-Type", /json/)
67
+ .expect(400)
68
+ expect(res.body.message).toEqual(
69
+ "The specified datasource could not be found"
70
+ )
71
+ })
72
+
73
+ it("should require a bucket parameter", async () => {
74
+ const res = await request
75
+ .post(`/api/attachments/${datasource._id}/url`)
76
+ .send({
77
+ bucket: undefined,
78
+ key: "bar",
79
+ })
80
+ .set(config.defaultHeaders())
81
+ .expect("Content-Type", /json/)
82
+ .expect(400)
83
+ expect(res.body.message).toEqual("bucket and key values are required")
84
+ })
85
+
86
+ it("should require a key parameter", async () => {
87
+ const res = await request
88
+ .post(`/api/attachments/${datasource._id}/url`)
89
+ .send({
90
+ bucket: "foo",
91
+ })
92
+ .set(config.defaultHeaders())
93
+ .expect("Content-Type", /json/)
94
+ .expect(400)
95
+ expect(res.body.message).toEqual("bucket and key values are required")
96
+ })
97
+ })
98
+ })
package/src/app.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  // need to load environment first
2
2
  import { ExtendableContext } from "koa"
3
3
 
4
- const env = require("./environment")
4
+ import * as env from "./environment"
5
5
  const CouchDB = require("./db")
6
6
  require("@budibase/backend-core").init(CouchDB)
7
7
  const Koa = require("koa")
@@ -16,6 +16,7 @@ const Sentry = require("@sentry/node")
16
16
  const fileSystem = require("./utilities/fileSystem")
17
17
  const bullboard = require("./automations/bullboard")
18
18
  const redis = require("./utilities/redis")
19
+ import * as migrations from "./migrations"
19
20
 
20
21
  const app = new Koa()
21
22
 
@@ -84,13 +85,25 @@ module.exports = server.listen(env.PORT || 0, async () => {
84
85
  await automations.init()
85
86
  })
86
87
 
87
- process.on("uncaughtException", err => {
88
- console.error(err)
88
+ const shutdown = () => {
89
89
  server.close()
90
90
  server.destroy()
91
+ }
92
+
93
+ process.on("uncaughtException", err => {
94
+ console.error(err)
95
+ shutdown()
91
96
  })
92
97
 
93
98
  process.on("SIGTERM", () => {
94
- server.close()
95
- server.destroy()
99
+ shutdown()
96
100
  })
101
+
102
+ // run migrations on startup if not done via http
103
+ // not recommended in a clustered environment
104
+ if (!env.HTTP_MIGRATIONS) {
105
+ migrations.migrate().catch(err => {
106
+ console.error("Error performing migrations. Exiting.\n", err)
107
+ shutdown()
108
+ })
109
+ }
@@ -1,6 +1,5 @@
1
1
  const rowController = require("../../api/controllers/row")
2
2
  const automationUtils = require("../automationUtils")
3
- const env = require("../../environment")
4
3
  const usage = require("../../utilities/usageQuota")
5
4
  const { buildCtx } = require("./utils")
6
5
 
@@ -83,10 +82,9 @@ exports.run = async function ({ inputs, appId, emitter }) {
83
82
  inputs.row.tableId,
84
83
  inputs.row
85
84
  )
86
- if (env.USE_QUOTAS) {
87
- await usage.update(usage.Properties.ROW, 1)
88
- }
85
+ await usage.update(usage.Properties.ROW, 1, { dryRun: true })
89
86
  await rowController.save(ctx)
87
+ await usage.update(usage.Properties.ROW, 1)
90
88
  return {
91
89
  row: inputs.row,
92
90
  response: ctx.body,
@@ -1,5 +1,4 @@
1
1
  const rowController = require("../../api/controllers/row")
2
- const env = require("../../environment")
3
2
  const usage = require("../../utilities/usageQuota")
4
3
  const { buildCtx } = require("./utils")
5
4
  const automationUtils = require("../automationUtils")
@@ -74,9 +73,7 @@ exports.run = async function ({ inputs, appId, emitter }) {
74
73
  })
75
74
 
76
75
  try {
77
- if (env.isProd()) {
78
- await usage.update(usage.Properties.ROW, -1)
79
- }
76
+ await usage.update(usage.Properties.ROW, -1)
80
77
  await rowController.destroy(ctx)
81
78
  return {
82
79
  response: ctx.body,
@@ -61,6 +61,11 @@ exports.RelationshipTypes = {
61
61
  MANY_TO_MANY: "many-to-many",
62
62
  }
63
63
 
64
+ exports.FormulaTypes = {
65
+ STATIC: "static",
66
+ DYNAMIC: "dynamic",
67
+ }
68
+
64
69
  exports.AuthTypes = {
65
70
  APP: "app",
66
71
  BUILDER: "builder",
@@ -78,6 +83,10 @@ exports.DataSourceOperation = {
78
83
  DELETE_TABLE: "DELETE_TABLE",
79
84
  }
80
85
 
86
+ exports.DatasourceAuthTypes = {
87
+ GOOGLE: "google",
88
+ }
89
+
81
90
  exports.SortDirection = {
82
91
  ASCENDING: "ASCENDING",
83
92
  DESCENDING: "DESCENDING",
@@ -72,7 +72,7 @@ async function getLinksForRows(appId, rows) {
72
72
  )
73
73
  }
74
74
 
75
- async function getFullLinkedDocs(ctx, appId, links) {
75
+ async function getFullLinkedDocs(appId, links) {
76
76
  // create DBs
77
77
  const db = new CouchDB(appId)
78
78
  const linkedRowIds = links.map(link => link.id)
@@ -146,13 +146,12 @@ exports.updateLinks = async function (args) {
146
146
  /**
147
147
  * Given a table and a list of rows this will retrieve all of the attached docs and enrich them into the row.
148
148
  * This is required for formula fields, this may only be utilised internally (for now).
149
- * @param {object} ctx The request which is looking for rows.
149
+ * @param {string} appId The ID of the app which this request is in the context of.
150
150
  * @param {object} table The table from which the rows originated.
151
151
  * @param {array<object>} rows The rows which are to be enriched.
152
152
  * @return {Promise<*>} returns the rows with all of the enriched relationships on it.
153
153
  */
154
- exports.attachFullLinkedDocs = async (ctx, table, rows) => {
155
- const appId = ctx.appId
154
+ exports.attachFullLinkedDocs = async (appId, table, rows) => {
156
155
  const linkedTableIds = getLinkedTableIDs(table)
157
156
  if (linkedTableIds.length === 0) {
158
157
  return rows
@@ -166,7 +165,7 @@ exports.attachFullLinkedDocs = async (ctx, table, rows) => {
166
165
  // clear any existing links that could be dupe'd
167
166
  rows = clearRelationshipFields(table, rows)
168
167
  // now get the docs and combine into the rows
169
- let linked = await getFullLinkedDocs(ctx, appId, links)
168
+ let linked = await getFullLinkedDocs(appId, links)
170
169
  const linkedTables = []
171
170
  for (let row of rows) {
172
171
  for (let link of links.filter(link => link.thisId === row._id)) {
@@ -17,6 +17,8 @@ export interface FieldSchema {
17
17
  autocolumn?: boolean
18
18
  throughFrom?: string
19
19
  throughTo?: string
20
+ formula?: string
21
+ formulaType?: string
20
22
  main?: boolean
21
23
  meta?: {
22
24
  toTable: string
@@ -46,6 +48,7 @@ export interface Table extends Base {
46
48
  schema: TableSchema
47
49
  primaryDisplay?: string
48
50
  sourceId?: string
51
+ relatedFormula?: string[]
49
52
  constrained?: string[]
50
53
  }
51
54
 
@@ -47,6 +47,7 @@ export enum SourceNames {
47
47
  ARANGODB = "ARANGODB",
48
48
  REST = "REST",
49
49
  ORACLE = "ORACLE",
50
+ GOOGLE_SHEETS = "GOOGLE_SHEETS",
50
51
  }
51
52
 
52
53
  export enum IncludeRelationships {
@@ -86,6 +87,8 @@ export interface ExtraQueryConfig {
86
87
  export interface Integration {
87
88
  docs: string
88
89
  plus?: boolean
90
+ auth?: { type: string }
91
+ relationships?: boolean
89
92
  description: string
90
93
  friendlyName: string
91
94
  datasource: {}
@@ -38,10 +38,12 @@ module.exports = {
38
38
  MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY,
39
39
  MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY,
40
40
  USE_QUOTAS: process.env.USE_QUOTAS,
41
+ EXCLUDE_QUOTAS_TENANTS: process.env.EXCLUDE_QUOTAS_TENANTS,
41
42
  REDIS_URL: process.env.REDIS_URL,
42
43
  REDIS_PASSWORD: process.env.REDIS_PASSWORD,
43
44
  INTERNAL_API_KEY: process.env.INTERNAL_API_KEY,
44
45
  MULTI_TENANCY: process.env.MULTI_TENANCY,
46
+ HTTP_MIGRATIONS: process.env.HTTP_MIGRATIONS,
45
47
  // environment
46
48
  NODE_ENV: process.env.NODE_ENV,
47
49
  JEST_WORKER_ID: process.env.JEST_WORKER_ID,
@@ -72,16 +72,22 @@ class InternalBuilder {
72
72
 
73
73
  // right now we only do filters on the specific table being queried
74
74
  addFilters(
75
- tableName: string,
76
75
  query: KnexQuery,
77
- filters: SearchFilters | undefined
76
+ filters: SearchFilters | undefined,
77
+ opts: { relationship?: boolean; tableName?: string }
78
78
  ): KnexQuery {
79
79
  function iterate(
80
80
  structure: { [key: string]: any },
81
81
  fn: (key: string, value: any) => void
82
82
  ) {
83
83
  for (let [key, value] of Object.entries(structure)) {
84
- fn(`${tableName}.${key}`, value)
84
+ const isRelationshipField = key.includes(".")
85
+ if (!opts.relationship && !isRelationshipField) {
86
+ fn(`${opts.tableName}.${key}`, value)
87
+ }
88
+ if (opts.relationship && isRelationshipField) {
89
+ fn(key, value)
90
+ }
85
91
  }
86
92
  }
87
93
  if (!filters) {
@@ -272,7 +278,7 @@ class InternalBuilder {
272
278
  if (foundOffset) {
273
279
  query = query.offset(foundOffset)
274
280
  }
275
- query = this.addFilters(tableName, query, filters)
281
+ query = this.addFilters(query, filters, { tableName })
276
282
  // add sorting to pre-query
277
283
  query = this.addSorting(query, json)
278
284
  // @ts-ignore
@@ -285,20 +291,21 @@ class InternalBuilder {
285
291
  preQuery = this.addSorting(preQuery, json)
286
292
  }
287
293
  // handle joins
288
- return this.addRelationships(
294
+ query = this.addRelationships(
289
295
  knex,
290
296
  preQuery,
291
297
  selectStatement,
292
298
  tableName,
293
299
  relationships
294
300
  )
301
+ return this.addFilters(query, filters, { relationship: true })
295
302
  }
296
303
 
297
304
  update(knex: Knex, json: QueryJson, opts: QueryOptions): KnexQuery {
298
305
  const { endpoint, body, filters } = json
299
306
  let query: KnexQuery = knex(endpoint.entityId)
300
307
  const parsedBody = parseBody(body)
301
- query = this.addFilters(endpoint.entityId, query, filters)
308
+ query = this.addFilters(query, filters, { tableName: endpoint.entityId })
302
309
  // mysql can't use returning
303
310
  if (opts.disableReturning) {
304
311
  return query.update(parsedBody)
@@ -310,7 +317,7 @@ class InternalBuilder {
310
317
  delete(knex: Knex, json: QueryJson, opts: QueryOptions): KnexQuery {
311
318
  const { endpoint, filters } = json
312
319
  let query: KnexQuery = knex(endpoint.entityId)
313
- query = this.addFilters(endpoint.entityId, query, filters)
320
+ query = this.addFilters(query, filters, { tableName: endpoint.entityId })
314
321
  // mysql can't use returning
315
322
  if (opts.disableReturning) {
316
323
  return query.delete()