@budibase/server 2.0.33 → 2.0.34-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/__mocks__/mongodb.ts +1 -1
  2. package/builder/assets/backups-default.8c011f1b.png +0 -0
  3. package/builder/assets/{index.dfa4073c.css → index.b15ffe95.css} +2 -2
  4. package/builder/assets/{index.ce49a649.js → index.cf543543.js} +340 -332
  5. package/builder/index.html +2 -2
  6. package/coverage/clover.xml +635 -690
  7. package/coverage/coverage-final.json +34 -36
  8. package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
  9. package/coverage/lcov-report/api/controllers/automation.js.html +1 -1
  10. package/coverage/lcov-report/api/controllers/cloud.js.html +74 -62
  11. package/coverage/lcov-report/api/controllers/datasource.js.html +1 -1
  12. package/coverage/lcov-report/api/controllers/dev.js.html +2 -2
  13. package/coverage/lcov-report/api/controllers/index.html +17 -32
  14. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  15. package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
  16. package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
  17. package/coverage/lcov-report/api/controllers/migrations.js.html +1 -1
  18. package/coverage/lcov-report/api/controllers/permission.js.html +1 -1
  19. package/coverage/lcov-report/api/controllers/query/index.html +1 -1
  20. package/coverage/lcov-report/api/controllers/query/validation.js.html +1 -1
  21. package/coverage/lcov-report/api/controllers/role.js.html +1 -1
  22. package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
  23. package/coverage/lcov-report/api/controllers/row/external.js.html +1 -1
  24. package/coverage/lcov-report/api/controllers/row/index.html +1 -1
  25. package/coverage/lcov-report/api/controllers/row/internal.js.html +12 -12
  26. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +1 -1
  27. package/coverage/lcov-report/api/controllers/row/staticFormula.js.html +15 -15
  28. package/coverage/lcov-report/api/controllers/row/utils.js.html +13 -13
  29. package/coverage/lcov-report/api/controllers/script.js.html +1 -1
  30. package/coverage/lcov-report/api/controllers/table/bulkFormula.js.html +12 -9
  31. package/coverage/lcov-report/api/controllers/table/external.js.html +10 -10
  32. package/coverage/lcov-report/api/controllers/table/index.html +16 -16
  33. package/coverage/lcov-report/api/controllers/table/index.js.html +5 -5
  34. package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
  35. package/coverage/lcov-report/api/controllers/user.js.html +1 -1
  36. package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
  37. package/coverage/lcov-report/api/controllers/view/index.html +1 -1
  38. package/coverage/lcov-report/api/controllers/view/index.js.html +3 -3
  39. package/coverage/lcov-report/api/controllers/view/utils.js.html +1 -1
  40. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +1 -1
  41. package/coverage/lcov-report/api/controllers/webhook.js.html +1 -1
  42. package/coverage/lcov-report/api/index.html +1 -1
  43. package/coverage/lcov-report/api/index.js.html +7 -7
  44. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  45. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  46. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  47. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  48. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  49. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  50. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  51. package/coverage/lcov-report/api/routes/dev.js.html +1 -1
  52. package/coverage/lcov-report/api/routes/index.html +5 -20
  53. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/migrations.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/query.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/utils/index.html +1 -1
  67. package/coverage/lcov-report/api/routes/utils/validators.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  70. package/coverage/lcov-report/automations/actions.js.html +1 -1
  71. package/coverage/lcov-report/automations/automationUtils.js.html +6 -6
  72. package/coverage/lcov-report/automations/bullboard.js.html +17 -92
  73. package/coverage/lcov-report/automations/index.html +20 -20
  74. package/coverage/lcov-report/automations/index.js.html +9 -9
  75. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  76. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  77. package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
  78. package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
  79. package/coverage/lcov-report/automations/steps/executeScript.js.html +1 -1
  80. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  81. package/coverage/lcov-report/automations/steps/index.html +1 -1
  82. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  83. package/coverage/lcov-report/automations/steps/loop.js.html +1 -1
  84. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +1 -1
  85. package/coverage/lcov-report/automations/steps/queryRows.js.html +1 -1
  86. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  87. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  88. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  89. package/coverage/lcov-report/automations/steps/updateRow.js.html +3 -3
  90. package/coverage/lcov-report/automations/steps/utils.js.html +1 -1
  91. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  92. package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
  93. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
  94. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  95. package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
  96. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
  97. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
  98. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
  99. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
  100. package/coverage/lcov-report/automations/triggers.js.html +30 -30
  101. package/coverage/lcov-report/constants/index.html +5 -5
  102. package/coverage/lcov-report/constants/index.js.html +10 -16
  103. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  104. package/coverage/lcov-report/constants/screens.js.html +1 -1
  105. package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
  106. package/coverage/lcov-report/db/index.html +19 -19
  107. package/coverage/lcov-report/db/index.js.html +1 -1
  108. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +11 -11
  109. package/coverage/lcov-report/db/linkedRows/index.html +1 -1
  110. package/coverage/lcov-report/db/linkedRows/index.js.html +11 -11
  111. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +3 -3
  112. package/coverage/lcov-report/db/newid.js.html +2 -2
  113. package/coverage/lcov-report/db/utils.js.html +37 -316
  114. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  115. package/coverage/lcov-report/events/index.html +1 -1
  116. package/coverage/lcov-report/events/index.js.html +2 -2
  117. package/coverage/lcov-report/events/utils.js.html +9 -9
  118. package/coverage/lcov-report/index.html +77 -77
  119. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  120. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  121. package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
  122. package/coverage/lcov-report/middleware/builder.js.html +11 -11
  123. package/coverage/lcov-report/middleware/currentapp.js.html +34 -34
  124. package/coverage/lcov-report/middleware/index.html +1 -1
  125. package/coverage/lcov-report/middleware/joi-validator.js.html +1 -1
  126. package/coverage/lcov-report/middleware/publicApi.js.html +1 -1
  127. package/coverage/lcov-report/middleware/resourceId.js.html +7 -7
  128. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  129. package/coverage/lcov-report/middleware/utils.js.html +2 -2
  130. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  131. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  132. package/coverage/lcov-report/utilities/csvParser.js.html +11 -8
  133. package/coverage/lcov-report/utilities/global.js.html +16 -16
  134. package/coverage/lcov-report/utilities/index.html +15 -15
  135. package/coverage/lcov-report/utilities/index.js.html +7 -7
  136. package/coverage/lcov-report/utilities/plugins.js.html +1 -1
  137. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +52 -52
  138. package/coverage/lcov-report/utilities/queue/index.html +21 -21
  139. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  140. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  141. package/coverage/lcov-report/utilities/rowProcessor/index.html +1 -1
  142. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +54 -39
  143. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +10 -10
  144. package/coverage/lcov-report/utilities/scriptRunner.js.html +1 -1
  145. package/coverage/lcov-report/utilities/security.js.html +2 -2
  146. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  147. package/coverage/lcov-report/utilities/usageQuota/index.html +14 -14
  148. package/coverage/lcov-report/utilities/usageQuota/rows.js.html +23 -11
  149. package/coverage/lcov-report/utilities/usageQuota/usageQuoteReset.js.html +1 -1
  150. package/coverage/lcov-report/utilities/users.js.html +1 -1
  151. package/coverage/lcov-report/utilities/workerRequests.js.html +1 -1
  152. package/coverage/lcov.info +1144 -1248
  153. package/dist/api/controllers/application.js +4 -7
  154. package/dist/api/controllers/backup.js +19 -13
  155. package/dist/api/controllers/cloud.js +38 -35
  156. package/dist/api/controllers/deploy/index.js +11 -2
  157. package/dist/api/controllers/dev.js +1 -1
  158. package/dist/api/controllers/query/index.js +7 -2
  159. package/dist/api/controllers/row/index.js +30 -10
  160. package/dist/api/controllers/static/index.js +2 -2
  161. package/dist/api/controllers/static/templates/preview.hbs +10 -0
  162. package/dist/api/controllers/table/bulkFormula.js +4 -3
  163. package/dist/api/controllers/table/external.js +5 -4
  164. package/dist/api/controllers/table/index.js +4 -4
  165. package/dist/api/controllers/table/internal.js +10 -3
  166. package/dist/api/controllers/table/utils.js +7 -49
  167. package/dist/api/controllers/view/index.js +2 -2
  168. package/dist/api/routes/backup.js +34 -7
  169. package/dist/api/routes/index.js +9 -4
  170. package/dist/app.js +18 -2
  171. package/dist/automations/automationUtils.js +2 -2
  172. package/dist/automations/bullboard.js +5 -33
  173. package/dist/automations/index.js +4 -4
  174. package/dist/automations/steps/updateRow.js +2 -2
  175. package/dist/automations/triggers.js +6 -6
  176. package/dist/automations/utils.js +18 -12
  177. package/dist/constants/index.js +4 -5
  178. package/dist/db/utils.js +10 -75
  179. package/dist/index.js +1 -0
  180. package/dist/integrations/arangodb.js +2 -0
  181. package/dist/integrations/base/sqlTable.js +1 -0
  182. package/dist/integrations/mongodb.js +300 -11
  183. package/dist/integrations/rest.js +38 -5
  184. package/dist/migrations/functions/backfill/app/tables.js +5 -8
  185. package/dist/migrations/functions/{quotas1.js → syncQuotas.js} +3 -7
  186. package/dist/migrations/functions/usageQuotas/syncApps.js +0 -1
  187. package/dist/migrations/functions/{pluginCount.js → usageQuotas/syncPlugins.js} +1 -3
  188. package/dist/migrations/functions/usageQuotas/syncRows.js +9 -5
  189. package/dist/migrations/index.js +10 -38
  190. package/dist/package.json +14 -14
  191. package/dist/sdk/app/backups/constants.js +5 -0
  192. package/dist/sdk/app/backups/exports.js +172 -0
  193. package/dist/sdk/app/backups/imports.js +162 -0
  194. package/dist/sdk/app/backups/index.js +29 -0
  195. package/dist/sdk/app/backups/statistics.js +73 -0
  196. package/dist/sdk/app/tables/index.js +61 -0
  197. package/dist/sdk/index.js +15 -0
  198. package/dist/tests/utilities/TestConfiguration.js +4 -2
  199. package/dist/threads/automation.js +12 -17
  200. package/dist/tsconfig.build.tsbuildinfo +1 -1
  201. package/dist/utilities/csvParser.js +1 -0
  202. package/dist/utilities/fileSystem/index.js +3 -95
  203. package/dist/utilities/fileSystem/utilities.js +2 -1
  204. package/dist/utilities/rowProcessor/index.js +5 -0
  205. package/dist/utilities/usageQuota/rows.js +6 -4
  206. package/package.json +15 -15
  207. package/specs/openapi.json +3 -0
  208. package/specs/openapi.yaml +3 -0
  209. package/src/api/controllers/application.ts +5 -12
  210. package/src/api/controllers/{backup.js → backup.ts} +7 -7
  211. package/src/api/controllers/cloud.js +44 -40
  212. package/src/api/controllers/deploy/index.ts +20 -8
  213. package/src/api/controllers/dev.js +1 -1
  214. package/src/api/controllers/query/index.ts +8 -2
  215. package/src/api/controllers/row/index.ts +36 -13
  216. package/src/api/controllers/static/index.ts +2 -2
  217. package/src/api/controllers/static/templates/preview.hbs +10 -0
  218. package/src/api/controllers/table/bulkFormula.js +4 -3
  219. package/src/api/controllers/table/external.js +4 -4
  220. package/src/api/controllers/table/index.js +4 -4
  221. package/src/api/controllers/table/internal.ts +8 -9
  222. package/src/api/controllers/table/utils.ts +4 -53
  223. package/src/api/controllers/view/index.js +2 -2
  224. package/src/api/routes/backup.ts +10 -0
  225. package/src/api/routes/index.ts +11 -5
  226. package/src/api/routes/tests/backup.spec.js +1 -1
  227. package/src/api/routes/tests/{deployment.spec.js → deployment.spec.ts} +4 -4
  228. package/src/api/routes/tests/row.spec.js +22 -27
  229. package/src/app.ts +19 -2
  230. package/src/automations/automationUtils.js +2 -2
  231. package/src/automations/bullboard.js +8 -33
  232. package/src/automations/index.js +4 -4
  233. package/src/automations/steps/updateRow.js +2 -2
  234. package/src/automations/tests/automation.spec.js +1 -6
  235. package/src/automations/triggers.js +6 -6
  236. package/src/automations/utils.ts +18 -12
  237. package/src/constants/index.js +4 -6
  238. package/src/db/utils.js +17 -110
  239. package/src/definitions/automations.ts +0 -12
  240. package/src/definitions/datasource.ts +1 -0
  241. package/src/index.ts +1 -0
  242. package/src/integrations/arangodb.ts +2 -0
  243. package/src/integrations/base/sqlTable.ts +1 -0
  244. package/src/integrations/mongodb.ts +290 -27
  245. package/src/integrations/rest.ts +17 -5
  246. package/src/integrations/tests/mongo.spec.ts +19 -19
  247. package/src/integrations/tests/rest.spec.ts +38 -22
  248. package/src/migrations/functions/backfill/app/tables.ts +7 -13
  249. package/src/migrations/functions/backfill/global.ts +1 -1
  250. package/src/migrations/functions/{quotas1.ts → syncQuotas.ts} +3 -8
  251. package/src/migrations/functions/tests/{quotas1.spec.js → syncQuotas.spec.js} +5 -2
  252. package/src/migrations/functions/usageQuotas/syncApps.ts +0 -1
  253. package/src/migrations/functions/usageQuotas/syncPlugins.ts +10 -0
  254. package/src/migrations/functions/usageQuotas/syncRows.ts +15 -6
  255. package/src/migrations/functions/usageQuotas/tests/syncRows.spec.ts +9 -1
  256. package/src/migrations/index.ts +21 -45
  257. package/src/migrations/tests/index.spec.ts +0 -1
  258. package/src/sdk/app/backups/constants.ts +2 -0
  259. package/src/sdk/app/backups/exports.ts +171 -0
  260. package/src/sdk/app/backups/imports.ts +169 -0
  261. package/src/sdk/app/backups/index.ts +9 -0
  262. package/src/sdk/app/backups/statistics.ts +78 -0
  263. package/src/sdk/app/tables/index.ts +61 -0
  264. package/src/sdk/index.ts +13 -0
  265. package/src/tests/utilities/TestConfiguration.js +5 -2
  266. package/src/threads/automation.ts +20 -27
  267. package/src/utilities/csvParser.js +1 -0
  268. package/src/utilities/fileSystem/index.js +2 -119
  269. package/src/utilities/fileSystem/utilities.js +2 -0
  270. package/src/utilities/rowProcessor/index.js +5 -0
  271. package/src/utilities/usageQuota/rows.js +7 -3
  272. package/coverage/lcov-report/api/controllers/backup.js.html +0 -142
  273. package/coverage/lcov-report/api/routes/backup.js.html +0 -115
  274. package/dist/automations/listeners.js +0 -102
  275. package/src/api/routes/backup.js +0 -10
  276. package/src/automations/listeners.ts +0 -78
  277. package/src/migrations/functions/pluginCount.ts +0 -12
@@ -0,0 +1,169 @@
1
+ import { db as dbCore } from "@budibase/backend-core"
2
+ import { 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
+ uploadDirectory,
7
+ upload,
8
+ } from "../../../utilities/fileSystem/utilities"
9
+ import { downloadTemplate } from "../../../utilities/fileSystem"
10
+ import { ObjectStoreBuckets, FieldTypes } from "../../../constants"
11
+ import { join } from "path"
12
+ import fs from "fs"
13
+ import sdk from "../../"
14
+ import { CouchFindOptions, RowAttachment } from "@budibase/types"
15
+ const uuid = require("uuid/v4")
16
+ const tar = require("tar")
17
+ import PouchDB from "pouchdb"
18
+
19
+ type TemplateType = {
20
+ file?: {
21
+ type: string
22
+ path: string
23
+ }
24
+ key?: string
25
+ }
26
+
27
+ async function updateAttachmentColumns(
28
+ prodAppId: string,
29
+ db: PouchDB.Database
30
+ ) {
31
+ // iterate through attachment documents and update them
32
+ const tables = await sdk.tables.getAllInternalTables(db)
33
+ for (let table of tables) {
34
+ const attachmentCols: string[] = []
35
+ for (let [key, column] of Object.entries(table.schema)) {
36
+ if (column.type === FieldTypes.ATTACHMENT) {
37
+ attachmentCols.push(key)
38
+ }
39
+ }
40
+ // no attachment columns, nothing to do
41
+ if (attachmentCols.length === 0) {
42
+ continue
43
+ }
44
+ // use the CouchDB Mango query API to lookup rows that have attachments
45
+ const params: CouchFindOptions = {
46
+ selector: {
47
+ _id: {
48
+ $regex: `^${TABLE_ROW_PREFIX}`,
49
+ },
50
+ },
51
+ }
52
+ attachmentCols.forEach(col => (params.selector[col] = { $exists: true }))
53
+ const { rows } = await dbCore.directCouchFind(db.name, params)
54
+ for (let row of rows) {
55
+ for (let column of attachmentCols) {
56
+ if (!Array.isArray(row[column])) {
57
+ continue
58
+ }
59
+ row[column] = row[column].map((attachment: RowAttachment) => {
60
+ // URL looks like: /prod-budi-app-assets/appId/attachments/file.csv
61
+ const urlParts = attachment.url.split("/")
62
+ // drop the first empty element
63
+ urlParts.shift()
64
+ // get the prefix
65
+ const prefix = urlParts.shift()
66
+ // remove the app ID
67
+ urlParts.shift()
68
+ // add new app ID
69
+ urlParts.unshift(prodAppId)
70
+ const key = urlParts.join("/")
71
+ return {
72
+ ...attachment,
73
+ key,
74
+ url: `/${prefix}/${key}`,
75
+ }
76
+ })
77
+ }
78
+ }
79
+ // write back the updated attachments
80
+ await db.bulkDocs(rows)
81
+ }
82
+ }
83
+
84
+ /**
85
+ * This function manages temporary template files which are stored by Koa.
86
+ * @param {Object} template The template object retrieved from the Koa context object.
87
+ * @returns {Object} Returns a fs read stream which can be loaded into the database.
88
+ */
89
+ async function getTemplateStream(template: TemplateType) {
90
+ if (template.file) {
91
+ return fs.createReadStream(template.file.path)
92
+ } else if (template.key) {
93
+ const [type, name] = template.key.split("/")
94
+ const tmpPath = await downloadTemplate(type, name)
95
+ return fs.createReadStream(join(tmpPath, name, "db", "dump.txt"))
96
+ }
97
+ }
98
+
99
+ export function untarFile(file: { path: string }) {
100
+ const tmpPath = join(budibaseTempDir(), uuid())
101
+ fs.mkdirSync(tmpPath)
102
+ // extract the tarball
103
+ tar.extract({
104
+ sync: true,
105
+ cwd: tmpPath,
106
+ file: file.path,
107
+ })
108
+ return tmpPath
109
+ }
110
+
111
+ export function getGlobalDBFile(tmpPath: string) {
112
+ return fs.readFileSync(join(tmpPath, GLOBAL_DB_EXPORT_FILE), "utf8")
113
+ }
114
+
115
+ export function getListOfAppsInMulti(tmpPath: string) {
116
+ return fs.readdirSync(tmpPath).filter(dir => dir !== GLOBAL_DB_EXPORT_FILE)
117
+ }
118
+
119
+ export async function importApp(
120
+ appId: string,
121
+ db: PouchDB.Database,
122
+ template: TemplateType
123
+ ) {
124
+ let prodAppId = dbCore.getProdAppID(appId)
125
+ let dbStream: any
126
+ const isTar = template.file && template.file.type === "application/gzip"
127
+ const isDirectory =
128
+ template.file && fs.lstatSync(template.file.path).isDirectory()
129
+ if (template.file && (isTar || isDirectory)) {
130
+ const tmpPath = isTar ? untarFile(template.file) : template.file.path
131
+ const contents = fs.readdirSync(tmpPath)
132
+ // have to handle object import
133
+ if (contents.length) {
134
+ let promises = []
135
+ let excludedFiles = [GLOBAL_DB_EXPORT_FILE, DB_EXPORT_FILE]
136
+ for (let filename of contents) {
137
+ const path = join(tmpPath, filename)
138
+ if (excludedFiles.includes(filename)) {
139
+ continue
140
+ }
141
+ filename = join(prodAppId, filename)
142
+ if (fs.lstatSync(path).isDirectory()) {
143
+ promises.push(
144
+ uploadDirectory(ObjectStoreBuckets.APPS, path, filename)
145
+ )
146
+ } else {
147
+ promises.push(
148
+ upload({
149
+ bucket: ObjectStoreBuckets.APPS,
150
+ path,
151
+ filename,
152
+ })
153
+ )
154
+ }
155
+ }
156
+ await Promise.all(promises)
157
+ }
158
+ dbStream = fs.createReadStream(join(tmpPath, DB_EXPORT_FILE))
159
+ } else {
160
+ dbStream = await getTemplateStream(template)
161
+ }
162
+ // @ts-ignore
163
+ const { ok } = await db.load(dbStream)
164
+ if (!ok) {
165
+ throw "Error loading database dump from template."
166
+ }
167
+ await updateAttachmentColumns(prodAppId, db)
168
+ return ok
169
+ }
@@ -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,13 @@
1
+ import { default as backups } from "./app/backups"
2
+ import { default as tables } from "./app/tables"
3
+
4
+ const sdk = {
5
+ backups,
6
+ tables,
7
+ }
8
+
9
+ // default export for TS
10
+ export default sdk
11
+
12
+ // default export for JS
13
+ module.exports = sdk
@@ -25,7 +25,7 @@ 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
29
 
30
30
  const GLOBAL_USER_ID = "us_uuid1"
31
31
  const EMAIL = "babs@babs.com"
@@ -95,7 +95,10 @@ class TestConfiguration {
95
95
 
96
96
  // use a new id as the name to avoid name collisions
97
97
  async init(appName = newid()) {
98
- await this.globalUser()
98
+ this.user = await this.globalUser()
99
+ this.globalUserId = this.user._id
100
+ this.userMetadataId = generateUserMetadataID(this.globalUserId)
101
+
99
102
  return this.createApp(appName)
100
103
  }
101
104
 
@@ -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) {
@@ -2,17 +2,11 @@ const { budibaseTempDir } = require("../budibaseDir")
2
2
  const fs = require("fs")
3
3
  const { join } = require("path")
4
4
  const uuid = require("uuid/v4")
5
- const {
6
- doWithDB,
7
- dangerousGetDB,
8
- closeDB,
9
- } = require("@budibase/backend-core/db")
10
5
  const { ObjectStoreBuckets } = require("../../constants")
11
6
  const {
12
7
  upload,
13
8
  retrieve,
14
9
  retrieveToTmp,
15
- streamUpload,
16
10
  deleteFolder,
17
11
  downloadTarball,
18
12
  downloadTarballDirect,
@@ -21,12 +15,6 @@ const {
21
15
  const { updateClientLibrary } = require("./clientLibrary")
22
16
  const { checkSlashesInUrl } = require("../")
23
17
  const env = require("../../environment")
24
- const {
25
- USER_METDATA_PREFIX,
26
- LINK_USER_METADATA_PREFIX,
27
- TABLE_ROW_PREFIX,
28
- } = require("../../db/utils")
29
- const MemoryStream = require("memorystream")
30
18
  const { getAppId } = require("@budibase/backend-core/context")
31
19
  const tar = require("tar")
32
20
  const fetch = require("node-fetch")
@@ -86,21 +74,6 @@ exports.checkDevelopmentEnvironment = () => {
86
74
  }
87
75
  }
88
76
 
89
- /**
90
- * This function manages temporary template files which are stored by Koa.
91
- * @param {Object} template The template object retrieved from the Koa context object.
92
- * @returns {Object} Returns an fs read stream which can be loaded into the database.
93
- */
94
- exports.getTemplateStream = async template => {
95
- if (template.file) {
96
- return fs.createReadStream(template.file.path)
97
- } else {
98
- const [type, name] = template.key.split("/")
99
- const tmpPath = await exports.downloadTemplate(type, name)
100
- return fs.createReadStream(join(tmpPath, name, "db", "dump.txt"))
101
- }
102
- }
103
-
104
77
  /**
105
78
  * Used to retrieve a handlebars file from the system which will be used as a template.
106
79
  * This is allowable as the template handlebars files should be static and identical across
@@ -124,98 +97,8 @@ exports.apiFileReturn = contents => {
124
97
  return fs.createReadStream(path)
125
98
  }
126
99
 
127
- exports.defineFilter = excludeRows => {
128
- const ids = [USER_METDATA_PREFIX, LINK_USER_METADATA_PREFIX]
129
- if (excludeRows) {
130
- ids.push(TABLE_ROW_PREFIX)
131
- }
132
- return doc =>
133
- !ids.map(key => doc._id.includes(key)).reduce((prev, curr) => prev || curr)
134
- }
135
-
136
- /**
137
- * Local utility to back up the database state for an app, excluding global user
138
- * data or user relationships.
139
- * @param {string} appId The app to backup
140
- * @param {object} config Config to send to export DB
141
- * @param {boolean} excludeRows Flag to state whether the export should include data.
142
- * @returns {*} either a string or a stream of the backup
143
- */
144
- const backupAppData = async (appId, config, excludeRows) => {
145
- return await exports.exportDB(appId, {
146
- ...config,
147
- filter: exports.defineFilter(excludeRows),
148
- })
149
- }
150
-
151
- /**
152
- * Takes a copy of the database state for an app to the object store.
153
- * @param {string} appId The ID of the app which is to be backed up.
154
- * @param {string} backupName The name of the backup located in the object store.
155
- * @return {*} a readable stream to the completed backup file
156
- */
157
- exports.performBackup = async (appId, backupName) => {
158
- return await backupAppData(appId, { exportName: backupName })
159
- }
160
-
161
- /**
162
- * Streams a backup of the database state for an app
163
- * @param {string} appId The ID of the app which is to be backed up.
164
- * @param {boolean} excludeRows Flag to state whether the export should include data.
165
- * @returns {*} a readable stream of the backup which is written in real time
166
- */
167
- exports.streamBackup = async (appId, excludeRows) => {
168
- return await backupAppData(appId, { stream: true }, excludeRows)
169
- }
170
-
171
- /**
172
- * Exports a DB to either file or a variable (memory).
173
- * @param {string} dbName the DB which is to be exported.
174
- * @param {string} exportName optional - provide a filename to write the backup to a file
175
- * @param {boolean} stream optional - whether to perform a full backup
176
- * @param {function} filter optional - a filter function to clear out any un-wanted docs.
177
- * @return {*} either a readable stream or a string
178
- */
179
- exports.exportDB = async (dbName, { stream, filter, exportName } = {}) => {
180
- // streaming a DB dump is a bit more complicated, can't close DB
181
- if (stream) {
182
- const db = dangerousGetDB(dbName)
183
- const memStream = new MemoryStream()
184
- memStream.on("end", async () => {
185
- await closeDB(db)
186
- })
187
- db.dump(memStream, { filter })
188
- return memStream
189
- }
190
-
191
- return doWithDB(dbName, async db => {
192
- // Write the dump to file if required
193
- if (exportName) {
194
- const path = join(budibaseTempDir(), exportName)
195
- const writeStream = fs.createWriteStream(path)
196
- await db.dump(writeStream, { filter })
197
-
198
- // Upload the dump to the object store if self hosted
199
- if (env.SELF_HOSTED) {
200
- await streamUpload(
201
- ObjectStoreBuckets.BACKUPS,
202
- join(dbName, exportName),
203
- fs.createReadStream(path)
204
- )
205
- }
206
-
207
- return fs.createReadStream(path)
208
- }
209
-
210
- // Stringify the dump in memory if required
211
- const memStream = new MemoryStream()
212
- let appString = ""
213
- memStream.on("data", chunk => {
214
- appString += chunk.toString()
215
- })
216
- await db.dump(memStream, { filter })
217
- return appString
218
- })
100
+ exports.streamFile = path => {
101
+ return fs.createReadStream(path)
219
102
  }
220
103
 
221
104
  /**
@@ -6,6 +6,7 @@ const {
6
6
  streamUpload,
7
7
  retrieve,
8
8
  retrieveToTmp,
9
+ retrieveDirectory,
9
10
  deleteFolder,
10
11
  uploadDirectory,
11
12
  downloadTarball,
@@ -27,6 +28,7 @@ exports.upload = upload
27
28
  exports.streamUpload = streamUpload
28
29
  exports.retrieve = retrieve
29
30
  exports.retrieveToTmp = retrieveToTmp
31
+ exports.retrieveDirectory = retrieveDirectory
30
32
  exports.deleteFolder = deleteFolder
31
33
  exports.uploadDirectory = uploadDirectory
32
34
  exports.downloadTarball = downloadTarball
@@ -48,6 +48,11 @@ const TYPE_TRANSFORM_MAP = {
48
48
  [null]: "",
49
49
  [undefined]: undefined,
50
50
  },
51
+ [FieldTypes.BARCODEQR]: {
52
+ "": "",
53
+ [null]: "",
54
+ [undefined]: undefined,
55
+ },
51
56
  [FieldTypes.FORMULA]: {
52
57
  "": "",
53
58
  [null]: "",
@@ -2,6 +2,7 @@ const { getRowParams, USER_METDATA_PREFIX } = require("../../db/utils")
2
2
  const {
3
3
  isDevAppID,
4
4
  getDevelopmentAppID,
5
+ getProdAppID,
5
6
  doWithDB,
6
7
  } = require("@budibase/backend-core/db")
7
8
 
@@ -52,7 +53,8 @@ const getAppRows = async appId => {
52
53
  * Rows duplicates may exist across apps due to data import so they are not filtered out.
53
54
  */
54
55
  exports.getUniqueRows = async appIds => {
55
- let uniqueRows = []
56
+ let uniqueRows = [],
57
+ rowsByApp = {}
56
58
  const pairs = getAppPairs(appIds)
57
59
 
58
60
  for (let pair of Object.values(pairs)) {
@@ -73,8 +75,10 @@ exports.getUniqueRows = async appIds => {
73
75
  // this can't be done on all rows because app import results in
74
76
  // duplicate row ids across apps
75
77
  // the array pre-concat is important to avoid stack overflow
76
- uniqueRows = uniqueRows.concat([...new Set(appRows)])
78
+ const prodId = getProdAppID(pair.devId || pair.prodId)
79
+ rowsByApp[prodId] = [...new Set(appRows)]
80
+ uniqueRows = uniqueRows.concat(rowsByApp[prodId])
77
81
  }
78
82
 
79
- return uniqueRows
83
+ return { rows: uniqueRows, appRows: rowsByApp }
80
84
  }