@budibase/server 1.3.13 → 1.3.14
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/builder/assets/{index.e50bda9f.css → index.a70e216a.css} +1 -1
- package/builder/assets/{index.fb22f106.js → index.d8428ac8.js} +166 -165
- package/builder/index.html +2 -2
- package/coverage/clover.xml +90 -68
- package/coverage/coverage-final.json +7 -7
- package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
- package/coverage/lcov-report/api/controllers/auth.js.html +1 -1
- package/coverage/lcov-report/api/controllers/automation.js.html +1 -1
- package/coverage/lcov-report/api/controllers/backup.js.html +8 -5
- package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
- package/coverage/lcov-report/api/controllers/component.js.html +1 -1
- package/coverage/lcov-report/api/controllers/datasource.js.html +1 -1
- package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
- package/coverage/lcov-report/api/controllers/index.html +7 -7
- package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
- package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
- package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
- package/coverage/lcov-report/api/controllers/migrations.js.html +1 -1
- 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 +1 -1
- package/coverage/lcov-report/api/controllers/role.js.html +1 -1
- package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
- 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 +1 -1
- package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/staticFormula.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/utils.js.html +1 -1
- package/coverage/lcov-report/api/controllers/screen.js.html +1 -1
- package/coverage/lcov-report/api/controllers/script.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/bulkFormula.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/external.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/index.html +1 -1
- package/coverage/lcov-report/api/controllers/table/index.js.html +1 -1
- package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
- 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 +1 -1
- package/coverage/lcov-report/api/controllers/view/utils.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +1 -1
- package/coverage/lcov-report/api/controllers/webhook.js.html +1 -1
- package/coverage/lcov-report/api/index.html +1 -1
- package/coverage/lcov-report/api/index.js.html +1 -1
- package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
- package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
- package/coverage/lcov-report/api/routes/auth.js.html +1 -1
- package/coverage/lcov-report/api/routes/automation.js.html +1 -1
- package/coverage/lcov-report/api/routes/backup.js.html +1 -1
- package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
- package/coverage/lcov-report/api/routes/component.js.html +1 -1
- package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
- package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
- package/coverage/lcov-report/api/routes/dev.js.html +1 -1
- package/coverage/lcov-report/api/routes/index.html +1 -1
- package/coverage/lcov-report/api/routes/integration.js.html +1 -1
- package/coverage/lcov-report/api/routes/layout.js.html +1 -1
- package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
- package/coverage/lcov-report/api/routes/migrations.js.html +1 -1
- package/coverage/lcov-report/api/routes/permission.js.html +1 -1
- package/coverage/lcov-report/api/routes/query.js.html +1 -1
- package/coverage/lcov-report/api/routes/role.js.html +1 -1
- package/coverage/lcov-report/api/routes/routing.js.html +1 -1
- package/coverage/lcov-report/api/routes/screen.js.html +1 -1
- package/coverage/lcov-report/api/routes/script.js.html +1 -1
- package/coverage/lcov-report/api/routes/table.js.html +1 -1
- package/coverage/lcov-report/api/routes/templates.js.html +1 -1
- package/coverage/lcov-report/api/routes/user.js.html +1 -1
- package/coverage/lcov-report/api/routes/utils/index.html +1 -1
- package/coverage/lcov-report/api/routes/utils/validators.js.html +1 -1
- package/coverage/lcov-report/api/routes/view.js.html +1 -1
- package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
- package/coverage/lcov-report/automations/actions.js.html +1 -1
- package/coverage/lcov-report/automations/automationUtils.js.html +1 -1
- package/coverage/lcov-report/automations/bullboard.js.html +1 -1
- package/coverage/lcov-report/automations/index.html +24 -24
- package/coverage/lcov-report/automations/index.js.html +18 -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 +1 -1
- 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 +1 -1
- package/coverage/lcov-report/automations/steps/utils.js.html +1 -1
- 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 +126 -24
- package/coverage/lcov-report/constants/index.html +1 -1
- package/coverage/lcov-report/constants/index.js.html +1 -1
- 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 +1 -1
- package/coverage/lcov-report/db/index.js.html +1 -1
- package/coverage/lcov-report/db/linkedRows/LinkController.js.html +1 -1
- package/coverage/lcov-report/db/linkedRows/index.html +1 -1
- package/coverage/lcov-report/db/linkedRows/index.js.html +1 -1
- package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +1 -1
- package/coverage/lcov-report/db/newid.js.html +1 -1
- package/coverage/lcov-report/db/utils.js.html +5 -5
- 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 +1 -1
- package/coverage/lcov-report/events/utils.js.html +1 -1
- package/coverage/lcov-report/index.html +34 -34
- 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 +1 -1
- package/coverage/lcov-report/middleware/builder.js.html +1 -1
- package/coverage/lcov-report/middleware/currentapp.js.html +1 -1
- 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 +1 -1
- package/coverage/lcov-report/middleware/resourceId.js.html +1 -1
- package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
- package/coverage/lcov-report/middleware/utils.js.html +1 -1
- package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
- package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
- package/coverage/lcov-report/utilities/csvParser.js.html +1 -1
- package/coverage/lcov-report/utilities/global.js.html +1 -1
- package/coverage/lcov-report/utilities/index.html +19 -19
- package/coverage/lcov-report/utilities/index.js.html +40 -16
- package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +1 -1
- package/coverage/lcov-report/utilities/queue/index.html +1 -1
- package/coverage/lcov-report/utilities/routing/index.html +1 -1
- package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
- package/coverage/lcov-report/utilities/rowProcessor/index.html +5 -5
- package/coverage/lcov-report/utilities/rowProcessor/index.js.html +1 -1
- package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +4 -4
- package/coverage/lcov-report/utilities/scriptRunner.js.html +1 -1
- package/coverage/lcov-report/utilities/security.js.html +1 -1
- package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
- package/coverage/lcov-report/utilities/usageQuota/index.html +1 -1
- package/coverage/lcov-report/utilities/usageQuota/rows.js.html +6 -3
- 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 +1 -1
- package/coverage/lcov.info +167 -127
- package/dist/api/controllers/application.js +1 -5
- package/dist/api/controllers/backup.js +2 -1
- package/dist/api/controllers/deploy/index.js +17 -2
- package/dist/api/controllers/row/ExternalRequest.js +1 -1
- package/dist/automations/index.js +10 -5
- package/dist/automations/triggers.js +39 -5
- package/dist/automations/utils.js +16 -7
- package/dist/integrations/rest.js +16 -1
- package/dist/package.json +6 -6
- package/dist/threads/automation.js +6 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utilities/fileSystem/index.js +6 -6
- package/dist/utilities/index.js +8 -0
- package/dist/utilities/rowProcessor/utils.js +1 -1
- package/dist/utilities/usageQuota/rows.js +2 -1
- package/package.json +7 -7
- package/src/api/controllers/application.ts +1 -5
- package/src/api/controllers/backup.js +2 -1
- package/src/api/controllers/deploy/index.ts +17 -4
- package/src/api/controllers/row/ExternalRequest.ts +1 -1
- package/src/api/routes/tests/query.spec.js +3 -3
- package/src/automations/index.js +6 -3
- package/src/automations/triggers.js +44 -10
- package/src/automations/utils.ts +21 -9
- package/src/definitions/datasource.ts +1 -0
- package/src/integrations/rest.ts +14 -1
- package/src/integrations/tests/rest.spec.js +38 -3
- package/src/threads/automation.ts +6 -0
- package/src/utilities/fileSystem/index.js +6 -6
- package/src/utilities/index.js +8 -0
- package/src/utilities/rowProcessor/utils.js +1 -1
- package/src/utilities/usageQuota/rows.js +2 -1
|
@@ -110,11 +110,11 @@ exports.defineFilter = excludeRows => {
|
|
|
110
110
|
* data or user relationships.
|
|
111
111
|
* @param {string} appId The app to backup
|
|
112
112
|
* @param {object} config Config to send to export DB
|
|
113
|
-
* @param {boolean}
|
|
113
|
+
* @param {boolean} excludeRows Flag to state whether the export should include data.
|
|
114
114
|
* @returns {*} either a string or a stream of the backup
|
|
115
115
|
*/
|
|
116
|
-
const backupAppData = (appId, config,
|
|
117
|
-
return yield exports.exportDB(appId, Object.assign(Object.assign({}, config), { filter: exports.defineFilter(
|
|
116
|
+
const backupAppData = (appId, config, excludeRows) => __awaiter(void 0, void 0, void 0, function* () {
|
|
117
|
+
return yield exports.exportDB(appId, Object.assign(Object.assign({}, config), { filter: exports.defineFilter(excludeRows) }));
|
|
118
118
|
});
|
|
119
119
|
/**
|
|
120
120
|
* Takes a copy of the database state for an app to the object store.
|
|
@@ -128,11 +128,11 @@ exports.performBackup = (appId, backupName) => __awaiter(void 0, void 0, void 0,
|
|
|
128
128
|
/**
|
|
129
129
|
* Streams a backup of the database state for an app
|
|
130
130
|
* @param {string} appId The ID of the app which is to be backed up.
|
|
131
|
-
* @param {boolean}
|
|
131
|
+
* @param {boolean} excludeRows Flag to state whether the export should include data.
|
|
132
132
|
* @returns {*} a readable stream of the backup which is written in real time
|
|
133
133
|
*/
|
|
134
|
-
exports.streamBackup = (appId,
|
|
135
|
-
return yield backupAppData(appId, { stream: true },
|
|
134
|
+
exports.streamBackup = (appId, excludeRows) => __awaiter(void 0, void 0, void 0, function* () {
|
|
135
|
+
return yield backupAppData(appId, { stream: true }, excludeRows);
|
|
136
136
|
});
|
|
137
137
|
/**
|
|
138
138
|
* Exports a DB to either file or a variable (memory).
|
package/dist/utilities/index.js
CHANGED
|
@@ -72,7 +72,8 @@ exports.getUniqueRows = (appIds) => __awaiter(void 0, void 0, void 0, function*
|
|
|
72
72
|
// ensure uniqueness on a per app pair basis
|
|
73
73
|
// this can't be done on all rows because app import results in
|
|
74
74
|
// duplicate row ids across apps
|
|
75
|
-
|
|
75
|
+
// the array pre-concat is important to avoid stack overflow
|
|
76
|
+
uniqueRows = uniqueRows.concat([...new Set(appRows)]);
|
|
76
77
|
}
|
|
77
78
|
return uniqueRows;
|
|
78
79
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/server",
|
|
3
3
|
"email": "hi@budibase.com",
|
|
4
|
-
"version": "1.3.
|
|
4
|
+
"version": "1.3.14",
|
|
5
5
|
"description": "Budibase Web Server",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"repository": {
|
|
@@ -77,11 +77,11 @@
|
|
|
77
77
|
"license": "GPL-3.0",
|
|
78
78
|
"dependencies": {
|
|
79
79
|
"@apidevtools/swagger-parser": "10.0.3",
|
|
80
|
-
"@budibase/backend-core": "^1.3.
|
|
81
|
-
"@budibase/client": "^1.3.
|
|
82
|
-
"@budibase/pro": "1.3.
|
|
83
|
-
"@budibase/string-templates": "^1.3.
|
|
84
|
-
"@budibase/types": "^1.3.
|
|
80
|
+
"@budibase/backend-core": "^1.3.14",
|
|
81
|
+
"@budibase/client": "^1.3.14",
|
|
82
|
+
"@budibase/pro": "1.3.13",
|
|
83
|
+
"@budibase/string-templates": "^1.3.14",
|
|
84
|
+
"@budibase/types": "^1.3.14",
|
|
85
85
|
"@bull-board/api": "3.7.0",
|
|
86
86
|
"@bull-board/koa": "3.9.4",
|
|
87
87
|
"@elastic/elasticsearch": "7.10.0",
|
|
@@ -196,5 +196,5 @@
|
|
|
196
196
|
"optionalDependencies": {
|
|
197
197
|
"oracledb": "5.3.0"
|
|
198
198
|
},
|
|
199
|
-
"gitHead": "
|
|
199
|
+
"gitHead": "a38626cf662d66aa8c04fb1d42dc7bc353ce7c31"
|
|
200
200
|
}
|
|
@@ -553,11 +553,7 @@ export const sync = async (ctx: any, next: any) => {
|
|
|
553
553
|
})
|
|
554
554
|
let error
|
|
555
555
|
try {
|
|
556
|
-
await replication.replicate(
|
|
557
|
-
filter: function (doc: any) {
|
|
558
|
-
return doc._id !== DocumentType.APP_METADATA
|
|
559
|
-
},
|
|
560
|
-
})
|
|
556
|
+
await replication.replicate(replication.appReplicateOpts())
|
|
561
557
|
} catch (err) {
|
|
562
558
|
error = err
|
|
563
559
|
} finally {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
const { streamBackup } = require("../../utilities/fileSystem")
|
|
2
2
|
const { events, context } = require("@budibase/backend-core")
|
|
3
3
|
const { DocumentType } = require("../../db/utils")
|
|
4
|
+
const { isQsTrue } = require("../../utilities")
|
|
4
5
|
|
|
5
6
|
exports.exportAppDump = async function (ctx) {
|
|
6
7
|
let { appId, excludeRows } = ctx.query
|
|
7
8
|
const appName = decodeURI(ctx.query.appname)
|
|
8
|
-
excludeRows = excludeRows
|
|
9
|
+
excludeRows = isQsTrue(excludeRows)
|
|
9
10
|
const backupIdentifier = `${appName}-export-${new Date().getTime()}.txt`
|
|
10
11
|
ctx.attachment(backupIdentifier)
|
|
11
12
|
ctx.body = await streamBackup(appId, excludeRows)
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
getAppId,
|
|
16
16
|
getAppDB,
|
|
17
17
|
getProdAppDB,
|
|
18
|
+
getDevAppDB,
|
|
18
19
|
} from "@budibase/backend-core/context"
|
|
19
20
|
import { quotas } from "@budibase/pro"
|
|
20
21
|
import { events } from "@budibase/backend-core"
|
|
@@ -110,17 +111,29 @@ async function deployApp(deployment: any) {
|
|
|
110
111
|
target: productionAppId,
|
|
111
112
|
}
|
|
112
113
|
replication = new Replication(config)
|
|
113
|
-
|
|
114
|
+
const devDb = getDevAppDB()
|
|
115
|
+
console.log("Compacting development DB")
|
|
116
|
+
await devDb.compact()
|
|
114
117
|
console.log("Replication object created")
|
|
115
|
-
await replication.replicate()
|
|
118
|
+
await replication.replicate(replication.appReplicateOpts())
|
|
116
119
|
console.log("replication complete.. replacing app meta doc")
|
|
120
|
+
// app metadata is excluded as it is likely to be in conflict
|
|
121
|
+
// replicate the app metadata document manually
|
|
117
122
|
const db = getProdAppDB()
|
|
118
|
-
const appDoc = await
|
|
123
|
+
const appDoc = await devDb.get(DocumentType.APP_METADATA)
|
|
124
|
+
try {
|
|
125
|
+
const prodAppDoc = await db.get(DocumentType.APP_METADATA)
|
|
126
|
+
appDoc._rev = prodAppDoc._rev
|
|
127
|
+
} catch (err) {
|
|
128
|
+
delete appDoc._rev
|
|
129
|
+
}
|
|
119
130
|
|
|
131
|
+
// switch to production app ID
|
|
120
132
|
deployment.appUrl = appDoc.url
|
|
121
|
-
|
|
122
133
|
appDoc.appId = productionAppId
|
|
123
134
|
appDoc.instance._id = productionAppId
|
|
135
|
+
// remove automation errors if they exist
|
|
136
|
+
delete appDoc.automationErrors
|
|
124
137
|
await db.put(appDoc)
|
|
125
138
|
await appCache.invalidateAppMetadata(productionAppId)
|
|
126
139
|
console.log("New app doc written successfully.")
|
|
@@ -534,7 +534,7 @@ module External {
|
|
|
534
534
|
})
|
|
535
535
|
// this is the response from knex if no rows found
|
|
536
536
|
const rows = !response[0].read ? response : []
|
|
537
|
-
const storeTo = isMany ? field.throughFrom || linkPrimaryKey :
|
|
537
|
+
const storeTo = isMany ? field.throughFrom || linkPrimaryKey : fieldName
|
|
538
538
|
related[storeTo] = { rows, isMany, tableId }
|
|
539
539
|
}
|
|
540
540
|
return related
|
|
@@ -311,7 +311,7 @@ describe("/queries", () => {
|
|
|
311
311
|
"url": "string",
|
|
312
312
|
"value": "string"
|
|
313
313
|
})
|
|
314
|
-
expect(res.body.rows[0].url).toContain("doctype
|
|
314
|
+
expect(res.body.rows[0].url).toContain("doctype%20html")
|
|
315
315
|
})
|
|
316
316
|
|
|
317
317
|
it("check that it automatically retries on fail with cached dynamics", async () => {
|
|
@@ -396,7 +396,7 @@ describe("/queries", () => {
|
|
|
396
396
|
"queryHdr": userDetails.firstName,
|
|
397
397
|
"secondHdr" : "1234"
|
|
398
398
|
})
|
|
399
|
-
expect(res.body.rows[0].url).toEqual("http://www.google.com?email=" + userDetails.email)
|
|
399
|
+
expect(res.body.rows[0].url).toEqual("http://www.google.com?email=" + userDetails.email.replace("@", "%40"))
|
|
400
400
|
})
|
|
401
401
|
|
|
402
402
|
it("should bind the current user to query parameters", async () => {
|
|
@@ -413,7 +413,7 @@ describe("/queries", () => {
|
|
|
413
413
|
"testParam" : "1234"
|
|
414
414
|
})
|
|
415
415
|
|
|
416
|
-
expect(res.body.rows[0].url).toEqual("http://www.google.com?test=" + userDetails.email +
|
|
416
|
+
expect(res.body.rows[0].url).toEqual("http://www.google.com?test=" + userDetails.email.replace("@", "%40") +
|
|
417
417
|
"&testName=" + userDetails.firstName + "&testParam=1234")
|
|
418
418
|
})
|
|
419
419
|
|
package/src/automations/index.js
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
const { processEvent } = require("./utils")
|
|
2
2
|
const { queue, shutdown } = require("./bullboard")
|
|
3
|
-
const { TRIGGER_DEFINITIONS } = require("./triggers")
|
|
3
|
+
const { TRIGGER_DEFINITIONS, rebootTrigger } = require("./triggers")
|
|
4
4
|
const { ACTION_DEFINITIONS } = require("./actions")
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* This module is built purely to kick off the worker farm and manage the inputs/outputs
|
|
8
8
|
*/
|
|
9
|
-
exports.init = function () {
|
|
9
|
+
exports.init = async function () {
|
|
10
10
|
// this promise will not complete
|
|
11
|
-
|
|
11
|
+
const promise = queue.process(async job => {
|
|
12
12
|
await processEvent(job)
|
|
13
13
|
})
|
|
14
|
+
// on init we need to trigger any reboot automations
|
|
15
|
+
await rebootTrigger()
|
|
16
|
+
return promise
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
exports.getQueues = () => {
|
|
@@ -9,6 +9,7 @@ const { checkTestFlag } = require("../utilities/redis")
|
|
|
9
9
|
const utils = require("./utils")
|
|
10
10
|
const env = require("../environment")
|
|
11
11
|
const { doInAppContext, getAppDB } = require("@budibase/backend-core/context")
|
|
12
|
+
const { getAllApps } = require("@budibase/backend-core/db")
|
|
12
13
|
|
|
13
14
|
const TRIGGER_DEFINITIONS = definitions
|
|
14
15
|
const JOB_OPTS = {
|
|
@@ -16,24 +17,27 @@ const JOB_OPTS = {
|
|
|
16
17
|
removeOnFail: true,
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
async function getAllAutomations() {
|
|
21
|
+
const db = getAppDB()
|
|
22
|
+
let automations = await db.allDocs(
|
|
23
|
+
getAutomationParams(null, { include_docs: true })
|
|
24
|
+
)
|
|
25
|
+
return automations.rows.map(row => row.doc)
|
|
26
|
+
}
|
|
27
|
+
|
|
19
28
|
async function queueRelevantRowAutomations(event, eventType) {
|
|
20
29
|
if (event.appId == null) {
|
|
21
30
|
throw `No appId specified for ${eventType} - check event emitters.`
|
|
22
31
|
}
|
|
23
32
|
|
|
24
33
|
doInAppContext(event.appId, async () => {
|
|
25
|
-
|
|
26
|
-
let automations = await db.allDocs(
|
|
27
|
-
getAutomationParams(null, { include_docs: true })
|
|
28
|
-
)
|
|
34
|
+
let automations = await getAllAutomations()
|
|
29
35
|
|
|
30
36
|
// filter down to the correct event type
|
|
31
|
-
automations = automations.
|
|
32
|
-
|
|
33
|
-
.
|
|
34
|
-
|
|
35
|
-
return trigger && trigger.event === eventType
|
|
36
|
-
})
|
|
37
|
+
automations = automations.filter(automation => {
|
|
38
|
+
const trigger = automation.definition.trigger
|
|
39
|
+
return trigger && trigger.event === eventType
|
|
40
|
+
})
|
|
37
41
|
|
|
38
42
|
for (let automation of automations) {
|
|
39
43
|
let automationDef = automation.definition
|
|
@@ -110,4 +114,34 @@ exports.externalTrigger = async function (
|
|
|
110
114
|
}
|
|
111
115
|
}
|
|
112
116
|
|
|
117
|
+
exports.rebootTrigger = async () => {
|
|
118
|
+
// reboot cron option is only available on the main thread at
|
|
119
|
+
// startup and only usable in self host
|
|
120
|
+
if (env.isInThread() || !env.SELF_HOSTED) {
|
|
121
|
+
return
|
|
122
|
+
}
|
|
123
|
+
// iterate through all production apps, find the reboot crons
|
|
124
|
+
// and trigger events for them
|
|
125
|
+
const appIds = await getAllApps({ dev: false, idsOnly: true })
|
|
126
|
+
for (let prodAppId of appIds) {
|
|
127
|
+
await doInAppContext(prodAppId, async () => {
|
|
128
|
+
let automations = await getAllAutomations()
|
|
129
|
+
let rebootEvents = []
|
|
130
|
+
for (let automation of automations) {
|
|
131
|
+
if (utils.isRebootTrigger(automation)) {
|
|
132
|
+
const job = {
|
|
133
|
+
automation,
|
|
134
|
+
event: {
|
|
135
|
+
appId: prodAppId,
|
|
136
|
+
timestamp: Date.now(),
|
|
137
|
+
},
|
|
138
|
+
}
|
|
139
|
+
rebootEvents.push(queue.add(job, JOB_OPTS))
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
await Promise.all(rebootEvents)
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
113
147
|
exports.TRIGGER_DEFINITIONS = TRIGGER_DEFINITIONS
|
package/src/automations/utils.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { tenancy } from "@budibase/backend-core"
|
|
|
17
17
|
import { quotas } from "@budibase/pro"
|
|
18
18
|
import { Automation } from "@budibase/types"
|
|
19
19
|
|
|
20
|
+
const REBOOT_CRON = "@reboot"
|
|
20
21
|
const WH_STEP_ID = definitions.WEBHOOK.stepId
|
|
21
22
|
const CRON_STEP_ID = definitions.CRON.stepId
|
|
22
23
|
const Runner = new Thread(ThreadType.AUTOMATION)
|
|
@@ -109,22 +110,33 @@ export async function clearMetadata() {
|
|
|
109
110
|
await db.bulkDocs(automationMetadata)
|
|
110
111
|
}
|
|
111
112
|
|
|
113
|
+
export function isCronTrigger(auto: Automation) {
|
|
114
|
+
return (
|
|
115
|
+
auto &&
|
|
116
|
+
auto.definition.trigger &&
|
|
117
|
+
auto.definition.trigger.stepId === CRON_STEP_ID
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function isRebootTrigger(auto: Automation) {
|
|
122
|
+
const trigger = auto ? auto.definition.trigger : null
|
|
123
|
+
return isCronTrigger(auto) && trigger?.inputs.cron === REBOOT_CRON
|
|
124
|
+
}
|
|
125
|
+
|
|
112
126
|
/**
|
|
113
127
|
* This function handles checking of any cron jobs that need to be enabled/updated.
|
|
114
128
|
* @param {string} appId The ID of the app in which we are checking for webhooks
|
|
115
129
|
* @param {object|undefined} automation The automation object to be updated.
|
|
116
130
|
*/
|
|
117
|
-
export async function enableCronTrigger(appId: any, automation:
|
|
131
|
+
export async function enableCronTrigger(appId: any, automation: Automation) {
|
|
118
132
|
const trigger = automation ? automation.definition.trigger : null
|
|
119
|
-
|
|
120
|
-
return (
|
|
121
|
-
auto &&
|
|
122
|
-
auto.definition.trigger &&
|
|
123
|
-
auto.definition.trigger.stepId === CRON_STEP_ID
|
|
124
|
-
)
|
|
125
|
-
}
|
|
133
|
+
|
|
126
134
|
// need to create cron job
|
|
127
|
-
if (
|
|
135
|
+
if (
|
|
136
|
+
isCronTrigger(automation) &&
|
|
137
|
+
!isRebootTrigger(automation) &&
|
|
138
|
+
trigger?.inputs.cron
|
|
139
|
+
) {
|
|
128
140
|
// make a job id rather than letting Bull decide, makes it easier to handle on way out
|
|
129
141
|
const jobId = `${appId}_cron_${newid()}`
|
|
130
142
|
const job: any = await queue.add(
|
package/src/integrations/rest.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
BearerAuthConfig,
|
|
15
15
|
} from "../definitions/datasource"
|
|
16
16
|
import { get } from "lodash"
|
|
17
|
+
import qs from "querystring"
|
|
17
18
|
|
|
18
19
|
const BodyTypes = {
|
|
19
20
|
NONE: "none",
|
|
@@ -79,6 +80,12 @@ module RestModule {
|
|
|
79
80
|
required: false,
|
|
80
81
|
default: {},
|
|
81
82
|
},
|
|
83
|
+
legacyHttpParser: {
|
|
84
|
+
display: "Legacy HTTP Support",
|
|
85
|
+
type: DatasourceFieldType.BOOLEAN,
|
|
86
|
+
required: false,
|
|
87
|
+
default: false,
|
|
88
|
+
},
|
|
82
89
|
},
|
|
83
90
|
query: {
|
|
84
91
|
create: {
|
|
@@ -215,7 +222,8 @@ module RestModule {
|
|
|
215
222
|
}
|
|
216
223
|
}
|
|
217
224
|
|
|
218
|
-
|
|
225
|
+
// make sure the query string is fully encoded
|
|
226
|
+
const main = `${path}?${qs.encode(qs.decode(queryString))}`
|
|
219
227
|
let complete = main
|
|
220
228
|
if (this.config.url && !main.startsWith("http")) {
|
|
221
229
|
complete = !this.config.url ? main : `${this.config.url}/${main}`
|
|
@@ -377,6 +385,11 @@ module RestModule {
|
|
|
377
385
|
paginationValues
|
|
378
386
|
)
|
|
379
387
|
|
|
388
|
+
if (this.config.legacyHttpParser) {
|
|
389
|
+
// https://github.com/nodejs/node/issues/43798
|
|
390
|
+
input.extraHttpOptions = { insecureHTTPParser: true }
|
|
391
|
+
}
|
|
392
|
+
|
|
380
393
|
this.startTimeMs = performance.now()
|
|
381
394
|
const url = this.getUrl(path, queryString, pagination, paginationValues)
|
|
382
395
|
const response = await fetch(url, input)
|
|
@@ -50,7 +50,7 @@ describe("REST Integration", () => {
|
|
|
50
50
|
name: "test",
|
|
51
51
|
}),
|
|
52
52
|
}
|
|
53
|
-
|
|
53
|
+
await config.integration.create(query)
|
|
54
54
|
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api?test=1`, {
|
|
55
55
|
method: "POST",
|
|
56
56
|
body: '{"name":"test"}',
|
|
@@ -295,7 +295,7 @@ describe("REST Integration", () => {
|
|
|
295
295
|
}
|
|
296
296
|
await config.integration.read(query)
|
|
297
297
|
expect(fetch).toHaveBeenCalledWith(
|
|
298
|
-
`${BASE_URL}/api?${pageParam}=${pageValue}&${sizeParam}=${sizeValue}
|
|
298
|
+
`${BASE_URL}/api?${pageParam}=${pageValue}&${sizeParam}=${sizeValue}`,
|
|
299
299
|
{
|
|
300
300
|
headers: {},
|
|
301
301
|
method: "GET",
|
|
@@ -420,7 +420,7 @@ describe("REST Integration", () => {
|
|
|
420
420
|
}
|
|
421
421
|
const res = await config.integration.read(query)
|
|
422
422
|
expect(fetch).toHaveBeenCalledWith(
|
|
423
|
-
`${BASE_URL}/api?${pageParam}=${pageValue}&${sizeParam}=${sizeValue}
|
|
423
|
+
`${BASE_URL}/api?${pageParam}=${pageValue}&${sizeParam}=${sizeValue}`,
|
|
424
424
|
{
|
|
425
425
|
headers: {},
|
|
426
426
|
method: "GET",
|
|
@@ -528,5 +528,40 @@ describe("REST Integration", () => {
|
|
|
528
528
|
expect(sentData.get(sizeParam)).toEqual(sizeValue.toString())
|
|
529
529
|
expect(res.pagination.cursor).toEqual(123)
|
|
530
530
|
})
|
|
531
|
+
|
|
532
|
+
it("should encode query string correctly", async () => {
|
|
533
|
+
const query = {
|
|
534
|
+
path: "api",
|
|
535
|
+
queryString: "test=1 2",
|
|
536
|
+
headers: HEADERS,
|
|
537
|
+
bodyType: "json",
|
|
538
|
+
requestBody: JSON.stringify({
|
|
539
|
+
name: "test",
|
|
540
|
+
}),
|
|
541
|
+
}
|
|
542
|
+
await config.integration.create(query)
|
|
543
|
+
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api?test=1%202`, {
|
|
544
|
+
method: "POST",
|
|
545
|
+
body: '{"name":"test"}',
|
|
546
|
+
headers: HEADERS,
|
|
547
|
+
})
|
|
548
|
+
})
|
|
549
|
+
})
|
|
550
|
+
|
|
551
|
+
describe("Configuration options", () => {
|
|
552
|
+
it("Attaches insecureHttpParams when legacy HTTP Parser option is set", async () => {
|
|
553
|
+
config = new TestConfiguration({
|
|
554
|
+
url: BASE_URL,
|
|
555
|
+
legacyHttpParser: true
|
|
556
|
+
})
|
|
557
|
+
await config.integration.read({})
|
|
558
|
+
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/?`, {
|
|
559
|
+
method: "GET",
|
|
560
|
+
headers: {},
|
|
561
|
+
extraHttpOptions: {
|
|
562
|
+
insecureHTTPParser: true
|
|
563
|
+
}
|
|
564
|
+
})
|
|
565
|
+
})
|
|
531
566
|
})
|
|
532
567
|
})
|
|
@@ -458,6 +458,9 @@ class Orchestrator {
|
|
|
458
458
|
|
|
459
459
|
export function execute(input: AutomationEvent, callback: WorkerCallback) {
|
|
460
460
|
const appId = input.data.event.appId
|
|
461
|
+
if (!appId) {
|
|
462
|
+
throw new Error("Unable to execute, event doesn't contain app ID.")
|
|
463
|
+
}
|
|
461
464
|
doInAppContext(appId, async () => {
|
|
462
465
|
const automationOrchestrator = new Orchestrator(
|
|
463
466
|
input.data.automation,
|
|
@@ -475,6 +478,9 @@ export function execute(input: AutomationEvent, callback: WorkerCallback) {
|
|
|
475
478
|
|
|
476
479
|
export const removeStalled = async (input: AutomationEvent) => {
|
|
477
480
|
const appId = input.data.event.appId
|
|
481
|
+
if (!appId) {
|
|
482
|
+
throw new Error("Unable to execute, event doesn't contain app ID.")
|
|
483
|
+
}
|
|
478
484
|
await doInAppContext(appId, async () => {
|
|
479
485
|
const automationOrchestrator = new Orchestrator(
|
|
480
486
|
input.data.automation,
|
|
@@ -125,13 +125,13 @@ exports.defineFilter = excludeRows => {
|
|
|
125
125
|
* data or user relationships.
|
|
126
126
|
* @param {string} appId The app to backup
|
|
127
127
|
* @param {object} config Config to send to export DB
|
|
128
|
-
* @param {boolean}
|
|
128
|
+
* @param {boolean} excludeRows Flag to state whether the export should include data.
|
|
129
129
|
* @returns {*} either a string or a stream of the backup
|
|
130
130
|
*/
|
|
131
|
-
const backupAppData = async (appId, config,
|
|
131
|
+
const backupAppData = async (appId, config, excludeRows) => {
|
|
132
132
|
return await exports.exportDB(appId, {
|
|
133
133
|
...config,
|
|
134
|
-
filter: exports.defineFilter(
|
|
134
|
+
filter: exports.defineFilter(excludeRows),
|
|
135
135
|
})
|
|
136
136
|
}
|
|
137
137
|
|
|
@@ -148,11 +148,11 @@ exports.performBackup = async (appId, backupName) => {
|
|
|
148
148
|
/**
|
|
149
149
|
* Streams a backup of the database state for an app
|
|
150
150
|
* @param {string} appId The ID of the app which is to be backed up.
|
|
151
|
-
* @param {boolean}
|
|
151
|
+
* @param {boolean} excludeRows Flag to state whether the export should include data.
|
|
152
152
|
* @returns {*} a readable stream of the backup which is written in real time
|
|
153
153
|
*/
|
|
154
|
-
exports.streamBackup = async (appId,
|
|
155
|
-
return await backupAppData(appId, { stream: true },
|
|
154
|
+
exports.streamBackup = async (appId, excludeRows) => {
|
|
155
|
+
return await backupAppData(appId, { stream: true }, excludeRows)
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
/**
|
package/src/utilities/index.js
CHANGED
|
@@ -72,7 +72,8 @@ exports.getUniqueRows = async appIds => {
|
|
|
72
72
|
// ensure uniqueness on a per app pair basis
|
|
73
73
|
// this can't be done on all rows because app import results in
|
|
74
74
|
// duplicate row ids across apps
|
|
75
|
-
|
|
75
|
+
// the array pre-concat is important to avoid stack overflow
|
|
76
|
+
uniqueRows = uniqueRows.concat([...new Set(appRows)])
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
return uniqueRows
|