@budibase/server 2.6.19-alpha.20 → 2.6.19-alpha.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/builder/assets/{index.9a9bace2.js → index.47cc7efc.js} +273 -272
  2. package/builder/assets/{index.46d94ca7.css → index.ffb6a106.css} +1 -1
  3. package/builder/index.html +2 -2
  4. package/dist/automation.js +205 -49
  5. package/dist/automation.js.map +4 -4
  6. package/dist/index.js +338 -137
  7. package/dist/index.js.map +4 -4
  8. package/dist/query.js +26 -8
  9. package/dist/query.js.map +4 -4
  10. package/package.json +8 -8
  11. package/src/api/controllers/automation.ts +37 -9
  12. package/src/api/controllers/webhook.ts +33 -9
  13. package/src/api/routes/automation.ts +0 -1
  14. package/src/api/routes/tests/{automation.spec.js → automation.spec.ts} +106 -31
  15. package/src/api/routes/tests/{webhook.spec.js → webhook.spec.ts} +33 -11
  16. package/src/automations/actions.ts +3 -0
  17. package/src/automations/steps/bash.ts +4 -0
  18. package/src/automations/steps/collect.ts +58 -0
  19. package/src/automations/steps/createRow.ts +4 -0
  20. package/src/automations/steps/delay.ts +1 -0
  21. package/src/automations/steps/deleteRow.ts +4 -0
  22. package/src/automations/steps/discord.ts +4 -0
  23. package/src/automations/steps/executeQuery.ts +4 -0
  24. package/src/automations/steps/executeScript.ts +4 -0
  25. package/src/automations/steps/filter.ts +1 -0
  26. package/src/automations/steps/loop.ts +1 -0
  27. package/src/automations/steps/make.ts +4 -0
  28. package/src/automations/steps/openai.ts +1 -0
  29. package/src/automations/steps/outgoingWebhook.ts +4 -0
  30. package/src/automations/steps/queryRows.ts +4 -0
  31. package/src/automations/steps/sendSmtpEmail.ts +4 -0
  32. package/src/automations/steps/serverLog.ts +4 -0
  33. package/src/automations/steps/slack.ts +4 -0
  34. package/src/automations/steps/updateRow.ts +4 -0
  35. package/src/automations/steps/zapier.ts +4 -0
  36. package/src/automations/triggers.ts +3 -2
  37. package/src/sdk/app/automations/index.ts +2 -0
  38. package/src/sdk/app/automations/utils.ts +7 -0
  39. package/src/tests/utilities/TestConfiguration.ts +4 -2
  40. package/src/tests/utilities/structures.ts +42 -0
  41. package/src/threads/automation.ts +39 -0
@@ -6,6 +6,7 @@ import {
6
6
  AutomationStepInput,
7
7
  AutomationStepType,
8
8
  AutomationIOType,
9
+ AutomationFeature,
9
10
  } from "@budibase/types"
10
11
 
11
12
  const DEFAULT_USERNAME = "Budibase Automate"
@@ -19,6 +20,9 @@ export const definition: AutomationStepSchema = {
19
20
  stepId: AutomationActionStepId.discord,
20
21
  type: AutomationStepType.ACTION,
21
22
  internal: false,
23
+ features: {
24
+ [AutomationFeature.LOOPING]: true,
25
+ },
22
26
  inputs: {},
23
27
  schema: {
24
28
  inputs: {
@@ -4,6 +4,7 @@ import * as automationUtils from "../automationUtils"
4
4
  import {
5
5
  AutomationActionStepId,
6
6
  AutomationCustomIOType,
7
+ AutomationFeature,
7
8
  AutomationIOType,
8
9
  AutomationStepInput,
9
10
  AutomationStepSchema,
@@ -18,6 +19,9 @@ export const definition: AutomationStepSchema = {
18
19
  type: AutomationStepType.ACTION,
19
20
  stepId: AutomationActionStepId.EXECUTE_QUERY,
20
21
  internal: true,
22
+ features: {
23
+ [AutomationFeature.LOOPING]: true,
24
+ },
21
25
  inputs: {},
22
26
  schema: {
23
27
  inputs: {
@@ -4,6 +4,7 @@ import * as automationUtils from "../automationUtils"
4
4
  import {
5
5
  AutomationActionStepId,
6
6
  AutomationCustomIOType,
7
+ AutomationFeature,
7
8
  AutomationIOType,
8
9
  AutomationStepInput,
9
10
  AutomationStepSchema,
@@ -19,6 +20,9 @@ export const definition: AutomationStepSchema = {
19
20
  internal: true,
20
21
  stepId: AutomationActionStepId.EXECUTE_SCRIPT,
21
22
  inputs: {},
23
+ features: {
24
+ [AutomationFeature.LOOPING]: true,
25
+ },
22
26
  schema: {
23
27
  inputs: {
24
28
  properties: {
@@ -28,6 +28,7 @@ export const definition: AutomationStepSchema = {
28
28
  "Conditionally halt automations which do not meet certain conditions",
29
29
  type: AutomationStepType.LOGIC,
30
30
  internal: true,
31
+ features: {},
31
32
  stepId: AutomationActionStepId.FILTER,
32
33
  inputs: {
33
34
  condition: FilterConditions.EQUAL,
@@ -13,6 +13,7 @@ export const definition: AutomationStepSchema = {
13
13
  description: "Loop",
14
14
  stepId: AutomationActionStepId.LOOP,
15
15
  internal: true,
16
+ features: {},
16
17
  inputs: {},
17
18
  schema: {
18
19
  inputs: {
@@ -6,6 +6,7 @@ import {
6
6
  AutomationStepInput,
7
7
  AutomationStepType,
8
8
  AutomationIOType,
9
+ AutomationFeature,
9
10
  } from "@budibase/types"
10
11
 
11
12
  export const definition: AutomationStepSchema = {
@@ -18,6 +19,9 @@ export const definition: AutomationStepSchema = {
18
19
  stepId: AutomationActionStepId.integromat,
19
20
  type: AutomationStepType.ACTION,
20
21
  internal: false,
22
+ features: {
23
+ [AutomationFeature.LOOPING]: true,
24
+ },
21
25
  inputs: {},
22
26
  schema: {
23
27
  inputs: {
@@ -22,6 +22,7 @@ export const definition: AutomationStepSchema = {
22
22
  description: "Interact with the OpenAI ChatGPT API.",
23
23
  type: AutomationStepType.ACTION,
24
24
  internal: true,
25
+ features: {},
25
26
  stepId: AutomationActionStepId.OPENAI,
26
27
  inputs: {
27
28
  prompt: "",
@@ -4,6 +4,7 @@ import * as automationUtils from "../automationUtils"
4
4
  import {
5
5
  AutomationActionStepId,
6
6
  AutomationCustomIOType,
7
+ AutomationFeature,
7
8
  AutomationIOType,
8
9
  AutomationStepInput,
9
10
  AutomationStepSchema,
@@ -32,6 +33,9 @@ export const definition: AutomationStepSchema = {
32
33
  description: "Send a request of specified method to a URL",
33
34
  type: AutomationStepType.ACTION,
34
35
  internal: true,
36
+ features: {
37
+ [AutomationFeature.LOOPING]: true,
38
+ },
35
39
  stepId: AutomationActionStepId.OUTGOING_WEBHOOK,
36
40
  inputs: {
37
41
  requestMethod: "POST",
@@ -6,6 +6,7 @@ import * as automationUtils from "../automationUtils"
6
6
  import {
7
7
  AutomationActionStepId,
8
8
  AutomationCustomIOType,
9
+ AutomationFeature,
9
10
  AutomationIOType,
10
11
  AutomationStepInput,
11
12
  AutomationStepSchema,
@@ -42,6 +43,9 @@ export const definition: AutomationStepSchema = {
42
43
  type: AutomationStepType.ACTION,
43
44
  stepId: AutomationActionStepId.QUERY_ROWS,
44
45
  internal: true,
46
+ features: {
47
+ [AutomationFeature.LOOPING]: true,
48
+ },
45
49
  inputs: {},
46
50
  schema: {
47
51
  inputs: {
@@ -6,6 +6,7 @@ import {
6
6
  AutomationStepInput,
7
7
  AutomationStepType,
8
8
  AutomationIOType,
9
+ AutomationFeature,
9
10
  } from "@budibase/types"
10
11
 
11
12
  export const definition: AutomationStepSchema = {
@@ -15,6 +16,9 @@ export const definition: AutomationStepSchema = {
15
16
  name: "Send Email (SMTP)",
16
17
  type: AutomationStepType.ACTION,
17
18
  internal: true,
19
+ features: {
20
+ [AutomationFeature.LOOPING]: true,
21
+ },
18
22
  stepId: AutomationActionStepId.SEND_EMAIL_SMTP,
19
23
  inputs: {},
20
24
  schema: {
@@ -4,6 +4,7 @@ import {
4
4
  AutomationStepInput,
5
5
  AutomationStepType,
6
6
  AutomationIOType,
7
+ AutomationFeature,
7
8
  } from "@budibase/types"
8
9
 
9
10
  /**
@@ -19,6 +20,9 @@ export const definition: AutomationStepSchema = {
19
20
  description: "Logs the given text to the server (using console.log)",
20
21
  type: AutomationStepType.ACTION,
21
22
  internal: true,
23
+ features: {
24
+ [AutomationFeature.LOOPING]: true,
25
+ },
22
26
  stepId: AutomationActionStepId.SERVER_LOG,
23
27
  inputs: {
24
28
  text: "",
@@ -6,6 +6,7 @@ import {
6
6
  AutomationStepInput,
7
7
  AutomationStepType,
8
8
  AutomationIOType,
9
+ AutomationFeature,
9
10
  } from "@budibase/types"
10
11
 
11
12
  export const definition: AutomationStepSchema = {
@@ -16,6 +17,9 @@ export const definition: AutomationStepSchema = {
16
17
  stepId: AutomationActionStepId.slack,
17
18
  type: AutomationStepType.ACTION,
18
19
  internal: false,
20
+ features: {
21
+ [AutomationFeature.LOOPING]: true,
22
+ },
19
23
  inputs: {},
20
24
  schema: {
21
25
  inputs: {
@@ -4,6 +4,7 @@ import { buildCtx } from "./utils"
4
4
  import {
5
5
  AutomationActionStepId,
6
6
  AutomationCustomIOType,
7
+ AutomationFeature,
7
8
  AutomationIOType,
8
9
  AutomationStepInput,
9
10
  AutomationStepSchema,
@@ -17,6 +18,9 @@ export const definition: AutomationStepSchema = {
17
18
  description: "Update a row in your database",
18
19
  type: AutomationStepType.ACTION,
19
20
  internal: true,
21
+ features: {
22
+ [AutomationFeature.LOOPING]: true,
23
+ },
20
24
  stepId: AutomationActionStepId.UPDATE_ROW,
21
25
  inputs: {},
22
26
  schema: {
@@ -6,6 +6,7 @@ import {
6
6
  AutomationStepInput,
7
7
  AutomationStepType,
8
8
  AutomationIOType,
9
+ AutomationFeature,
9
10
  } from "@budibase/types"
10
11
 
11
12
  export const definition: AutomationStepSchema = {
@@ -13,6 +14,9 @@ export const definition: AutomationStepSchema = {
13
14
  stepId: AutomationActionStepId.zapier,
14
15
  type: AutomationStepType.ACTION,
15
16
  internal: false,
17
+ features: {
18
+ [AutomationFeature.LOOPING]: true,
19
+ },
16
20
  description: "Trigger a Zapier Zap via webhooks",
17
21
  tagline: "Trigger a Zapier Zap",
18
22
  icon: "ri-flashlight-line",
@@ -10,6 +10,7 @@ import * as utils from "./utils"
10
10
  import env from "../environment"
11
11
  import { context, db as dbCore } from "@budibase/backend-core"
12
12
  import { Automation, Row, AutomationData, AutomationJob } from "@budibase/types"
13
+ import { executeSynchronously } from "../threads/automation"
13
14
 
14
15
  export const TRIGGER_DEFINITIONS = definitions
15
16
  const JOB_OPTS = {
@@ -91,7 +92,7 @@ emitter.on("row:delete", async function (event) {
91
92
 
92
93
  export async function externalTrigger(
93
94
  automation: Automation,
94
- params: { fields: Record<string, any> },
95
+ params: { fields: Record<string, any>; timeout?: number },
95
96
  { getResponses }: { getResponses?: boolean } = {}
96
97
  ) {
97
98
  if (
@@ -118,7 +119,7 @@ export async function externalTrigger(
118
119
  automation,
119
120
  }
120
121
  const job = { data } as AutomationJob
121
- return utils.processEvent(job)
122
+ return executeSynchronously(job)
122
123
  } else {
123
124
  return automationQueue.add(data, JOB_OPTS)
124
125
  }
@@ -1,5 +1,7 @@
1
1
  import * as webhook from "./webhook"
2
+ import * as utils from "./utils"
2
3
 
3
4
  export default {
4
5
  webhook,
6
+ utils,
5
7
  }
@@ -0,0 +1,7 @@
1
+ import { Automation, AutomationActionStepId } from "@budibase/types"
2
+
3
+ export function checkForCollectStep(automation: Automation) {
4
+ return automation.definition.steps.some(
5
+ (step: any) => step.stepId === AutomationActionStepId.COLLECT
6
+ )
7
+ }
@@ -373,7 +373,7 @@ class TestConfiguration {
373
373
 
374
374
  // HEADERS
375
375
 
376
- defaultHeaders(extras = {}) {
376
+ defaultHeaders(extras = {}, prodApp = false) {
377
377
  const tenantId = this.getTenantId()
378
378
  const authObj: AuthToken = {
379
379
  userId: this.defaultUserValues.globalUserId,
@@ -390,7 +390,9 @@ class TestConfiguration {
390
390
  ...extras,
391
391
  }
392
392
 
393
- if (this.appId) {
393
+ if (prodApp) {
394
+ headers[constants.Header.APP_ID] = this.prodAppId
395
+ } else if (this.appId) {
394
396
  headers[constants.Header.APP_ID] = this.appId
395
397
  }
396
398
  return headers
@@ -199,6 +199,48 @@ export function loopAutomation(tableId: string, loopOpts?: any): Automation {
199
199
  return automation as Automation
200
200
  }
201
201
 
202
+ export function collectAutomation(tableId?: string): Automation {
203
+ const automation: any = {
204
+ name: "looping",
205
+ type: "automation",
206
+ definition: {
207
+ steps: [
208
+ {
209
+ id: "b",
210
+ type: "ACTION",
211
+ internal: true,
212
+ stepId: AutomationActionStepId.EXECUTE_SCRIPT,
213
+ inputs: {
214
+ code: "return [1,2,3]",
215
+ },
216
+ schema: BUILTIN_ACTION_DEFINITIONS.EXECUTE_SCRIPT.schema,
217
+ },
218
+ {
219
+ id: "c",
220
+ type: "ACTION",
221
+ internal: true,
222
+ stepId: AutomationActionStepId.COLLECT,
223
+ inputs: {
224
+ collection: "{{ literal steps.1.value }}",
225
+ },
226
+ schema: BUILTIN_ACTION_DEFINITIONS.SERVER_LOG.schema,
227
+ },
228
+ ],
229
+ trigger: {
230
+ id: "a",
231
+ type: "TRIGGER",
232
+ event: "row:save",
233
+ stepId: AutomationTriggerStepId.ROW_SAVED,
234
+ inputs: {
235
+ tableId,
236
+ },
237
+ schema: TRIGGER_DEFINITIONS.ROW_SAVED.schema,
238
+ },
239
+ },
240
+ }
241
+ return automation as Automation
242
+ }
243
+
202
244
  export function basicRow(tableId: string) {
203
245
  return {
204
246
  name: "Test Contact",
@@ -68,6 +68,7 @@ class Orchestrator {
68
68
  constructor(job: AutomationJob) {
69
69
  let automation = job.data.automation
70
70
  let triggerOutput = job.data.event
71
+ let timeout = job.data.event.timeout
71
72
  const metadata = triggerOutput.metadata
72
73
  this._chainCount = metadata ? metadata.automationChainCount! : 0
73
74
  this._appId = triggerOutput.appId as string
@@ -240,7 +241,9 @@ class Orchestrator {
240
241
  let loopStepNumber: any = undefined
241
242
  let loopSteps: LoopStep[] | undefined = []
242
243
  let metadata
244
+ let timeoutFlag = false
243
245
  let wasLoopStep = false
246
+ let timeout = this._job.data.event.timeout
244
247
  // check if this is a recurring automation,
245
248
  if (isProdAppID(this._appId) && isRecurring(automation)) {
246
249
  metadata = await this.getMetadata()
@@ -251,6 +254,16 @@ class Orchestrator {
251
254
  }
252
255
 
253
256
  for (let step of automation.definition.steps) {
257
+ if (timeoutFlag) {
258
+ break
259
+ }
260
+
261
+ if (timeout) {
262
+ setTimeout(() => {
263
+ timeoutFlag = true
264
+ }, timeout || 12000)
265
+ }
266
+
254
267
  stepCount++
255
268
  let input: any,
256
269
  iterations = 1,
@@ -495,6 +508,32 @@ export function execute(job: Job, callback: WorkerCallback) {
495
508
  })
496
509
  }
497
510
 
511
+ export function executeSynchronously(job: Job) {
512
+ const appId = job.data.event.appId
513
+ if (!appId) {
514
+ throw new Error("Unable to execute, event doesn't contain app ID.")
515
+ }
516
+
517
+ const timeoutPromise = new Promise((resolve, reject) => {
518
+ setTimeout(() => {
519
+ reject(new Error("Timeout exceeded"))
520
+ }, job.data.event.timeout || 12000)
521
+ })
522
+
523
+ return context.doInAppContext(appId, async () => {
524
+ const envVars = await sdkUtils.getEnvironmentVariables()
525
+ // put into automation thread for whole context
526
+ return context.doInEnvironmentContext(envVars, async () => {
527
+ const automationOrchestrator = new Orchestrator(job)
528
+ const response = await Promise.race([
529
+ automationOrchestrator.execute(),
530
+ timeoutPromise,
531
+ ])
532
+ return response
533
+ })
534
+ })
535
+ }
536
+
498
537
  export const removeStalled = async (job: Job) => {
499
538
  const appId = job.data.event.appId
500
539
  if (!appId) {