2022-11-25 20:57:07 +01:00
|
|
|
import * as triggers from "../../automations/triggers"
|
2024-07-23 12:07:05 +02:00
|
|
|
import { sdk as coreSdk } from "@budibase/shared-core"
|
2024-07-17 13:32:45 +02:00
|
|
|
import { DocumentType } from "../../db/utils"
|
2024-07-17 13:26:40 +02:00
|
|
|
import { updateTestHistory, removeDeprecated } from "../../automations/utils"
|
2025-01-20 18:38:38 +01:00
|
|
|
import { withTestFlag } from "../../utilities/redis"
|
2023-11-20 16:19:31 +01:00
|
|
|
import { context, cache, events, db as dbCore } from "@budibase/backend-core"
|
2023-05-26 17:06:16 +02:00
|
|
|
import { automations, features } from "@budibase/pro"
|
2023-05-09 13:10:20 +02:00
|
|
|
import {
|
2023-07-18 11:41:51 +02:00
|
|
|
App,
|
2023-05-09 13:10:20 +02:00
|
|
|
Automation,
|
|
|
|
AutomationActionStepId,
|
2024-02-14 12:44:07 +01:00
|
|
|
UserCtx,
|
2024-02-28 17:35:15 +01:00
|
|
|
DeleteAutomationResponse,
|
2024-07-19 13:35:50 +02:00
|
|
|
FetchAutomationResponse,
|
2024-11-29 18:00:24 +01:00
|
|
|
GetAutomationTriggerDefinitionsResponse,
|
|
|
|
GetAutomationStepDefinitionsResponse,
|
|
|
|
GetAutomationActionDefinitionsResponse,
|
|
|
|
FindAutomationResponse,
|
|
|
|
UpdateAutomationRequest,
|
|
|
|
UpdateAutomationResponse,
|
|
|
|
CreateAutomationRequest,
|
|
|
|
CreateAutomationResponse,
|
|
|
|
SearchAutomationLogsRequest,
|
|
|
|
SearchAutomationLogsResponse,
|
|
|
|
ClearAutomationLogRequest,
|
|
|
|
ClearAutomationLogResponse,
|
|
|
|
TriggerAutomationRequest,
|
|
|
|
TriggerAutomationResponse,
|
|
|
|
TestAutomationRequest,
|
|
|
|
TestAutomationResponse,
|
2023-05-09 13:10:20 +02:00
|
|
|
} from "@budibase/types"
|
2025-01-21 18:06:09 +01:00
|
|
|
import { getActionDefinitions as actionDefs } from "../../automations/actions"
|
2023-05-16 17:05:37 +02:00
|
|
|
import sdk from "../../sdk"
|
2023-07-10 10:30:15 +02:00
|
|
|
import { builderSocket } from "../../websockets"
|
2024-02-14 18:15:42 +01:00
|
|
|
import env from "../../environment"
|
2021-05-18 17:37:54 +02:00
|
|
|
|
2023-04-11 00:48:54 +02:00
|
|
|
async function getActionDefinitions() {
|
|
|
|
return removeDeprecated(await actionDefs())
|
|
|
|
}
|
|
|
|
|
|
|
|
function getTriggerDefinitions() {
|
|
|
|
return removeDeprecated(triggers.TRIGGER_DEFINITIONS)
|
|
|
|
}
|
2021-12-14 18:59:02 +01:00
|
|
|
|
2020-09-10 12:06:13 +02:00
|
|
|
/*************************
|
|
|
|
* *
|
|
|
|
* BUILDER FUNCTIONS *
|
|
|
|
* *
|
|
|
|
*************************/
|
2020-05-20 18:02:46 +02:00
|
|
|
|
2024-02-28 12:46:58 +01:00
|
|
|
export async function create(
|
2024-11-29 18:00:24 +01:00
|
|
|
ctx: UserCtx<CreateAutomationRequest, CreateAutomationResponse>
|
2024-02-28 12:46:58 +01:00
|
|
|
) {
|
2020-09-21 14:49:34 +02:00
|
|
|
let automation = ctx.request.body
|
2021-03-29 18:32:05 +02:00
|
|
|
automation.appId = ctx.appId
|
2020-05-20 18:02:46 +02:00
|
|
|
|
2021-03-10 12:47:39 +01:00
|
|
|
// call through to update if already exists
|
|
|
|
if (automation._id && automation._rev) {
|
2023-02-23 14:55:18 +01:00
|
|
|
await update(ctx)
|
|
|
|
return
|
2021-03-10 12:47:39 +01:00
|
|
|
}
|
|
|
|
|
2024-07-17 13:26:40 +02:00
|
|
|
const createdAutomation = await sdk.automations.create(automation)
|
2020-05-20 18:02:46 +02:00
|
|
|
|
|
|
|
ctx.body = {
|
2020-09-21 14:49:34 +02:00
|
|
|
message: "Automation created successfully",
|
2024-07-17 13:26:40 +02:00
|
|
|
automation: createdAutomation,
|
2020-05-28 21:20:03 +02:00
|
|
|
}
|
2023-07-10 10:30:15 +02:00
|
|
|
builderSocket?.emitAutomationUpdate(ctx, automation)
|
2020-05-20 18:02:46 +02:00
|
|
|
}
|
|
|
|
|
2024-11-29 18:00:24 +01:00
|
|
|
export async function update(
|
|
|
|
ctx: UserCtx<UpdateAutomationRequest, UpdateAutomationResponse>
|
|
|
|
) {
|
2020-09-21 14:49:34 +02:00
|
|
|
let automation = ctx.request.body
|
2021-03-29 18:32:05 +02:00
|
|
|
automation.appId = ctx.appId
|
2023-02-23 14:55:18 +01:00
|
|
|
|
|
|
|
// Call through to create if it doesn't exist
|
|
|
|
if (!automation._id || !automation._rev) {
|
|
|
|
await create(ctx)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-07-17 13:26:40 +02:00
|
|
|
const updatedAutomation = await sdk.automations.update(automation)
|
2022-04-06 14:54:57 +02:00
|
|
|
|
2020-05-22 17:32:23 +02:00
|
|
|
ctx.body = {
|
2020-09-21 14:49:34 +02:00
|
|
|
message: `Automation ${automation._id} updated successfully.`,
|
2024-07-17 13:26:40 +02:00
|
|
|
automation: updatedAutomation,
|
2020-05-22 17:32:23 +02:00
|
|
|
}
|
2023-07-10 10:30:15 +02:00
|
|
|
builderSocket?.emitAutomationUpdate(ctx, automation)
|
2020-05-20 18:02:46 +02:00
|
|
|
}
|
|
|
|
|
2024-07-19 13:35:50 +02:00
|
|
|
export async function fetch(ctx: UserCtx<void, FetchAutomationResponse>) {
|
2024-07-19 12:24:45 +02:00
|
|
|
const automations = await sdk.automations.fetch()
|
2024-07-19 13:35:50 +02:00
|
|
|
ctx.body = { automations }
|
2020-05-20 18:02:46 +02:00
|
|
|
}
|
|
|
|
|
2024-11-29 18:00:24 +01:00
|
|
|
export async function find(ctx: UserCtx<void, FindAutomationResponse>) {
|
2024-07-17 13:32:45 +02:00
|
|
|
ctx.body = await sdk.automations.get(ctx.params.id)
|
2020-05-20 18:02:46 +02:00
|
|
|
}
|
|
|
|
|
2024-02-28 17:35:15 +01:00
|
|
|
export async function destroy(ctx: UserCtx<void, DeleteAutomationResponse>) {
|
2021-09-13 19:03:09 +02:00
|
|
|
const automationId = ctx.params.id
|
2024-07-17 13:26:40 +02:00
|
|
|
|
2024-07-23 10:28:57 +02:00
|
|
|
const automation = await sdk.automations.get(ctx.params.id)
|
2024-07-23 12:07:05 +02:00
|
|
|
if (coreSdk.automations.isRowAction(automation)) {
|
2024-07-23 12:21:52 +02:00
|
|
|
ctx.throw("Row actions automations cannot be deleted", 422)
|
2024-07-23 10:28:57 +02:00
|
|
|
}
|
|
|
|
|
2024-07-17 13:26:40 +02:00
|
|
|
ctx.body = await sdk.automations.remove(automationId, ctx.params.rev)
|
2023-07-10 10:30:15 +02:00
|
|
|
builderSocket?.emitAutomationDeletion(ctx, automationId)
|
2020-09-10 12:06:13 +02:00
|
|
|
}
|
|
|
|
|
2024-11-29 18:00:24 +01:00
|
|
|
export async function logSearch(
|
|
|
|
ctx: UserCtx<SearchAutomationLogsRequest, SearchAutomationLogsResponse>
|
|
|
|
) {
|
2022-07-04 16:44:47 +02:00
|
|
|
ctx.body = await automations.logs.logSearch(ctx.request.body)
|
2022-06-01 17:01:06 +02:00
|
|
|
}
|
|
|
|
|
2024-11-29 18:00:24 +01:00
|
|
|
export async function clearLogError(
|
|
|
|
ctx: UserCtx<ClearAutomationLogRequest, ClearAutomationLogResponse>
|
|
|
|
) {
|
2022-06-22 21:23:18 +02:00
|
|
|
const { automationId, appId } = ctx.request.body
|
2022-11-22 17:52:25 +01:00
|
|
|
await context.doInAppContext(appId, async () => {
|
|
|
|
const db = context.getProdAppDB()
|
2023-07-18 11:41:51 +02:00
|
|
|
const metadata = await db.get<App>(DocumentType.APP_METADATA)
|
2022-06-22 21:23:18 +02:00
|
|
|
if (!automationId) {
|
|
|
|
delete metadata.automationErrors
|
|
|
|
} else if (
|
|
|
|
metadata.automationErrors &&
|
|
|
|
metadata.automationErrors[automationId]
|
|
|
|
) {
|
|
|
|
delete metadata.automationErrors[automationId]
|
|
|
|
}
|
|
|
|
await db.put(metadata)
|
2022-11-22 17:52:25 +01:00
|
|
|
await cache.app.invalidateAppMetadata(metadata.appId, metadata)
|
2022-06-22 21:23:18 +02:00
|
|
|
ctx.body = { message: `Error logs cleared.` }
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-11-29 18:00:24 +01:00
|
|
|
export async function getActionList(
|
|
|
|
ctx: UserCtx<void, GetAutomationActionDefinitionsResponse>
|
|
|
|
) {
|
2023-04-11 00:48:54 +02:00
|
|
|
ctx.body = await getActionDefinitions()
|
2020-05-26 22:34:01 +02:00
|
|
|
}
|
|
|
|
|
2024-11-29 18:00:24 +01:00
|
|
|
export async function getTriggerList(
|
|
|
|
ctx: UserCtx<void, GetAutomationTriggerDefinitionsResponse>
|
|
|
|
) {
|
2023-04-11 00:48:54 +02:00
|
|
|
ctx.body = getTriggerDefinitions()
|
2020-09-10 12:06:13 +02:00
|
|
|
}
|
|
|
|
|
2024-11-29 18:00:24 +01:00
|
|
|
export async function getDefinitionList(
|
|
|
|
ctx: UserCtx<void, GetAutomationStepDefinitionsResponse>
|
|
|
|
) {
|
2020-09-14 16:34:09 +02:00
|
|
|
ctx.body = {
|
2023-04-11 00:48:54 +02:00
|
|
|
trigger: getTriggerDefinitions(),
|
|
|
|
action: await getActionDefinitions(),
|
2020-09-14 16:34:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-10 12:06:13 +02:00
|
|
|
/*********************
|
|
|
|
* *
|
|
|
|
* API FUNCTIONS *
|
|
|
|
* *
|
|
|
|
*********************/
|
|
|
|
|
2024-11-29 18:00:24 +01:00
|
|
|
export async function trigger(
|
|
|
|
ctx: UserCtx<TriggerAutomationRequest, TriggerAutomationResponse>
|
|
|
|
) {
|
2022-11-22 17:52:25 +01:00
|
|
|
const db = context.getAppDB()
|
2023-07-18 11:41:51 +02:00
|
|
|
let automation = await db.get<Automation>(ctx.params.id)
|
2021-09-07 14:58:53 +02:00
|
|
|
|
2023-05-16 10:29:40 +02:00
|
|
|
let hasCollectStep = sdk.automations.utils.checkForCollectStep(automation)
|
2023-05-26 17:06:16 +02:00
|
|
|
if (hasCollectStep && (await features.isSyncAutomationsEnabled())) {
|
2024-05-20 16:13:08 +02:00
|
|
|
try {
|
2024-11-29 18:13:39 +01:00
|
|
|
const response = await triggers.externalTrigger(
|
2024-05-20 16:13:08 +02:00
|
|
|
automation,
|
|
|
|
{
|
|
|
|
fields: ctx.request.body.fields,
|
2024-10-22 11:52:52 +02:00
|
|
|
user: sdk.users.getUserContextBindings(ctx.user),
|
2024-05-20 16:13:08 +02:00
|
|
|
timeout:
|
|
|
|
ctx.request.body.timeout * 1000 || env.AUTOMATION_THREAD_TIMEOUT,
|
|
|
|
},
|
|
|
|
{ getResponses: true }
|
|
|
|
)
|
|
|
|
|
2024-11-29 18:13:39 +01:00
|
|
|
if (!("steps" in response)) {
|
|
|
|
ctx.throw(400, "Unable to collect response")
|
|
|
|
}
|
|
|
|
|
2024-05-20 16:13:08 +02:00
|
|
|
let collectedValue = response.steps.find(
|
|
|
|
step => step.stepId === AutomationActionStepId.COLLECT
|
|
|
|
)
|
|
|
|
ctx.body = collectedValue?.outputs
|
|
|
|
} catch (err: any) {
|
|
|
|
if (err.message) {
|
|
|
|
ctx.throw(400, err.message)
|
|
|
|
} else {
|
|
|
|
throw err
|
|
|
|
}
|
|
|
|
}
|
2023-05-12 16:56:24 +02:00
|
|
|
} else {
|
2023-05-19 16:14:01 +02:00
|
|
|
if (ctx.appId && !dbCore.isProdAppID(ctx.appId)) {
|
2023-05-19 16:12:08 +02:00
|
|
|
ctx.throw(400, "Only apps in production support this endpoint")
|
|
|
|
}
|
2023-05-12 16:56:24 +02:00
|
|
|
await triggers.externalTrigger(automation, {
|
|
|
|
...ctx.request.body,
|
|
|
|
appId: ctx.appId,
|
2024-10-22 11:52:52 +02:00
|
|
|
user: sdk.users.getUserContextBindings(ctx.user),
|
2023-05-12 16:56:24 +02:00
|
|
|
})
|
|
|
|
ctx.body = {
|
|
|
|
message: `Automation ${automation._id} has been triggered.`,
|
|
|
|
automation,
|
|
|
|
}
|
|
|
|
}
|
2023-05-09 13:10:20 +02:00
|
|
|
}
|
|
|
|
|
2024-11-29 18:00:24 +01:00
|
|
|
function prepareTestInput(input: TestAutomationRequest) {
|
2021-09-17 18:18:52 +02:00
|
|
|
// prepare the test parameters
|
|
|
|
if (input.id && input.row) {
|
|
|
|
input.row._id = input.id
|
|
|
|
}
|
|
|
|
if (input.revision && input.row) {
|
|
|
|
input.row._rev = input.revision
|
|
|
|
}
|
|
|
|
return input
|
|
|
|
}
|
|
|
|
|
2024-11-29 18:00:24 +01:00
|
|
|
export async function test(
|
|
|
|
ctx: UserCtx<TestAutomationRequest, TestAutomationResponse>
|
|
|
|
) {
|
2022-11-22 17:52:25 +01:00
|
|
|
const db = context.getAppDB()
|
2025-01-20 18:18:29 +01:00
|
|
|
const automation = await db.tryGet<Automation>(ctx.params.id)
|
|
|
|
if (!automation) {
|
|
|
|
ctx.throw(404, `Automation ${ctx.params.id} not found`)
|
|
|
|
}
|
|
|
|
|
|
|
|
const { request, appId } = ctx
|
|
|
|
const { body } = request
|
|
|
|
|
2025-01-20 18:38:38 +01:00
|
|
|
ctx.body = await withTestFlag(automation._id!, async () => {
|
|
|
|
const occurredAt = new Date().getTime()
|
|
|
|
await updateTestHistory(appId, automation, { ...body, occurredAt })
|
2025-01-20 18:18:29 +01:00
|
|
|
|
2025-01-20 18:38:38 +01:00
|
|
|
const user = sdk.users.getUserContextBindings(ctx.user)
|
|
|
|
return await triggers.externalTrigger(
|
|
|
|
automation,
|
|
|
|
{ ...prepareTestInput(body), appId, user },
|
|
|
|
{ getResponses: true }
|
|
|
|
)
|
|
|
|
})
|
2025-01-20 18:18:29 +01:00
|
|
|
|
2022-05-31 22:04:41 +02:00
|
|
|
await events.automation.tested(automation)
|
2021-09-07 14:59:58 +02:00
|
|
|
}
|