@budibase/server 1.1.32 → 1.1.33-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/builder/assets/{index.192adf4c.css → index.4d57d3d4.css} +2 -2
- package/builder/assets/index.f51f2e5c.js +977 -0
- package/builder/assets/{vendor.162098da.js → vendor.6bcafa60.js} +1 -1
- package/builder/index.html +3 -3
- package/coverage/clover.xml +381 -696
- package/coverage/coverage-final.json +13 -16
- 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 +1 -1
- 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 +1 -1
- 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 +4 -4
- 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 +18 -18
- package/coverage/lcov-report/api/controllers/row/internal.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +94 -49
- 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 +15 -15
- 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 +2 -2
- 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 +17 -17
- package/coverage/lcov-report/automations/index.html +1 -1
- package/coverage/lcov-report/automations/index.js.html +1 -1
- 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 +5 -2
- 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 +18 -18
- package/coverage/lcov-report/constants/index.html +1 -1
- package/coverage/lcov-report/constants/index.js.html +5 -14
- 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 +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 +61 -13
- 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 +57 -72
- 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 +21 -21
- package/coverage/lcov-report/middleware/joi-validator.js.html +43 -43
- 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 +68 -14
- package/coverage/lcov-report/utilities/index.html +19 -19
- package/coverage/lcov-report/utilities/index.js.html +1 -1
- package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +26 -14
- package/coverage/lcov-report/utilities/queue/index.html +17 -17
- 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 +1 -1
- package/coverage/lcov-report/utilities/rowProcessor/index.js.html +1 -1
- package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +1 -1
- 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 +1 -1
- package/coverage/lcov-report/utilities/usageQuota/rows.js.html +1 -1
- 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 +695 -1214
- package/dist/api/controllers/deploy/index.js +1 -0
- package/dist/api/controllers/query/index.js +1 -1
- package/dist/api/controllers/query/validation.js +3 -3
- package/dist/api/controllers/row/internalSearch.js +30 -16
- package/dist/api/routes/utils/validators.js +1 -1
- package/dist/automations/utils.js +47 -1
- package/dist/constants/index.js +1 -4
- package/dist/db/utils.js +14 -1
- package/dist/definitions/automations.js +8 -0
- package/dist/integrations/base/sql.js +43 -14
- package/dist/package.json +8 -8
- package/dist/threads/automation.js +147 -54
- package/dist/threads/definitions.js +2 -0
- package/dist/threads/index.js +27 -20
- package/dist/threads/query.js +23 -15
- package/dist/threads/utils.js +47 -24
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utilities/global.js +24 -5
- package/dist/utilities/queue/inMemoryQueue.js +5 -0
- package/package.json +9 -9
- package/src/api/controllers/deploy/index.ts +6 -1
- package/src/api/controllers/query/index.ts +1 -1
- package/src/api/controllers/query/validation.js +3 -3
- package/src/api/controllers/row/internalSearch.js +35 -20
- package/src/api/routes/tests/user.spec.js +2 -0
- package/src/api/routes/utils/validators.js +1 -1
- package/src/automations/steps/executeQuery.js +1 -0
- package/src/automations/tests/automation.spec.js +2 -2
- package/src/automations/utils.ts +54 -1
- package/src/constants/index.js +2 -5
- package/src/db/utils.js +16 -0
- package/src/definitions/automations.ts +49 -0
- package/src/definitions/datasource.ts +3 -0
- package/src/integrations/base/sql.ts +44 -13
- package/src/integrations/tests/sql.spec.js +50 -4
- package/src/middleware/tests/currentapp.spec.js +11 -0
- package/src/threads/{automation.js → automation.ts} +172 -48
- package/src/threads/definitions.ts +18 -0
- package/src/threads/index.ts +31 -23
- package/src/threads/{query.js → query.ts} +32 -12
- package/src/threads/{utils.js → utils.ts} +31 -12
- package/src/utilities/global.js +21 -3
- package/src/utilities/queue/inMemoryQueue.js +4 -0
- package/builder/assets/index.133db51f.js +0 -978
- package/coverage/lcov-report/threads/automation.js.html +0 -1114
- package/coverage/lcov-report/threads/index.html +0 -146
- package/coverage/lcov-report/threads/query.js.html +0 -916
- package/coverage/lcov-report/threads/utils.js.html +0 -319
|
@@ -1,36 +1,47 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import { default as threadUtils } from "./utils"
|
|
2
|
+
threadUtils.threadSetup()
|
|
3
|
+
import { isRecurring, disableCron, isErrorInOutput } from "../automations/utils"
|
|
4
|
+
import { default as actions } from "../automations/actions"
|
|
5
|
+
import { default as automationUtils } from "../automations/automationUtils"
|
|
6
|
+
import { default as AutomationEmitter } from "../events/AutomationEmitter"
|
|
7
|
+
import { generateAutomationMetadataID, isProdAppID } from "../db/utils"
|
|
8
|
+
import { definitions as triggerDefs } from "../automations/triggerInfo"
|
|
9
|
+
import { AutomationErrors, MAX_AUTOMATION_RECURRING_ERRORS } from "../constants"
|
|
10
|
+
import { storeLog } from "../automations/logging"
|
|
11
|
+
import { Automation, AutomationStep, AutomationStatus } from "@budibase/types"
|
|
12
|
+
import {
|
|
13
|
+
LoopStep,
|
|
14
|
+
LoopStepTypes,
|
|
15
|
+
LoopInput,
|
|
16
|
+
AutomationEvent,
|
|
17
|
+
TriggerOutput,
|
|
18
|
+
AutomationContext,
|
|
19
|
+
AutomationMetadata,
|
|
20
|
+
} from "../definitions/automations"
|
|
21
|
+
import { WorkerCallback } from "./definitions"
|
|
8
22
|
const { doInAppContext, getAppDB } = require("@budibase/backend-core/context")
|
|
9
|
-
const {
|
|
10
|
-
const {
|
|
23
|
+
const { logAlertWithInfo, logWarn } = require("@budibase/backend-core/logging")
|
|
24
|
+
const { processObject } = require("@budibase/string-templates")
|
|
11
25
|
const FILTER_STEP_ID = actions.ACTION_DEFINITIONS.FILTER.stepId
|
|
12
26
|
const LOOP_STEP_ID = actions.ACTION_DEFINITIONS.LOOP.stepId
|
|
13
|
-
|
|
14
27
|
const CRON_STEP_ID = triggerDefs.CRON.stepId
|
|
15
|
-
const STOPPED_STATUS = { success: true, status:
|
|
28
|
+
const STOPPED_STATUS = { success: true, status: AutomationStatus.STOPPED }
|
|
16
29
|
const { cloneDeep } = require("lodash/fp")
|
|
17
30
|
const env = require("../environment")
|
|
18
31
|
|
|
19
|
-
function typecastForLooping(loopStep, input) {
|
|
32
|
+
function typecastForLooping(loopStep: LoopStep, input: LoopInput) {
|
|
20
33
|
if (!input || !input.binding) {
|
|
21
34
|
return null
|
|
22
35
|
}
|
|
23
|
-
const isArray = Array.isArray(input.binding),
|
|
24
|
-
isString = typeof input.binding === "string"
|
|
25
36
|
try {
|
|
26
37
|
switch (loopStep.inputs.option) {
|
|
27
38
|
case LoopStepTypes.ARRAY:
|
|
28
|
-
if (
|
|
39
|
+
if (typeof input.binding === "string") {
|
|
29
40
|
return JSON.parse(input.binding)
|
|
30
41
|
}
|
|
31
42
|
break
|
|
32
43
|
case LoopStepTypes.STRING:
|
|
33
|
-
if (isArray) {
|
|
44
|
+
if (Array.isArray(input.binding)) {
|
|
34
45
|
return input.binding.join(",")
|
|
35
46
|
}
|
|
36
47
|
break
|
|
@@ -41,7 +52,7 @@ function typecastForLooping(loopStep, input) {
|
|
|
41
52
|
return input.binding
|
|
42
53
|
}
|
|
43
54
|
|
|
44
|
-
function getLoopIterations(loopStep, input) {
|
|
55
|
+
function getLoopIterations(loopStep: LoopStep, input: LoopInput) {
|
|
45
56
|
const binding = typecastForLooping(loopStep, input)
|
|
46
57
|
if (!loopStep || !binding) {
|
|
47
58
|
return 1
|
|
@@ -57,11 +68,24 @@ function getLoopIterations(loopStep, input) {
|
|
|
57
68
|
* inputs and handles any outputs.
|
|
58
69
|
*/
|
|
59
70
|
class Orchestrator {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
71
|
+
_chainCount: number
|
|
72
|
+
_appId: string
|
|
73
|
+
_automation: Automation
|
|
74
|
+
_emitter: any
|
|
75
|
+
_context: AutomationContext
|
|
76
|
+
_repeat?: { jobId: string; jobKey: string }
|
|
77
|
+
executionOutput: AutomationContext
|
|
78
|
+
|
|
79
|
+
constructor(automation: Automation, triggerOutput: TriggerOutput, opts: any) {
|
|
80
|
+
const metadata = triggerOutput.metadata
|
|
81
|
+
this._chainCount = metadata ? metadata.automationChainCount : 0
|
|
82
|
+
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
|
+
}
|
|
65
89
|
const triggerStepId = automation.definition.trigger.stepId
|
|
66
90
|
triggerOutput = this.cleanupTriggerOutputs(triggerStepId, triggerOutput)
|
|
67
91
|
// remove from context
|
|
@@ -79,14 +103,14 @@ class Orchestrator {
|
|
|
79
103
|
this.updateExecutionOutput(triggerId, triggerStepId, null, triggerOutput)
|
|
80
104
|
}
|
|
81
105
|
|
|
82
|
-
cleanupTriggerOutputs(stepId, triggerOutput) {
|
|
106
|
+
cleanupTriggerOutputs(stepId: string, triggerOutput: TriggerOutput) {
|
|
83
107
|
if (stepId === CRON_STEP_ID) {
|
|
84
108
|
triggerOutput.timestamp = Date.now()
|
|
85
109
|
}
|
|
86
110
|
return triggerOutput
|
|
87
111
|
}
|
|
88
112
|
|
|
89
|
-
async getStepFunctionality(stepId) {
|
|
113
|
+
async getStepFunctionality(stepId: string) {
|
|
90
114
|
let step = await actions.getAction(stepId)
|
|
91
115
|
if (step == null) {
|
|
92
116
|
throw `Cannot find automation step by name ${stepId}`
|
|
@@ -94,25 +118,107 @@ class Orchestrator {
|
|
|
94
118
|
return step
|
|
95
119
|
}
|
|
96
120
|
|
|
97
|
-
async
|
|
98
|
-
|
|
99
|
-
|
|
121
|
+
async getMetadata(): Promise<AutomationMetadata> {
|
|
122
|
+
const metadataId = generateAutomationMetadataID(this._automation._id)
|
|
123
|
+
const db = getAppDB()
|
|
124
|
+
let metadata: AutomationMetadata
|
|
125
|
+
try {
|
|
126
|
+
metadata = await db.get(metadataId)
|
|
127
|
+
} catch (err) {
|
|
128
|
+
metadata = {
|
|
129
|
+
_id: metadataId,
|
|
130
|
+
errorCount: 0,
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return metadata
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async checkIfShouldStop(metadata: AutomationMetadata): Promise<boolean> {
|
|
137
|
+
if (!metadata.errorCount || !this._repeat) {
|
|
138
|
+
return false
|
|
139
|
+
}
|
|
140
|
+
const automation = this._automation
|
|
141
|
+
const trigger = automation.definition.trigger
|
|
142
|
+
if (metadata.errorCount >= MAX_AUTOMATION_RECURRING_ERRORS) {
|
|
143
|
+
logWarn(
|
|
144
|
+
`CRON disabled due to errors - ${this._appId}/${this._automation._id}`
|
|
145
|
+
)
|
|
146
|
+
await disableCron(this._repeat?.jobId, this._repeat?.jobKey)
|
|
147
|
+
this.updateExecutionOutput(
|
|
148
|
+
trigger.id,
|
|
149
|
+
trigger.stepId,
|
|
150
|
+
{},
|
|
151
|
+
{
|
|
152
|
+
status: AutomationStatus.STOPPED_ERROR,
|
|
153
|
+
success: false,
|
|
154
|
+
}
|
|
155
|
+
)
|
|
156
|
+
await storeLog(automation, this.executionOutput)
|
|
157
|
+
return true
|
|
158
|
+
}
|
|
159
|
+
return false
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async updateMetadata(metadata: AutomationMetadata) {
|
|
163
|
+
const output = this.executionOutput,
|
|
164
|
+
automation = this._automation
|
|
165
|
+
if (!output || !isRecurring(automation)) {
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
const count = metadata.errorCount
|
|
169
|
+
const isError = isErrorInOutput(output)
|
|
170
|
+
// nothing to do in this scenario, escape
|
|
171
|
+
if (!count && !isError) {
|
|
172
|
+
return
|
|
173
|
+
}
|
|
174
|
+
if (isError) {
|
|
175
|
+
metadata.errorCount = count ? count + 1 : 1
|
|
176
|
+
} else {
|
|
177
|
+
metadata.errorCount = 0
|
|
100
178
|
}
|
|
101
179
|
const db = getAppDB()
|
|
102
|
-
|
|
103
|
-
|
|
180
|
+
try {
|
|
181
|
+
await db.put(metadata)
|
|
182
|
+
} catch (err) {
|
|
183
|
+
logAlertWithInfo(
|
|
184
|
+
"Failed to write automation metadata",
|
|
185
|
+
db.name,
|
|
186
|
+
automation._id,
|
|
187
|
+
err
|
|
188
|
+
)
|
|
189
|
+
}
|
|
104
190
|
}
|
|
105
191
|
|
|
106
|
-
updateExecutionOutput(id, stepId, inputs, outputs) {
|
|
192
|
+
updateExecutionOutput(id: string, stepId: string, inputs: any, outputs: any) {
|
|
107
193
|
const stepObj = { id, stepId, inputs, outputs }
|
|
194
|
+
// replacing trigger when disabling CRON
|
|
195
|
+
if (
|
|
196
|
+
stepId === CRON_STEP_ID &&
|
|
197
|
+
outputs.status === AutomationStatus.STOPPED_ERROR
|
|
198
|
+
) {
|
|
199
|
+
this.executionOutput.trigger = stepObj
|
|
200
|
+
this.executionOutput.steps = [stepObj]
|
|
201
|
+
return
|
|
202
|
+
}
|
|
108
203
|
// first entry is always the trigger (constructor)
|
|
109
|
-
if (
|
|
204
|
+
if (
|
|
205
|
+
this.executionOutput.steps.length === 0 ||
|
|
206
|
+
this.executionOutput.trigger.id === id
|
|
207
|
+
) {
|
|
110
208
|
this.executionOutput.trigger = stepObj
|
|
111
209
|
}
|
|
112
210
|
this.executionOutput.steps.push(stepObj)
|
|
113
211
|
}
|
|
114
212
|
|
|
115
|
-
updateContextAndOutput(
|
|
213
|
+
updateContextAndOutput(
|
|
214
|
+
loopStepNumber: number | undefined,
|
|
215
|
+
step: AutomationStep,
|
|
216
|
+
output: any,
|
|
217
|
+
result: { success: boolean; status: string }
|
|
218
|
+
) {
|
|
219
|
+
if (!loopStepNumber) {
|
|
220
|
+
throw new Error("No loop step number provided.")
|
|
221
|
+
}
|
|
116
222
|
this.executionOutput.steps.splice(loopStepNumber, 0, {
|
|
117
223
|
id: step.id,
|
|
118
224
|
stepId: step.stepId,
|
|
@@ -133,11 +239,22 @@ class Orchestrator {
|
|
|
133
239
|
async execute() {
|
|
134
240
|
let automation = this._automation
|
|
135
241
|
let stopped = false
|
|
136
|
-
let loopStep =
|
|
242
|
+
let loopStep: AutomationStep | undefined = undefined
|
|
137
243
|
|
|
138
244
|
let stepCount = 0
|
|
139
|
-
let loopStepNumber =
|
|
140
|
-
let loopSteps = []
|
|
245
|
+
let loopStepNumber: any = undefined
|
|
246
|
+
let loopSteps: LoopStep[] | undefined = []
|
|
247
|
+
let metadata
|
|
248
|
+
|
|
249
|
+
// check if this is a recurring automation,
|
|
250
|
+
if (isProdAppID(this._appId) && isRecurring(automation)) {
|
|
251
|
+
metadata = await this.getMetadata()
|
|
252
|
+
const shouldStop = await this.checkIfShouldStop(metadata)
|
|
253
|
+
if (shouldStop) {
|
|
254
|
+
return
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
141
258
|
for (let step of automation.definition.steps) {
|
|
142
259
|
stepCount++
|
|
143
260
|
let input,
|
|
@@ -151,7 +268,7 @@ class Orchestrator {
|
|
|
151
268
|
|
|
152
269
|
if (loopStep) {
|
|
153
270
|
input = await processObject(loopStep.inputs, this._context)
|
|
154
|
-
iterations = getLoopIterations(loopStep, input)
|
|
271
|
+
iterations = getLoopIterations(loopStep as LoopStep, input)
|
|
155
272
|
}
|
|
156
273
|
|
|
157
274
|
for (let index = 0; index < iterations; index++) {
|
|
@@ -166,14 +283,17 @@ class Orchestrator {
|
|
|
166
283
|
|
|
167
284
|
let tempOutput = { items: loopSteps, iterations: iterationCount }
|
|
168
285
|
try {
|
|
169
|
-
newInput.binding = typecastForLooping(
|
|
286
|
+
newInput.binding = typecastForLooping(
|
|
287
|
+
loopStep as LoopStep,
|
|
288
|
+
newInput
|
|
289
|
+
)
|
|
170
290
|
} catch (err) {
|
|
171
291
|
this.updateContextAndOutput(loopStepNumber, step, tempOutput, {
|
|
172
292
|
status: AutomationErrors.INCORRECT_TYPE,
|
|
173
293
|
success: false,
|
|
174
294
|
})
|
|
175
|
-
loopSteps =
|
|
176
|
-
loopStep =
|
|
295
|
+
loopSteps = undefined
|
|
296
|
+
loopStep = undefined
|
|
177
297
|
break
|
|
178
298
|
}
|
|
179
299
|
|
|
@@ -223,8 +343,8 @@ class Orchestrator {
|
|
|
223
343
|
status: AutomationErrors.MAX_ITERATIONS,
|
|
224
344
|
success: true,
|
|
225
345
|
})
|
|
226
|
-
loopSteps =
|
|
227
|
-
loopStep =
|
|
346
|
+
loopSteps = undefined
|
|
347
|
+
loopStep = undefined
|
|
228
348
|
break
|
|
229
349
|
}
|
|
230
350
|
|
|
@@ -232,7 +352,7 @@ class Orchestrator {
|
|
|
232
352
|
const currentItem = this._context.steps[loopStepNumber]?.currentItem
|
|
233
353
|
if (currentItem && typeof currentItem === "object") {
|
|
234
354
|
isFailure = Object.keys(currentItem).some(value => {
|
|
235
|
-
return currentItem[value] === loopStep
|
|
355
|
+
return currentItem[value] === loopStep?.inputs.failure
|
|
236
356
|
})
|
|
237
357
|
} else {
|
|
238
358
|
isFailure = currentItem && currentItem === loopStep.inputs.failure
|
|
@@ -243,8 +363,8 @@ class Orchestrator {
|
|
|
243
363
|
status: AutomationErrors.FAILURE_CONDITION,
|
|
244
364
|
success: false,
|
|
245
365
|
})
|
|
246
|
-
loopSteps =
|
|
247
|
-
loopStep =
|
|
366
|
+
loopSteps = undefined
|
|
367
|
+
loopStep = undefined
|
|
248
368
|
break
|
|
249
369
|
}
|
|
250
370
|
}
|
|
@@ -295,7 +415,7 @@ class Orchestrator {
|
|
|
295
415
|
if (loopStep) {
|
|
296
416
|
iterationCount++
|
|
297
417
|
if (index === iterations - 1) {
|
|
298
|
-
loopStep =
|
|
418
|
+
loopStep = undefined
|
|
299
419
|
this._context.steps.splice(loopStepNumber, 1)
|
|
300
420
|
break
|
|
301
421
|
}
|
|
@@ -316,22 +436,26 @@ class Orchestrator {
|
|
|
316
436
|
})
|
|
317
437
|
|
|
318
438
|
this._context.steps.splice(loopStepNumber, 0, tempOutput)
|
|
319
|
-
loopSteps =
|
|
439
|
+
loopSteps = undefined
|
|
320
440
|
}
|
|
321
441
|
}
|
|
322
442
|
|
|
323
443
|
// store the logs for the automation run
|
|
324
444
|
await storeLog(this._automation, this.executionOutput)
|
|
445
|
+
if (isProdAppID(this._appId) && isRecurring(automation) && metadata) {
|
|
446
|
+
await this.updateMetadata(metadata)
|
|
447
|
+
}
|
|
325
448
|
return this.executionOutput
|
|
326
449
|
}
|
|
327
450
|
}
|
|
328
451
|
|
|
329
|
-
|
|
452
|
+
export function execute(input: AutomationEvent, callback: WorkerCallback) {
|
|
330
453
|
const appId = input.data.event.appId
|
|
331
454
|
doInAppContext(appId, async () => {
|
|
332
455
|
const automationOrchestrator = new Orchestrator(
|
|
333
456
|
input.data.automation,
|
|
334
|
-
input.data.event
|
|
457
|
+
input.data.event,
|
|
458
|
+
input.opts
|
|
335
459
|
)
|
|
336
460
|
try {
|
|
337
461
|
const response = await automationOrchestrator.execute()
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type WorkerCallback = (error: any, response?: any) => void
|
|
2
|
+
|
|
3
|
+
export interface QueryEvent {
|
|
4
|
+
appId?: string
|
|
5
|
+
datasource: any
|
|
6
|
+
queryVerb: string
|
|
7
|
+
fields: { [key: string]: any }
|
|
8
|
+
parameters: { [key: string]: any }
|
|
9
|
+
pagination?: any
|
|
10
|
+
transformer: any
|
|
11
|
+
queryId: string
|
|
12
|
+
ctx?: any
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface QueryVariable {
|
|
16
|
+
queryId: string
|
|
17
|
+
name: string
|
|
18
|
+
}
|
package/src/threads/index.ts
CHANGED
|
@@ -18,26 +18,23 @@ function typeToFile(type: any) {
|
|
|
18
18
|
default:
|
|
19
19
|
throw "Unknown thread type"
|
|
20
20
|
}
|
|
21
|
+
// have to use require here, to make it work with worker-farm
|
|
21
22
|
return require.resolve(filename)
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export class Thread {
|
|
25
26
|
type: any
|
|
26
27
|
count: any
|
|
27
|
-
disableThreading: any
|
|
28
28
|
workers: any
|
|
29
29
|
timeoutMs: any
|
|
30
|
+
disableThreading: boolean
|
|
30
31
|
|
|
31
32
|
static workerRefs: any[] = []
|
|
32
33
|
|
|
33
34
|
constructor(type: any, opts: any = { timeoutMs: null, count: 1 }) {
|
|
34
35
|
this.type = type
|
|
35
36
|
this.count = opts.count ? opts.count : 1
|
|
36
|
-
this.disableThreading =
|
|
37
|
-
env.isTest() ||
|
|
38
|
-
env.DISABLE_THREADING ||
|
|
39
|
-
this.count === 0 ||
|
|
40
|
-
env.isInThread()
|
|
37
|
+
this.disableThreading = this.shouldDisableThreading()
|
|
41
38
|
if (!this.disableThreading) {
|
|
42
39
|
const workerOpts: any = {
|
|
43
40
|
autoStart: true,
|
|
@@ -47,33 +44,44 @@ export class Thread {
|
|
|
47
44
|
this.timeoutMs = opts.timeoutMs
|
|
48
45
|
workerOpts.maxCallTime = opts.timeoutMs
|
|
49
46
|
}
|
|
50
|
-
this.workers = workerFarm(workerOpts, typeToFile(type))
|
|
47
|
+
this.workers = workerFarm(workerOpts, typeToFile(type), ["execute"])
|
|
51
48
|
Thread.workerRefs.push(this.workers)
|
|
52
49
|
}
|
|
53
50
|
}
|
|
54
51
|
|
|
52
|
+
shouldDisableThreading(): boolean {
|
|
53
|
+
return !!(
|
|
54
|
+
env.isTest() ||
|
|
55
|
+
env.DISABLE_THREADING ||
|
|
56
|
+
this.count === 0 ||
|
|
57
|
+
env.isInThread()
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
55
61
|
run(data: any) {
|
|
62
|
+
const timeout = this.timeoutMs
|
|
56
63
|
return new Promise((resolve, reject) => {
|
|
57
|
-
|
|
64
|
+
function fire(worker: any) {
|
|
65
|
+
worker.execute(data, (err: any, response: any) => {
|
|
66
|
+
if (err && err.type === "TimeoutError") {
|
|
67
|
+
reject(
|
|
68
|
+
new Error(`Query response time exceeded ${timeout}ms timeout.`)
|
|
69
|
+
)
|
|
70
|
+
} else if (err) {
|
|
71
|
+
reject(err)
|
|
72
|
+
} else {
|
|
73
|
+
resolve(response)
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
}
|
|
58
77
|
// if in test then don't use threading
|
|
59
78
|
if (this.disableThreading) {
|
|
60
|
-
|
|
79
|
+
import(typeToFile(this.type)).then((thread: any) => {
|
|
80
|
+
fire(thread)
|
|
81
|
+
})
|
|
61
82
|
} else {
|
|
62
|
-
|
|
83
|
+
fire(this.workers)
|
|
63
84
|
}
|
|
64
|
-
fncToCall(data, (err: any, response: any) => {
|
|
65
|
-
if (err && err.type === "TimeoutError") {
|
|
66
|
-
reject(
|
|
67
|
-
new Error(
|
|
68
|
-
`Query response time exceeded ${this.timeoutMs}ms timeout.`
|
|
69
|
-
)
|
|
70
|
-
)
|
|
71
|
-
} else if (err) {
|
|
72
|
-
reject(err)
|
|
73
|
-
} else {
|
|
74
|
-
resolve(response)
|
|
75
|
-
}
|
|
76
|
-
})
|
|
77
85
|
})
|
|
78
86
|
}
|
|
79
87
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import { default as threadUtils } from "./utils"
|
|
2
2
|
threadUtils.threadSetup()
|
|
3
|
+
import { WorkerCallback, QueryEvent, QueryVariable } from "./definitions"
|
|
3
4
|
const ScriptRunner = require("../utilities/scriptRunner")
|
|
4
5
|
const { integrations } = require("../integrations")
|
|
5
6
|
const { processStringSync } = require("@budibase/string-templates")
|
|
@@ -19,7 +20,22 @@ const {
|
|
|
19
20
|
} = require("../integrations/queries/sql")
|
|
20
21
|
|
|
21
22
|
class QueryRunner {
|
|
22
|
-
|
|
23
|
+
datasource: any
|
|
24
|
+
queryVerb: string
|
|
25
|
+
queryId: string
|
|
26
|
+
fields: any
|
|
27
|
+
parameters: any
|
|
28
|
+
pagination: any
|
|
29
|
+
transformer: any
|
|
30
|
+
cachedVariables: any[]
|
|
31
|
+
ctx: any
|
|
32
|
+
queryResponse: any
|
|
33
|
+
noRecursiveQuery: boolean
|
|
34
|
+
hasRerun: boolean
|
|
35
|
+
hasRefreshedOAuth: boolean
|
|
36
|
+
hasDynamicVariables: boolean
|
|
37
|
+
|
|
38
|
+
constructor(input: QueryEvent, flags = { noRecursiveQuery: false }) {
|
|
23
39
|
this.datasource = input.datasource
|
|
24
40
|
this.queryVerb = input.queryVerb
|
|
25
41
|
this.fields = input.fields
|
|
@@ -37,9 +53,10 @@ class QueryRunner {
|
|
|
37
53
|
this.queryResponse = {}
|
|
38
54
|
this.hasRerun = false
|
|
39
55
|
this.hasRefreshedOAuth = false
|
|
56
|
+
this.hasDynamicVariables = false
|
|
40
57
|
}
|
|
41
58
|
|
|
42
|
-
async execute() {
|
|
59
|
+
async execute(): Promise<any> {
|
|
43
60
|
let { datasource, fields, queryVerb, transformer } = this
|
|
44
61
|
|
|
45
62
|
let datasourceClone = cloneDeep(datasource)
|
|
@@ -52,7 +69,7 @@ class QueryRunner {
|
|
|
52
69
|
|
|
53
70
|
if (datasourceClone.config.authConfigs) {
|
|
54
71
|
datasourceClone.config.authConfigs =
|
|
55
|
-
datasourceClone.config.authConfigs.map(config => {
|
|
72
|
+
datasourceClone.config.authConfigs.map((config: any) => {
|
|
56
73
|
return enrichQueryFields(config, this.ctx)
|
|
57
74
|
})
|
|
58
75
|
}
|
|
@@ -138,8 +155,8 @@ class QueryRunner {
|
|
|
138
155
|
}
|
|
139
156
|
|
|
140
157
|
// map into JSON if just raw primitive here
|
|
141
|
-
if (rows.find(row => typeof row !== "object")) {
|
|
142
|
-
rows = rows.map(value => ({ value }))
|
|
158
|
+
if (rows.find((row: any) => typeof row !== "object")) {
|
|
159
|
+
rows = rows.map((value: any) => ({ value }))
|
|
143
160
|
}
|
|
144
161
|
|
|
145
162
|
// get all the potential fields in the schema
|
|
@@ -152,7 +169,7 @@ class QueryRunner {
|
|
|
152
169
|
return { rows, keys, info, extra, pagination }
|
|
153
170
|
}
|
|
154
171
|
|
|
155
|
-
async runAnotherQuery(queryId, parameters) {
|
|
172
|
+
async runAnotherQuery(queryId: string, parameters: any) {
|
|
156
173
|
const db = getAppDB()
|
|
157
174
|
const query = await db.get(queryId)
|
|
158
175
|
const datasource = await db.get(query.datasourceId)
|
|
@@ -163,12 +180,13 @@ class QueryRunner {
|
|
|
163
180
|
fields: query.fields,
|
|
164
181
|
parameters,
|
|
165
182
|
transformer: query.transformer,
|
|
183
|
+
queryId,
|
|
166
184
|
},
|
|
167
185
|
{ noRecursiveQuery: true }
|
|
168
186
|
).execute()
|
|
169
187
|
}
|
|
170
188
|
|
|
171
|
-
async refreshOAuth2(ctx) {
|
|
189
|
+
async refreshOAuth2(ctx: any) {
|
|
172
190
|
const { oauth2, providerType, _id } = ctx.user
|
|
173
191
|
const { configId } = ctx.auth
|
|
174
192
|
|
|
@@ -200,7 +218,7 @@ class QueryRunner {
|
|
|
200
218
|
return resp
|
|
201
219
|
}
|
|
202
220
|
|
|
203
|
-
async getDynamicVariable(variable) {
|
|
221
|
+
async getDynamicVariable(variable: QueryVariable) {
|
|
204
222
|
let { parameters } = this
|
|
205
223
|
const queryId = variable.queryId,
|
|
206
224
|
name = variable.name
|
|
@@ -233,7 +251,7 @@ class QueryRunner {
|
|
|
233
251
|
if (!this.noRecursiveQuery) {
|
|
234
252
|
// need to see if this uses any variables
|
|
235
253
|
const stringFields = JSON.stringify(fields)
|
|
236
|
-
const foundVars = dynamicVars.filter(variable => {
|
|
254
|
+
const foundVars = dynamicVars.filter((variable: QueryVariable) => {
|
|
237
255
|
// don't allow a query to use its own dynamic variable (loop)
|
|
238
256
|
if (variable.queryId === this.queryId) {
|
|
239
257
|
return false
|
|
@@ -242,7 +260,9 @@ class QueryRunner {
|
|
|
242
260
|
const regex = new RegExp(`{{[ ]*${variable.name}[ ]*}}`)
|
|
243
261
|
return regex.test(stringFields)
|
|
244
262
|
})
|
|
245
|
-
const dynamics = foundVars.map(dynVar =>
|
|
263
|
+
const dynamics = foundVars.map((dynVar: QueryVariable) =>
|
|
264
|
+
this.getDynamicVariable(dynVar)
|
|
265
|
+
)
|
|
246
266
|
const responses = await Promise.all(dynamics)
|
|
247
267
|
for (let i = 0; i < foundVars.length; i++) {
|
|
248
268
|
const variable = foundVars[i]
|
|
@@ -264,7 +284,7 @@ class QueryRunner {
|
|
|
264
284
|
}
|
|
265
285
|
}
|
|
266
286
|
|
|
267
|
-
|
|
287
|
+
export function execute(input: QueryEvent, callback: WorkerCallback) {
|
|
268
288
|
doInAppContext(input.appId, async () => {
|
|
269
289
|
const Runner = new QueryRunner(input)
|
|
270
290
|
try {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { QueryVariable } from "./definitions"
|
|
1
2
|
const env = require("../environment")
|
|
2
3
|
const db = require("../db")
|
|
3
4
|
const redis = require("@budibase/backend-core/redis")
|
|
4
5
|
const { SEPARATOR } = require("@budibase/backend-core/db")
|
|
5
6
|
|
|
6
7
|
const VARIABLE_TTL_SECONDS = 3600
|
|
7
|
-
let client
|
|
8
|
+
let client: any
|
|
8
9
|
|
|
9
10
|
async function getClient() {
|
|
10
11
|
if (!client) {
|
|
@@ -14,10 +15,16 @@ async function getClient() {
|
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
process.on("exit", async () => {
|
|
17
|
-
if (client)
|
|
18
|
+
if (client) {
|
|
19
|
+
await client.finish()
|
|
20
|
+
}
|
|
18
21
|
})
|
|
19
22
|
|
|
20
|
-
|
|
23
|
+
function makeVariableKey(queryId: string, variable: string) {
|
|
24
|
+
return `${queryId}${SEPARATOR}${variable}`
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function threadSetup() {
|
|
21
28
|
// don't run this if not threading
|
|
22
29
|
if (env.isTest() || env.DISABLE_THREADING) {
|
|
23
30
|
return
|
|
@@ -27,16 +34,15 @@ exports.threadSetup = () => {
|
|
|
27
34
|
db.init()
|
|
28
35
|
}
|
|
29
36
|
|
|
30
|
-
function
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
exports.checkCacheForDynamicVariable = async (queryId, variable) => {
|
|
37
|
+
export async function checkCacheForDynamicVariable(
|
|
38
|
+
queryId: string,
|
|
39
|
+
variable: string
|
|
40
|
+
) {
|
|
35
41
|
const cache = await getClient()
|
|
36
42
|
return cache.get(makeVariableKey(queryId, variable))
|
|
37
43
|
}
|
|
38
44
|
|
|
39
|
-
|
|
45
|
+
export async function invalidateDynamicVariables(cachedVars: QueryVariable[]) {
|
|
40
46
|
const cache = await getClient()
|
|
41
47
|
let promises = []
|
|
42
48
|
for (let variable of cachedVars) {
|
|
@@ -47,7 +53,11 @@ exports.invalidateDynamicVariables = async cachedVars => {
|
|
|
47
53
|
await Promise.all(promises)
|
|
48
54
|
}
|
|
49
55
|
|
|
50
|
-
|
|
56
|
+
export async function storeDynamicVariable(
|
|
57
|
+
queryId: string,
|
|
58
|
+
variable: string,
|
|
59
|
+
value: any
|
|
60
|
+
) {
|
|
51
61
|
const cache = await getClient()
|
|
52
62
|
await cache.store(
|
|
53
63
|
makeVariableKey(queryId, variable),
|
|
@@ -56,7 +66,7 @@ exports.storeDynamicVariable = async (queryId, variable, value) => {
|
|
|
56
66
|
)
|
|
57
67
|
}
|
|
58
68
|
|
|
59
|
-
|
|
69
|
+
export function formatResponse(resp: any) {
|
|
60
70
|
if (typeof resp === "string") {
|
|
61
71
|
try {
|
|
62
72
|
resp = JSON.parse(resp)
|
|
@@ -67,7 +77,7 @@ exports.formatResponse = resp => {
|
|
|
67
77
|
return resp
|
|
68
78
|
}
|
|
69
79
|
|
|
70
|
-
|
|
80
|
+
export function hasExtraData(response: any) {
|
|
71
81
|
return (
|
|
72
82
|
typeof response === "object" &&
|
|
73
83
|
!Array.isArray(response) &&
|
|
@@ -76,3 +86,12 @@ exports.hasExtraData = response => {
|
|
|
76
86
|
response.info != null
|
|
77
87
|
)
|
|
78
88
|
}
|
|
89
|
+
|
|
90
|
+
export default {
|
|
91
|
+
hasExtraData,
|
|
92
|
+
formatResponse,
|
|
93
|
+
storeDynamicVariable,
|
|
94
|
+
invalidateDynamicVariables,
|
|
95
|
+
checkCacheForDynamicVariable,
|
|
96
|
+
threadSetup,
|
|
97
|
+
}
|