budibase/packages/server/src/api/controllers/automation.ts

256 lines
7.2 KiB
TypeScript
Raw Normal View History

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"
import { withTestFlag } from "../../utilities/redis"
2023-11-20 16:19:31 +01:00
import { context, cache, events, db as dbCore } from "@budibase/backend-core"
import { automations, features } from "@budibase/pro"
import {
2023-07-18 11:41:51 +02:00
App,
Automation,
AutomationActionStepId,
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,
} from "@budibase/types"
import { getActionDefinitions as actionDefs } from "../../automations/actions"
2023-05-16 17:05:37 +02:00
import sdk from "../../sdk"
import { builderSocket } from "../../websockets"
2024-02-14 18:15:42 +01:00
import env from "../../environment"
2021-05-18 17:37:54 +02:00
async function getActionDefinitions() {
return removeDeprecated(await actionDefs())
}
function getTriggerDefinitions() {
return removeDeprecated(triggers.TRIGGER_DEFINITIONS)
}
/*************************
* *
* 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
) {
let automation = ctx.request.body
automation.appId = ctx.appId
2020-05-20 18:02:46 +02:00
// call through to update if already exists
if (automation._id && automation._rev) {
Undo/Redo for Design and Automate sections + automations refactor (#9714) * Add full undo/redo support for screens * Add loading states to disable spamming undo/redo * Add keyboard shortcuts for undo and redo * Fix modals not closing in design section when escape is pressed * Remove log * Add smart metadata saving to undo/redo * Add error handling to undo/redo * Add active state to hoverable icons * Fix screen deletion * Always attempt to get latest doc version before deleting in case rev has changed * Move undo listener top level, hide controls when on certain tabs, and improve selection state * Add tooltips to undo/redo control * Update automation section nav to match other sections * Fix automation list padding * Fix some styles in create automation modal * Improve automation section styles and add undo/redo * Update styles in add action modal * Fix button size when creating admin user * Fix styles in add automation step modal * Fix issue selecting disabled automation steps * Reset automation history store when changing app * Reduce spammy unnecessary API calls when editing cron trigger * WIP automation refactor * Rewrite most automation state * Rewrite most of the rest of automation state * Finish refactor of automation state * Fix selection state when selecting new doc after history recreates it * Prune nullish or empty block inputs from automations and avoid sending API requests when no changes have been made * Fix animation issues with automations * Sort automations and refetch list when adding or deleting * Fix formatting * Add back in ability to swap between values and bindings for block inputs * Lint * Format * Fix potential issue in design section when selected screen is unset * Fix automation arrow directions everywhere, tidy up logic and fix crash when using invalid looping * Lint * Fix more cases of automation errors * Fix implicity any TS error * Respect _id specified when creating automations * Fix crash in history store when reverting a change on a doc whose ID has changed * Lint * Ensure cloneDeep helper doesn't crash when a nullish value is passed in * Remove deprecated frontend automation test --------- Co-authored-by: Rory Powell <rory.codes@gmail.com>
2023-02-23 14:55:18 +01:00
await update(ctx)
return
}
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 = {
message: "Automation created successfully",
2024-07-17 13:26:40 +02:00
automation: createdAutomation,
}
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>
) {
let automation = ctx.request.body
automation.appId = ctx.appId
Undo/Redo for Design and Automate sections + automations refactor (#9714) * Add full undo/redo support for screens * Add loading states to disable spamming undo/redo * Add keyboard shortcuts for undo and redo * Fix modals not closing in design section when escape is pressed * Remove log * Add smart metadata saving to undo/redo * Add error handling to undo/redo * Add active state to hoverable icons * Fix screen deletion * Always attempt to get latest doc version before deleting in case rev has changed * Move undo listener top level, hide controls when on certain tabs, and improve selection state * Add tooltips to undo/redo control * Update automation section nav to match other sections * Fix automation list padding * Fix some styles in create automation modal * Improve automation section styles and add undo/redo * Update styles in add action modal * Fix button size when creating admin user * Fix styles in add automation step modal * Fix issue selecting disabled automation steps * Reset automation history store when changing app * Reduce spammy unnecessary API calls when editing cron trigger * WIP automation refactor * Rewrite most automation state * Rewrite most of the rest of automation state * Finish refactor of automation state * Fix selection state when selecting new doc after history recreates it * Prune nullish or empty block inputs from automations and avoid sending API requests when no changes have been made * Fix animation issues with automations * Sort automations and refetch list when adding or deleting * Fix formatting * Add back in ability to swap between values and bindings for block inputs * Lint * Format * Fix potential issue in design section when selected screen is unset * Fix automation arrow directions everywhere, tidy up logic and fix crash when using invalid looping * Lint * Fix more cases of automation errors * Fix implicity any TS error * Respect _id specified when creating automations * Fix crash in history store when reverting a change on a doc whose ID has changed * Lint * Ensure cloneDeep helper doesn't crash when a nullish value is passed in * Remove deprecated frontend automation test --------- Co-authored-by: Rory Powell <rory.codes@gmail.com>
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 = {
message: `Automation ${automation._id} updated successfully.`,
2024-07-17 13:26:40 +02:00
automation: updatedAutomation,
2020-05-22 17:32:23 +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>) {
const automationId = ctx.params.id
2024-07-17 13:26:40 +02:00
const automation = await sdk.automations.get(ctx.params.id)
2024-07-23 12:07:05 +02:00
if (coreSdk.automations.isRowAction(automation)) {
ctx.throw("Row actions automations cannot be deleted", 422)
}
2024-07-17 13:26:40 +02:00
ctx.body = await sdk.automations.remove(automationId, ctx.params.rev)
builderSocket?.emitAutomationDeletion(ctx, automationId)
}
2024-11-29 18:00:24 +01:00
export async function logSearch(
ctx: UserCtx<SearchAutomationLogsRequest, SearchAutomationLogsResponse>
) {
ctx.body = await automations.logs.logSearch(ctx.request.body)
}
2024-11-29 18:00:24 +01:00
export async function clearLogError(
ctx: UserCtx<ClearAutomationLogRequest, ClearAutomationLogResponse>
) {
const { automationId, appId } = ctx.request.body
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)
if (!automationId) {
delete metadata.automationErrors
} else if (
metadata.automationErrors &&
metadata.automationErrors[automationId]
) {
delete metadata.automationErrors[automationId]
}
await db.put(metadata)
await cache.app.invalidateAppMetadata(metadata.appId, metadata)
ctx.body = { message: `Error logs cleared.` }
})
}
2024-11-29 18:00:24 +01:00
export async function getActionList(
ctx: UserCtx<void, GetAutomationActionDefinitionsResponse>
) {
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>
) {
ctx.body = getTriggerDefinitions()
}
2024-11-29 18:00:24 +01:00
export async function getDefinitionList(
ctx: UserCtx<void, GetAutomationStepDefinitionsResponse>
) {
ctx.body = {
trigger: getTriggerDefinitions(),
action: await getActionDefinitions(),
}
}
/*********************
* *
* API FUNCTIONS *
* *
*********************/
2024-11-29 18:00:24 +01:00
export async function trigger(
ctx: UserCtx<TriggerAutomationRequest, TriggerAutomationResponse>
) {
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
let hasCollectStep = sdk.automations.utils.checkForCollectStep(automation)
if (hasCollectStep && (await features.isSyncAutomationsEnabled())) {
try {
2024-11-29 18:13:39 +01:00
const response = await triggers.externalTrigger(
automation,
{
fields: ctx.request.body.fields,
user: sdk.users.getUserContextBindings(ctx.user),
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")
}
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
}
}
} else {
2023-05-19 16:14:01 +02:00
if (ctx.appId && !dbCore.isProdAppID(ctx.appId)) {
ctx.throw(400, "Only apps in production support this endpoint")
}
await triggers.externalTrigger(automation, {
...ctx.request.body,
appId: ctx.appId,
user: sdk.users.getUserContextBindings(ctx.user),
})
ctx.body = {
message: `Automation ${automation._id} has been triggered.`,
automation,
}
}
}
2024-11-29 18:00:24 +01:00
function prepareTestInput(input: TestAutomationRequest) {
// 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>
) {
const db = context.getAppDB()
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
ctx.body = await withTestFlag(automation._id!, async () => {
const occurredAt = new Date().getTime()
await updateTestHistory(appId, automation, { ...body, occurredAt })
const user = sdk.users.getUserContextBindings(ctx.user)
return await triggers.externalTrigger(
automation,
{ ...prepareTestInput(body), appId, user },
{ getResponses: true }
)
})
2022-05-31 22:04:41 +02:00
await events.automation.tested(automation)
2021-09-07 14:59:58 +02:00
}