@budibase/server 2.0.39 → 2.0.40
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.
- package/__mocks__/mongodb.ts +1 -1
- package/__mocks__/node-fetch.ts +12 -2
- package/builder/assets/backups-default.8c011f1b.png +0 -0
- package/builder/assets/{index.e0ddd30b.css → index.442de33f.css} +2 -2
- package/builder/assets/{index.b20dee52.js → index.72aa3989.js} +340 -332
- package/builder/index.html +2 -2
- package/coverage/clover.xml +1125 -1250
- package/coverage/coverage-final.json +73 -77
- package/coverage/lcov-report/api/controllers/apikeys.js.html +6 -6
- package/coverage/lcov-report/api/controllers/automation.js.html +1 -1
- package/coverage/lcov-report/api/controllers/cloud.js.html +79 -67
- package/coverage/lcov-report/api/controllers/datasource.js.html +1 -1
- package/coverage/lcov-report/api/controllers/dev.js.html +21 -21
- package/coverage/lcov-report/api/controllers/index.html +17 -47
- package/coverage/lcov-report/api/controllers/integration.js.html +4 -4
- package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
- package/coverage/lcov-report/api/controllers/metadata.js.html +9 -9
- package/coverage/lcov-report/api/controllers/migrations.js.html +4 -4
- package/coverage/lcov-report/api/controllers/permission.js.html +1 -1
- package/coverage/lcov-report/api/controllers/query/index.html +1 -1
- package/coverage/lcov-report/api/controllers/query/validation.js.html +4 -4
- package/coverage/lcov-report/api/controllers/role.js.html +1 -1
- package/coverage/lcov-report/api/controllers/routing.js.html +9 -9
- package/coverage/lcov-report/api/controllers/row/external.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/index.html +1 -1
- package/coverage/lcov-report/api/controllers/row/internal.js.html +13 -13
- package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/staticFormula.js.html +15 -15
- package/coverage/lcov-report/api/controllers/row/utils.js.html +13 -13
- package/coverage/lcov-report/api/controllers/script.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/bulkFormula.js.html +12 -9
- package/coverage/lcov-report/api/controllers/table/external.js.html +10 -10
- package/coverage/lcov-report/api/controllers/table/index.html +16 -16
- package/coverage/lcov-report/api/controllers/table/index.js.html +5 -5
- package/coverage/lcov-report/api/controllers/templates.js.html +6 -6
- package/coverage/lcov-report/api/controllers/user.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/index.html +1 -1
- package/coverage/lcov-report/api/controllers/view/index.js.html +3 -3
- package/coverage/lcov-report/api/controllers/view/utils.js.html +13 -13
- package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +8 -8
- package/coverage/lcov-report/api/index.html +1 -1
- package/coverage/lcov-report/api/index.js.html +30 -30
- package/coverage/lcov-report/api/routes/analytics.js.html +7 -7
- package/coverage/lcov-report/api/routes/apikeys.js.html +8 -8
- package/coverage/lcov-report/api/routes/automation.js.html +11 -11
- package/coverage/lcov-report/api/routes/cloud.js.html +8 -8
- package/coverage/lcov-report/api/routes/component.js.html +8 -8
- package/coverage/lcov-report/api/routes/datasource.js.html +9 -9
- package/coverage/lcov-report/api/routes/deploy.js.html +8 -8
- package/coverage/lcov-report/api/routes/dev.js.html +13 -13
- package/coverage/lcov-report/api/routes/index.html +5 -35
- package/coverage/lcov-report/api/routes/integration.js.html +8 -8
- package/coverage/lcov-report/api/routes/layout.js.html +8 -8
- package/coverage/lcov-report/api/routes/metadata.js.html +9 -9
- package/coverage/lcov-report/api/routes/migrations.js.html +7 -7
- package/coverage/lcov-report/api/routes/permission.js.html +9 -9
- package/coverage/lcov-report/api/routes/query.js.html +10 -10
- package/coverage/lcov-report/api/routes/role.js.html +9 -9
- package/coverage/lcov-report/api/routes/routing.js.html +8 -8
- package/coverage/lcov-report/api/routes/screen.js.html +9 -9
- package/coverage/lcov-report/api/routes/script.js.html +1 -1
- package/coverage/lcov-report/api/routes/table.js.html +10 -10
- package/coverage/lcov-report/api/routes/templates.js.html +8 -8
- package/coverage/lcov-report/api/routes/user.js.html +8 -8
- package/coverage/lcov-report/api/routes/utils/index.html +1 -1
- package/coverage/lcov-report/api/routes/utils/validators.js.html +45 -45
- package/coverage/lcov-report/api/routes/view.js.html +10 -10
- package/coverage/lcov-report/automations/actions.js.html +1 -1
- package/coverage/lcov-report/automations/automationUtils.js.html +6 -6
- package/coverage/lcov-report/automations/bullboard.js.html +24 -84
- package/coverage/lcov-report/automations/index.html +21 -21
- package/coverage/lcov-report/automations/index.js.html +9 -9
- package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
- package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
- package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
- package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
- package/coverage/lcov-report/automations/steps/executeScript.js.html +1 -1
- package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
- package/coverage/lcov-report/automations/steps/index.html +15 -15
- package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
- package/coverage/lcov-report/automations/steps/loop.js.html +1 -1
- package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +1 -1
- package/coverage/lcov-report/automations/steps/queryRows.js.html +1 -1
- package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
- package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
- package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
- package/coverage/lcov-report/automations/steps/updateRow.js.html +3 -3
- package/coverage/lcov-report/automations/steps/utils.js.html +10 -10
- package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
- package/coverage/lcov-report/automations/triggers.js.html +49 -49
- package/coverage/lcov-report/constants/index.html +5 -5
- package/coverage/lcov-report/constants/index.js.html +10 -28
- package/coverage/lcov-report/constants/layouts.js.html +1 -1
- package/coverage/lcov-report/constants/screens.js.html +1 -1
- package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
- package/coverage/lcov-report/db/index.html +19 -19
- package/coverage/lcov-report/db/index.js.html +1 -1
- package/coverage/lcov-report/db/linkedRows/LinkController.js.html +17 -17
- package/coverage/lcov-report/db/linkedRows/index.html +1 -1
- package/coverage/lcov-report/db/linkedRows/index.js.html +28 -28
- package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +15 -15
- package/coverage/lcov-report/db/newid.js.html +2 -2
- package/coverage/lcov-report/db/utils.js.html +37 -316
- package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
- package/coverage/lcov-report/events/index.html +1 -1
- package/coverage/lcov-report/events/index.js.html +3 -3
- package/coverage/lcov-report/events/utils.js.html +9 -9
- package/coverage/lcov-report/index.html +88 -88
- package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
- package/coverage/lcov-report/integrations/tests/index.html +1 -1
- package/coverage/lcov-report/middleware/appInfo.js.html +5 -5
- package/coverage/lcov-report/middleware/builder.js.html +19 -19
- package/coverage/lcov-report/middleware/currentapp.js.html +49 -49
- package/coverage/lcov-report/middleware/index.html +1 -1
- package/coverage/lcov-report/middleware/joi-validator.js.html +1 -1
- package/coverage/lcov-report/middleware/publicApi.js.html +5 -5
- package/coverage/lcov-report/middleware/resourceId.js.html +28 -28
- package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
- package/coverage/lcov-report/middleware/utils.js.html +4 -4
- package/coverage/lcov-report/utilities/budibaseDir.js.html +3 -3
- package/coverage/lcov-report/utilities/centralPath.js.html +4 -4
- package/coverage/lcov-report/utilities/csvParser.js.html +15 -12
- package/coverage/lcov-report/utilities/global.js.html +31 -31
- package/coverage/lcov-report/utilities/index.html +28 -28
- package/coverage/lcov-report/utilities/index.js.html +8 -8
- package/coverage/lcov-report/utilities/plugins.js.html +1 -1
- package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +52 -52
- package/coverage/lcov-report/utilities/queue/index.html +21 -21
- package/coverage/lcov-report/utilities/routing/index.html +1 -1
- package/coverage/lcov-report/utilities/routing/index.js.html +6 -6
- package/coverage/lcov-report/utilities/rowProcessor/index.html +1 -1
- package/coverage/lcov-report/utilities/rowProcessor/index.js.html +72 -57
- package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +15 -15
- package/coverage/lcov-report/utilities/scriptRunner.js.html +1 -1
- package/coverage/lcov-report/utilities/security.js.html +2 -2
- package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
- package/coverage/lcov-report/utilities/usageQuota/index.html +14 -14
- package/coverage/lcov-report/utilities/usageQuota/rows.js.html +23 -11
- package/coverage/lcov-report/utilities/usageQuota/usageQuoteReset.js.html +1 -1
- package/coverage/lcov-report/utilities/users.js.html +1 -1
- package/coverage/lcov-report/utilities/workerRequests.js.html +46 -46
- package/coverage/lcov.info +1691 -1899
- package/dist/api/controllers/application.js +4 -7
- package/dist/api/controllers/backup.js +19 -13
- package/dist/api/controllers/cloud.js +38 -35
- package/dist/api/controllers/deploy/index.js +11 -2
- package/dist/api/controllers/dev.js +1 -1
- package/dist/api/controllers/query/index.js +7 -2
- package/dist/api/controllers/row/index.js +40 -66
- package/dist/api/controllers/row/internal.js +1 -1
- package/dist/api/controllers/static/index.js +3 -2
- package/dist/api/controllers/static/templates/BudibaseApp.svelte +11 -0
- package/dist/api/controllers/static/templates/preview.hbs +10 -0
- package/dist/api/controllers/table/bulkFormula.js +4 -3
- package/dist/api/controllers/table/external.js +5 -4
- package/dist/api/controllers/table/index.js +4 -4
- package/dist/api/controllers/table/internal.js +10 -3
- package/dist/api/controllers/table/utils.js +7 -49
- package/dist/api/controllers/view/index.js +2 -2
- package/dist/api/controllers/webhook.js +88 -89
- package/dist/api/routes/backup.js +34 -7
- package/dist/api/routes/index.js +9 -4
- package/dist/api/routes/utils/validators.js +2 -2
- package/dist/api/routes/webhook.js +39 -11
- package/dist/app.js +2 -68
- package/dist/automations/automationUtils.js +2 -2
- package/dist/automations/bullboard.js +12 -35
- package/dist/automations/index.js +4 -4
- package/dist/automations/steps/updateRow.js +2 -2
- package/dist/automations/triggers.js +6 -6
- package/dist/automations/utils.js +23 -49
- package/dist/constants/index.js +4 -8
- package/dist/db/utils.js +10 -75
- package/dist/index.js +1 -0
- package/dist/integrations/arangodb.js +2 -0
- package/dist/integrations/base/sqlTable.js +1 -0
- package/dist/integrations/mongodb.js +300 -11
- package/dist/integrations/rest.js +38 -5
- package/dist/migrations/functions/backfill/app/tables.js +5 -8
- package/dist/migrations/functions/{quotas1.js → syncQuotas.js} +3 -7
- package/dist/migrations/functions/usageQuotas/syncApps.js +0 -1
- package/dist/migrations/functions/{pluginCount.js → usageQuotas/syncPlugins.js} +1 -3
- package/dist/migrations/functions/usageQuotas/syncRows.js +9 -5
- package/dist/migrations/index.js +10 -38
- package/dist/package.json +14 -14
- package/dist/sdk/app/automations/index.js +29 -0
- package/dist/sdk/app/automations/webhook.js +54 -0
- package/dist/sdk/app/backups/constants.js +5 -0
- package/dist/sdk/app/backups/exports.js +172 -0
- package/dist/sdk/app/backups/imports.js +190 -0
- package/dist/sdk/app/backups/index.js +29 -0
- package/dist/sdk/app/backups/statistics.js +73 -0
- package/dist/sdk/app/tables/index.js +61 -0
- package/dist/sdk/index.js +17 -0
- package/dist/startup.js +152 -0
- package/dist/tests/utilities/TestConfiguration.js +12 -2
- package/dist/threads/automation.js +12 -17
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utilities/csvParser.js +1 -0
- package/dist/utilities/fileSystem/index.js +3 -95
- package/dist/utilities/fileSystem/utilities.js +2 -1
- package/dist/utilities/rowProcessor/index.js +5 -0
- package/dist/utilities/usageQuota/rows.js +6 -4
- package/package.json +15 -15
- package/specs/openapi.json +3 -0
- package/specs/openapi.yaml +3 -0
- package/src/api/controllers/application.ts +5 -12
- package/src/api/controllers/{backup.js → backup.ts} +7 -7
- package/src/api/controllers/cloud.js +44 -40
- package/src/api/controllers/deploy/index.ts +20 -8
- package/src/api/controllers/dev.js +1 -1
- package/src/api/controllers/query/index.ts +8 -2
- package/src/api/controllers/row/index.ts +50 -65
- package/src/api/controllers/row/internal.js +1 -1
- package/src/api/controllers/static/index.ts +4 -2
- package/src/api/controllers/static/templates/BudibaseApp.svelte +11 -0
- package/src/api/controllers/static/templates/preview.hbs +10 -0
- package/src/api/controllers/table/bulkFormula.js +4 -3
- package/src/api/controllers/table/external.js +4 -4
- package/src/api/controllers/table/index.js +4 -4
- package/src/api/controllers/table/internal.ts +8 -9
- package/src/api/controllers/table/utils.ts +4 -53
- package/src/api/controllers/view/index.js +2 -2
- package/src/api/controllers/{webhook.js → webhook.ts} +33 -48
- package/src/api/routes/backup.ts +10 -0
- package/src/api/routes/index.ts +11 -5
- package/src/api/routes/tests/backup.spec.js +1 -1
- package/src/api/routes/tests/{deployment.spec.js → deployment.spec.ts} +4 -4
- package/src/api/routes/tests/row.spec.js +22 -27
- package/src/api/routes/utils/validators.js +2 -2
- package/src/api/routes/{webhook.js → webhook.ts} +7 -6
- package/src/app.ts +3 -91
- package/src/automations/automationUtils.js +2 -2
- package/src/automations/bullboard.js +14 -34
- package/src/automations/index.js +4 -4
- package/src/automations/steps/updateRow.js +2 -2
- package/src/automations/tests/automation.spec.js +1 -6
- package/src/automations/triggers.js +6 -6
- package/src/automations/utils.ts +30 -32
- package/src/constants/index.js +4 -10
- package/src/db/utils.js +17 -110
- package/src/definitions/automations.ts +1 -18
- package/src/definitions/datasource.ts +1 -0
- package/src/index.ts +1 -0
- package/src/integrations/arangodb.ts +2 -0
- package/src/integrations/base/sqlTable.ts +1 -0
- package/src/integrations/mongodb.ts +290 -27
- package/src/integrations/rest.ts +17 -5
- package/src/integrations/tests/mongo.spec.ts +19 -19
- package/src/integrations/tests/rest.spec.ts +38 -22
- package/src/migrations/functions/backfill/app/tables.ts +7 -13
- package/src/migrations/functions/backfill/global.ts +1 -1
- package/src/migrations/functions/{quotas1.ts → syncQuotas.ts} +3 -8
- package/src/migrations/functions/tests/{quotas1.spec.js → syncQuotas.spec.js} +6 -5
- package/src/migrations/functions/tests/userEmailViewCasing.spec.js +6 -4
- package/src/migrations/functions/usageQuotas/syncApps.ts +0 -1
- package/src/migrations/functions/usageQuotas/syncPlugins.ts +10 -0
- package/src/migrations/functions/usageQuotas/syncRows.ts +15 -6
- package/src/migrations/functions/usageQuotas/tests/syncRows.spec.ts +9 -1
- package/src/migrations/index.ts +21 -45
- package/src/migrations/tests/index.spec.ts +0 -1
- package/src/sdk/app/automations/index.ts +5 -0
- package/src/sdk/app/automations/webhook.ts +43 -0
- package/src/sdk/app/backups/constants.ts +2 -0
- package/src/sdk/app/backups/exports.ts +171 -0
- package/src/sdk/app/backups/imports.ts +206 -0
- package/src/sdk/app/backups/index.ts +9 -0
- package/src/sdk/app/backups/statistics.ts +78 -0
- package/src/sdk/app/tables/index.ts +61 -0
- package/src/sdk/index.ts +15 -0
- package/src/startup.ts +138 -0
- package/src/tests/utilities/TestConfiguration.js +12 -2
- package/src/threads/automation.ts +20 -27
- package/src/utilities/csvParser.js +1 -0
- package/src/utilities/fileSystem/index.js +2 -119
- package/src/utilities/fileSystem/utilities.js +2 -0
- package/src/utilities/rowProcessor/index.js +5 -0
- package/src/utilities/usageQuota/rows.js +7 -3
- package/coverage/lcov-report/api/controllers/backup.js.html +0 -142
- package/coverage/lcov-report/api/controllers/webhook.js.html +0 -424
- package/coverage/lcov-report/api/routes/backup.js.html +0 -115
- package/coverage/lcov-report/api/routes/webhook.js.html +0 -163
- package/dist/automations/listeners.js +0 -102
- package/src/api/routes/backup.js +0 -10
- package/src/automations/listeners.ts +0 -78
- package/src/migrations/functions/pluginCount.ts +0 -12
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
jest.
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
return { "content-type": ["application/json"] }
|
|
6
|
-
},
|
|
7
|
-
get: () => ["application/json"],
|
|
1
|
+
const mockFetch = jest.fn(() => ({
|
|
2
|
+
headers: {
|
|
3
|
+
raw: () => {
|
|
4
|
+
return { "content-type": ["application/json"] }
|
|
8
5
|
},
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}))
|
|
14
|
-
)
|
|
6
|
+
get: () => ["application/json"],
|
|
7
|
+
},
|
|
8
|
+
json: jest.fn(() => ({
|
|
9
|
+
my_next_cursor: 123,
|
|
10
|
+
})),
|
|
11
|
+
text: jest.fn(),
|
|
12
|
+
}))
|
|
13
|
+
jest.mock("node-fetch", () => mockFetch)
|
|
15
14
|
import fetch from "node-fetch"
|
|
16
15
|
import { default as RestIntegration } from "../rest"
|
|
17
16
|
const FormData = require("form-data")
|
|
@@ -256,7 +255,7 @@ describe("REST Integration", () => {
|
|
|
256
255
|
authConfigId: "c59c14bd1898a43baa08da68959b24686",
|
|
257
256
|
}
|
|
258
257
|
await config.integration.read(query)
|
|
259
|
-
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}
|
|
258
|
+
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/`, {
|
|
260
259
|
method: "GET",
|
|
261
260
|
headers: {
|
|
262
261
|
Authorization: "Basic dXNlcjpwYXNzd29yZA==",
|
|
@@ -269,7 +268,7 @@ describe("REST Integration", () => {
|
|
|
269
268
|
authConfigId: "0d91d732f34e4befabeff50b392a8ff3",
|
|
270
269
|
}
|
|
271
270
|
await config.integration.read(query)
|
|
272
|
-
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}
|
|
271
|
+
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/`, {
|
|
273
272
|
method: "GET",
|
|
274
273
|
headers: {
|
|
275
274
|
Authorization: "Bearer mytoken",
|
|
@@ -327,7 +326,7 @@ describe("REST Integration", () => {
|
|
|
327
326
|
},
|
|
328
327
|
}
|
|
329
328
|
await config.integration.create(query)
|
|
330
|
-
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api
|
|
329
|
+
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api`, {
|
|
331
330
|
body: JSON.stringify({
|
|
332
331
|
[pageParam]: pageValue,
|
|
333
332
|
[sizeParam]: sizeValue,
|
|
@@ -359,7 +358,7 @@ describe("REST Integration", () => {
|
|
|
359
358
|
},
|
|
360
359
|
}
|
|
361
360
|
await config.integration.create(query)
|
|
362
|
-
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api
|
|
361
|
+
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api`, {
|
|
363
362
|
body: expect.any(FormData),
|
|
364
363
|
headers: {},
|
|
365
364
|
method: "POST",
|
|
@@ -390,7 +389,7 @@ describe("REST Integration", () => {
|
|
|
390
389
|
},
|
|
391
390
|
}
|
|
392
391
|
await config.integration.create(query)
|
|
393
|
-
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api
|
|
392
|
+
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api`, {
|
|
394
393
|
body: expect.any(URLSearchParams),
|
|
395
394
|
headers: {},
|
|
396
395
|
method: "POST",
|
|
@@ -456,7 +455,7 @@ describe("REST Integration", () => {
|
|
|
456
455
|
},
|
|
457
456
|
}
|
|
458
457
|
const res = await config.integration.create(query)
|
|
459
|
-
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api
|
|
458
|
+
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api`, {
|
|
460
459
|
body: JSON.stringify({
|
|
461
460
|
[pageParam]: pageValue,
|
|
462
461
|
[sizeParam]: sizeValue,
|
|
@@ -490,7 +489,7 @@ describe("REST Integration", () => {
|
|
|
490
489
|
},
|
|
491
490
|
}
|
|
492
491
|
const res = await config.integration.create(query)
|
|
493
|
-
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api
|
|
492
|
+
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api`, {
|
|
494
493
|
body: expect.any(FormData),
|
|
495
494
|
headers: {},
|
|
496
495
|
method: "POST",
|
|
@@ -523,7 +522,7 @@ describe("REST Integration", () => {
|
|
|
523
522
|
},
|
|
524
523
|
}
|
|
525
524
|
const res = await config.integration.create(query)
|
|
526
|
-
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api
|
|
525
|
+
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api`, {
|
|
527
526
|
body: expect.any(URLSearchParams),
|
|
528
527
|
headers: {},
|
|
529
528
|
method: "POST",
|
|
@@ -563,7 +562,7 @@ describe("REST Integration", () => {
|
|
|
563
562
|
legacyHttpParser: true,
|
|
564
563
|
})
|
|
565
564
|
await config.integration.read({})
|
|
566
|
-
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}
|
|
565
|
+
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/`, {
|
|
567
566
|
method: "GET",
|
|
568
567
|
headers: {},
|
|
569
568
|
extraHttpOptions: {
|
|
@@ -572,4 +571,21 @@ describe("REST Integration", () => {
|
|
|
572
571
|
})
|
|
573
572
|
})
|
|
574
573
|
})
|
|
574
|
+
|
|
575
|
+
it("Attaches custom agent when Reject Unauthorized option is false", async () => {
|
|
576
|
+
config = new TestConfiguration({
|
|
577
|
+
url: BASE_URL,
|
|
578
|
+
rejectUnauthorized: false,
|
|
579
|
+
})
|
|
580
|
+
await config.integration.read({})
|
|
581
|
+
|
|
582
|
+
const calls: any = mockFetch.mock.calls[0]
|
|
583
|
+
const url = calls[0]
|
|
584
|
+
expect(url).toBe(`${BASE_URL}/`)
|
|
585
|
+
|
|
586
|
+
const calledConfig = calls[1]
|
|
587
|
+
expect(calledConfig.method).toBe("GET")
|
|
588
|
+
expect(calledConfig.headers).toEqual({})
|
|
589
|
+
expect(calledConfig.agent.options.rejectUnauthorized).toBe(false)
|
|
590
|
+
})
|
|
575
591
|
})
|
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
import { events } from "@budibase/backend-core"
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import sdk from "../../../../sdk"
|
|
3
|
+
import PouchDB from "pouchdb"
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
)
|
|
11
|
-
return response.rows.map((row: any) => row.doc)
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const backfill = async (appDb: any, timestamp: string | number) => {
|
|
15
|
-
const tables = await getTables(appDb)
|
|
5
|
+
export const backfill = async (
|
|
6
|
+
appDb: PouchDB.Database,
|
|
7
|
+
timestamp: string | number
|
|
8
|
+
) => {
|
|
9
|
+
const tables = await sdk.tables.getAllInternalTables(appDb)
|
|
16
10
|
|
|
17
11
|
for (const table of tables) {
|
|
18
12
|
await events.table.created(table, timestamp)
|
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
import { runQuotaMigration } from "./usageQuotas"
|
|
2
2
|
import * as syncApps from "./usageQuotas/syncApps"
|
|
3
3
|
import * as syncRows from "./usageQuotas/syncRows"
|
|
4
|
+
import * as syncPlugins from "./usageQuotas/syncPlugins"
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
|
-
*
|
|
7
|
-
* January 2022
|
|
8
|
-
*
|
|
9
|
-
* Description:
|
|
10
|
-
* Synchronise the app and row quotas to the state of the db after it was
|
|
11
|
-
* discovered that the quota resets were still in place and the row quotas
|
|
12
|
-
* weren't being decremented correctly.
|
|
7
|
+
* Synchronise quotas to the state of the db.
|
|
13
8
|
*/
|
|
14
|
-
|
|
15
9
|
export const run = async () => {
|
|
16
10
|
await runQuotaMigration(async () => {
|
|
17
11
|
await syncApps.run()
|
|
18
12
|
await syncRows.run()
|
|
13
|
+
await syncPlugins.run()
|
|
19
14
|
})
|
|
20
15
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
const TestConfig = require("../../../tests/utilities/TestConfiguration")
|
|
2
|
-
|
|
3
1
|
const syncApps = jest.fn()
|
|
4
2
|
const syncRows = jest.fn()
|
|
5
|
-
|
|
3
|
+
const syncPlugins = jest.fn()
|
|
6
4
|
jest.mock("../usageQuotas/syncApps", () => ({ run: syncApps }) )
|
|
7
5
|
jest.mock("../usageQuotas/syncRows", () => ({ run: syncRows }) )
|
|
6
|
+
jest.mock("../usageQuotas/syncPlugins", () => ({ run: syncPlugins }) )
|
|
8
7
|
|
|
9
|
-
const
|
|
8
|
+
const TestConfig = require("../../../tests/utilities/TestConfiguration")
|
|
9
|
+
const migration = require("../syncQuotas")
|
|
10
10
|
|
|
11
11
|
describe("run", () => {
|
|
12
12
|
let config = new TestConfig(false)
|
|
@@ -17,9 +17,10 @@ describe("run", () => {
|
|
|
17
17
|
|
|
18
18
|
afterAll(config.end)
|
|
19
19
|
|
|
20
|
-
it("
|
|
20
|
+
it("run", async () => {
|
|
21
21
|
await migration.run()
|
|
22
22
|
expect(syncApps).toHaveBeenCalledTimes(1)
|
|
23
23
|
expect(syncRows).toHaveBeenCalledTimes(1)
|
|
24
|
+
expect(syncPlugins).toHaveBeenCalledTimes(1)
|
|
24
25
|
})
|
|
25
26
|
})
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
jest.mock("@budibase/backend-core/db", () => ({
|
|
2
|
+
...jest.requireActual("@budibase/backend-core/db"),
|
|
3
|
+
createNewUserEmailView: jest.fn(),
|
|
4
|
+
}))
|
|
5
|
+
const coreDb = require("@budibase/backend-core/db")
|
|
1
6
|
const TestConfig = require("../../../tests/utilities/TestConfiguration")
|
|
2
7
|
const { TENANT_ID } = require("../../../tests/utilities/structures")
|
|
3
8
|
const { getGlobalDB, doInTenant } = require("@budibase/backend-core/tenancy")
|
|
4
9
|
|
|
5
10
|
// mock email view creation
|
|
6
|
-
const coreDb = require("@budibase/backend-core/db")
|
|
7
|
-
const createNewUserEmailView = jest.fn()
|
|
8
|
-
coreDb.createNewUserEmailView = createNewUserEmailView
|
|
9
11
|
|
|
10
12
|
const migration = require("../userEmailViewCasing")
|
|
11
13
|
|
|
@@ -22,7 +24,7 @@ describe("run", () => {
|
|
|
22
24
|
await doInTenant(TENANT_ID, async () => {
|
|
23
25
|
const globalDb = getGlobalDB()
|
|
24
26
|
await migration.run(globalDb)
|
|
25
|
-
expect(createNewUserEmailView).toHaveBeenCalledTimes(1)
|
|
27
|
+
expect(coreDb.createNewUserEmailView).toHaveBeenCalledTimes(1)
|
|
26
28
|
})
|
|
27
29
|
})
|
|
28
30
|
})
|
|
@@ -2,19 +2,28 @@ import { getTenantId } from "@budibase/backend-core/tenancy"
|
|
|
2
2
|
import { getAllApps } from "@budibase/backend-core/db"
|
|
3
3
|
import { getUniqueRows } from "../../../utilities/usageQuota/rows"
|
|
4
4
|
import { quotas } from "@budibase/pro"
|
|
5
|
-
import {
|
|
5
|
+
import { StaticQuotaName, QuotaUsageType } from "@budibase/types"
|
|
6
6
|
|
|
7
7
|
export const run = async () => {
|
|
8
8
|
// get all rows in all apps
|
|
9
|
-
// @ts-ignore
|
|
10
9
|
const allApps = await getAllApps({ all: true })
|
|
11
|
-
// @ts-ignore
|
|
12
10
|
const appIds = allApps ? allApps.map((app: { appId: any }) => app.appId) : []
|
|
13
|
-
const
|
|
14
|
-
|
|
11
|
+
const { appRows } = await getUniqueRows(appIds)
|
|
12
|
+
|
|
13
|
+
// get the counts per app
|
|
14
|
+
const counts: { [key: string]: number } = {}
|
|
15
|
+
let rowCount = 0
|
|
16
|
+
Object.entries(appRows).forEach(([appId, rows]) => {
|
|
17
|
+
counts[appId] = rows.length
|
|
18
|
+
rowCount += rows.length
|
|
19
|
+
})
|
|
15
20
|
|
|
16
21
|
// sync row count
|
|
17
22
|
const tenantId = getTenantId()
|
|
18
23
|
console.log(`[Tenant: ${tenantId}] Syncing row count: ${rowCount}`)
|
|
19
|
-
await quotas.
|
|
24
|
+
await quotas.setUsagePerApp(
|
|
25
|
+
counts,
|
|
26
|
+
StaticQuotaName.ROWS,
|
|
27
|
+
QuotaUsageType.STATIC
|
|
28
|
+
)
|
|
20
29
|
}
|
|
@@ -2,6 +2,7 @@ import TestConfig from "../../../../tests/utilities/TestConfiguration"
|
|
|
2
2
|
import * as syncRows from "../syncRows"
|
|
3
3
|
import { quotas } from "@budibase/pro"
|
|
4
4
|
import { QuotaUsageType, StaticQuotaName } from "@budibase/types"
|
|
5
|
+
const { getProdAppID } = require("@budibase/backend-core/db")
|
|
5
6
|
|
|
6
7
|
describe("syncRows", () => {
|
|
7
8
|
let config = new TestConfig(false)
|
|
@@ -22,10 +23,11 @@ describe("syncRows", () => {
|
|
|
22
23
|
expect(usageDoc.usageQuota.rows).toEqual(300)
|
|
23
24
|
|
|
24
25
|
// app 1
|
|
26
|
+
const app1 = config.app
|
|
25
27
|
await config.createTable()
|
|
26
28
|
await config.createRow()
|
|
27
29
|
// app 2
|
|
28
|
-
await config.createApp("second-app")
|
|
30
|
+
const app2 = await config.createApp("second-app")
|
|
29
31
|
await config.createTable()
|
|
30
32
|
await config.createRow()
|
|
31
33
|
await config.createRow()
|
|
@@ -36,6 +38,12 @@ describe("syncRows", () => {
|
|
|
36
38
|
// assert the migration worked
|
|
37
39
|
usageDoc = await quotas.getQuotaUsage()
|
|
38
40
|
expect(usageDoc.usageQuota.rows).toEqual(3)
|
|
41
|
+
expect(usageDoc.apps?.[getProdAppID(app1.appId)].usageQuota.rows).toEqual(
|
|
42
|
+
1
|
|
43
|
+
)
|
|
44
|
+
expect(usageDoc.apps?.[getProdAppID(app2.appId)].usageQuota.rows).toEqual(
|
|
45
|
+
2
|
|
46
|
+
)
|
|
39
47
|
})
|
|
40
48
|
})
|
|
41
49
|
})
|
package/src/migrations/index.ts
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { locks, migrations } from "@budibase/backend-core"
|
|
2
|
+
import {
|
|
3
|
+
Migration,
|
|
4
|
+
MigrationOptions,
|
|
5
|
+
MigrationName,
|
|
6
|
+
LockType,
|
|
7
|
+
LockName,
|
|
8
|
+
} from "@budibase/types"
|
|
3
9
|
import env from "../environment"
|
|
4
10
|
|
|
5
11
|
// migration functions
|
|
6
12
|
import * as userEmailViewCasing from "./functions/userEmailViewCasing"
|
|
7
|
-
import * as
|
|
13
|
+
import * as syncQuotas from "./functions/syncQuotas"
|
|
8
14
|
import * as appUrls from "./functions/appUrls"
|
|
9
15
|
import * as backfill from "./functions/backfill"
|
|
10
|
-
import * as pluginCount from "./functions/pluginCount"
|
|
11
|
-
|
|
12
16
|
/**
|
|
13
17
|
* Populate the migration function and additional configuration from
|
|
14
18
|
* the static migration definitions.
|
|
@@ -26,10 +30,10 @@ export const buildMigrations = () => {
|
|
|
26
30
|
})
|
|
27
31
|
break
|
|
28
32
|
}
|
|
29
|
-
case MigrationName.
|
|
33
|
+
case MigrationName.SYNC_QUOTAS: {
|
|
30
34
|
serverMigrations.push({
|
|
31
35
|
...definition,
|
|
32
|
-
fn:
|
|
36
|
+
fn: syncQuotas.run,
|
|
33
37
|
})
|
|
34
38
|
break
|
|
35
39
|
}
|
|
@@ -69,16 +73,6 @@ export const buildMigrations = () => {
|
|
|
69
73
|
})
|
|
70
74
|
break
|
|
71
75
|
}
|
|
72
|
-
case MigrationName.PLUGIN_COUNT: {
|
|
73
|
-
if (env.SELF_HOSTED) {
|
|
74
|
-
serverMigrations.push({
|
|
75
|
-
...definition,
|
|
76
|
-
fn: pluginCount.run,
|
|
77
|
-
silent: !!env.SELF_HOSTED,
|
|
78
|
-
preventRetry: false,
|
|
79
|
-
})
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
76
|
}
|
|
83
77
|
}
|
|
84
78
|
|
|
@@ -98,33 +92,15 @@ export const migrate = async (options?: MigrationOptions) => {
|
|
|
98
92
|
}
|
|
99
93
|
|
|
100
94
|
const migrateWithLock = async (options?: MigrationOptions) => {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
migrationLock = await redlock.lock("migrations", ttl)
|
|
111
|
-
} catch (e: any) {
|
|
112
|
-
if (e.name === "LockError") {
|
|
113
|
-
return
|
|
114
|
-
} else {
|
|
115
|
-
throw e
|
|
95
|
+
await locks.doWithLock(
|
|
96
|
+
{
|
|
97
|
+
type: LockType.TRY_ONCE,
|
|
98
|
+
name: LockName.MIGRATIONS,
|
|
99
|
+
ttl: 1000 * 60 * 15, // auto expire the migration lock after 15 minutes
|
|
100
|
+
systemLock: true,
|
|
101
|
+
},
|
|
102
|
+
async () => {
|
|
103
|
+
await migrations.runMigrations(MIGRATIONS, options)
|
|
116
104
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
// run migrations
|
|
120
|
-
try {
|
|
121
|
-
await migrations.runMigrations(MIGRATIONS, options)
|
|
122
|
-
} finally {
|
|
123
|
-
// release lock
|
|
124
|
-
try {
|
|
125
|
-
await migrationLock.unlock()
|
|
126
|
-
} catch (e) {
|
|
127
|
-
console.error("unable to release migration lock")
|
|
128
|
-
}
|
|
129
|
-
}
|
|
105
|
+
)
|
|
130
106
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Webhook, WebhookActionType } from "@budibase/types"
|
|
2
|
+
import { db as dbCore, context } from "@budibase/backend-core"
|
|
3
|
+
import { generateWebhookID } from "../../../db/utils"
|
|
4
|
+
|
|
5
|
+
function isWebhookID(id: string) {
|
|
6
|
+
return id.startsWith(dbCore.DocumentType.WEBHOOK)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function newDoc(
|
|
10
|
+
name: string,
|
|
11
|
+
type: WebhookActionType,
|
|
12
|
+
target: string
|
|
13
|
+
): Webhook {
|
|
14
|
+
return {
|
|
15
|
+
live: true,
|
|
16
|
+
name,
|
|
17
|
+
action: {
|
|
18
|
+
type,
|
|
19
|
+
target,
|
|
20
|
+
},
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function save(webhook: Webhook) {
|
|
25
|
+
const db = context.getAppDB()
|
|
26
|
+
// check that the webhook exists
|
|
27
|
+
if (webhook._id && isWebhookID(webhook._id)) {
|
|
28
|
+
await db.get(webhook._id)
|
|
29
|
+
} else {
|
|
30
|
+
webhook._id = generateWebhookID()
|
|
31
|
+
}
|
|
32
|
+
const response = await db.put(webhook)
|
|
33
|
+
webhook._rev = response.rev
|
|
34
|
+
return webhook
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function destroy(id: string, rev: string) {
|
|
38
|
+
const db = context.getAppDB()
|
|
39
|
+
if (!id || !isWebhookID(id)) {
|
|
40
|
+
throw new Error("Provided webhook ID is not valid.")
|
|
41
|
+
}
|
|
42
|
+
return await db.remove(id, rev)
|
|
43
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { db as dbCore } from "@budibase/backend-core"
|
|
2
|
+
import { budibaseTempDir } from "../../../utilities/budibaseDir"
|
|
3
|
+
import { retrieveDirectory } from "../../../utilities/fileSystem/utilities"
|
|
4
|
+
import { streamFile, createTempFolder } from "../../../utilities/fileSystem"
|
|
5
|
+
import { ObjectStoreBuckets } from "../../../constants"
|
|
6
|
+
import {
|
|
7
|
+
LINK_USER_METADATA_PREFIX,
|
|
8
|
+
TABLE_ROW_PREFIX,
|
|
9
|
+
USER_METDATA_PREFIX,
|
|
10
|
+
} from "../../../db/utils"
|
|
11
|
+
import { DB_EXPORT_FILE, GLOBAL_DB_EXPORT_FILE } from "./constants"
|
|
12
|
+
import fs from "fs"
|
|
13
|
+
import { join } from "path"
|
|
14
|
+
import env from "../../../environment"
|
|
15
|
+
const uuid = require("uuid/v4")
|
|
16
|
+
const tar = require("tar")
|
|
17
|
+
const MemoryStream = require("memorystream")
|
|
18
|
+
|
|
19
|
+
type ExportOpts = {
|
|
20
|
+
filter?: any
|
|
21
|
+
exportPath?: string
|
|
22
|
+
tar?: boolean
|
|
23
|
+
excludeRows?: boolean
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function tarFilesToTmp(tmpDir: string, files: string[]) {
|
|
27
|
+
const exportFile = join(budibaseTempDir(), `${uuid()}.tar.gz`)
|
|
28
|
+
tar.create(
|
|
29
|
+
{
|
|
30
|
+
sync: true,
|
|
31
|
+
gzip: true,
|
|
32
|
+
file: exportFile,
|
|
33
|
+
recursive: true,
|
|
34
|
+
cwd: tmpDir,
|
|
35
|
+
},
|
|
36
|
+
files
|
|
37
|
+
)
|
|
38
|
+
return exportFile
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Exports a DB to either file or a variable (memory).
|
|
43
|
+
* @param {string} dbName the DB which is to be exported.
|
|
44
|
+
* @param {object} opts various options for the export, e.g. whether to stream,
|
|
45
|
+
* a filter function or the name of the export.
|
|
46
|
+
* @return {*} either a readable stream or a string
|
|
47
|
+
*/
|
|
48
|
+
export async function exportDB(dbName: string, opts: ExportOpts = {}) {
|
|
49
|
+
return dbCore.doWithDB(dbName, async (db: any) => {
|
|
50
|
+
// Write the dump to file if required
|
|
51
|
+
if (opts?.exportPath) {
|
|
52
|
+
const path = opts?.exportPath
|
|
53
|
+
const writeStream = fs.createWriteStream(path)
|
|
54
|
+
await db.dump(writeStream, { filter: opts?.filter })
|
|
55
|
+
return path
|
|
56
|
+
} else {
|
|
57
|
+
// Stringify the dump in memory if required
|
|
58
|
+
const memStream = new MemoryStream()
|
|
59
|
+
let appString = ""
|
|
60
|
+
memStream.on("data", (chunk: any) => {
|
|
61
|
+
appString += chunk.toString()
|
|
62
|
+
})
|
|
63
|
+
await db.dump(memStream, { filter: opts?.filter })
|
|
64
|
+
return appString
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function defineFilter(excludeRows?: boolean) {
|
|
70
|
+
const ids = [USER_METDATA_PREFIX, LINK_USER_METADATA_PREFIX]
|
|
71
|
+
if (excludeRows) {
|
|
72
|
+
ids.push(TABLE_ROW_PREFIX)
|
|
73
|
+
}
|
|
74
|
+
return (doc: any) =>
|
|
75
|
+
!ids.map(key => doc._id.includes(key)).reduce((prev, curr) => prev || curr)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Local utility to back up the database state for an app, excluding global user
|
|
80
|
+
* data or user relationships.
|
|
81
|
+
* @param {string} appId The app to back up
|
|
82
|
+
* @param {object} config Config to send to export DB/attachment export
|
|
83
|
+
* @returns {*} either a string or a stream of the backup
|
|
84
|
+
*/
|
|
85
|
+
export async function exportApp(appId: string, config?: ExportOpts) {
|
|
86
|
+
const prodAppId = dbCore.getProdAppID(appId)
|
|
87
|
+
const appPath = `${prodAppId}/`
|
|
88
|
+
// export bucket contents
|
|
89
|
+
let tmpPath
|
|
90
|
+
if (!env.isTest()) {
|
|
91
|
+
tmpPath = await retrieveDirectory(ObjectStoreBuckets.APPS, appPath)
|
|
92
|
+
} else {
|
|
93
|
+
tmpPath = createTempFolder(uuid())
|
|
94
|
+
}
|
|
95
|
+
const downloadedPath = join(tmpPath, appPath)
|
|
96
|
+
if (fs.existsSync(downloadedPath)) {
|
|
97
|
+
const allFiles = fs.readdirSync(downloadedPath)
|
|
98
|
+
for (let file of allFiles) {
|
|
99
|
+
const path = join(downloadedPath, file)
|
|
100
|
+
// move out of app directory, simplify structure
|
|
101
|
+
fs.renameSync(path, join(downloadedPath, "..", file))
|
|
102
|
+
}
|
|
103
|
+
// remove the old app directory created by object export
|
|
104
|
+
fs.rmdirSync(downloadedPath)
|
|
105
|
+
}
|
|
106
|
+
// enforce an export of app DB to the tmp path
|
|
107
|
+
const dbPath = join(tmpPath, DB_EXPORT_FILE)
|
|
108
|
+
await exportDB(appId, {
|
|
109
|
+
...config,
|
|
110
|
+
filter: defineFilter(config?.excludeRows),
|
|
111
|
+
exportPath: dbPath,
|
|
112
|
+
})
|
|
113
|
+
// if tar requested, return where the tarball is
|
|
114
|
+
if (config?.tar) {
|
|
115
|
+
// now the tmpPath contains both the DB export and attachments, tar this
|
|
116
|
+
const tarPath = tarFilesToTmp(tmpPath, fs.readdirSync(tmpPath))
|
|
117
|
+
// cleanup the tmp export files as tarball returned
|
|
118
|
+
fs.rmSync(tmpPath, { recursive: true, force: true })
|
|
119
|
+
return tarPath
|
|
120
|
+
}
|
|
121
|
+
// tar not requested, turn the directory where export is
|
|
122
|
+
else {
|
|
123
|
+
return tmpPath
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Export all apps + global DB (if supplied) to a single tarball, this includes
|
|
129
|
+
* the attachments for each app as well.
|
|
130
|
+
* @param {object[]} appMetadata The IDs and names of apps to export.
|
|
131
|
+
* @param {string} globalDbContents The contents of the global DB to export as well.
|
|
132
|
+
* @return {string} The path to the tarball.
|
|
133
|
+
*/
|
|
134
|
+
export async function exportMultipleApps(
|
|
135
|
+
appMetadata: { appId: string; name: string }[],
|
|
136
|
+
globalDbContents?: string
|
|
137
|
+
) {
|
|
138
|
+
const tmpPath = join(budibaseTempDir(), uuid())
|
|
139
|
+
fs.mkdirSync(tmpPath)
|
|
140
|
+
let exportPromises: Promise<void>[] = []
|
|
141
|
+
// export each app to a directory, then move it into the complete export
|
|
142
|
+
const exportAndMove = async (appId: string, appName: string) => {
|
|
143
|
+
const path = await exportApp(appId)
|
|
144
|
+
await fs.promises.rename(path, join(tmpPath, appName))
|
|
145
|
+
}
|
|
146
|
+
for (let metadata of appMetadata) {
|
|
147
|
+
exportPromises.push(exportAndMove(metadata.appId, metadata.name))
|
|
148
|
+
}
|
|
149
|
+
// wait for all exports to finish
|
|
150
|
+
await Promise.all(exportPromises)
|
|
151
|
+
// add the global DB contents
|
|
152
|
+
if (globalDbContents) {
|
|
153
|
+
fs.writeFileSync(join(tmpPath, GLOBAL_DB_EXPORT_FILE), globalDbContents)
|
|
154
|
+
}
|
|
155
|
+
const appNames = appMetadata.map(metadata => metadata.name)
|
|
156
|
+
const tarPath = tarFilesToTmp(tmpPath, [...appNames, GLOBAL_DB_EXPORT_FILE])
|
|
157
|
+
// clear up the tmp path now tarball generated
|
|
158
|
+
fs.rmSync(tmpPath, { recursive: true, force: true })
|
|
159
|
+
return tarPath
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Streams a backup of the database state for an app
|
|
164
|
+
* @param {string} appId The ID of the app which is to be backed up.
|
|
165
|
+
* @param {boolean} excludeRows Flag to state whether the export should include data.
|
|
166
|
+
* @returns {*} a readable stream of the backup which is written in real time
|
|
167
|
+
*/
|
|
168
|
+
export async function streamExportApp(appId: string, excludeRows: boolean) {
|
|
169
|
+
const tmpPath = await exportApp(appId, { excludeRows, tar: true })
|
|
170
|
+
return streamFile(tmpPath)
|
|
171
|
+
}
|