@budibase/server 2.6.19-alpha.20 → 2.6.19-alpha.22
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.9a9bace2.js → index.47cc7efc.js} +273 -272
- package/builder/assets/{index.46d94ca7.css → index.ffb6a106.css} +1 -1
- package/builder/index.html +2 -2
- package/dist/automation.js +205 -49
- package/dist/automation.js.map +4 -4
- package/dist/index.js +338 -137
- package/dist/index.js.map +4 -4
- package/dist/query.js +26 -8
- package/dist/query.js.map +4 -4
- package/package.json +8 -8
- package/src/api/controllers/automation.ts +37 -9
- package/src/api/controllers/webhook.ts +33 -9
- package/src/api/routes/automation.ts +0 -1
- package/src/api/routes/tests/{automation.spec.js → automation.spec.ts} +106 -31
- package/src/api/routes/tests/{webhook.spec.js → webhook.spec.ts} +33 -11
- package/src/automations/actions.ts +3 -0
- package/src/automations/steps/bash.ts +4 -0
- package/src/automations/steps/collect.ts +58 -0
- package/src/automations/steps/createRow.ts +4 -0
- package/src/automations/steps/delay.ts +1 -0
- package/src/automations/steps/deleteRow.ts +4 -0
- package/src/automations/steps/discord.ts +4 -0
- package/src/automations/steps/executeQuery.ts +4 -0
- package/src/automations/steps/executeScript.ts +4 -0
- package/src/automations/steps/filter.ts +1 -0
- package/src/automations/steps/loop.ts +1 -0
- package/src/automations/steps/make.ts +4 -0
- package/src/automations/steps/openai.ts +1 -0
- package/src/automations/steps/outgoingWebhook.ts +4 -0
- package/src/automations/steps/queryRows.ts +4 -0
- package/src/automations/steps/sendSmtpEmail.ts +4 -0
- package/src/automations/steps/serverLog.ts +4 -0
- package/src/automations/steps/slack.ts +4 -0
- package/src/automations/steps/updateRow.ts +4 -0
- package/src/automations/steps/zapier.ts +4 -0
- package/src/automations/triggers.ts +3 -2
- package/src/sdk/app/automations/index.ts +2 -0
- package/src/sdk/app/automations/utils.ts +7 -0
- package/src/tests/utilities/TestConfiguration.ts +4 -2
- package/src/tests/utilities/structures.ts +42 -0
- package/src/threads/automation.ts +39 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/server",
|
|
3
3
|
"email": "hi@budibase.com",
|
|
4
|
-
"version": "2.6.19-alpha.
|
|
4
|
+
"version": "2.6.19-alpha.22",
|
|
5
5
|
"description": "Budibase Web Server",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"repository": {
|
|
@@ -46,12 +46,12 @@
|
|
|
46
46
|
"license": "GPL-3.0",
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@apidevtools/swagger-parser": "10.0.3",
|
|
49
|
-
"@budibase/backend-core": "2.6.19-alpha.
|
|
50
|
-
"@budibase/client": "2.6.19-alpha.
|
|
51
|
-
"@budibase/pro": "2.6.19-alpha.
|
|
52
|
-
"@budibase/shared-core": "2.6.19-alpha.
|
|
53
|
-
"@budibase/string-templates": "2.6.19-alpha.
|
|
54
|
-
"@budibase/types": "2.6.19-alpha.
|
|
49
|
+
"@budibase/backend-core": "2.6.19-alpha.22",
|
|
50
|
+
"@budibase/client": "2.6.19-alpha.22",
|
|
51
|
+
"@budibase/pro": "2.6.19-alpha.22",
|
|
52
|
+
"@budibase/shared-core": "2.6.19-alpha.22",
|
|
53
|
+
"@budibase/string-templates": "2.6.19-alpha.22",
|
|
54
|
+
"@budibase/types": "2.6.19-alpha.22",
|
|
55
55
|
"@bull-board/api": "3.7.0",
|
|
56
56
|
"@bull-board/koa": "3.9.4",
|
|
57
57
|
"@elastic/elasticsearch": "7.10.0",
|
|
@@ -194,5 +194,5 @@
|
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
196
|
},
|
|
197
|
-
"gitHead": "
|
|
197
|
+
"gitHead": "7b7fac4bc297dbccb29b5679c1d584dfd6d999a9"
|
|
198
198
|
}
|
|
@@ -14,9 +14,16 @@ import { deleteEntityMetadata } from "../../utilities"
|
|
|
14
14
|
import { MetadataTypes } from "../../constants"
|
|
15
15
|
import { setTestFlag, clearTestFlag } from "../../utilities/redis"
|
|
16
16
|
import { context, cache, events } from "@budibase/backend-core"
|
|
17
|
-
import { automations } from "@budibase/pro"
|
|
18
|
-
import {
|
|
17
|
+
import { automations, features } from "@budibase/pro"
|
|
18
|
+
import {
|
|
19
|
+
Automation,
|
|
20
|
+
AutomationActionStepId,
|
|
21
|
+
AutomationResults,
|
|
22
|
+
BBContext,
|
|
23
|
+
} from "@budibase/types"
|
|
19
24
|
import { getActionDefinitions as actionDefs } from "../../automations/actions"
|
|
25
|
+
import sdk from "../../sdk"
|
|
26
|
+
import { db as dbCore } from "@budibase/backend-core"
|
|
20
27
|
|
|
21
28
|
async function getActionDefinitions() {
|
|
22
29
|
return removeDeprecated(await actionDefs())
|
|
@@ -257,13 +264,34 @@ export async function getDefinitionList(ctx: BBContext) {
|
|
|
257
264
|
export async function trigger(ctx: BBContext) {
|
|
258
265
|
const db = context.getAppDB()
|
|
259
266
|
let automation = await db.get(ctx.params.id)
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
+
|
|
268
|
+
let hasCollectStep = sdk.automations.utils.checkForCollectStep(automation)
|
|
269
|
+
if (hasCollectStep && (await features.isSyncAutomationsEnabled())) {
|
|
270
|
+
const response: AutomationResults = await triggers.externalTrigger(
|
|
271
|
+
automation,
|
|
272
|
+
{
|
|
273
|
+
fields: ctx.request.body.fields,
|
|
274
|
+
timeout: ctx.request.body.timeout * 1000 || 120000,
|
|
275
|
+
},
|
|
276
|
+
{ getResponses: true }
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
let collectedValue = response.steps.find(
|
|
280
|
+
step => step.stepId === AutomationActionStepId.COLLECT
|
|
281
|
+
)
|
|
282
|
+
ctx.body = collectedValue?.outputs
|
|
283
|
+
} else {
|
|
284
|
+
if (ctx.appId && !dbCore.isProdAppID(ctx.appId)) {
|
|
285
|
+
ctx.throw(400, "Only apps in production support this endpoint")
|
|
286
|
+
}
|
|
287
|
+
await triggers.externalTrigger(automation, {
|
|
288
|
+
...ctx.request.body,
|
|
289
|
+
appId: ctx.appId,
|
|
290
|
+
})
|
|
291
|
+
ctx.body = {
|
|
292
|
+
message: `Automation ${automation._id} has been triggered.`,
|
|
293
|
+
automation,
|
|
294
|
+
}
|
|
267
295
|
}
|
|
268
296
|
}
|
|
269
297
|
|
|
@@ -6,8 +6,11 @@ import {
|
|
|
6
6
|
WebhookActionType,
|
|
7
7
|
BBContext,
|
|
8
8
|
Automation,
|
|
9
|
+
AutomationActionStepId,
|
|
9
10
|
} from "@budibase/types"
|
|
10
11
|
import sdk from "../../sdk"
|
|
12
|
+
import * as pro from "@budibase/pro"
|
|
13
|
+
|
|
11
14
|
const toJsonSchema = require("to-json-schema")
|
|
12
15
|
const validate = require("jsonschema").validate
|
|
13
16
|
|
|
@@ -78,15 +81,36 @@ export async function trigger(ctx: BBContext) {
|
|
|
78
81
|
if (webhook.action.type === WebhookActionType.AUTOMATION) {
|
|
79
82
|
// trigger with both the pure request and then expand it
|
|
80
83
|
// incase the user has produced a schema to bind to
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
84
|
+
let hasCollectStep = sdk.automations.utils.checkForCollectStep(target)
|
|
85
|
+
|
|
86
|
+
if (hasCollectStep && (await pro.features.isSyncAutomationsEnabled())) {
|
|
87
|
+
const response = await triggers.externalTrigger(
|
|
88
|
+
target,
|
|
89
|
+
{
|
|
90
|
+
body: ctx.request.body,
|
|
91
|
+
...ctx.request.body,
|
|
92
|
+
appId: prodAppId,
|
|
93
|
+
},
|
|
94
|
+
{ getResponses: true }
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
let collectedValue = response.steps.find(
|
|
98
|
+
(step: any) => step.stepId === AutomationActionStepId.COLLECT
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
ctx.status = 200
|
|
102
|
+
ctx.body = collectedValue.outputs
|
|
103
|
+
} else {
|
|
104
|
+
await triggers.externalTrigger(target, {
|
|
105
|
+
body: ctx.request.body,
|
|
106
|
+
...ctx.request.body,
|
|
107
|
+
appId: prodAppId,
|
|
108
|
+
})
|
|
109
|
+
ctx.status = 200
|
|
110
|
+
ctx.body = {
|
|
111
|
+
message: "Webhook trigger fired successfully",
|
|
112
|
+
}
|
|
113
|
+
}
|
|
90
114
|
}
|
|
91
115
|
} catch (err: any) {
|
|
92
116
|
if (err.status === 404) {
|
|
@@ -1,15 +1,27 @@
|
|
|
1
|
-
|
|
1
|
+
import {
|
|
2
2
|
checkBuilderEndpoint,
|
|
3
3
|
getAllTableRows,
|
|
4
4
|
clearAllAutomations,
|
|
5
5
|
testAutomation,
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
} from "./utilities/TestFunctions"
|
|
7
|
+
import * as setup from "./utilities"
|
|
8
|
+
import {
|
|
9
|
+
TRIGGER_DEFINITIONS,
|
|
10
|
+
BUILTIN_ACTION_DEFINITIONS,
|
|
11
|
+
} from "../../../automations"
|
|
12
|
+
import { events } from "@budibase/backend-core"
|
|
13
|
+
import sdk from "../../../sdk"
|
|
14
|
+
import { Automation } from "@budibase/types"
|
|
15
|
+
import { mocks } from "@budibase/backend-core/tests"
|
|
12
16
|
|
|
17
|
+
const MAX_RETRIES = 4
|
|
18
|
+
let {
|
|
19
|
+
basicAutomation,
|
|
20
|
+
newAutomation,
|
|
21
|
+
automationTrigger,
|
|
22
|
+
automationStep,
|
|
23
|
+
collectAutomation,
|
|
24
|
+
} = setup.structures
|
|
13
25
|
|
|
14
26
|
jest.setTimeout(30000)
|
|
15
27
|
|
|
@@ -24,6 +36,7 @@ describe("/automations", () => {
|
|
|
24
36
|
})
|
|
25
37
|
|
|
26
38
|
beforeEach(() => {
|
|
39
|
+
// @ts-ignore
|
|
27
40
|
events.automation.deleted.mockClear()
|
|
28
41
|
})
|
|
29
42
|
|
|
@@ -32,7 +45,7 @@ describe("/automations", () => {
|
|
|
32
45
|
const res = await request
|
|
33
46
|
.get(`/api/automations/action/list`)
|
|
34
47
|
.set(config.defaultHeaders())
|
|
35
|
-
.expect(
|
|
48
|
+
.expect("Content-Type", /json/)
|
|
36
49
|
.expect(200)
|
|
37
50
|
|
|
38
51
|
expect(Object.keys(res.body).length).not.toEqual(0)
|
|
@@ -42,7 +55,7 @@ describe("/automations", () => {
|
|
|
42
55
|
const res = await request
|
|
43
56
|
.get(`/api/automations/trigger/list`)
|
|
44
57
|
.set(config.defaultHeaders())
|
|
45
|
-
.expect(
|
|
58
|
+
.expect("Content-Type", /json/)
|
|
46
59
|
.expect(200)
|
|
47
60
|
|
|
48
61
|
expect(Object.keys(res.body).length).not.toEqual(0)
|
|
@@ -52,14 +65,18 @@ describe("/automations", () => {
|
|
|
52
65
|
const res = await request
|
|
53
66
|
.get(`/api/automations/definitions/list`)
|
|
54
67
|
.set(config.defaultHeaders())
|
|
55
|
-
.expect(
|
|
68
|
+
.expect("Content-Type", /json/)
|
|
56
69
|
.expect(200)
|
|
57
70
|
|
|
58
71
|
let definitionsLength = Object.keys(BUILTIN_ACTION_DEFINITIONS).length
|
|
59
72
|
definitionsLength-- // OUTGOING_WEBHOOK is deprecated
|
|
60
73
|
|
|
61
|
-
expect(Object.keys(res.body.action).length).toBeGreaterThanOrEqual(
|
|
62
|
-
|
|
74
|
+
expect(Object.keys(res.body.action).length).toBeGreaterThanOrEqual(
|
|
75
|
+
definitionsLength
|
|
76
|
+
)
|
|
77
|
+
expect(Object.keys(res.body.trigger).length).toEqual(
|
|
78
|
+
Object.keys(TRIGGER_DEFINITIONS).length
|
|
79
|
+
)
|
|
63
80
|
})
|
|
64
81
|
})
|
|
65
82
|
|
|
@@ -72,7 +89,7 @@ describe("/automations", () => {
|
|
|
72
89
|
.post(`/api/automations`)
|
|
73
90
|
.set(config.defaultHeaders())
|
|
74
91
|
.send(automation)
|
|
75
|
-
.expect(
|
|
92
|
+
.expect("Content-Type", /json/)
|
|
76
93
|
.expect(200)
|
|
77
94
|
|
|
78
95
|
expect(res.body.message).toEqual("Automation created successfully")
|
|
@@ -91,7 +108,7 @@ describe("/automations", () => {
|
|
|
91
108
|
.post(`/api/automations`)
|
|
92
109
|
.set(config.defaultHeaders())
|
|
93
110
|
.send(automation)
|
|
94
|
-
.expect(
|
|
111
|
+
.expect("Content-Type", /json/)
|
|
95
112
|
.expect(200)
|
|
96
113
|
|
|
97
114
|
expect(res.body.message).toEqual("Automation created successfully")
|
|
@@ -107,7 +124,7 @@ describe("/automations", () => {
|
|
|
107
124
|
config,
|
|
108
125
|
method: "POST",
|
|
109
126
|
url: `/api/automations`,
|
|
110
|
-
body: automation
|
|
127
|
+
body: automation,
|
|
111
128
|
})
|
|
112
129
|
})
|
|
113
130
|
})
|
|
@@ -118,7 +135,7 @@ describe("/automations", () => {
|
|
|
118
135
|
const res = await request
|
|
119
136
|
.get(`/api/automations/${automation._id}`)
|
|
120
137
|
.set(config.defaultHeaders())
|
|
121
|
-
.expect(
|
|
138
|
+
.expect("Content-Type", /json/)
|
|
122
139
|
.expect(200)
|
|
123
140
|
expect(res.body._id).toEqual(automation._id)
|
|
124
141
|
expect(res.body._rev).toEqual(automation._rev)
|
|
@@ -134,8 +151,8 @@ describe("/automations", () => {
|
|
|
134
151
|
row: {
|
|
135
152
|
name: "{{trigger.row.name}}",
|
|
136
153
|
description: "{{trigger.row.description}}",
|
|
137
|
-
tableId: table._id
|
|
138
|
-
}
|
|
154
|
+
tableId: table._id,
|
|
155
|
+
},
|
|
139
156
|
}
|
|
140
157
|
automation.appId = config.appId
|
|
141
158
|
automation = await config.createAutomation(automation)
|
|
@@ -162,23 +179,68 @@ describe("/automations", () => {
|
|
|
162
179
|
})
|
|
163
180
|
})
|
|
164
181
|
|
|
165
|
-
describe("
|
|
182
|
+
describe("trigger", () => {
|
|
183
|
+
it("does not trigger an automation when not synchronous and in dev", async () => {
|
|
184
|
+
let automation = newAutomation()
|
|
185
|
+
automation = await config.createAutomation(automation)
|
|
186
|
+
const res = await request
|
|
187
|
+
.post(`/api/automations/${automation._id}/trigger`)
|
|
188
|
+
.set(config.defaultHeaders())
|
|
189
|
+
.expect("Content-Type", /json/)
|
|
190
|
+
.expect(400)
|
|
191
|
+
|
|
192
|
+
expect(res.body.message).toEqual(
|
|
193
|
+
"Only apps in production support this endpoint"
|
|
194
|
+
)
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
it("triggers a synchronous automation", async () => {
|
|
198
|
+
mocks.licenses.useSyncAutomations()
|
|
199
|
+
let automation = collectAutomation()
|
|
200
|
+
automation = await config.createAutomation(automation)
|
|
201
|
+
const res = await request
|
|
202
|
+
.post(`/api/automations/${automation._id}/trigger`)
|
|
203
|
+
.set(config.defaultHeaders())
|
|
204
|
+
.expect("Content-Type", /json/)
|
|
205
|
+
.expect(200)
|
|
206
|
+
|
|
207
|
+
expect(res.body.success).toEqual(true)
|
|
208
|
+
expect(res.body.value).toEqual([1, 2, 3])
|
|
209
|
+
})
|
|
166
210
|
|
|
167
|
-
|
|
211
|
+
it("triggers an asynchronous automation", async () => {
|
|
212
|
+
let automation = newAutomation()
|
|
213
|
+
automation = await config.createAutomation(automation)
|
|
214
|
+
await config.publish()
|
|
215
|
+
|
|
216
|
+
const res = await request
|
|
217
|
+
.post(`/api/automations/${automation._id}/trigger`)
|
|
218
|
+
.set(config.defaultHeaders({}, true))
|
|
219
|
+
.expect("Content-Type", /json/)
|
|
220
|
+
.expect(200)
|
|
221
|
+
|
|
222
|
+
expect(res.body.message).toEqual(
|
|
223
|
+
`Automation ${automation._id} has been triggered.`
|
|
224
|
+
)
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
describe("update", () => {
|
|
229
|
+
const update = async (automation: Automation) => {
|
|
168
230
|
return request
|
|
169
231
|
.put(`/api/automations`)
|
|
170
232
|
.set(config.defaultHeaders())
|
|
171
233
|
.send(automation)
|
|
172
|
-
.expect(
|
|
234
|
+
.expect("Content-Type", /json/)
|
|
173
235
|
.expect(200)
|
|
174
236
|
}
|
|
175
237
|
|
|
176
|
-
const updateWithPost = async (automation) => {
|
|
238
|
+
const updateWithPost = async (automation: Automation) => {
|
|
177
239
|
return request
|
|
178
240
|
.post(`/api/automations`)
|
|
179
241
|
.set(config.defaultHeaders())
|
|
180
242
|
.send(automation)
|
|
181
|
-
.expect(
|
|
243
|
+
.expect("Content-Type", /json/)
|
|
182
244
|
.expect(200)
|
|
183
245
|
}
|
|
184
246
|
|
|
@@ -199,7 +261,9 @@ describe("/automations", () => {
|
|
|
199
261
|
expect(automationRes._rev).not.toEqual(automation._rev)
|
|
200
262
|
// content updates
|
|
201
263
|
expect(automationRes.name).toEqual("Updated Name")
|
|
202
|
-
expect(message).toEqual(
|
|
264
|
+
expect(message).toEqual(
|
|
265
|
+
`Automation ${automation._id} updated successfully.`
|
|
266
|
+
)
|
|
203
267
|
// events
|
|
204
268
|
expect(events.automation.created).not.toBeCalled()
|
|
205
269
|
expect(events.automation.stepCreated).not.toBeCalled()
|
|
@@ -207,7 +271,6 @@ describe("/automations", () => {
|
|
|
207
271
|
expect(events.automation.triggerUpdated).not.toBeCalled()
|
|
208
272
|
})
|
|
209
273
|
|
|
210
|
-
|
|
211
274
|
it("updates a automations name using POST request", async () => {
|
|
212
275
|
let automation = newAutomation()
|
|
213
276
|
await config.createAutomation(automation)
|
|
@@ -226,7 +289,9 @@ describe("/automations", () => {
|
|
|
226
289
|
expect(automationRes._rev).not.toEqual(automation._rev)
|
|
227
290
|
// content updates
|
|
228
291
|
expect(automationRes.name).toEqual("Updated Name")
|
|
229
|
-
expect(message).toEqual(
|
|
292
|
+
expect(message).toEqual(
|
|
293
|
+
`Automation ${automation._id} updated successfully.`
|
|
294
|
+
)
|
|
230
295
|
// events
|
|
231
296
|
expect(events.automation.created).not.toBeCalled()
|
|
232
297
|
expect(events.automation.stepCreated).not.toBeCalled()
|
|
@@ -237,7 +302,9 @@ describe("/automations", () => {
|
|
|
237
302
|
it("updates an automation trigger", async () => {
|
|
238
303
|
let automation = newAutomation()
|
|
239
304
|
automation = await config.createAutomation(automation)
|
|
240
|
-
automation.definition.trigger = automationTrigger(
|
|
305
|
+
automation.definition.trigger = automationTrigger(
|
|
306
|
+
TRIGGER_DEFINITIONS.WEBHOOK
|
|
307
|
+
)
|
|
241
308
|
jest.clearAllMocks()
|
|
242
309
|
|
|
243
310
|
await update(automation)
|
|
@@ -266,7 +333,6 @@ describe("/automations", () => {
|
|
|
266
333
|
expect(events.automation.triggerUpdated).not.toBeCalled()
|
|
267
334
|
})
|
|
268
335
|
|
|
269
|
-
|
|
270
336
|
it("removes automation steps", async () => {
|
|
271
337
|
let automation = newAutomation()
|
|
272
338
|
automation.definition.steps.push(automationStep())
|
|
@@ -305,11 +371,11 @@ describe("/automations", () => {
|
|
|
305
371
|
it("return all the automations for an instance", async () => {
|
|
306
372
|
await clearAllAutomations(config)
|
|
307
373
|
const autoConfig = basicAutomation()
|
|
308
|
-
|
|
374
|
+
await config.createAutomation(autoConfig)
|
|
309
375
|
const res = await request
|
|
310
376
|
.get(`/api/automations`)
|
|
311
377
|
.set(config.defaultHeaders())
|
|
312
|
-
.expect(
|
|
378
|
+
.expect("Content-Type", /json/)
|
|
313
379
|
.expect(200)
|
|
314
380
|
|
|
315
381
|
expect(res.body[0]).toEqual(expect.objectContaining(autoConfig))
|
|
@@ -330,7 +396,7 @@ describe("/automations", () => {
|
|
|
330
396
|
const res = await request
|
|
331
397
|
.delete(`/api/automations/${automation.id}/${automation.rev}`)
|
|
332
398
|
.set(config.defaultHeaders())
|
|
333
|
-
.expect(
|
|
399
|
+
.expect("Content-Type", /json/)
|
|
334
400
|
.expect(200)
|
|
335
401
|
|
|
336
402
|
expect(res.body.id).toEqual(automation._id)
|
|
@@ -346,4 +412,13 @@ describe("/automations", () => {
|
|
|
346
412
|
})
|
|
347
413
|
})
|
|
348
414
|
})
|
|
415
|
+
|
|
416
|
+
describe("checkForCollectStep", () => {
|
|
417
|
+
it("should return true if a collect step exists in an automation", async () => {
|
|
418
|
+
let automation = collectAutomation()
|
|
419
|
+
await config.createAutomation(automation)
|
|
420
|
+
let res = await sdk.automations.utils.checkForCollectStep(automation)
|
|
421
|
+
expect(res).toEqual(true)
|
|
422
|
+
})
|
|
423
|
+
})
|
|
349
424
|
})
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { Webhook } from "@budibase/types"
|
|
2
|
+
import * as setup from "./utilities"
|
|
3
|
+
import { checkBuilderEndpoint } from "./utilities/TestFunctions"
|
|
4
|
+
import { mocks } from "@budibase/backend-core/tests"
|
|
5
|
+
const { basicWebhook, basicAutomation, collectAutomation } = setup.structures
|
|
4
6
|
|
|
5
7
|
describe("/webhooks", () => {
|
|
6
8
|
let request = setup.getRequest()
|
|
7
9
|
let config = setup.getConfig()
|
|
8
|
-
let webhook
|
|
10
|
+
let webhook: Webhook
|
|
9
11
|
|
|
10
12
|
afterAll(setup.afterAll)
|
|
11
13
|
|
|
@@ -13,10 +15,11 @@ describe("/webhooks", () => {
|
|
|
13
15
|
config.modeSelf()
|
|
14
16
|
await config.init()
|
|
15
17
|
const autoConfig = basicAutomation()
|
|
16
|
-
autoConfig.definition.trigger = {
|
|
17
|
-
|
|
18
|
-
inputs: {},
|
|
18
|
+
autoConfig.definition.trigger.schema = {
|
|
19
|
+
outputs: { properties: {} },
|
|
20
|
+
inputs: { properties: {} },
|
|
19
21
|
}
|
|
22
|
+
autoConfig.definition.trigger.inputs = {}
|
|
20
23
|
await config.createAutomation(autoConfig)
|
|
21
24
|
webhook = await config.createWebhook()
|
|
22
25
|
}
|
|
@@ -70,7 +73,7 @@ describe("/webhooks", () => {
|
|
|
70
73
|
|
|
71
74
|
describe("delete", () => {
|
|
72
75
|
beforeAll(setupTest)
|
|
73
|
-
|
|
76
|
+
|
|
74
77
|
it("should successfully delete", async () => {
|
|
75
78
|
const res = await request
|
|
76
79
|
.delete(`/api/webhooks/${webhook._id}/${webhook._rev}`)
|
|
@@ -97,7 +100,7 @@ describe("/webhooks", () => {
|
|
|
97
100
|
const res = await request
|
|
98
101
|
.post(`/api/webhooks/schema/${config.getAppId()}/${webhook._id}`)
|
|
99
102
|
.send({
|
|
100
|
-
a: 1
|
|
103
|
+
a: 1,
|
|
101
104
|
})
|
|
102
105
|
.set(config.defaultHeaders())
|
|
103
106
|
.expect("Content-Type", /json/)
|
|
@@ -112,7 +115,7 @@ describe("/webhooks", () => {
|
|
|
112
115
|
expect(fetch.body[0]).toBeDefined()
|
|
113
116
|
expect(fetch.body[0].bodySchema).toEqual({
|
|
114
117
|
properties: {
|
|
115
|
-
a: { type: "integer" }
|
|
118
|
+
a: { type: "integer" },
|
|
116
119
|
},
|
|
117
120
|
type: "object",
|
|
118
121
|
})
|
|
@@ -131,4 +134,23 @@ describe("/webhooks", () => {
|
|
|
131
134
|
expect(res.body.message).toBeDefined()
|
|
132
135
|
})
|
|
133
136
|
})
|
|
134
|
-
|
|
137
|
+
|
|
138
|
+
it("should trigger a synchronous webhook call ", async () => {
|
|
139
|
+
mocks.licenses.useSyncAutomations()
|
|
140
|
+
let automation = collectAutomation()
|
|
141
|
+
let newAutomation = await config.createAutomation(automation)
|
|
142
|
+
let syncWebhook = await config.createWebhook(
|
|
143
|
+
basicWebhook(newAutomation._id)
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
// replicate changes before checking webhook
|
|
147
|
+
await config.publish()
|
|
148
|
+
|
|
149
|
+
const res = await request
|
|
150
|
+
.post(`/api/webhooks/trigger/${config.prodAppId}/${syncWebhook._id}`)
|
|
151
|
+
.expect("Content-Type", /json/)
|
|
152
|
+
.expect(200)
|
|
153
|
+
expect(res.body.success).toEqual(true)
|
|
154
|
+
expect(res.body.value).toEqual([1, 2, 3])
|
|
155
|
+
})
|
|
156
|
+
})
|
|
@@ -14,6 +14,7 @@ import * as filter from "./steps/filter"
|
|
|
14
14
|
import * as delay from "./steps/delay"
|
|
15
15
|
import * as queryRow from "./steps/queryRows"
|
|
16
16
|
import * as loop from "./steps/loop"
|
|
17
|
+
import * as collect from "./steps/collect"
|
|
17
18
|
import env from "../environment"
|
|
18
19
|
import {
|
|
19
20
|
AutomationStepSchema,
|
|
@@ -39,6 +40,7 @@ const ACTION_IMPLS: Record<
|
|
|
39
40
|
DELAY: delay.run,
|
|
40
41
|
FILTER: filter.run,
|
|
41
42
|
QUERY_ROWS: queryRow.run,
|
|
43
|
+
COLLECT: collect.run,
|
|
42
44
|
// these used to be lowercase step IDs, maintain for backwards compat
|
|
43
45
|
discord: discord.run,
|
|
44
46
|
slack: slack.run,
|
|
@@ -59,6 +61,7 @@ export const BUILTIN_ACTION_DEFINITIONS: Record<string, AutomationStepSchema> =
|
|
|
59
61
|
FILTER: filter.definition,
|
|
60
62
|
QUERY_ROWS: queryRow.definition,
|
|
61
63
|
LOOP: loop.definition,
|
|
64
|
+
COLLECT: collect.definition,
|
|
62
65
|
// these used to be lowercase step IDs, maintain for backwards compat
|
|
63
66
|
discord: discord.definition,
|
|
64
67
|
slack: slack.definition,
|
|
@@ -5,6 +5,7 @@ import environment from "../../environment"
|
|
|
5
5
|
import {
|
|
6
6
|
AutomationActionStepId,
|
|
7
7
|
AutomationCustomIOType,
|
|
8
|
+
AutomationFeature,
|
|
8
9
|
AutomationIOType,
|
|
9
10
|
AutomationStepInput,
|
|
10
11
|
AutomationStepSchema,
|
|
@@ -18,6 +19,9 @@ export const definition: AutomationStepSchema = {
|
|
|
18
19
|
description: "Run a bash script",
|
|
19
20
|
type: AutomationStepType.ACTION,
|
|
20
21
|
internal: true,
|
|
22
|
+
features: {
|
|
23
|
+
[AutomationFeature.LOOPING]: true,
|
|
24
|
+
},
|
|
21
25
|
stepId: AutomationActionStepId.EXECUTE_BASH,
|
|
22
26
|
inputs: {},
|
|
23
27
|
schema: {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AutomationActionStepId,
|
|
3
|
+
AutomationStepSchema,
|
|
4
|
+
AutomationStepInput,
|
|
5
|
+
AutomationStepType,
|
|
6
|
+
AutomationIOType,
|
|
7
|
+
AutomationFeature,
|
|
8
|
+
} from "@budibase/types"
|
|
9
|
+
|
|
10
|
+
export const definition: AutomationStepSchema = {
|
|
11
|
+
name: "Collect Data",
|
|
12
|
+
tagline: "Collect data to be sent to design",
|
|
13
|
+
icon: "Collection",
|
|
14
|
+
description:
|
|
15
|
+
"Collects specified data so it can be provided to the design section",
|
|
16
|
+
type: AutomationStepType.ACTION,
|
|
17
|
+
internal: true,
|
|
18
|
+
features: {},
|
|
19
|
+
stepId: AutomationActionStepId.COLLECT,
|
|
20
|
+
inputs: {},
|
|
21
|
+
schema: {
|
|
22
|
+
inputs: {
|
|
23
|
+
properties: {
|
|
24
|
+
collection: {
|
|
25
|
+
type: AutomationIOType.STRING,
|
|
26
|
+
title: "What to Collect",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
required: ["collection"],
|
|
30
|
+
},
|
|
31
|
+
outputs: {
|
|
32
|
+
properties: {
|
|
33
|
+
success: {
|
|
34
|
+
type: AutomationIOType.BOOLEAN,
|
|
35
|
+
description: "Whether the action was successful",
|
|
36
|
+
},
|
|
37
|
+
value: {
|
|
38
|
+
type: AutomationIOType.STRING,
|
|
39
|
+
description: "Collected data",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
required: ["success", "value"],
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function run({ inputs }: AutomationStepInput) {
|
|
48
|
+
if (!inputs.collection) {
|
|
49
|
+
return {
|
|
50
|
+
success: false,
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
return {
|
|
54
|
+
success: true,
|
|
55
|
+
value: inputs.collection,
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -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: "Add a row to your database",
|
|
18
19
|
type: AutomationStepType.ACTION,
|
|
19
20
|
internal: true,
|
|
21
|
+
features: {
|
|
22
|
+
[AutomationFeature.LOOPING]: true,
|
|
23
|
+
},
|
|
20
24
|
stepId: AutomationActionStepId.CREATE_ROW,
|
|
21
25
|
inputs: {},
|
|
22
26
|
schema: {
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
AutomationStepType,
|
|
9
9
|
AutomationIOType,
|
|
10
10
|
AutomationCustomIOType,
|
|
11
|
+
AutomationFeature,
|
|
11
12
|
} from "@budibase/types"
|
|
12
13
|
|
|
13
14
|
export const definition: AutomationStepSchema = {
|
|
@@ -18,6 +19,9 @@ export const definition: AutomationStepSchema = {
|
|
|
18
19
|
type: AutomationStepType.ACTION,
|
|
19
20
|
stepId: AutomationActionStepId.DELETE_ROW,
|
|
20
21
|
internal: true,
|
|
22
|
+
features: {
|
|
23
|
+
[AutomationFeature.LOOPING]: true,
|
|
24
|
+
},
|
|
21
25
|
inputs: {},
|
|
22
26
|
schema: {
|
|
23
27
|
inputs: {
|