@budibase/server 2.0.38 → 2.0.40-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (295) hide show
  1. package/__mocks__/mongodb.ts +1 -1
  2. package/__mocks__/node-fetch.ts +12 -2
  3. package/builder/assets/backups-default.8c011f1b.png +0 -0
  4. package/builder/assets/{index.e0ddd30b.css → index.442de33f.css} +2 -2
  5. package/builder/assets/{index.598a950c.js → index.d7b4247d.js} +342 -334
  6. package/builder/index.html +2 -2
  7. package/coverage/clover.xml +1125 -1250
  8. package/coverage/coverage-final.json +73 -77
  9. package/coverage/lcov-report/api/controllers/apikeys.js.html +6 -6
  10. package/coverage/lcov-report/api/controllers/automation.js.html +1 -1
  11. package/coverage/lcov-report/api/controllers/cloud.js.html +79 -67
  12. package/coverage/lcov-report/api/controllers/datasource.js.html +1 -1
  13. package/coverage/lcov-report/api/controllers/dev.js.html +21 -21
  14. package/coverage/lcov-report/api/controllers/index.html +17 -47
  15. package/coverage/lcov-report/api/controllers/integration.js.html +4 -4
  16. package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
  17. package/coverage/lcov-report/api/controllers/metadata.js.html +9 -9
  18. package/coverage/lcov-report/api/controllers/migrations.js.html +4 -4
  19. package/coverage/lcov-report/api/controllers/permission.js.html +1 -1
  20. package/coverage/lcov-report/api/controllers/query/index.html +1 -1
  21. package/coverage/lcov-report/api/controllers/query/validation.js.html +4 -4
  22. package/coverage/lcov-report/api/controllers/role.js.html +1 -1
  23. package/coverage/lcov-report/api/controllers/routing.js.html +9 -9
  24. package/coverage/lcov-report/api/controllers/row/external.js.html +1 -1
  25. package/coverage/lcov-report/api/controllers/row/index.html +1 -1
  26. package/coverage/lcov-report/api/controllers/row/internal.js.html +13 -13
  27. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +1 -1
  28. package/coverage/lcov-report/api/controllers/row/staticFormula.js.html +15 -15
  29. package/coverage/lcov-report/api/controllers/row/utils.js.html +13 -13
  30. package/coverage/lcov-report/api/controllers/script.js.html +1 -1
  31. package/coverage/lcov-report/api/controllers/table/bulkFormula.js.html +12 -9
  32. package/coverage/lcov-report/api/controllers/table/external.js.html +10 -10
  33. package/coverage/lcov-report/api/controllers/table/index.html +16 -16
  34. package/coverage/lcov-report/api/controllers/table/index.js.html +5 -5
  35. package/coverage/lcov-report/api/controllers/templates.js.html +6 -6
  36. package/coverage/lcov-report/api/controllers/user.js.html +1 -1
  37. package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
  38. package/coverage/lcov-report/api/controllers/view/index.html +1 -1
  39. package/coverage/lcov-report/api/controllers/view/index.js.html +3 -3
  40. package/coverage/lcov-report/api/controllers/view/utils.js.html +13 -13
  41. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +8 -8
  42. package/coverage/lcov-report/api/index.html +1 -1
  43. package/coverage/lcov-report/api/index.js.html +30 -30
  44. package/coverage/lcov-report/api/routes/analytics.js.html +7 -7
  45. package/coverage/lcov-report/api/routes/apikeys.js.html +8 -8
  46. package/coverage/lcov-report/api/routes/automation.js.html +11 -11
  47. package/coverage/lcov-report/api/routes/cloud.js.html +8 -8
  48. package/coverage/lcov-report/api/routes/component.js.html +8 -8
  49. package/coverage/lcov-report/api/routes/datasource.js.html +9 -9
  50. package/coverage/lcov-report/api/routes/deploy.js.html +8 -8
  51. package/coverage/lcov-report/api/routes/dev.js.html +13 -13
  52. package/coverage/lcov-report/api/routes/index.html +5 -35
  53. package/coverage/lcov-report/api/routes/integration.js.html +8 -8
  54. package/coverage/lcov-report/api/routes/layout.js.html +8 -8
  55. package/coverage/lcov-report/api/routes/metadata.js.html +9 -9
  56. package/coverage/lcov-report/api/routes/migrations.js.html +7 -7
  57. package/coverage/lcov-report/api/routes/permission.js.html +9 -9
  58. package/coverage/lcov-report/api/routes/query.js.html +10 -10
  59. package/coverage/lcov-report/api/routes/role.js.html +9 -9
  60. package/coverage/lcov-report/api/routes/routing.js.html +8 -8
  61. package/coverage/lcov-report/api/routes/screen.js.html +9 -9
  62. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/table.js.html +10 -10
  64. package/coverage/lcov-report/api/routes/templates.js.html +8 -8
  65. package/coverage/lcov-report/api/routes/user.js.html +8 -8
  66. package/coverage/lcov-report/api/routes/utils/index.html +1 -1
  67. package/coverage/lcov-report/api/routes/utils/validators.js.html +45 -45
  68. package/coverage/lcov-report/api/routes/view.js.html +10 -10
  69. package/coverage/lcov-report/automations/actions.js.html +1 -1
  70. package/coverage/lcov-report/automations/automationUtils.js.html +6 -6
  71. package/coverage/lcov-report/automations/bullboard.js.html +24 -84
  72. package/coverage/lcov-report/automations/index.html +21 -21
  73. package/coverage/lcov-report/automations/index.js.html +9 -9
  74. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  75. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  76. package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
  77. package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
  78. package/coverage/lcov-report/automations/steps/executeScript.js.html +1 -1
  79. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  80. package/coverage/lcov-report/automations/steps/index.html +15 -15
  81. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  82. package/coverage/lcov-report/automations/steps/loop.js.html +1 -1
  83. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +1 -1
  84. package/coverage/lcov-report/automations/steps/queryRows.js.html +1 -1
  85. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  86. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  87. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  88. package/coverage/lcov-report/automations/steps/updateRow.js.html +3 -3
  89. package/coverage/lcov-report/automations/steps/utils.js.html +10 -10
  90. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  91. package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
  92. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
  93. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  94. package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
  95. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
  96. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
  97. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
  98. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
  99. package/coverage/lcov-report/automations/triggers.js.html +49 -49
  100. package/coverage/lcov-report/constants/index.html +5 -5
  101. package/coverage/lcov-report/constants/index.js.html +10 -28
  102. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  103. package/coverage/lcov-report/constants/screens.js.html +1 -1
  104. package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
  105. package/coverage/lcov-report/db/index.html +19 -19
  106. package/coverage/lcov-report/db/index.js.html +1 -1
  107. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +17 -17
  108. package/coverage/lcov-report/db/linkedRows/index.html +1 -1
  109. package/coverage/lcov-report/db/linkedRows/index.js.html +28 -28
  110. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +15 -15
  111. package/coverage/lcov-report/db/newid.js.html +2 -2
  112. package/coverage/lcov-report/db/utils.js.html +37 -316
  113. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  114. package/coverage/lcov-report/events/index.html +1 -1
  115. package/coverage/lcov-report/events/index.js.html +3 -3
  116. package/coverage/lcov-report/events/utils.js.html +9 -9
  117. package/coverage/lcov-report/index.html +88 -88
  118. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  119. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  120. package/coverage/lcov-report/middleware/appInfo.js.html +5 -5
  121. package/coverage/lcov-report/middleware/builder.js.html +19 -19
  122. package/coverage/lcov-report/middleware/currentapp.js.html +49 -49
  123. package/coverage/lcov-report/middleware/index.html +1 -1
  124. package/coverage/lcov-report/middleware/joi-validator.js.html +1 -1
  125. package/coverage/lcov-report/middleware/publicApi.js.html +5 -5
  126. package/coverage/lcov-report/middleware/resourceId.js.html +28 -28
  127. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  128. package/coverage/lcov-report/middleware/utils.js.html +4 -4
  129. package/coverage/lcov-report/utilities/budibaseDir.js.html +3 -3
  130. package/coverage/lcov-report/utilities/centralPath.js.html +4 -4
  131. package/coverage/lcov-report/utilities/csvParser.js.html +15 -12
  132. package/coverage/lcov-report/utilities/global.js.html +31 -31
  133. package/coverage/lcov-report/utilities/index.html +28 -28
  134. package/coverage/lcov-report/utilities/index.js.html +8 -8
  135. package/coverage/lcov-report/utilities/plugins.js.html +1 -1
  136. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +52 -52
  137. package/coverage/lcov-report/utilities/queue/index.html +21 -21
  138. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  139. package/coverage/lcov-report/utilities/routing/index.js.html +6 -6
  140. package/coverage/lcov-report/utilities/rowProcessor/index.html +1 -1
  141. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +72 -57
  142. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +15 -15
  143. package/coverage/lcov-report/utilities/scriptRunner.js.html +1 -1
  144. package/coverage/lcov-report/utilities/security.js.html +2 -2
  145. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  146. package/coverage/lcov-report/utilities/usageQuota/index.html +14 -14
  147. package/coverage/lcov-report/utilities/usageQuota/rows.js.html +23 -11
  148. package/coverage/lcov-report/utilities/usageQuota/usageQuoteReset.js.html +1 -1
  149. package/coverage/lcov-report/utilities/users.js.html +1 -1
  150. package/coverage/lcov-report/utilities/workerRequests.js.html +46 -46
  151. package/coverage/lcov.info +1691 -1899
  152. package/dist/api/controllers/application.js +4 -7
  153. package/dist/api/controllers/backup.js +19 -13
  154. package/dist/api/controllers/cloud.js +38 -35
  155. package/dist/api/controllers/deploy/index.js +11 -2
  156. package/dist/api/controllers/dev.js +1 -1
  157. package/dist/api/controllers/query/index.js +7 -2
  158. package/dist/api/controllers/row/index.js +40 -66
  159. package/dist/api/controllers/row/internal.js +1 -1
  160. package/dist/api/controllers/static/index.js +3 -2
  161. package/dist/api/controllers/static/templates/BudibaseApp.svelte +11 -0
  162. package/dist/api/controllers/static/templates/preview.hbs +10 -0
  163. package/dist/api/controllers/table/bulkFormula.js +4 -3
  164. package/dist/api/controllers/table/external.js +5 -4
  165. package/dist/api/controllers/table/index.js +4 -4
  166. package/dist/api/controllers/table/internal.js +10 -3
  167. package/dist/api/controllers/table/utils.js +7 -49
  168. package/dist/api/controllers/view/index.js +2 -2
  169. package/dist/api/controllers/webhook.js +88 -89
  170. package/dist/api/routes/backup.js +34 -7
  171. package/dist/api/routes/index.js +9 -4
  172. package/dist/api/routes/utils/validators.js +2 -2
  173. package/dist/api/routes/webhook.js +39 -11
  174. package/dist/app.js +2 -68
  175. package/dist/automations/automationUtils.js +2 -2
  176. package/dist/automations/bullboard.js +12 -35
  177. package/dist/automations/index.js +4 -4
  178. package/dist/automations/steps/updateRow.js +2 -2
  179. package/dist/automations/triggers.js +6 -6
  180. package/dist/automations/utils.js +23 -49
  181. package/dist/constants/index.js +4 -8
  182. package/dist/db/utils.js +10 -75
  183. package/dist/index.js +1 -0
  184. package/dist/integrations/arangodb.js +2 -0
  185. package/dist/integrations/base/sqlTable.js +1 -0
  186. package/dist/integrations/mongodb.js +300 -11
  187. package/dist/integrations/rest.js +38 -5
  188. package/dist/migrations/functions/backfill/app/tables.js +5 -8
  189. package/dist/migrations/functions/{quotas1.js → syncQuotas.js} +3 -7
  190. package/dist/migrations/functions/usageQuotas/syncApps.js +0 -1
  191. package/dist/migrations/functions/{pluginCount.js → usageQuotas/syncPlugins.js} +1 -3
  192. package/dist/migrations/functions/usageQuotas/syncRows.js +9 -5
  193. package/dist/migrations/index.js +10 -38
  194. package/dist/package.json +14 -14
  195. package/dist/sdk/app/automations/index.js +29 -0
  196. package/dist/sdk/app/automations/webhook.js +54 -0
  197. package/dist/sdk/app/backups/constants.js +5 -0
  198. package/dist/sdk/app/backups/exports.js +172 -0
  199. package/dist/sdk/app/backups/imports.js +190 -0
  200. package/dist/sdk/app/backups/index.js +29 -0
  201. package/dist/sdk/app/backups/statistics.js +73 -0
  202. package/dist/sdk/app/tables/index.js +61 -0
  203. package/dist/sdk/index.js +17 -0
  204. package/dist/startup.js +152 -0
  205. package/dist/tests/utilities/TestConfiguration.js +12 -2
  206. package/dist/threads/automation.js +12 -17
  207. package/dist/tsconfig.build.tsbuildinfo +1 -1
  208. package/dist/utilities/csvParser.js +1 -0
  209. package/dist/utilities/fileSystem/index.js +3 -95
  210. package/dist/utilities/fileSystem/utilities.js +2 -1
  211. package/dist/utilities/rowProcessor/index.js +5 -0
  212. package/dist/utilities/usageQuota/rows.js +6 -4
  213. package/package.json +15 -15
  214. package/specs/openapi.json +3 -0
  215. package/specs/openapi.yaml +3 -0
  216. package/src/api/controllers/application.ts +5 -12
  217. package/src/api/controllers/{backup.js → backup.ts} +7 -7
  218. package/src/api/controllers/cloud.js +44 -40
  219. package/src/api/controllers/deploy/index.ts +20 -8
  220. package/src/api/controllers/dev.js +1 -1
  221. package/src/api/controllers/query/index.ts +8 -2
  222. package/src/api/controllers/row/index.ts +50 -65
  223. package/src/api/controllers/row/internal.js +1 -1
  224. package/src/api/controllers/static/index.ts +4 -2
  225. package/src/api/controllers/static/templates/BudibaseApp.svelte +11 -0
  226. package/src/api/controllers/static/templates/preview.hbs +10 -0
  227. package/src/api/controllers/table/bulkFormula.js +4 -3
  228. package/src/api/controllers/table/external.js +4 -4
  229. package/src/api/controllers/table/index.js +4 -4
  230. package/src/api/controllers/table/internal.ts +8 -9
  231. package/src/api/controllers/table/utils.ts +4 -53
  232. package/src/api/controllers/view/index.js +2 -2
  233. package/src/api/controllers/{webhook.js → webhook.ts} +33 -48
  234. package/src/api/routes/backup.ts +10 -0
  235. package/src/api/routes/index.ts +11 -5
  236. package/src/api/routes/tests/backup.spec.js +1 -1
  237. package/src/api/routes/tests/{deployment.spec.js → deployment.spec.ts} +4 -4
  238. package/src/api/routes/tests/row.spec.js +22 -27
  239. package/src/api/routes/utils/validators.js +2 -2
  240. package/src/api/routes/{webhook.js → webhook.ts} +7 -6
  241. package/src/app.ts +3 -91
  242. package/src/automations/automationUtils.js +2 -2
  243. package/src/automations/bullboard.js +14 -34
  244. package/src/automations/index.js +4 -4
  245. package/src/automations/steps/updateRow.js +2 -2
  246. package/src/automations/tests/automation.spec.js +1 -6
  247. package/src/automations/triggers.js +6 -6
  248. package/src/automations/utils.ts +30 -32
  249. package/src/constants/index.js +4 -10
  250. package/src/db/utils.js +17 -110
  251. package/src/definitions/automations.ts +1 -18
  252. package/src/definitions/datasource.ts +1 -0
  253. package/src/index.ts +1 -0
  254. package/src/integrations/arangodb.ts +2 -0
  255. package/src/integrations/base/sqlTable.ts +1 -0
  256. package/src/integrations/mongodb.ts +290 -27
  257. package/src/integrations/rest.ts +17 -5
  258. package/src/integrations/tests/mongo.spec.ts +19 -19
  259. package/src/integrations/tests/rest.spec.ts +38 -22
  260. package/src/migrations/functions/backfill/app/tables.ts +7 -13
  261. package/src/migrations/functions/backfill/global.ts +1 -1
  262. package/src/migrations/functions/{quotas1.ts → syncQuotas.ts} +3 -8
  263. package/src/migrations/functions/tests/{quotas1.spec.js → syncQuotas.spec.js} +6 -5
  264. package/src/migrations/functions/tests/userEmailViewCasing.spec.js +6 -4
  265. package/src/migrations/functions/usageQuotas/syncApps.ts +0 -1
  266. package/src/migrations/functions/usageQuotas/syncPlugins.ts +10 -0
  267. package/src/migrations/functions/usageQuotas/syncRows.ts +15 -6
  268. package/src/migrations/functions/usageQuotas/tests/syncRows.spec.ts +9 -1
  269. package/src/migrations/index.ts +21 -45
  270. package/src/migrations/tests/index.spec.ts +0 -1
  271. package/src/sdk/app/automations/index.ts +5 -0
  272. package/src/sdk/app/automations/webhook.ts +43 -0
  273. package/src/sdk/app/backups/constants.ts +2 -0
  274. package/src/sdk/app/backups/exports.ts +171 -0
  275. package/src/sdk/app/backups/imports.ts +206 -0
  276. package/src/sdk/app/backups/index.ts +9 -0
  277. package/src/sdk/app/backups/statistics.ts +78 -0
  278. package/src/sdk/app/tables/index.ts +61 -0
  279. package/src/sdk/index.ts +15 -0
  280. package/src/startup.ts +138 -0
  281. package/src/tests/utilities/TestConfiguration.js +12 -2
  282. package/src/threads/automation.ts +20 -27
  283. package/src/utilities/csvParser.js +1 -0
  284. package/src/utilities/fileSystem/index.js +2 -119
  285. package/src/utilities/fileSystem/utilities.js +2 -0
  286. package/src/utilities/rowProcessor/index.js +5 -0
  287. package/src/utilities/usageQuota/rows.js +7 -3
  288. package/coverage/lcov-report/api/controllers/backup.js.html +0 -142
  289. package/coverage/lcov-report/api/controllers/webhook.js.html +0 -424
  290. package/coverage/lcov-report/api/routes/backup.js.html +0 -115
  291. package/coverage/lcov-report/api/routes/webhook.js.html +0 -163
  292. package/dist/automations/listeners.js +0 -102
  293. package/src/api/routes/backup.js +0 -10
  294. package/src/automations/listeners.ts +0 -78
  295. package/src/migrations/functions/pluginCount.ts +0 -12
@@ -0,0 +1,206 @@
1
+ import { db as dbCore } from "@budibase/backend-core"
2
+ import { getAutomationParams, TABLE_ROW_PREFIX } from "../../../db/utils"
3
+ import { budibaseTempDir } from "../../../utilities/budibaseDir"
4
+ import { DB_EXPORT_FILE, GLOBAL_DB_EXPORT_FILE } from "./constants"
5
+ import {
6
+ upload,
7
+ uploadDirectory,
8
+ } from "../../../utilities/fileSystem/utilities"
9
+ import { downloadTemplate } from "../../../utilities/fileSystem"
10
+ import { FieldTypes, ObjectStoreBuckets } from "../../../constants"
11
+ import { join } from "path"
12
+ import fs from "fs"
13
+ import sdk from "../../"
14
+ import {
15
+ Automation,
16
+ AutomationTriggerStepId,
17
+ CouchFindOptions,
18
+ RowAttachment,
19
+ } from "@budibase/types"
20
+ import PouchDB from "pouchdb"
21
+ const uuid = require("uuid/v4")
22
+ const tar = require("tar")
23
+
24
+ type TemplateType = {
25
+ file?: {
26
+ type: string
27
+ path: string
28
+ }
29
+ key?: string
30
+ }
31
+
32
+ async function updateAttachmentColumns(
33
+ prodAppId: string,
34
+ db: PouchDB.Database
35
+ ) {
36
+ // iterate through attachment documents and update them
37
+ const tables = await sdk.tables.getAllInternalTables(db)
38
+ for (let table of tables) {
39
+ const attachmentCols: string[] = []
40
+ for (let [key, column] of Object.entries(table.schema)) {
41
+ if (column.type === FieldTypes.ATTACHMENT) {
42
+ attachmentCols.push(key)
43
+ }
44
+ }
45
+ // no attachment columns, nothing to do
46
+ if (attachmentCols.length === 0) {
47
+ continue
48
+ }
49
+ // use the CouchDB Mango query API to lookup rows that have attachments
50
+ const params: CouchFindOptions = {
51
+ selector: {
52
+ _id: {
53
+ $regex: `^${TABLE_ROW_PREFIX}`,
54
+ },
55
+ },
56
+ }
57
+ attachmentCols.forEach(col => (params.selector[col] = { $exists: true }))
58
+ const { rows } = await dbCore.directCouchFind(db.name, params)
59
+ for (let row of rows) {
60
+ for (let column of attachmentCols) {
61
+ if (!Array.isArray(row[column])) {
62
+ continue
63
+ }
64
+ row[column] = row[column].map((attachment: RowAttachment) => {
65
+ // URL looks like: /prod-budi-app-assets/appId/attachments/file.csv
66
+ const urlParts = attachment.url.split("/")
67
+ // drop the first empty element
68
+ urlParts.shift()
69
+ // get the prefix
70
+ const prefix = urlParts.shift()
71
+ // remove the app ID
72
+ urlParts.shift()
73
+ // add new app ID
74
+ urlParts.unshift(prodAppId)
75
+ const key = urlParts.join("/")
76
+ return {
77
+ ...attachment,
78
+ key,
79
+ url: `/${prefix}/${key}`,
80
+ }
81
+ })
82
+ }
83
+ }
84
+ // write back the updated attachments
85
+ await db.bulkDocs(rows)
86
+ }
87
+ }
88
+
89
+ async function updateAutomations(prodAppId: string, db: PouchDB.Database) {
90
+ const automations = (
91
+ await db.allDocs(
92
+ getAutomationParams(null, {
93
+ include_docs: true,
94
+ })
95
+ )
96
+ ).rows.map(row => row.doc) as Automation[]
97
+ const devAppId = dbCore.getDevAppID(prodAppId)
98
+ let toSave: Automation[] = []
99
+ for (let automation of automations) {
100
+ const oldDevAppId = automation.appId,
101
+ oldProdAppId = dbCore.getProdAppID(automation.appId)
102
+ if (
103
+ automation.definition.trigger.stepId === AutomationTriggerStepId.WEBHOOK
104
+ ) {
105
+ const old = automation.definition.trigger.inputs
106
+ automation.definition.trigger.inputs = {
107
+ schemaUrl: old.schemaUrl.replace(oldDevAppId, devAppId),
108
+ triggerUrl: old.triggerUrl.replace(oldProdAppId, prodAppId),
109
+ }
110
+ }
111
+ automation.appId = devAppId
112
+ toSave.push(automation)
113
+ }
114
+ await db.bulkDocs(toSave)
115
+ }
116
+
117
+ /**
118
+ * This function manages temporary template files which are stored by Koa.
119
+ * @param {Object} template The template object retrieved from the Koa context object.
120
+ * @returns {Object} Returns a fs read stream which can be loaded into the database.
121
+ */
122
+ async function getTemplateStream(template: TemplateType) {
123
+ if (template.file && template.file.type !== "text/plain") {
124
+ throw new Error("Cannot import a non-text based file.")
125
+ }
126
+ if (template.file) {
127
+ return fs.createReadStream(template.file.path)
128
+ } else if (template.key) {
129
+ const [type, name] = template.key.split("/")
130
+ const tmpPath = await downloadTemplate(type, name)
131
+ return fs.createReadStream(join(tmpPath, name, "db", "dump.txt"))
132
+ }
133
+ }
134
+
135
+ export function untarFile(file: { path: string }) {
136
+ const tmpPath = join(budibaseTempDir(), uuid())
137
+ fs.mkdirSync(tmpPath)
138
+ // extract the tarball
139
+ tar.extract({
140
+ sync: true,
141
+ cwd: tmpPath,
142
+ file: file.path,
143
+ })
144
+ return tmpPath
145
+ }
146
+
147
+ export function getGlobalDBFile(tmpPath: string) {
148
+ return fs.readFileSync(join(tmpPath, GLOBAL_DB_EXPORT_FILE), "utf8")
149
+ }
150
+
151
+ export function getListOfAppsInMulti(tmpPath: string) {
152
+ return fs.readdirSync(tmpPath).filter(dir => dir !== GLOBAL_DB_EXPORT_FILE)
153
+ }
154
+
155
+ export async function importApp(
156
+ appId: string,
157
+ db: PouchDB.Database,
158
+ template: TemplateType
159
+ ) {
160
+ let prodAppId = dbCore.getProdAppID(appId)
161
+ let dbStream: any
162
+ const isTar = template.file && template?.file?.type?.endsWith("gzip")
163
+ const isDirectory =
164
+ template.file && fs.lstatSync(template.file.path).isDirectory()
165
+ if (template.file && (isTar || isDirectory)) {
166
+ const tmpPath = isTar ? untarFile(template.file) : template.file.path
167
+ const contents = fs.readdirSync(tmpPath)
168
+ // have to handle object import
169
+ if (contents.length) {
170
+ let promises = []
171
+ let excludedFiles = [GLOBAL_DB_EXPORT_FILE, DB_EXPORT_FILE]
172
+ for (let filename of contents) {
173
+ const path = join(tmpPath, filename)
174
+ if (excludedFiles.includes(filename)) {
175
+ continue
176
+ }
177
+ filename = join(prodAppId, filename)
178
+ if (fs.lstatSync(path).isDirectory()) {
179
+ promises.push(
180
+ uploadDirectory(ObjectStoreBuckets.APPS, path, filename)
181
+ )
182
+ } else {
183
+ promises.push(
184
+ upload({
185
+ bucket: ObjectStoreBuckets.APPS,
186
+ path,
187
+ filename,
188
+ })
189
+ )
190
+ }
191
+ }
192
+ await Promise.all(promises)
193
+ }
194
+ dbStream = fs.createReadStream(join(tmpPath, DB_EXPORT_FILE))
195
+ } else {
196
+ dbStream = await getTemplateStream(template)
197
+ }
198
+ // @ts-ignore
199
+ const { ok } = await db.load(dbStream)
200
+ if (!ok) {
201
+ throw "Error loading database dump from template."
202
+ }
203
+ await updateAttachmentColumns(prodAppId, db)
204
+ await updateAutomations(prodAppId, db)
205
+ return ok
206
+ }
@@ -0,0 +1,9 @@
1
+ import * as exportApps from "./exports"
2
+ import * as importApps from "./imports"
3
+ import * as statistics from "./statistics"
4
+
5
+ export default {
6
+ ...exportApps,
7
+ ...importApps,
8
+ ...statistics,
9
+ }
@@ -0,0 +1,78 @@
1
+ import { context, db as dbCore } from "@budibase/backend-core"
2
+ import {
3
+ getDatasourceParams,
4
+ getTableParams,
5
+ getAutomationParams,
6
+ getScreenParams,
7
+ } from "../../../db/utils"
8
+ import PouchDB from "pouchdb"
9
+
10
+ async function runInContext(appId: string, cb: any, db?: PouchDB.Database) {
11
+ if (db) {
12
+ return cb(db)
13
+ } else {
14
+ const devAppId = dbCore.getDevAppID(appId)
15
+ return context.doInAppContext(devAppId, () => {
16
+ const db = context.getAppDB()
17
+ return cb(db)
18
+ })
19
+ }
20
+ }
21
+
22
+ export async function calculateDatasourceCount(
23
+ appId: string,
24
+ db?: PouchDB.Database
25
+ ) {
26
+ return runInContext(
27
+ appId,
28
+ async (db: PouchDB.Database) => {
29
+ const datasourceList = await db.allDocs(getDatasourceParams())
30
+ const tableList = await db.allDocs(getTableParams())
31
+ return datasourceList.rows.length + tableList.rows.length
32
+ },
33
+ db
34
+ )
35
+ }
36
+
37
+ export async function calculateAutomationCount(
38
+ appId: string,
39
+ db?: PouchDB.Database
40
+ ) {
41
+ return runInContext(
42
+ appId,
43
+ async (db: PouchDB.Database) => {
44
+ const automationList = await db.allDocs(getAutomationParams())
45
+ return automationList.rows.length
46
+ },
47
+ db
48
+ )
49
+ }
50
+
51
+ export async function calculateScreenCount(
52
+ appId: string,
53
+ db?: PouchDB.Database
54
+ ) {
55
+ return runInContext(
56
+ appId,
57
+ async (db: PouchDB.Database) => {
58
+ const screenList = await db.allDocs(getScreenParams())
59
+ return screenList.rows.length
60
+ },
61
+ db
62
+ )
63
+ }
64
+
65
+ export async function calculateBackupStats(appId: string) {
66
+ return runInContext(appId, async (db: PouchDB.Database) => {
67
+ const promises = []
68
+ promises.push(calculateDatasourceCount(appId, db))
69
+ promises.push(calculateAutomationCount(appId, db))
70
+ promises.push(calculateScreenCount(appId, db))
71
+ const responses = await Promise.all(promises)
72
+ return {
73
+ datasources: responses[0],
74
+ automations: responses[1],
75
+ screens: responses[2],
76
+ }
77
+ })
78
+ }
@@ -0,0 +1,61 @@
1
+ import { getAppDB } from "@budibase/backend-core/context"
2
+ import { BudibaseInternalDB, getTableParams } from "../../../db/utils"
3
+ import {
4
+ breakExternalTableId,
5
+ isExternalTable,
6
+ isSQL,
7
+ } from "../../../integrations/utils"
8
+ import { Table } from "@budibase/types"
9
+ import PouchDB from "pouchdb"
10
+
11
+ async function getAllInternalTables(db?: PouchDB.Database): Promise<Table[]> {
12
+ if (!db) {
13
+ db = getAppDB() as PouchDB.Database
14
+ }
15
+ const internalTables = await db.allDocs(
16
+ getTableParams(null, {
17
+ include_docs: true,
18
+ })
19
+ )
20
+ return internalTables.rows.map((tableDoc: any) => ({
21
+ ...tableDoc.doc,
22
+ type: "internal",
23
+ sourceId: BudibaseInternalDB._id,
24
+ }))
25
+ }
26
+
27
+ async function getAllExternalTables(datasourceId: any): Promise<Table[]> {
28
+ const db = getAppDB()
29
+ const datasource = await db.get(datasourceId)
30
+ if (!datasource || !datasource.entities) {
31
+ throw "Datasource is not configured fully."
32
+ }
33
+ return datasource.entities
34
+ }
35
+
36
+ async function getExternalTable(
37
+ datasourceId: any,
38
+ tableName: any
39
+ ): Promise<Table> {
40
+ const entities = await getAllExternalTables(datasourceId)
41
+ return entities[tableName]
42
+ }
43
+
44
+ async function getTable(tableId: any): Promise<Table> {
45
+ const db = getAppDB()
46
+ if (isExternalTable(tableId)) {
47
+ let { datasourceId, tableName } = breakExternalTableId(tableId)
48
+ const datasource = await db.get(datasourceId)
49
+ const table = await getExternalTable(datasourceId, tableName)
50
+ return { ...table, sql: isSQL(datasource) }
51
+ } else {
52
+ return db.get(tableId)
53
+ }
54
+ }
55
+
56
+ export default {
57
+ getAllInternalTables,
58
+ getAllExternalTables,
59
+ getExternalTable,
60
+ getTable,
61
+ }
@@ -0,0 +1,15 @@
1
+ import { default as backups } from "./app/backups"
2
+ import { default as tables } from "./app/tables"
3
+ import { default as automations } from "./app/automations"
4
+
5
+ const sdk = {
6
+ backups,
7
+ tables,
8
+ automations,
9
+ }
10
+
11
+ // default export for TS
12
+ export default sdk
13
+
14
+ // default export for JS
15
+ module.exports = sdk
package/src/startup.ts ADDED
@@ -0,0 +1,138 @@
1
+ import * as env from "./environment"
2
+ import redis from "./utilities/redis"
3
+ import {
4
+ createAdminUser,
5
+ generateApiKey,
6
+ getChecklist,
7
+ } from "./utilities/workerRequests"
8
+ import {
9
+ installation,
10
+ pinoSettings,
11
+ tenancy,
12
+ logging,
13
+ } from "@budibase/backend-core"
14
+ import fs from "fs"
15
+ import { watch } from "./watch"
16
+ import automations from "./automations"
17
+ import fileSystem from "./utilities/fileSystem"
18
+ import eventEmitter from "./events"
19
+ import * as migrations from "./migrations"
20
+ import bullboard from "./automations/bullboard"
21
+ import * as pro from "@budibase/pro"
22
+ import api from "./api"
23
+ import sdk from "./sdk"
24
+ const pino = require("koa-pino-logger")
25
+
26
+ let STARTUP_RAN = false
27
+
28
+ async function initRoutes(app: any) {
29
+ app.use(pino(pinoSettings()))
30
+
31
+ if (!env.isTest()) {
32
+ const plugin = await bullboard.init()
33
+ app.use(plugin)
34
+ }
35
+
36
+ app.context.eventEmitter = eventEmitter
37
+ app.context.auth = {}
38
+
39
+ // api routes
40
+ app.use(api.router.routes())
41
+ }
42
+
43
+ async function initPro() {
44
+ await pro.init({
45
+ backups: {
46
+ processing: {
47
+ exportAppFn: sdk.backups.exportApp,
48
+ importAppFn: sdk.backups.importApp,
49
+ statsFn: sdk.backups.calculateBackupStats,
50
+ },
51
+ },
52
+ })
53
+ }
54
+
55
+ function shutdown(server?: any) {
56
+ server.close()
57
+ server.destroy()
58
+ }
59
+
60
+ export async function startup(app?: any, server?: any) {
61
+ if (STARTUP_RAN) {
62
+ return
63
+ }
64
+ STARTUP_RAN = true
65
+ if (server) {
66
+ console.log(`Budibase running on ${JSON.stringify(server.address())}`)
67
+ env._set("PORT", server.address().port)
68
+ }
69
+ eventEmitter.emitPort(env.PORT)
70
+ fileSystem.init()
71
+ await redis.init()
72
+
73
+ // run migrations on startup if not done via http
74
+ // not recommended in a clustered environment
75
+ if (!env.HTTP_MIGRATIONS && !env.isTest()) {
76
+ try {
77
+ await migrations.migrate()
78
+ } catch (e) {
79
+ logging.logAlert("Error performing migrations. Exiting.", e)
80
+ shutdown()
81
+ }
82
+ }
83
+
84
+ // check and create admin user if required
85
+ if (
86
+ env.SELF_HOSTED &&
87
+ !env.MULTI_TENANCY &&
88
+ env.BB_ADMIN_USER_EMAIL &&
89
+ env.BB_ADMIN_USER_PASSWORD
90
+ ) {
91
+ const checklist = await getChecklist()
92
+ if (!checklist?.adminUser?.checked) {
93
+ try {
94
+ const tenantId = tenancy.getTenantId()
95
+ const user = await createAdminUser(
96
+ env.BB_ADMIN_USER_EMAIL,
97
+ env.BB_ADMIN_USER_PASSWORD,
98
+ tenantId
99
+ )
100
+ // Need to set up an API key for automated integration tests
101
+ if (env.isTest()) {
102
+ await generateApiKey(user._id)
103
+ }
104
+
105
+ console.log(
106
+ "Admin account automatically created for",
107
+ env.BB_ADMIN_USER_EMAIL
108
+ )
109
+ } catch (e) {
110
+ logging.logAlert("Error creating initial admin user. Exiting.", e)
111
+ shutdown()
112
+ }
113
+ }
114
+ }
115
+
116
+ // monitor plugin directory if required
117
+ if (
118
+ env.SELF_HOSTED &&
119
+ !env.MULTI_TENANCY &&
120
+ env.PLUGINS_DIR &&
121
+ fs.existsSync(env.PLUGINS_DIR)
122
+ ) {
123
+ watch()
124
+ }
125
+
126
+ // check for version updates
127
+ await installation.checkInstallVersion()
128
+
129
+ // get the references to the queue promises, don't await as
130
+ // they will never end, unless the processing stops
131
+ let queuePromises = []
132
+ queuePromises.push(automations.init())
133
+ queuePromises.push(initPro())
134
+ if (app) {
135
+ // bring routes online as final step once everything ready
136
+ await initRoutes(app)
137
+ }
138
+ }
@@ -25,7 +25,8 @@ const newid = require("../../db/newid")
25
25
  const context = require("@budibase/backend-core/context")
26
26
  const { generateDevInfoID, SEPARATOR } = require("@budibase/backend-core/db")
27
27
  const { encrypt } = require("@budibase/backend-core/encryption")
28
- const { DocumentType } = require("../../db/utils")
28
+ const { DocumentType, generateUserMetadataID } = require("../../db/utils")
29
+ const { startup } = require("../../startup")
29
30
 
30
31
  const GLOBAL_USER_ID = "us_uuid1"
31
32
  const EMAIL = "babs@babs.com"
@@ -41,6 +42,9 @@ class TestConfiguration {
41
42
  this.server = require("../../app")
42
43
  // we need the request for logging in, involves cookies, hard to fake
43
44
  this.request = supertest(this.server)
45
+ this.started = true
46
+ } else {
47
+ this.started = false
44
48
  }
45
49
  this.appId = null
46
50
  this.allApps = []
@@ -95,7 +99,13 @@ class TestConfiguration {
95
99
 
96
100
  // use a new id as the name to avoid name collisions
97
101
  async init(appName = newid()) {
98
- await this.globalUser()
102
+ if (!this.started) {
103
+ await startup()
104
+ }
105
+ this.user = await this.globalUser()
106
+ this.globalUserId = this.user._id
107
+ this.userMetadataId = generateUserMetadataID(this.globalUserId)
108
+
99
109
  return this.createApp(appName)
100
110
  }
101
111
 
@@ -1,6 +1,11 @@
1
1
  import { default as threadUtils } from "./utils"
2
+ import { Job } from "bull"
2
3
  threadUtils.threadSetup()
3
- import { isRecurring, disableCron, isErrorInOutput } from "../automations/utils"
4
+ import {
5
+ isRecurring,
6
+ disableCronById,
7
+ isErrorInOutput,
8
+ } from "../automations/utils"
4
9
  import { default as actions } from "../automations/actions"
5
10
  import { default as automationUtils } from "../automations/automationUtils"
6
11
  import { default as AutomationEmitter } from "../events/AutomationEmitter"
@@ -13,7 +18,6 @@ import {
13
18
  LoopStep,
14
19
  LoopStepType,
15
20
  LoopInput,
16
- AutomationEvent,
17
21
  TriggerOutput,
18
22
  AutomationContext,
19
23
  AutomationMetadata,
@@ -73,19 +77,16 @@ class Orchestrator {
73
77
  _automation: Automation
74
78
  _emitter: any
75
79
  _context: AutomationContext
76
- _repeat?: { jobId: string; jobKey: string }
80
+ _job: Job
77
81
  executionOutput: AutomationContext
78
82
 
79
- constructor(automation: Automation, triggerOutput: TriggerOutput, opts: any) {
83
+ constructor(job: Job) {
84
+ let automation = job.data.automation,
85
+ triggerOutput = job.data.event
80
86
  const metadata = triggerOutput.metadata
81
87
  this._chainCount = metadata ? metadata.automationChainCount : 0
82
88
  this._appId = triggerOutput.appId as string
83
- if (opts?.repeat) {
84
- this._repeat = {
85
- jobId: opts.repeat.jobId,
86
- jobKey: opts.repeat.key,
87
- }
88
- }
89
+ this._job = job
89
90
  const triggerStepId = automation.definition.trigger.stepId
90
91
  triggerOutput = this.cleanupTriggerOutputs(triggerStepId, triggerOutput)
91
92
  // remove from context
@@ -134,7 +135,7 @@ class Orchestrator {
134
135
  }
135
136
 
136
137
  async stopCron(reason: string) {
137
- if (!this._repeat) {
138
+ if (!this._job.opts.repeat) {
138
139
  return
139
140
  }
140
141
  logWarn(
@@ -142,7 +143,7 @@ class Orchestrator {
142
143
  )
143
144
  const automation = this._automation
144
145
  const trigger = automation.definition.trigger
145
- await disableCron(this._repeat?.jobId, this._repeat?.jobKey)
146
+ await disableCronById(this._job.id)
146
147
  this.updateExecutionOutput(
147
148
  trigger.id,
148
149
  trigger.stepId,
@@ -156,7 +157,7 @@ class Orchestrator {
156
157
  }
157
158
 
158
159
  async checkIfShouldStop(metadata: AutomationMetadata): Promise<boolean> {
159
- if (!metadata.errorCount || !this._repeat) {
160
+ if (!metadata.errorCount || !this._job.opts.repeat) {
160
161
  return false
161
162
  }
162
163
  if (metadata.errorCount >= MAX_AUTOMATION_RECURRING_ERRORS) {
@@ -475,17 +476,13 @@ class Orchestrator {
475
476
  }
476
477
  }
477
478
 
478
- export function execute(input: AutomationEvent, callback: WorkerCallback) {
479
- const appId = input.data.event.appId
479
+ export function execute(job: Job, callback: WorkerCallback) {
480
+ const appId = job.data.event.appId
480
481
  if (!appId) {
481
482
  throw new Error("Unable to execute, event doesn't contain app ID.")
482
483
  }
483
484
  doInAppContext(appId, async () => {
484
- const automationOrchestrator = new Orchestrator(
485
- input.data.automation,
486
- input.data.event,
487
- input.opts
488
- )
485
+ const automationOrchestrator = new Orchestrator(job)
489
486
  try {
490
487
  const response = await automationOrchestrator.execute()
491
488
  callback(null, response)
@@ -495,17 +492,13 @@ export function execute(input: AutomationEvent, callback: WorkerCallback) {
495
492
  })
496
493
  }
497
494
 
498
- export const removeStalled = async (input: AutomationEvent) => {
499
- const appId = input.data.event.appId
495
+ export const removeStalled = async (job: Job) => {
496
+ const appId = job.data.event.appId
500
497
  if (!appId) {
501
498
  throw new Error("Unable to execute, event doesn't contain app ID.")
502
499
  }
503
500
  await doInAppContext(appId, async () => {
504
- const automationOrchestrator = new Orchestrator(
505
- input.data.automation,
506
- input.data.event,
507
- input.opts
508
- )
501
+ const automationOrchestrator = new Orchestrator(job)
509
502
  await automationOrchestrator.stopCron("stalled")
510
503
  })
511
504
  }
@@ -4,6 +4,7 @@ const { FieldTypes } = require("../constants")
4
4
  const VALIDATORS = {
5
5
  [FieldTypes.STRING]: () => true,
6
6
  [FieldTypes.OPTIONS]: () => true,
7
+ [FieldTypes.BARCODEQR]: () => true,
7
8
  [FieldTypes.NUMBER]: attribute => {
8
9
  // allow not to be present
9
10
  if (!attribute) {