Merge branch 'master' of github.com:Budibase/budibase into fix/helper-overlap-formulas

This commit is contained in:
mike12345567 2024-08-15 10:49:53 +01:00
commit 3ed2fbbcb4
45 changed files with 780 additions and 479 deletions

View File

@ -553,7 +553,10 @@ describe("/automations", () => {
it.each(testCases)( it.each(testCases)(
"$description", "$description",
async ({ filters, row, oldRow, expectToRun }) => { async ({ filters, row, oldRow, expectToRun }) => {
let automation = await updateRowAutomationWithFilters(config.getAppId()) let automation = await updateRowAutomationWithFilters(
config.getAppId(),
table._id!
)
automation.definition.trigger.inputs = { automation.definition.trigger.inputs = {
tableId: table._id, tableId: table._id,
filters, filters,

View File

@ -19,13 +19,12 @@ import * as collect from "./steps/collect"
import * as triggerAutomationRun from "./steps/triggerAutomationRun" import * as triggerAutomationRun from "./steps/triggerAutomationRun"
import env from "../environment" import env from "../environment"
import { import {
AutomationStepSchema,
PluginType, PluginType,
AutomationStep,
AutomationActionStepId, AutomationActionStepId,
ActionImplementations, ActionImplementations,
Hosting, Hosting,
ActionImplementation, ActionImplementation,
AutomationStepDefinition,
} from "@budibase/types" } from "@budibase/types"
import sdk from "../sdk" import sdk from "../sdk"
import { getAutomationPlugin } from "../utilities/fileSystem" import { getAutomationPlugin } from "../utilities/fileSystem"
@ -56,29 +55,31 @@ const ACTION_IMPLS: ActionImplType = {
n8n: n8n.run, n8n: n8n.run,
} }
export const BUILTIN_ACTION_DEFINITIONS: Record<string, AutomationStepSchema> = export const BUILTIN_ACTION_DEFINITIONS: Record<
{ string,
SEND_EMAIL_SMTP: sendSmtpEmail.definition, AutomationStepDefinition
CREATE_ROW: createRow.definition, > = {
UPDATE_ROW: updateRow.definition, SEND_EMAIL_SMTP: sendSmtpEmail.definition,
DELETE_ROW: deleteRow.definition, CREATE_ROW: createRow.definition,
OUTGOING_WEBHOOK: outgoingWebhook.definition, UPDATE_ROW: updateRow.definition,
EXECUTE_SCRIPT: executeScript.definition, DELETE_ROW: deleteRow.definition,
EXECUTE_QUERY: executeQuery.definition, OUTGOING_WEBHOOK: outgoingWebhook.definition,
SERVER_LOG: serverLog.definition, EXECUTE_SCRIPT: executeScript.definition,
DELAY: delay.definition, EXECUTE_QUERY: executeQuery.definition,
FILTER: filter.definition, SERVER_LOG: serverLog.definition,
QUERY_ROWS: queryRow.definition, DELAY: delay.definition,
LOOP: loop.definition, FILTER: filter.definition,
COLLECT: collect.definition, QUERY_ROWS: queryRow.definition,
TRIGGER_AUTOMATION_RUN: triggerAutomationRun.definition, LOOP: loop.definition,
// these used to be lowercase step IDs, maintain for backwards compat COLLECT: collect.definition,
discord: discord.definition, TRIGGER_AUTOMATION_RUN: triggerAutomationRun.definition,
slack: slack.definition, // these used to be lowercase step IDs, maintain for backwards compat
zapier: zapier.definition, discord: discord.definition,
integromat: make.definition, slack: slack.definition,
n8n: n8n.definition, zapier: zapier.definition,
} integromat: make.definition,
n8n: n8n.definition,
}
// don't add the bash script/definitions unless in self host // don't add the bash script/definitions unless in self host
// the fact this isn't included in any definitions means it cannot be // the fact this isn't included in any definitions means it cannot be
@ -101,7 +102,7 @@ export async function getActionDefinitions() {
if (env.SELF_HOSTED) { if (env.SELF_HOSTED) {
const plugins = await sdk.plugins.fetch(PluginType.AUTOMATION) const plugins = await sdk.plugins.fetch(PluginType.AUTOMATION)
for (let plugin of plugins) { for (let plugin of plugins) {
const schema = plugin.schema.schema as AutomationStep const schema = plugin.schema.schema as AutomationStepDefinition
actionDefinitions[schema.stepId] = { actionDefinitions[schema.stepId] = {
...schema, ...schema,
custom: true, custom: true,

View File

@ -9,8 +9,8 @@ import {
FieldType, FieldType,
Row, Row,
LoopStepType, LoopStepType,
LoopStepInputs,
} from "@budibase/types" } from "@budibase/types"
import { LoopInput } from "../definitions/automations"
import { objectStore, context } from "@budibase/backend-core" import { objectStore, context } from "@budibase/backend-core"
import * as uuid from "uuid" import * as uuid from "uuid"
import path from "path" import path from "path"
@ -31,7 +31,10 @@ import path from "path"
* @returns The inputs object which has had all the various types supported by this function converted to their * @returns The inputs object which has had all the various types supported by this function converted to their
* primitive types. * primitive types.
*/ */
export function cleanInputValues(inputs: Record<string, any>, schema?: any) { export function cleanInputValues<T extends Record<string, any>>(
inputs: any,
schema?: any
): T {
if (schema == null) { if (schema == null) {
return inputs return inputs
} }
@ -61,16 +64,18 @@ export function cleanInputValues(inputs: Record<string, any>, schema?: any) {
} }
} }
//Check if input field for Update Row should be a relationship and cast to array //Check if input field for Update Row should be a relationship and cast to array
for (let key in inputs.row) { if (inputs?.row) {
if ( for (let key in inputs.row) {
inputs.schema?.[key]?.type === "link" && if (
inputs.row[key] && inputs.schema?.[key]?.type === "link" &&
typeof inputs.row[key] === "string" inputs.row[key] &&
) { typeof inputs.row[key] === "string"
try { ) {
inputs.row[key] = JSON.parse(inputs.row[key]) try {
} catch (e) { inputs.row[key] = JSON.parse(inputs.row[key])
//Link is not an array or object, so continue } catch (e) {
//Link is not an array or object, so continue
}
} }
} }
} }
@ -267,7 +272,7 @@ export function stringSplit(value: string | string[]) {
return value.split(",") return value.split(",")
} }
export function typecastForLooping(input: LoopInput) { export function typecastForLooping(input: LoopStepInputs) {
if (!input || !input.binding) { if (!input || !input.binding) {
return null return null
} }

View File

@ -5,14 +5,15 @@ type ObjValue = {
[key: string]: string | ObjValue [key: string]: string | ObjValue
} }
export function replaceFakeBindings( export function replaceFakeBindings<T extends Record<string, any>>(
originalStepInput: Record<string, any>, originalStepInput: T,
loopStepNumber: number loopStepNumber: number
) { ): T {
const result: Record<string, any> = {}
for (const [key, value] of Object.entries(originalStepInput)) { for (const [key, value] of Object.entries(originalStepInput)) {
originalStepInput[key] = replaceBindingsRecursive(value, loopStepNumber) result[key] = replaceBindingsRecursive(value, loopStepNumber)
} }
return originalStepInput return result as T
} }
function replaceBindingsRecursive( function replaceBindingsRecursive(

View File

@ -7,13 +7,13 @@ import {
AutomationCustomIOType, AutomationCustomIOType,
AutomationFeature, AutomationFeature,
AutomationIOType, AutomationIOType,
AutomationStepSchema, AutomationStepDefinition,
AutomationStepType, AutomationStepType,
BashStepInputs, BashStepInputs,
BashStepOutputs, BashStepOutputs,
} from "@budibase/types" } from "@budibase/types"
export const definition: AutomationStepSchema = { export const definition: AutomationStepDefinition = {
name: "Bash Scripting", name: "Bash Scripting",
tagline: "Execute a bash command", tagline: "Execute a bash command",
icon: "JourneyEvent", icon: "JourneyEvent",

View File

@ -1,13 +1,13 @@
import { import {
AutomationActionStepId, AutomationActionStepId,
AutomationStepSchema, AutomationStepDefinition,
AutomationStepType, AutomationStepType,
AutomationIOType, AutomationIOType,
CollectStepInputs, CollectStepInputs,
CollectStepOutputs, CollectStepOutputs,
} from "@budibase/types" } from "@budibase/types"
export const definition: AutomationStepSchema = { export const definition: AutomationStepDefinition = {
name: "Collect Data", name: "Collect Data",
tagline: "Collect data to be sent to design", tagline: "Collect data to be sent to design",
icon: "Collection", icon: "Collection",

View File

@ -10,14 +10,14 @@ import {
AutomationCustomIOType, AutomationCustomIOType,
AutomationFeature, AutomationFeature,
AutomationIOType, AutomationIOType,
AutomationStepSchema, AutomationStepDefinition,
AutomationStepType, AutomationStepType,
CreateRowStepInputs, CreateRowStepInputs,
CreateRowStepOutputs, CreateRowStepOutputs,
} from "@budibase/types" } from "@budibase/types"
import { EventEmitter } from "events" import { EventEmitter } from "events"
export const definition: AutomationStepSchema = { export const definition: AutomationStepDefinition = {
name: "Create Row", name: "Create Row",
tagline: "Create a {{inputs.enriched.table.name}} row", tagline: "Create a {{inputs.enriched.table.name}} row",
icon: "TableRowAddBottom", icon: "TableRowAddBottom",

View File

@ -2,13 +2,13 @@ import { wait } from "../../utilities"
import { import {
AutomationActionStepId, AutomationActionStepId,
AutomationIOType, AutomationIOType,
AutomationStepSchema, AutomationStepDefinition,
AutomationStepType, AutomationStepType,
DelayStepInputs, DelayStepInputs,
DelayStepOutputs, DelayStepOutputs,
} from "@budibase/types" } from "@budibase/types"
export const definition: AutomationStepSchema = { export const definition: AutomationStepDefinition = {
name: "Delay", name: "Delay",
icon: "Clock", icon: "Clock",
tagline: "Delay for {{inputs.time}} milliseconds", tagline: "Delay for {{inputs.time}} milliseconds",

View File

@ -4,16 +4,16 @@ import { buildCtx } from "./utils"
import { getError } from "../automationUtils" import { getError } from "../automationUtils"
import { import {
AutomationActionStepId, AutomationActionStepId,
AutomationStepSchema,
AutomationStepType, AutomationStepType,
AutomationIOType, AutomationIOType,
AutomationCustomIOType, AutomationCustomIOType,
AutomationFeature, AutomationFeature,
DeleteRowStepInputs, DeleteRowStepInputs,
DeleteRowStepOutputs, DeleteRowStepOutputs,
AutomationStepDefinition,
} from "@budibase/types" } from "@budibase/types"
export const definition: AutomationStepSchema = { export const definition: AutomationStepDefinition = {
description: "Delete a row from your database", description: "Delete a row from your database",
icon: "TableRowRemoveCenter", icon: "TableRowRemoveCenter",
name: "Delete Row", name: "Delete Row",

View File

@ -2,18 +2,18 @@ import fetch from "node-fetch"
import { getFetchResponse } from "./utils" import { getFetchResponse } from "./utils"
import { import {
AutomationActionStepId, AutomationActionStepId,
AutomationStepSchema,
AutomationStepType, AutomationStepType,
AutomationIOType, AutomationIOType,
AutomationFeature, AutomationFeature,
ExternalAppStepOutputs, ExternalAppStepOutputs,
DiscordStepInputs, DiscordStepInputs,
AutomationStepDefinition,
} from "@budibase/types" } from "@budibase/types"
const DEFAULT_USERNAME = "Budibase Automate" const DEFAULT_USERNAME = "Budibase Automate"
const DEFAULT_AVATAR_URL = "https://i.imgur.com/a1cmTKM.png" const DEFAULT_AVATAR_URL = "https://i.imgur.com/a1cmTKM.png"
export const definition: AutomationStepSchema = { export const definition: AutomationStepDefinition = {
name: "Discord Message", name: "Discord Message",
tagline: "Send a message to a Discord server", tagline: "Send a message to a Discord server",
description: "Send a message to a Discord server", description: "Send a message to a Discord server",

View File

@ -7,13 +7,13 @@ import {
AutomationCustomIOType, AutomationCustomIOType,
AutomationFeature, AutomationFeature,
AutomationIOType, AutomationIOType,
AutomationStepSchema, AutomationStepDefinition,
AutomationStepType, AutomationStepType,
ExecuteQueryStepInputs, ExecuteQueryStepInputs,
ExecuteQueryStepOutputs, ExecuteQueryStepOutputs,
} from "@budibase/types" } from "@budibase/types"
export const definition: AutomationStepSchema = { export const definition: AutomationStepDefinition = {
name: "External Data Connector", name: "External Data Connector",
tagline: "Execute Data Connector", tagline: "Execute Data Connector",
icon: "Data", icon: "Data",

View File

@ -6,14 +6,14 @@ import {
AutomationCustomIOType, AutomationCustomIOType,
AutomationFeature, AutomationFeature,
AutomationIOType, AutomationIOType,
AutomationStepSchema, AutomationStepDefinition,
AutomationStepType, AutomationStepType,
ExecuteScriptStepInputs, ExecuteScriptStepInputs,
ExecuteScriptStepOutputs, ExecuteScriptStepOutputs,
} from "@budibase/types" } from "@budibase/types"
import { EventEmitter } from "events" import { EventEmitter } from "events"
export const definition: AutomationStepSchema = { export const definition: AutomationStepDefinition = {
name: "JS Scripting", name: "JS Scripting",
tagline: "Execute JavaScript Code", tagline: "Execute JavaScript Code",
icon: "Code", icon: "Code",

View File

@ -1,6 +1,6 @@
import { import {
AutomationActionStepId, AutomationActionStepId,
AutomationStepSchema, AutomationStepDefinition,
AutomationStepType, AutomationStepType,
AutomationIOType, AutomationIOType,
FilterStepInputs, FilterStepInputs,
@ -21,7 +21,7 @@ export const PrettyFilterConditions = {
[FilterConditions.LESS_THAN]: "Less than", [FilterConditions.LESS_THAN]: "Less than",
} }
export const definition: AutomationStepSchema = { export const definition: AutomationStepDefinition = {
name: "Condition", name: "Condition",
tagline: "{{inputs.field}} {{inputs.condition}} {{inputs.value}}", tagline: "{{inputs.field}} {{inputs.condition}} {{inputs.value}}",
icon: "Branch2", icon: "Branch2",

View File

@ -2,11 +2,11 @@ import {
AutomationActionStepId, AutomationActionStepId,
AutomationCustomIOType, AutomationCustomIOType,
AutomationIOType, AutomationIOType,
AutomationStepSchema, AutomationStepDefinition,
AutomationStepType, AutomationStepType,
} from "@budibase/types" } from "@budibase/types"
export const definition: AutomationStepSchema = { export const definition: AutomationStepDefinition = {
name: "Looping", name: "Looping",
icon: "Reuse", icon: "Reuse",
tagline: "Loop the block", tagline: "Loop the block",

View File

@ -2,7 +2,7 @@ import fetch from "node-fetch"
import { getFetchResponse } from "./utils" import { getFetchResponse } from "./utils"
import { import {
AutomationActionStepId, AutomationActionStepId,
AutomationStepSchema, AutomationStepDefinition,
AutomationStepType, AutomationStepType,
AutomationIOType, AutomationIOType,
AutomationFeature, AutomationFeature,
@ -10,7 +10,7 @@ import {
MakeIntegrationInputs, MakeIntegrationInputs,
} from "@budibase/types" } from "@budibase/types"
export const definition: AutomationStepSchema = { export const definition: AutomationStepDefinition = {
name: "Make Integration", name: "Make Integration",
stepTitle: "Make", stepTitle: "Make",
tagline: "Trigger a Make scenario", tagline: "Trigger a Make scenario",

View File

@ -2,7 +2,7 @@ import fetch, { HeadersInit } from "node-fetch"
import { getFetchResponse } from "./utils" import { getFetchResponse } from "./utils"
import { import {
AutomationActionStepId, AutomationActionStepId,
AutomationStepSchema, AutomationStepDefinition,
AutomationStepType, AutomationStepType,
AutomationIOType, AutomationIOType,
AutomationFeature, AutomationFeature,
@ -11,7 +11,7 @@ import {
n8nStepInputs, n8nStepInputs,
} from "@budibase/types" } from "@budibase/types"
export const definition: AutomationStepSchema = { export const definition: AutomationStepDefinition = {
name: "n8n Integration", name: "n8n Integration",
stepTitle: "n8n", stepTitle: "n8n",
tagline: "Trigger an n8n workflow", tagline: "Trigger an n8n workflow",

View File

@ -2,7 +2,7 @@ import { OpenAI } from "openai"
import { import {
AutomationActionStepId, AutomationActionStepId,
AutomationStepSchema, AutomationStepDefinition,
AutomationStepType, AutomationStepType,
AutomationIOType, AutomationIOType,
OpenAIStepInputs, OpenAIStepInputs,
@ -17,7 +17,7 @@ enum Model {
GPT_4 = "gpt-4", GPT_4 = "gpt-4",
} }
export const definition: AutomationStepSchema = { export const definition: AutomationStepDefinition = {
name: "OpenAI", name: "OpenAI",
tagline: "Send prompts to ChatGPT", tagline: "Send prompts to ChatGPT",
icon: "Algorithm", icon: "Algorithm",

View File

@ -6,7 +6,7 @@ import {
AutomationCustomIOType, AutomationCustomIOType,
AutomationFeature, AutomationFeature,
AutomationIOType, AutomationIOType,
AutomationStepSchema, AutomationStepDefinition,
AutomationStepType, AutomationStepType,
ExternalAppStepOutputs, ExternalAppStepOutputs,
OutgoingWebhookStepInputs, OutgoingWebhookStepInputs,
@ -26,7 +26,7 @@ const BODY_REQUESTS = [RequestType.POST, RequestType.PUT, RequestType.PATCH]
* NOTE: this functionality is deprecated - it no longer should be used. * NOTE: this functionality is deprecated - it no longer should be used.
*/ */
export const definition: AutomationStepSchema = { export const definition: AutomationStepDefinition = {
deprecated: true, deprecated: true,
name: "Outgoing webhook", name: "Outgoing webhook",
tagline: "Send a {{inputs.requestMethod}} request", tagline: "Send a {{inputs.requestMethod}} request",

View File

@ -8,7 +8,7 @@ import {
AutomationCustomIOType, AutomationCustomIOType,
AutomationFeature, AutomationFeature,
AutomationIOType, AutomationIOType,
AutomationStepSchema, AutomationStepDefinition,
AutomationStepType, AutomationStepType,
EmptyFilterOption, EmptyFilterOption,
SortOrder, SortOrder,
@ -21,7 +21,7 @@ const SortOrderPretty = {
[SortOrder.DESCENDING]: "Descending", [SortOrder.DESCENDING]: "Descending",
} }
export const definition: AutomationStepSchema = { export const definition: AutomationStepDefinition = {
description: "Query rows from the database", description: "Query rows from the database",
icon: "Search", icon: "Search",
name: "Query rows", name: "Query rows",

View File

@ -2,7 +2,7 @@ import { sendSmtpEmail } from "../../utilities/workerRequests"
import * as automationUtils from "../automationUtils" import * as automationUtils from "../automationUtils"
import { import {
AutomationActionStepId, AutomationActionStepId,
AutomationStepSchema, AutomationStepDefinition,
AutomationStepType, AutomationStepType,
AutomationIOType, AutomationIOType,
AutomationFeature, AutomationFeature,
@ -11,7 +11,7 @@ import {
BaseAutomationOutputs, BaseAutomationOutputs,
} from "@budibase/types" } from "@budibase/types"
export const definition: AutomationStepSchema = { export const definition: AutomationStepDefinition = {
description: "Send an email using SMTP", description: "Send an email using SMTP",
tagline: "Send SMTP email to {{inputs.to}}", tagline: "Send SMTP email to {{inputs.to}}",
icon: "Email", icon: "Email",

View File

@ -1,6 +1,6 @@
import { import {
AutomationActionStepId, AutomationActionStepId,
AutomationStepSchema, AutomationStepDefinition,
AutomationStepType, AutomationStepType,
AutomationIOType, AutomationIOType,
AutomationFeature, AutomationFeature,
@ -14,7 +14,7 @@ import {
* GET/DELETE requests cannot handle body elements so they will not be sent if configured. * GET/DELETE requests cannot handle body elements so they will not be sent if configured.
*/ */
export const definition: AutomationStepSchema = { export const definition: AutomationStepDefinition = {
name: "Backend log", name: "Backend log",
tagline: "Console log a value in the backend", tagline: "Console log a value in the backend",
icon: "Monitoring", icon: "Monitoring",

View File

@ -2,7 +2,7 @@ import fetch from "node-fetch"
import { getFetchResponse } from "./utils" import { getFetchResponse } from "./utils"
import { import {
AutomationActionStepId, AutomationActionStepId,
AutomationStepSchema, AutomationStepDefinition,
AutomationStepType, AutomationStepType,
AutomationIOType, AutomationIOType,
AutomationFeature, AutomationFeature,
@ -10,7 +10,7 @@ import {
SlackStepInputs, SlackStepInputs,
} from "@budibase/types" } from "@budibase/types"
export const definition: AutomationStepSchema = { export const definition: AutomationStepDefinition = {
name: "Slack Message", name: "Slack Message",
tagline: "Send a message to Slack", tagline: "Send a message to Slack",
description: "Send a message to Slack", description: "Send a message to Slack",

View File

@ -1,6 +1,6 @@
import { import {
AutomationActionStepId, AutomationActionStepId,
AutomationStepSchema, AutomationStepDefinition,
AutomationStepType, AutomationStepType,
AutomationIOType, AutomationIOType,
AutomationResults, AutomationResults,
@ -14,7 +14,7 @@ import { context } from "@budibase/backend-core"
import { features } from "@budibase/pro" import { features } from "@budibase/pro"
import env from "../../environment" import env from "../../environment"
export const definition: AutomationStepSchema = { export const definition: AutomationStepDefinition = {
name: "Trigger an automation", name: "Trigger an automation",
tagline: "Triggers an automation synchronously", tagline: "Triggers an automation synchronously",
icon: "Sync", icon: "Sync",

View File

@ -7,13 +7,13 @@ import {
AutomationCustomIOType, AutomationCustomIOType,
AutomationFeature, AutomationFeature,
AutomationIOType, AutomationIOType,
AutomationStepSchema, AutomationStepDefinition,
AutomationStepType, AutomationStepType,
UpdateRowStepInputs, UpdateRowStepInputs,
UpdateRowStepOutputs, UpdateRowStepOutputs,
} from "@budibase/types" } from "@budibase/types"
export const definition: AutomationStepSchema = { export const definition: AutomationStepDefinition = {
name: "Update Row", name: "Update Row",
tagline: "Update a {{inputs.enriched.table.name}} row", tagline: "Update a {{inputs.enriched.table.name}} row",
icon: "Refresh", icon: "Refresh",

View File

@ -2,7 +2,7 @@ import fetch from "node-fetch"
import { getFetchResponse } from "./utils" import { getFetchResponse } from "./utils"
import { import {
AutomationActionStepId, AutomationActionStepId,
AutomationStepSchema, AutomationStepDefinition,
AutomationStepType, AutomationStepType,
AutomationIOType, AutomationIOType,
AutomationFeature, AutomationFeature,
@ -10,7 +10,7 @@ import {
ZapierStepOutputs, ZapierStepOutputs,
} from "@budibase/types" } from "@budibase/types"
export const definition: AutomationStepSchema = { export const definition: AutomationStepDefinition = {
name: "Zapier Webhook", name: "Zapier Webhook",
stepId: AutomationActionStepId.zapier, stepId: AutomationActionStepId.zapier,
type: AutomationStepType.ACTION, type: AutomationStepType.ACTION,

View File

@ -1,23 +1,9 @@
import { v4 as uuidv4 } from "uuid" import { v4 as uuidv4 } from "uuid"
import { testAutomation } from "../../../api/routes/tests/utilities/TestFunctions" import { testAutomation } from "../../../api/routes/tests/utilities/TestFunctions"
import {
RowCreatedTriggerInputs,
RowCreatedTriggerOutputs,
} from "../../triggerInfo/rowSaved"
import {
RowUpdatedTriggerInputs,
RowUpdatedTriggerOutputs,
} from "../../triggerInfo/rowUpdated"
import {} from "../../steps/createRow" import {} from "../../steps/createRow"
import { BUILTIN_ACTION_DEFINITIONS } from "../../actions" import { BUILTIN_ACTION_DEFINITIONS } from "../../actions"
import { TRIGGER_DEFINITIONS } from "../../triggers" import { TRIGGER_DEFINITIONS } from "../../triggers"
import { import {
RowDeletedTriggerInputs,
RowDeletedTriggerOutputs,
} from "../../triggerInfo/rowDeleted"
import {
AutomationStepSchema,
AutomationTriggerSchema,
LoopStepInputs, LoopStepInputs,
DeleteRowStepInputs, DeleteRowStepInputs,
UpdateRowStepInputs, UpdateRowStepInputs,
@ -28,16 +14,26 @@ import {
SmtpEmailStepInputs, SmtpEmailStepInputs,
ExecuteQueryStepInputs, ExecuteQueryStepInputs,
QueryRowsStepInputs, QueryRowsStepInputs,
AutomationActionStepId,
AutomationTriggerStepId,
AutomationStep,
AutomationTriggerDefinition,
RowDeletedTriggerInputs,
RowDeletedTriggerOutputs,
RowUpdatedTriggerOutputs,
RowUpdatedTriggerInputs,
RowCreatedTriggerInputs,
RowCreatedTriggerOutputs,
AppActionTriggerOutputs,
CronTriggerOutputs,
AppActionTriggerInputs,
AutomationStepInputs,
AutomationTriggerInputs,
ServerLogStepInputs, ServerLogStepInputs,
} from "@budibase/types" } from "@budibase/types"
import {} from "../../steps/loop" import {} from "../../steps/loop"
import TestConfiguration from "../../../tests/utilities/TestConfiguration" import TestConfiguration from "../../../tests/utilities/TestConfiguration"
import * as setup from "../utilities" import * as setup from "../utilities"
import {
AppActionTriggerInputs,
AppActionTriggerOutputs,
} from "../../triggerInfo/app"
import { CronTriggerOutputs } from "../../triggerInfo/cron"
type TriggerOutputs = type TriggerOutputs =
| RowCreatedTriggerOutputs | RowCreatedTriggerOutputs
@ -68,7 +64,12 @@ class AutomationBuilder {
// TRIGGERS // TRIGGERS
rowSaved(inputs: RowCreatedTriggerInputs, outputs: RowCreatedTriggerOutputs) { rowSaved(inputs: RowCreatedTriggerInputs, outputs: RowCreatedTriggerOutputs) {
this.triggerOutputs = outputs this.triggerOutputs = outputs
return this.trigger(TRIGGER_DEFINITIONS.ROW_SAVED, inputs, outputs) return this.trigger(
TRIGGER_DEFINITIONS.ROW_SAVED,
AutomationTriggerStepId.ROW_SAVED,
inputs,
outputs
)
} }
rowUpdated( rowUpdated(
@ -76,7 +77,12 @@ class AutomationBuilder {
outputs: RowUpdatedTriggerOutputs outputs: RowUpdatedTriggerOutputs
) { ) {
this.triggerOutputs = outputs this.triggerOutputs = outputs
return this.trigger(TRIGGER_DEFINITIONS.ROW_UPDATED, inputs, outputs) return this.trigger(
TRIGGER_DEFINITIONS.ROW_UPDATED,
AutomationTriggerStepId.ROW_UPDATED,
inputs,
outputs
)
} }
rowDeleted( rowDeleted(
@ -84,57 +90,102 @@ class AutomationBuilder {
outputs: RowDeletedTriggerOutputs outputs: RowDeletedTriggerOutputs
) { ) {
this.triggerOutputs = outputs this.triggerOutputs = outputs
return this.trigger(TRIGGER_DEFINITIONS.ROW_DELETED, inputs, outputs) return this.trigger(
TRIGGER_DEFINITIONS.ROW_DELETED,
AutomationTriggerStepId.ROW_DELETED,
inputs,
outputs
)
} }
appAction(outputs: AppActionTriggerOutputs, inputs?: AppActionTriggerInputs) { appAction(outputs: AppActionTriggerOutputs, inputs?: AppActionTriggerInputs) {
this.triggerOutputs = outputs this.triggerOutputs = outputs
return this.trigger(TRIGGER_DEFINITIONS.APP, inputs, outputs) return this.trigger(
TRIGGER_DEFINITIONS.APP,
AutomationTriggerStepId.APP,
inputs,
outputs
)
} }
// STEPS // STEPS
createRow(inputs: CreateRowStepInputs): this { createRow(inputs: CreateRowStepInputs): this {
return this.step(BUILTIN_ACTION_DEFINITIONS.CREATE_ROW, inputs) return this.step(
AutomationActionStepId.CREATE_ROW,
BUILTIN_ACTION_DEFINITIONS.CREATE_ROW,
inputs
)
} }
updateRow(inputs: UpdateRowStepInputs): this { updateRow(inputs: UpdateRowStepInputs): this {
return this.step(BUILTIN_ACTION_DEFINITIONS.UPDATE_ROW, inputs) return this.step(
AutomationActionStepId.UPDATE_ROW,
BUILTIN_ACTION_DEFINITIONS.UPDATE_ROW,
inputs
)
} }
deleteRow(inputs: DeleteRowStepInputs): this { deleteRow(inputs: DeleteRowStepInputs): this {
return this.step(BUILTIN_ACTION_DEFINITIONS.DELETE_ROW, inputs) return this.step(
AutomationActionStepId.DELETE_ROW,
BUILTIN_ACTION_DEFINITIONS.DELETE_ROW,
inputs
)
} }
sendSmtpEmail(inputs: SmtpEmailStepInputs): this { sendSmtpEmail(inputs: SmtpEmailStepInputs): this {
return this.step(BUILTIN_ACTION_DEFINITIONS.SEND_EMAIL_SMTP, inputs) return this.step(
AutomationActionStepId.SEND_EMAIL_SMTP,
BUILTIN_ACTION_DEFINITIONS.SEND_EMAIL_SMTP,
inputs
)
} }
executeQuery(inputs: ExecuteQueryStepInputs): this { executeQuery(inputs: ExecuteQueryStepInputs): this {
return this.step(BUILTIN_ACTION_DEFINITIONS.EXECUTE_QUERY, inputs) return this.step(
AutomationActionStepId.EXECUTE_QUERY,
BUILTIN_ACTION_DEFINITIONS.EXECUTE_QUERY,
inputs
)
} }
queryRows(inputs: QueryRowsStepInputs): this { queryRows(inputs: QueryRowsStepInputs): this {
return this.step(BUILTIN_ACTION_DEFINITIONS.QUERY_ROWS, inputs) return this.step(
AutomationActionStepId.QUERY_ROWS,
BUILTIN_ACTION_DEFINITIONS.QUERY_ROWS,
inputs
)
} }
loop(inputs: LoopStepInputs): this { loop(inputs: LoopStepInputs): this {
return this.step(BUILTIN_ACTION_DEFINITIONS.LOOP, inputs) return this.step(
AutomationActionStepId.LOOP,
BUILTIN_ACTION_DEFINITIONS.LOOP,
inputs
)
} }
serverLog(input: ServerLogStepInputs): this { serverLog(input: ServerLogStepInputs): this {
return this.step(BUILTIN_ACTION_DEFINITIONS.SERVER_LOG, input) return this.step(
AutomationActionStepId.SERVER_LOG,
BUILTIN_ACTION_DEFINITIONS.SERVER_LOG,
input
)
} }
private trigger<T extends { [key: string]: any }>( private trigger<TStep extends AutomationTriggerStepId>(
triggerSchema: AutomationTriggerSchema, triggerSchema: AutomationTriggerDefinition,
inputs?: T, stepId: TStep,
inputs?: AutomationTriggerInputs<TStep>,
outputs?: TriggerOutputs outputs?: TriggerOutputs
): this { ): this {
if (this.triggerSet) { if (this.triggerSet) {
throw new Error("Only one trigger can be set for an automation.") throw new Error("Only one trigger can be set for an automation.")
} }
this.automationConfig.definition.trigger = { this.automationConfig.definition.trigger = {
...triggerSchema, ...triggerSchema,
inputs: inputs || {}, stepId,
inputs: inputs || ({} as any),
id: uuidv4(), id: uuidv4(),
} }
this.triggerOutputs = outputs this.triggerOutputs = outputs
@ -143,14 +194,16 @@ class AutomationBuilder {
return this return this
} }
private step<T extends { [key: string]: any }>( private step<TStep extends AutomationActionStepId>(
stepSchema: AutomationStepSchema, stepId: TStep,
inputs: T stepSchema: Omit<AutomationStep, "id" | "stepId" | "inputs">,
inputs: AutomationStepInputs<TStep>
): this { ): this {
this.automationConfig.definition.steps.push({ this.automationConfig.definition.steps.push({
...stepSchema, ...stepSchema,
inputs, inputs: inputs as any,
id: uuidv4(), id: uuidv4(),
stepId,
}) })
return this return this
} }

View File

@ -2,12 +2,12 @@ import {
AutomationCustomIOType, AutomationCustomIOType,
AutomationIOType, AutomationIOType,
AutomationStepType, AutomationStepType,
AutomationTriggerSchema,
AutomationTriggerStepId, AutomationTriggerStepId,
AutomationEventType, AutomationEventType,
AutomationTriggerDefinition,
} from "@budibase/types" } from "@budibase/types"
export const definition: AutomationTriggerSchema = { export const definition: AutomationTriggerDefinition = {
name: "App Action", name: "App Action",
event: AutomationEventType.APP_TRIGGER, event: AutomationEventType.APP_TRIGGER,
icon: "Apps", icon: "Apps",
@ -39,11 +39,3 @@ export const definition: AutomationTriggerSchema = {
}, },
type: AutomationStepType.TRIGGER, type: AutomationStepType.TRIGGER,
} }
export type AppActionTriggerInputs = {
fields: object
}
export type AppActionTriggerOutputs = {
fields: object
}

View File

@ -2,12 +2,12 @@ import {
AutomationCustomIOType, AutomationCustomIOType,
AutomationIOType, AutomationIOType,
AutomationStepType, AutomationStepType,
AutomationTriggerSchema, AutomationTriggerDefinition,
AutomationTriggerStepId, AutomationTriggerStepId,
AutomationEventType, AutomationEventType,
} from "@budibase/types" } from "@budibase/types"
export const definition: AutomationTriggerSchema = { export const definition: AutomationTriggerDefinition = {
name: "Cron Trigger", name: "Cron Trigger",
event: AutomationEventType.CRON_TRIGGER, event: AutomationEventType.CRON_TRIGGER,
icon: "Clock", icon: "Clock",
@ -38,11 +38,3 @@ export const definition: AutomationTriggerSchema = {
}, },
type: AutomationStepType.TRIGGER, type: AutomationStepType.TRIGGER,
} }
export type CronTriggerInputs = {
cron: string
}
export type CronTriggerOutputs = {
timestamp: number
}

View File

@ -1,5 +1,5 @@
import { import {
AutomationTriggerSchema, AutomationTriggerDefinition,
AutomationTriggerStepId, AutomationTriggerStepId,
} from "@budibase/types" } from "@budibase/types"
import * as app from "./app" import * as app from "./app"
@ -12,7 +12,7 @@ import * as rowAction from "./rowAction"
export const definitions: Record< export const definitions: Record<
keyof typeof AutomationTriggerStepId, keyof typeof AutomationTriggerStepId,
AutomationTriggerSchema AutomationTriggerDefinition
> = { > = {
ROW_SAVED: rowSaved.definition, ROW_SAVED: rowSaved.definition,
ROW_UPDATED: rowUpdated.definition, ROW_UPDATED: rowUpdated.definition,

View File

@ -2,12 +2,12 @@ import {
AutomationCustomIOType, AutomationCustomIOType,
AutomationIOType, AutomationIOType,
AutomationStepType, AutomationStepType,
AutomationTriggerSchema,
AutomationTriggerStepId, AutomationTriggerStepId,
AutomationEventType, AutomationEventType,
AutomationTriggerDefinition,
} from "@budibase/types" } from "@budibase/types"
export const definition: AutomationTriggerSchema = { export const definition: AutomationTriggerDefinition = {
type: AutomationStepType.TRIGGER, type: AutomationStepType.TRIGGER,
name: "Row Action", name: "Row Action",
event: AutomationEventType.ROW_ACTION, // TODO event: AutomationEventType.ROW_ACTION, // TODO

View File

@ -2,13 +2,12 @@ import {
AutomationCustomIOType, AutomationCustomIOType,
AutomationIOType, AutomationIOType,
AutomationStepType, AutomationStepType,
AutomationTriggerSchema, AutomationTriggerDefinition,
AutomationTriggerStepId, AutomationTriggerStepId,
AutomationEventType, AutomationEventType,
Row,
} from "@budibase/types" } from "@budibase/types"
export const definition: AutomationTriggerSchema = { export const definition: AutomationTriggerDefinition = {
name: "Row Deleted", name: "Row Deleted",
event: AutomationEventType.ROW_DELETE, event: AutomationEventType.ROW_DELETE,
icon: "TableRowRemoveCenter", icon: "TableRowRemoveCenter",
@ -40,11 +39,3 @@ export const definition: AutomationTriggerSchema = {
}, },
type: AutomationStepType.TRIGGER, type: AutomationStepType.TRIGGER,
} }
export type RowDeletedTriggerInputs = {
tableId: string
}
export type RowDeletedTriggerOutputs = {
row: Row
}

View File

@ -2,14 +2,12 @@ import {
AutomationCustomIOType, AutomationCustomIOType,
AutomationIOType, AutomationIOType,
AutomationStepType, AutomationStepType,
AutomationTriggerSchema,
AutomationTriggerStepId, AutomationTriggerStepId,
AutomationEventType, AutomationEventType,
Row, AutomationTriggerDefinition,
} from "@budibase/types" } from "@budibase/types"
import { SearchFilters } from "aws-sdk/clients/elasticbeanstalk"
export const definition: AutomationTriggerSchema = { export const definition: AutomationTriggerDefinition = {
name: "Row Created", name: "Row Created",
event: AutomationEventType.ROW_SAVE, event: AutomationEventType.ROW_SAVE,
icon: "TableRowAddBottom", icon: "TableRowAddBottom",
@ -54,14 +52,3 @@ export const definition: AutomationTriggerSchema = {
}, },
type: AutomationStepType.TRIGGER, type: AutomationStepType.TRIGGER,
} }
export type RowCreatedTriggerInputs = {
tableId: string
filters?: SearchFilters
}
export type RowCreatedTriggerOutputs = {
row: Row
id: string
revision: string
}

View File

@ -2,14 +2,12 @@ import {
AutomationCustomIOType, AutomationCustomIOType,
AutomationIOType, AutomationIOType,
AutomationStepType, AutomationStepType,
AutomationTriggerSchema, AutomationTriggerDefinition,
AutomationTriggerStepId, AutomationTriggerStepId,
AutomationEventType, AutomationEventType,
Row,
SearchFilters,
} from "@budibase/types" } from "@budibase/types"
export const definition: AutomationTriggerSchema = { export const definition: AutomationTriggerDefinition = {
name: "Row Updated", name: "Row Updated",
event: AutomationEventType.ROW_UPDATE, event: AutomationEventType.ROW_UPDATE,
icon: "Refresh", icon: "Refresh",
@ -61,14 +59,3 @@ export const definition: AutomationTriggerSchema = {
}, },
type: AutomationStepType.TRIGGER, type: AutomationStepType.TRIGGER,
} }
export type RowUpdatedTriggerInputs = {
tableId: string
filters?: SearchFilters
}
export type RowUpdatedTriggerOutputs = {
row: Row
id: string
revision?: string
}

View File

@ -2,12 +2,12 @@ import {
AutomationCustomIOType, AutomationCustomIOType,
AutomationIOType, AutomationIOType,
AutomationStepType, AutomationStepType,
AutomationTriggerSchema,
AutomationTriggerStepId, AutomationTriggerStepId,
AutomationEventType, AutomationEventType,
AutomationTriggerDefinition,
} from "@budibase/types" } from "@budibase/types"
export const definition: AutomationTriggerSchema = { export const definition: AutomationTriggerDefinition = {
name: "Webhook", name: "Webhook",
event: AutomationEventType.WEBHOOK_TRIGGER, event: AutomationEventType.WEBHOOK_TRIGGER,
icon: "Send", icon: "Send",

View File

@ -54,9 +54,9 @@ async function queueRelevantRowAutomations(
return trigger && trigger.event === eventType && !automation.disabled return trigger && trigger.event === eventType && !automation.disabled
}) })
for (let automation of automations) { for (const automation of automations) {
let automationDef = automation.definition const automationDef = automation.definition
let automationTrigger = automationDef?.trigger const automationTrigger = automationDef?.trigger
// don't queue events which are for dev apps, only way to test automations is // don't queue events which are for dev apps, only way to test automations is
// running tests on them, in production the test flag will never // running tests on them, in production the test flag will never
// be checked due to lazy evaluation (first always false) // be checked due to lazy evaluation (first always false)

View File

@ -44,7 +44,9 @@ describe("automationUtils", () => {
}) })
it("should handle null values", () => { it("should handle null values", () => {
// expect it to handle where the binding is null // expect it to handle where the binding is null
expect(typecastForLooping({ option: LoopStepType.ARRAY })).toEqual(null) expect(
typecastForLooping({ option: LoopStepType.ARRAY, binding: null })
).toEqual(null)
expect(() => expect(() =>
typecastForLooping({ option: LoopStepType.ARRAY, binding: "test" }) typecastForLooping({ option: LoopStepType.ARRAY, binding: "test" })
).toThrow() ).toThrow()

View File

@ -10,7 +10,8 @@ import { quotas } from "@budibase/pro"
import { import {
Automation, Automation,
AutomationJob, AutomationJob,
AutomationStepSchema, AutomationStepDefinition,
AutomationTriggerDefinition,
} from "@budibase/types" } from "@budibase/types"
import { automationsEnabled } from "../features" import { automationsEnabled } from "../features"
import { helpers, REBOOT_CRON } from "@budibase/shared-core" import { helpers, REBOOT_CRON } from "@budibase/shared-core"
@ -116,7 +117,10 @@ export async function updateTestHistory(
} }
export function removeDeprecated( export function removeDeprecated(
definitions: Record<string, AutomationStepSchema> definitions: Record<
string,
AutomationStepDefinition | AutomationTriggerDefinition
>
) { ) {
const base = cloneDeep(definitions) const base = cloneDeep(definitions)
for (let key of Object.keys(base)) { for (let key of Object.keys(base)) {

View File

@ -1,12 +1,4 @@
import { import { AutomationResults, LoopStepType } from "@budibase/types"
AutomationResults,
AutomationStep,
LoopStepType,
} from "@budibase/types"
export interface LoopStep extends AutomationStep {
inputs: LoopInput
}
export interface LoopInput { export interface LoopInput {
option: LoopStepType option: LoopStepType

View File

@ -1,6 +1,7 @@
import { context, HTTPError, utils } from "@budibase/backend-core" import { context, HTTPError, utils } from "@budibase/backend-core"
import { import {
AutomationTriggerStepId,
SEPARATOR, SEPARATOR,
TableRowActions, TableRowActions,
VirtualDocumentType, VirtualDocumentType,
@ -61,6 +62,7 @@ export async function create(tableId: string, rowAction: { name: string }) {
trigger: { trigger: {
id: "trigger", id: "trigger",
...TRIGGER_DEFINITIONS.ROW_ACTION, ...TRIGGER_DEFINITIONS.ROW_ACTION,
stepId: AutomationTriggerStepId.ROW_ACTION,
inputs: { inputs: {
tableId, tableId,
rowActionId: newRowActionId, rowActionId: newRowActionId,

View File

@ -151,6 +151,8 @@ export function automationStep(
return { return {
id: utils.newid(), id: utils.newid(),
...actionDefinition, ...actionDefinition,
stepId: AutomationActionStepId.CREATE_ROW,
inputs: { row: {} },
} }
} }
@ -160,7 +162,7 @@ export function automationTrigger(
return { return {
id: utils.newid(), id: utils.newid(),
...triggerDefinition, ...triggerDefinition,
} } as AutomationTrigger
} }
export function newAutomation({ export function newAutomation({
@ -209,7 +211,9 @@ export function basicAutomation(appId?: string): Automation {
description: "test", description: "test",
type: AutomationStepType.TRIGGER, type: AutomationStepType.TRIGGER,
id: "test", id: "test",
inputs: {}, inputs: {
fields: {},
},
schema: { schema: {
inputs: { inputs: {
properties: {}, properties: {},
@ -241,7 +245,7 @@ export function serverLogAutomation(appId?: string): Automation {
description: "test", description: "test",
type: AutomationStepType.TRIGGER, type: AutomationStepType.TRIGGER,
id: "test", id: "test",
inputs: {}, inputs: { fields: {} },
schema: { schema: {
inputs: { inputs: {
properties: {}, properties: {},
@ -394,7 +398,7 @@ export function filterAutomation(appId: string, tableId?: string): Automation {
type: AutomationStepType.ACTION, type: AutomationStepType.ACTION,
internal: true, internal: true,
stepId: AutomationActionStepId.FILTER, stepId: AutomationActionStepId.FILTER,
inputs: {}, inputs: { field: "name", value: "test", condition: "EQ" },
schema: BUILTIN_ACTION_DEFINITIONS.EXECUTE_SCRIPT.schema, schema: BUILTIN_ACTION_DEFINITIONS.EXECUTE_SCRIPT.schema,
}, },
], ],
@ -408,7 +412,7 @@ export function filterAutomation(appId: string, tableId?: string): Automation {
event: "row:save", event: "row:save",
stepId: AutomationTriggerStepId.ROW_SAVED, stepId: AutomationTriggerStepId.ROW_SAVED,
inputs: { inputs: {
tableId, tableId: tableId!,
}, },
schema: TRIGGER_DEFINITIONS.ROW_SAVED.schema, schema: TRIGGER_DEFINITIONS.ROW_SAVED.schema,
}, },
@ -417,7 +421,10 @@ export function filterAutomation(appId: string, tableId?: string): Automation {
return automation return automation
} }
export function updateRowAutomationWithFilters(appId: string): Automation { export function updateRowAutomationWithFilters(
appId: string,
tableId: string
): Automation {
const automation: Automation = { const automation: Automation = {
name: "updateRowWithFilters", name: "updateRowWithFilters",
type: "automation", type: "automation",
@ -433,7 +440,7 @@ export function updateRowAutomationWithFilters(appId: string): Automation {
type: AutomationStepType.ACTION, type: AutomationStepType.ACTION,
internal: true, internal: true,
stepId: AutomationActionStepId.SERVER_LOG, stepId: AutomationActionStepId.SERVER_LOG,
inputs: {}, inputs: { text: "log statement" },
schema: BUILTIN_ACTION_DEFINITIONS.SERVER_LOG.schema, schema: BUILTIN_ACTION_DEFINITIONS.SERVER_LOG.schema,
}, },
], ],
@ -442,12 +449,11 @@ export function updateRowAutomationWithFilters(appId: string): Automation {
tagline: "An automation trigger", tagline: "An automation trigger",
description: "A trigger", description: "A trigger",
icon: "Icon", icon: "Icon",
id: "a", id: "a",
type: AutomationStepType.TRIGGER, type: AutomationStepType.TRIGGER,
event: "row:update", event: "row:update",
stepId: AutomationTriggerStepId.ROW_UPDATED, stepId: AutomationTriggerStepId.ROW_UPDATED,
inputs: {}, inputs: { tableId },
schema: TRIGGER_DEFINITIONS.ROW_UPDATED.schema, schema: TRIGGER_DEFINITIONS.ROW_UPDATED.schema,
}, },
}, },

View File

@ -23,13 +23,9 @@ import {
AutomationStatus, AutomationStatus,
AutomationStep, AutomationStep,
AutomationStepStatus, AutomationStepStatus,
} from "@budibase/types"
import {
AutomationContext,
LoopInput,
LoopStep, LoopStep,
TriggerOutput, } from "@budibase/types"
} from "../definitions/automations" import { AutomationContext, TriggerOutput } from "../definitions/automations"
import { WorkerCallback } from "./definitions" import { WorkerCallback } from "./definitions"
import { context, logging } from "@budibase/backend-core" import { context, logging } from "@budibase/backend-core"
import { processObject } from "@budibase/string-templates" import { processObject } from "@budibase/string-templates"
@ -40,8 +36,6 @@ import env from "../environment"
import tracer from "dd-trace" import tracer from "dd-trace"
threadUtils.threadSetup() threadUtils.threadSetup()
const FILTER_STEP_ID = actions.BUILTIN_ACTION_DEFINITIONS.FILTER.stepId
const LOOP_STEP_ID = actions.BUILTIN_ACTION_DEFINITIONS.LOOP.stepId
const CRON_STEP_ID = triggerDefs.CRON.stepId const CRON_STEP_ID = triggerDefs.CRON.stepId
const STOPPED_STATUS = { success: true, status: AutomationStatus.STOPPED } const STOPPED_STATUS = { success: true, status: AutomationStatus.STOPPED }
@ -256,7 +250,7 @@ class Orchestrator {
this._context.env = await sdkUtils.getEnvironmentVariables() this._context.env = await sdkUtils.getEnvironmentVariables()
let automation = this._automation let automation = this._automation
let stopped = false let stopped = false
let loopStep: LoopStep | undefined = undefined let loopStep: LoopStep | undefined
let stepCount = 0 let stepCount = 0
let currentLoopStepIndex: number = 0 let currentLoopStepIndex: number = 0
@ -276,7 +270,7 @@ class Orchestrator {
} }
} }
const start = performance.now() const start = performance.now()
for (let step of automation.definition.steps) { for (const step of automation.definition.steps) {
const stepSpan = tracer.startSpan("Orchestrator.execute.step", { const stepSpan = tracer.startSpan("Orchestrator.execute.step", {
childOf: span, childOf: span,
}) })
@ -293,7 +287,7 @@ class Orchestrator {
}, },
}) })
let input: LoopInput | undefined, let input,
iterations = 1, iterations = 1,
iterationCount = 0 iterationCount = 0
@ -310,8 +304,8 @@ class Orchestrator {
} }
stepCount++ stepCount++
if (step.stepId === LOOP_STEP_ID) { if (step.stepId === AutomationActionStepId.LOOP) {
loopStep = step as LoopStep loopStep = step
currentLoopStepIndex = stepCount currentLoopStepIndex = stepCount
continue continue
} }
@ -331,7 +325,7 @@ class Orchestrator {
} }
try { try {
loopStep.inputs.binding = automationUtils.typecastForLooping( loopStep.inputs.binding = automationUtils.typecastForLooping(
loopStep.inputs as LoopInput loopStep.inputs
) )
} catch (err) { } catch (err) {
this.updateContextAndOutput( this.updateContextAndOutput(
@ -368,7 +362,7 @@ class Orchestrator {
if ( if (
stepIndex === env.AUTOMATION_MAX_ITERATIONS || stepIndex === env.AUTOMATION_MAX_ITERATIONS ||
(loopStep.inputs.iterations && (loopStep.inputs.iterations &&
stepIndex === parseInt(loopStep.inputs.iterations)) stepIndex === loopStep.inputs.iterations)
) { ) {
this.updateContextAndOutput( this.updateContextAndOutput(
currentLoopStepIndex, currentLoopStepIndex,
@ -443,7 +437,10 @@ class Orchestrator {
this._context.steps[stepCount] = outputs this._context.steps[stepCount] = outputs
// if filter causes us to stop execution don't break the loop, set a var // if filter causes us to stop execution don't break the loop, set a var
// so that we can finish iterating through the steps and record that it stopped // so that we can finish iterating through the steps and record that it stopped
if (step.stepId === FILTER_STEP_ID && !outputs.result) { if (
step.stepId === AutomationActionStepId.FILTER &&
!outputs.result
) {
stopped = true stopped = true
this.updateExecutionOutput( this.updateExecutionOutput(
step.id, step.id,

View File

@ -0,0 +1,277 @@
import { SortOrder } from "../../../api"
import { SearchFilters, EmptyFilterOption } from "../../../sdk"
import { HttpMethod } from "../query"
import { Row } from "../row"
import { LoopStepType, EmailAttachment, AutomationResults } from "./automation"
import { AutomationStep, AutomationStepOutputs } from "./schema"
export type BaseAutomationOutputs = {
success?: boolean
response?: {
[key: string]: any
message?: string
}
}
export type ExternalAppStepOutputs = {
httpStatus?: number
response: string
success: boolean
}
export type BashStepInputs = {
code: string
}
export type BashStepOutputs = BaseAutomationOutputs & {
stdout?: string
}
export type CollectStepInputs = {
collection: string
}
export type CollectStepOutputs = BaseAutomationOutputs & {
value?: any
}
export type CreateRowStepInputs = {
row: Row
}
export type CreateRowStepOutputs = BaseAutomationOutputs & {
row?: Row
id?: string
revision?: string
}
export type DelayStepInputs = {
time: number
}
export type DelayStepOutputs = BaseAutomationOutputs
export type DeleteRowStepInputs = {
tableId: string
id: string
revision?: string
}
export type DeleteRowStepOutputs = BaseAutomationOutputs & {
row?: Row
}
export type DiscordStepInputs = {
url: string
username?: string
avatar_url?: string
content: string
}
export type ExecuteQueryStepInputs = {
query: {
queryId: string
}
}
export type ExecuteQueryStepOutputs = BaseAutomationOutputs & {
info?: any
}
export type ExecuteScriptStepInputs = {
code: string
}
export type ExecuteScriptStepOutputs = BaseAutomationOutputs & {
value?: string
}
export type FilterStepInputs = {
field: any
condition: string
value: any
}
export type FilterStepOutputs = BaseAutomationOutputs & {
result: boolean
refValue?: any
comparisonValue?: any
}
export type LoopStepInputs = {
option: LoopStepType
binding: any
iterations?: number
failure?: string
}
export type LoopStepOutputs = {
items: AutomationStepOutputs[]
success: boolean
iterations: number
}
export type BranchStepInputs = {
conditions: SearchFilters
children?: Record<string, AutomationStep[]>
}
export type MakeIntegrationInputs = {
url: string
body: any
}
export type n8nStepInputs = {
url: string
method: HttpMethod
authorization: string
body: any
}
export type OpenAIStepInputs = {
prompt: string
model: Model
}
enum Model {
GPT_35_TURBO = "gpt-3.5-turbo",
// will only work with api keys that have access to the GPT4 API
GPT_4 = "gpt-4",
}
export type OpenAIStepOutputs = Omit<BaseAutomationOutputs, "response"> & {
response?: string | null
}
export type QueryRowsStepInputs = {
tableId: string
filters?: SearchFilters
"filters-def"?: any
sortColumn?: string
sortOrder?: SortOrder
limit?: number
onEmptyFilter?: EmptyFilterOption
}
export type QueryRowsStepOutputs = BaseAutomationOutputs & {
rows?: Row[]
}
export type SmtpEmailStepInputs = {
to: string
from: string
subject: string
contents: string
cc: string
bcc: string
addInvite?: boolean
startTime: Date
endTime: Date
summary: string
location?: string
url?: string
attachments?: EmailAttachment[]
}
export type SmtpEmailStepOutputs = BaseAutomationOutputs
export type ServerLogStepInputs = {
text: string
}
export type ServerLogStepOutputs = BaseAutomationOutputs & {
message: string
}
export type SlackStepInputs = {
url: string
text: string
}
export type TriggerAutomationStepInputs = {
automation: {
automationId: string
}
timeout: number
}
export type TriggerAutomationStepOutputs = BaseAutomationOutputs & {
value?: AutomationResults["steps"]
}
export type UpdateRowStepInputs = {
meta: Record<string, any>
row: Row
rowId: string
}
export type UpdateRowStepOutputs = BaseAutomationOutputs & {
row?: Row
id?: string
revision?: string
}
export type ZapierStepInputs = {
url: string
body: any
}
export type ZapierStepOutputs = Omit<ExternalAppStepOutputs, "response"> & {
response: string
}
enum RequestType {
POST = "POST",
GET = "GET",
PUT = "PUT",
DELETE = "DELETE",
PATCH = "PATCH",
}
export type OutgoingWebhookStepInputs = {
requestMethod: RequestType
url: string
requestBody: string
headers: string
}
export type AppActionTriggerInputs = {
fields: object
}
export type AppActionTriggerOutputs = {
fields: object
}
export type CronTriggerInputs = {
cron: string
}
export type CronTriggerOutputs = {
timestamp: number
}
export type RowDeletedTriggerInputs = {
tableId: string
}
export type RowDeletedTriggerOutputs = {
row: Row
}
export type RowCreatedTriggerInputs = {
tableId: string
filters?: SearchFilters
}
export type RowCreatedTriggerOutputs = {
row: Row
id: string
revision: string
}
export type RowUpdatedTriggerInputs = {
tableId: string
filters?: SearchFilters
}
export type RowUpdatedTriggerOutputs = {
row: Row
id: string
revision?: string
}

View File

@ -4,6 +4,7 @@ import { User } from "../../global"
import { ReadStream } from "fs" import { ReadStream } from "fs"
import { Row } from "../row" import { Row } from "../row"
import { Table } from "../table" import { Table } from "../table"
import { AutomationStep, AutomationTrigger } from "./schema"
export enum AutomationIOType { export enum AutomationIOType {
OBJECT = "object", OBJECT = "object",
@ -71,6 +72,7 @@ export enum AutomationActionStepId {
COLLECT = "COLLECT", COLLECT = "COLLECT",
OPENAI = "OPENAI", OPENAI = "OPENAI",
TRIGGER_AUTOMATION_RUN = "TRIGGER_AUTOMATION_RUN", TRIGGER_AUTOMATION_RUN = "TRIGGER_AUTOMATION_RUN",
BRANCH = "BRANCH",
// these used to be lowercase step IDs, maintain for backwards compat // these used to be lowercase step IDs, maintain for backwards compat
discord = "discord", discord = "discord",
slack = "slack", slack = "slack",
@ -164,44 +166,10 @@ export interface InputOutputBlock {
required?: string[] required?: string[]
} }
export interface AutomationStepSchema {
name: string
stepTitle?: string
tagline: string
icon: string
description: string
type: AutomationStepType
internal?: boolean
deprecated?: boolean
stepId: AutomationTriggerStepId | AutomationActionStepId
blockToLoop?: string
inputs: Record<string, any>
schema: {
inputs: InputOutputBlock
outputs: InputOutputBlock
}
custom?: boolean
features?: Partial<Record<AutomationFeature, boolean>>
}
export enum AutomationFeature { export enum AutomationFeature {
LOOPING = "LOOPING", LOOPING = "LOOPING",
} }
export interface AutomationStep extends AutomationStepSchema {
id: string
}
export interface AutomationTriggerSchema extends AutomationStepSchema {
type: AutomationStepType.TRIGGER
event?: string
cronJobId?: string
}
export interface AutomationTrigger extends AutomationTriggerSchema {
id: string
}
export enum AutomationStepStatus { export enum AutomationStepStatus {
NO_ITERATIONS = "no_iterations", NO_ITERATIONS = "no_iterations",
} }

View File

@ -1,2 +1,3 @@
export * from "./automation" export * from "./automation"
export * from "./schema" export * from "./schema"
export * from "./StepInputsOutputs"

View File

@ -1,14 +1,57 @@
import { SortOrder } from "../../../api" import { Hosting } from "../../../sdk"
import { EmptyFilterOption, Hosting, SearchFilters } from "../../../sdk"
import { HttpMethod } from "../query"
import { Row } from "../row"
import { import {
AutomationActionStepId, AutomationActionStepId,
AutomationResults,
EmailAttachment,
LoopStepType,
ActionImplementation, ActionImplementation,
AutomationStepType,
AutomationFeature,
InputOutputBlock,
AutomationTriggerStepId,
} from "./automation" } from "./automation"
import {
CollectStepInputs,
CollectStepOutputs,
CreateRowStepInputs,
CreateRowStepOutputs,
DelayStepInputs,
DelayStepOutputs,
DeleteRowStepInputs,
DeleteRowStepOutputs,
ExecuteQueryStepInputs,
ExecuteQueryStepOutputs,
ExecuteScriptStepInputs,
ExecuteScriptStepOutputs,
FilterStepInputs,
FilterStepOutputs,
QueryRowsStepInputs,
QueryRowsStepOutputs,
SmtpEmailStepInputs,
ServerLogStepInputs,
ServerLogStepOutputs,
TriggerAutomationStepInputs,
TriggerAutomationStepOutputs,
UpdateRowStepInputs,
UpdateRowStepOutputs,
OutgoingWebhookStepInputs,
ExternalAppStepOutputs,
DiscordStepInputs,
SlackStepInputs,
ZapierStepInputs,
ZapierStepOutputs,
MakeIntegrationInputs,
n8nStepInputs,
BashStepInputs,
BashStepOutputs,
OpenAIStepInputs,
OpenAIStepOutputs,
LoopStepInputs,
AppActionTriggerInputs,
CronTriggerInputs,
RowUpdatedTriggerInputs,
RowCreatedTriggerInputs,
RowDeletedTriggerInputs,
BranchStepInputs,
BaseAutomationOutputs,
} from "./StepInputsOutputs"
export type ActionImplementations<T extends Hosting> = { export type ActionImplementations<T extends Hosting> = {
[AutomationActionStepId.COLLECT]: ActionImplementation< [AutomationActionStepId.COLLECT]: ActionImplementation<
@ -27,7 +70,6 @@ export type ActionImplementations<T extends Hosting> = {
DeleteRowStepInputs, DeleteRowStepInputs,
DeleteRowStepOutputs DeleteRowStepOutputs
> >
[AutomationActionStepId.EXECUTE_QUERY]: ActionImplementation< [AutomationActionStepId.EXECUTE_QUERY]: ActionImplementation<
ExecuteQueryStepInputs, ExecuteQueryStepInputs,
ExecuteQueryStepOutputs ExecuteQueryStepOutputs
@ -72,7 +114,6 @@ export type ActionImplementations<T extends Hosting> = {
SlackStepInputs, SlackStepInputs,
ExternalAppStepOutputs ExternalAppStepOutputs
> >
[AutomationActionStepId.zapier]: ActionImplementation< [AutomationActionStepId.zapier]: ActionImplementation<
ZapierStepInputs, ZapierStepInputs,
ZapierStepOutputs ZapierStepOutputs
@ -98,6 +139,24 @@ export type ActionImplementations<T extends Hosting> = {
} }
: {}) : {})
export interface AutomationStepSchemaBase {
name: string
stepTitle?: string
tagline: string
icon: string
description: string
type: AutomationStepType
internal?: boolean
deprecated?: boolean
blockToLoop?: string
schema: {
inputs: InputOutputBlock
outputs: InputOutputBlock
}
custom?: boolean
features?: Partial<Record<AutomationFeature, boolean>>
}
export type AutomationStepOutputs = export type AutomationStepOutputs =
| CollectStepOutputs | CollectStepOutputs
| CreateRowStepOutputs | CreateRowStepOutputs
@ -116,223 +175,204 @@ export type AutomationStepOutputs =
| UpdateRowStepOutputs | UpdateRowStepOutputs
| ZapierStepOutputs | ZapierStepOutputs
export type BaseAutomationOutputs = { export type AutomationStepInputs<T extends AutomationActionStepId> =
success?: boolean T extends AutomationActionStepId.COLLECT
response?: { ? CollectStepInputs
[key: string]: any : T extends AutomationActionStepId.CREATE_ROW
message?: string ? CreateRowStepInputs
} : T extends AutomationActionStepId.DELAY
} ? DelayStepInputs
: T extends AutomationActionStepId.DELETE_ROW
? DeleteRowStepInputs
: T extends AutomationActionStepId.EXECUTE_QUERY
? ExecuteQueryStepInputs
: T extends AutomationActionStepId.EXECUTE_SCRIPT
? ExecuteScriptStepInputs
: T extends AutomationActionStepId.FILTER
? FilterStepInputs
: T extends AutomationActionStepId.QUERY_ROWS
? QueryRowsStepInputs
: T extends AutomationActionStepId.SEND_EMAIL_SMTP
? SmtpEmailStepInputs
: T extends AutomationActionStepId.SERVER_LOG
? ServerLogStepInputs
: T extends AutomationActionStepId.TRIGGER_AUTOMATION_RUN
? TriggerAutomationStepInputs
: T extends AutomationActionStepId.UPDATE_ROW
? UpdateRowStepInputs
: T extends AutomationActionStepId.OUTGOING_WEBHOOK
? OutgoingWebhookStepInputs
: T extends AutomationActionStepId.discord
? DiscordStepInputs
: T extends AutomationActionStepId.slack
? SlackStepInputs
: T extends AutomationActionStepId.zapier
? ZapierStepInputs
: T extends AutomationActionStepId.integromat
? MakeIntegrationInputs
: T extends AutomationActionStepId.n8n
? n8nStepInputs
: T extends AutomationActionStepId.EXECUTE_BASH
? BashStepInputs
: T extends AutomationActionStepId.OPENAI
? OpenAIStepInputs
: T extends AutomationActionStepId.LOOP
? LoopStepInputs
: T extends AutomationActionStepId.BRANCH
? BranchStepInputs
: never
export type ExternalAppStepOutputs = { export interface AutomationStepSchema<TStep extends AutomationActionStepId>
httpStatus?: number extends AutomationStepSchemaBase {
response: string
success: boolean
}
export type BashStepInputs = {
code: string
}
export type BashStepOutputs = BaseAutomationOutputs & {
stdout?: string
}
export type CollectStepInputs = {
collection: string
}
export type CollectStepOutputs = BaseAutomationOutputs & {
value?: any
}
export type CreateRowStepInputs = {
row: Row
}
export type CreateRowStepOutputs = BaseAutomationOutputs & {
row?: Row
id?: string
revision?: string
}
export type DelayStepInputs = {
time: number
}
export type DelayStepOutputs = BaseAutomationOutputs
export type DeleteRowStepInputs = {
tableId: string
id: string id: string
revision?: string stepId: TStep
inputs: AutomationStepInputs<TStep> & Record<string, any> // The record union to be removed once the types are fixed
} }
export type DeleteRowStepOutputs = BaseAutomationOutputs & { export type CollectStep = AutomationStepSchema<AutomationActionStepId.COLLECT>
row?: Row
export type CreateRowStep =
AutomationStepSchema<AutomationActionStepId.CREATE_ROW>
export type DelayStep = AutomationStepSchema<AutomationActionStepId.DELAY>
export type DeleteRowStep =
AutomationStepSchema<AutomationActionStepId.DELETE_ROW>
export type ExecuteQueryStep =
AutomationStepSchema<AutomationActionStepId.EXECUTE_QUERY>
export type ExecuteScriptStep =
AutomationStepSchema<AutomationActionStepId.EXECUTE_SCRIPT>
export type FilterStep = AutomationStepSchema<AutomationActionStepId.FILTER>
export type QueryRowsStep =
AutomationStepSchema<AutomationActionStepId.QUERY_ROWS>
export type SendEmailSmtpStep =
AutomationStepSchema<AutomationActionStepId.SEND_EMAIL_SMTP>
export type ServerLogStep =
AutomationStepSchema<AutomationActionStepId.SERVER_LOG>
export type TriggerAutomationRunStep =
AutomationStepSchema<AutomationActionStepId.TRIGGER_AUTOMATION_RUN>
export type UpdateRowStep =
AutomationStepSchema<AutomationActionStepId.UPDATE_ROW>
export type OutgoingWebhookStep =
AutomationStepSchema<AutomationActionStepId.OUTGOING_WEBHOOK>
export type DiscordStep = AutomationStepSchema<AutomationActionStepId.discord>
export type SlackStep = AutomationStepSchema<AutomationActionStepId.slack>
export type ZapierStep = AutomationStepSchema<AutomationActionStepId.zapier>
export type IntegromatStep =
AutomationStepSchema<AutomationActionStepId.integromat>
export type N8nStep = AutomationStepSchema<AutomationActionStepId.n8n>
export type ExecuteBashStep =
AutomationStepSchema<AutomationActionStepId.EXECUTE_BASH>
export type OpenAIStep = AutomationStepSchema<AutomationActionStepId.OPENAI>
export type LoopStep = AutomationStepSchema<AutomationActionStepId.LOOP>
export type BranchStep = AutomationStepSchema<AutomationActionStepId.BRANCH>
export type AutomationStep =
| CollectStep
| CreateRowStep
| DelayStep
| DeleteRowStep
| ExecuteQueryStep
| ExecuteScriptStep
| FilterStep
| QueryRowsStep
| SendEmailSmtpStep
| ServerLogStep
| TriggerAutomationRunStep
| UpdateRowStep
| OutgoingWebhookStep
| DiscordStep
| SlackStep
| ZapierStep
| IntegromatStep
| N8nStep
| LoopStep
| ExecuteBashStep
| OpenAIStep
| BranchStep
type EmptyInputs = {}
export type AutomationStepDefinition = Omit<AutomationStep, "id" | "inputs"> & {
inputs: EmptyInputs
} }
export type DiscordStepInputs = { export type AutomationTriggerDefinition = Omit<
url: string AutomationTrigger,
username?: string "id" | "inputs"
avatar_url?: string > & {
content: string inputs: EmptyInputs
} }
export type ExecuteQueryStepInputs = { export type AutomationTriggerInputs<T extends AutomationTriggerStepId> =
query: { T extends AutomationTriggerStepId.APP
queryId: string ? AppActionTriggerInputs
} : T extends AutomationTriggerStepId.CRON
? CronTriggerInputs
: T extends AutomationTriggerStepId.ROW_ACTION
? Record<string, any>
: T extends AutomationTriggerStepId.ROW_DELETED
? RowDeletedTriggerInputs
: T extends AutomationTriggerStepId.ROW_SAVED
? RowCreatedTriggerInputs
: T extends AutomationTriggerStepId.ROW_UPDATED
? RowUpdatedTriggerInputs
: T extends AutomationTriggerStepId.WEBHOOK
? Record<string, any>
: never
export interface AutomationTriggerSchema<
TTrigger extends AutomationTriggerStepId
> extends AutomationStepSchemaBase {
id: string
type: AutomationStepType.TRIGGER
event?: string
cronJobId?: string
stepId: TTrigger
inputs: AutomationTriggerInputs<TTrigger> & Record<string, any> // The record union to be removed once the types are fixed
} }
export type ExecuteQueryStepOutputs = BaseAutomationOutputs & { export type AutomationTrigger =
info?: any | AppActionTrigger
} | CronTrigger
| RowActionTrigger
| RowDeletedTrigger
| RowSavedTrigger
| RowUpdatedTrigger
| WebhookTrigger
export type ExecuteScriptStepInputs = { export type AppActionTrigger =
code: string AutomationTriggerSchema<AutomationTriggerStepId.APP>
}
export type ExecuteScriptStepOutputs = BaseAutomationOutputs & { export type CronTrigger = AutomationTriggerSchema<AutomationTriggerStepId.CRON>
value?: string
}
export type FilterStepInputs = { export type RowActionTrigger =
field: any AutomationTriggerSchema<AutomationTriggerStepId.ROW_ACTION>
condition: string
value: any
}
export type FilterStepOutputs = BaseAutomationOutputs & { export type RowDeletedTrigger =
result: boolean AutomationTriggerSchema<AutomationTriggerStepId.ROW_DELETED>
refValue?: any
comparisonValue?: any
}
export type LoopStepInputs = { export type RowSavedTrigger =
option: LoopStepType AutomationTriggerSchema<AutomationTriggerStepId.ROW_SAVED>
binding: any
iterations?: number
failure?: string
}
export type LoopStepOutputs = { export type RowUpdatedTrigger =
items: AutomationStepOutputs[] AutomationTriggerSchema<AutomationTriggerStepId.ROW_UPDATED>
success: boolean
iterations: number
}
export type MakeIntegrationInputs = { export type WebhookTrigger =
url: string AutomationTriggerSchema<AutomationTriggerStepId.WEBHOOK>
body: any
}
export type n8nStepInputs = {
url: string
method: HttpMethod
authorization: string
body: any
}
export type OpenAIStepInputs = {
prompt: string
model: Model
}
enum Model {
GPT_35_TURBO = "gpt-3.5-turbo",
// will only work with api keys that have access to the GPT4 API
GPT_4 = "gpt-4",
}
export type OpenAIStepOutputs = Omit<BaseAutomationOutputs, "response"> & {
response?: string | null
}
export type QueryRowsStepInputs = {
tableId: string
filters?: SearchFilters
"filters-def"?: any
sortColumn?: string
sortOrder?: SortOrder
limit?: number
onEmptyFilter?: EmptyFilterOption
}
export type QueryRowsStepOutputs = BaseAutomationOutputs & {
rows?: Row[]
}
export type SmtpEmailStepInputs = {
to: string
from: string
subject: string
contents: string
cc: string
bcc: string
addInvite?: boolean
startTime: Date
endTime: Date
summary: string
location?: string
url?: string
attachments?: EmailAttachment[]
}
export type ServerLogStepInputs = {
text: string
}
export type ServerLogStepOutputs = BaseAutomationOutputs & {
message: string
}
export type SlackStepInputs = {
url: string
text: string
}
export type TriggerAutomationStepInputs = {
automation: {
automationId: string
}
timeout: number
}
export type TriggerAutomationStepOutputs = BaseAutomationOutputs & {
value?: AutomationResults["steps"]
}
export type UpdateRowStepInputs = {
meta: Record<string, any>
row: Row
rowId: string
}
export type UpdateRowStepOutputs = BaseAutomationOutputs & {
row?: Row
id?: string
revision?: string
}
export type ZapierStepInputs = {
url: string
body: any
}
export type ZapierStepOutputs = Omit<ExternalAppStepOutputs, "response"> & {
response: string
}
enum RequestType {
POST = "POST",
GET = "GET",
PUT = "PUT",
DELETE = "DELETE",
PATCH = "PATCH",
}
export type OutgoingWebhookStepInputs = {
requestMethod: RequestType
url: string
requestBody: string
headers: string
}