Merge branch 'grid-layout-expansion' of github.com:Budibase/budibase into grid-layout-expansion

This commit is contained in:
Andrew Kingston 2024-08-15 09:36:08 +01:00
commit 5f6ccd82dd
No known key found for this signature in database
51 changed files with 828 additions and 489 deletions

View File

@ -73,7 +73,7 @@ sed -i "s#COUCHDB_ERLANG_COOKIE#${COUCHDB_ERLANG_COOKIE}#g" /opt/clouseau/clouse
/docker-entrypoint.sh /opt/couchdb/bin/couchdb > /dev/stdout 2>&1 &
# Start SQS. Use 127.0.0.1 instead of localhost to avoid IPv6 issues.
/opt/sqs/sqs --server "http://127.0.0.1:5984" --data-dir ${DATA_DIR}/sqs --bind-address=0.0.0.0 > /dev/stdout 2>&1 &
/opt/sqs/sqs --server "http://127.0.0.1:5984" --data-dir ${DATA_DIR}/sqs --bind-address=0.0.0.0 --max-threads=20 > /dev/stdout 2>&1 &
# Wait for CouchDB to start up.
while [[ $(curl -s -w "%{http_code}\n" http://localhost:5984/_up -o /dev/null) -ne 200 ]]; do

View File

@ -42,7 +42,7 @@ services:
couchdb-service:
container_name: budi-couchdb3-dev
restart: on-failure
image: budibase/couchdb:v3.2.1-sqs
image: budibase/couchdb:v3.3.3
environment:
- COUCHDB_PASSWORD=${COUCH_DB_PASSWORD}
- COUCHDB_USER=${COUCH_DB_USER}

View File

@ -375,3 +375,22 @@ export function getCurrentContext(): ContextMap | undefined {
return undefined
}
}
export function getFeatureFlags<T extends Record<string, any>>(
key: string
): T | undefined {
const context = getCurrentContext()
if (!context) {
return undefined
}
return context.featureFlagCache?.[key] as T
}
export function setFeatureFlags(key: string, value: Record<string, any>) {
const context = getCurrentContext()
if (!context) {
return
}
context.featureFlagCache ??= {}
context.featureFlagCache[key] = value
}

View File

@ -18,4 +18,7 @@ export type ContextMap = {
oauthClient: OAuth2Client
clients: Record<string, GoogleSpreadsheet>
}
featureFlagCache?: {
[key: string]: Record<string, any>
}
}

View File

@ -18,6 +18,10 @@ export function init(opts?: PostHogOptions) {
}
}
export function shutdown() {
posthog?.shutdown()
}
export abstract class Flag<T> {
static boolean(defaultValue: boolean): Flag<boolean> {
return new BooleanFlag(defaultValue)
@ -87,7 +91,14 @@ class NumberFlag extends Flag<number> {
}
export class FlagSet<V extends Flag<any>, T extends { [key: string]: V }> {
constructor(private readonly flagSchema: T) {}
// This is used to safely cache flags sets in the current request context.
// Because multiple sets could theoretically exist, we don't want the cache of
// one to leak into another.
private readonly setId: string
constructor(private readonly flagSchema: T) {
this.setId = crypto.randomUUID()
}
defaults(): FlagValues<T> {
return Object.keys(this.flagSchema).reduce((acc, key) => {
@ -119,6 +130,12 @@ export class FlagSet<V extends Flag<any>, T extends { [key: string]: V }> {
async fetch(ctx?: UserCtx): Promise<FlagValues<T>> {
return await tracer.trace("features.fetch", async span => {
const cachedFlags = context.getFeatureFlags<FlagValues<T>>(this.setId)
if (cachedFlags) {
span?.addTags({ fromCache: true })
return cachedFlags
}
const tags: Record<string, any> = {}
const flagValues = this.defaults()
const currentTenantId = context.getTenantId()
@ -187,10 +204,7 @@ export class FlagSet<V extends Flag<any>, T extends { [key: string]: V }> {
tags[`identity.tenantId`] = identity?.tenantId
tags[`identity._id`] = identity?._id
// Until we're confident this performs well, we're only enabling it in QA
// and test environments.
const usePosthog = env.isTest() || env.isQA()
if (usePosthog && posthog && identity?.type === IdentityType.USER) {
if (posthog && identity?.type === IdentityType.USER) {
tags[`readFromPostHog`] = true
const personProperties: Record<string, string> = {}
@ -204,7 +218,6 @@ export class FlagSet<V extends Flag<any>, T extends { [key: string]: V }> {
personProperties,
}
)
console.log("posthog flags", JSON.stringify(posthogFlags))
for (const [name, value] of Object.entries(posthogFlags.featureFlags)) {
if (!this.isFlagName(name)) {
@ -236,6 +249,7 @@ export class FlagSet<V extends Flag<any>, T extends { [key: string]: V }> {
}
}
context.setFeatureFlags(this.setId, flagValues)
for (const [key, value] of Object.entries(flagValues)) {
tags[`flags.${key}.value`] = value
}
@ -255,5 +269,5 @@ export const flags = new FlagSet({
GOOGLE_SHEETS: Flag.boolean(false),
USER_GROUPS: Flag.boolean(false),
ONBOARDING_TOUR: Flag.boolean(false),
DEFAULT_VALUES: Flag.boolean(true),
DEFAULT_VALUES: Flag.boolean(false),
})

View File

@ -1,5 +1,5 @@
import { IdentityContext, IdentityType, UserCtx } from "@budibase/types"
import { Flag, FlagSet, FlagValues, init } from "../"
import { Flag, FlagSet, FlagValues, init, shutdown } from "../"
import { context } from "../.."
import environment, { withEnv } from "../../environment"
import nodeFetch from "node-fetch"
@ -197,6 +197,8 @@ describe("feature flags", () => {
throw new Error("No expected value")
}
})
shutdown()
})
}
)

View File

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

View File

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

View File

@ -9,8 +9,8 @@ import {
FieldType,
Row,
LoopStepType,
LoopStepInputs,
} from "@budibase/types"
import { LoopInput } from "../definitions/automations"
import { objectStore, context } from "@budibase/backend-core"
import * as uuid from "uuid"
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
* 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) {
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
for (let key in inputs.row) {
if (
inputs.schema?.[key]?.type === "link" &&
inputs.row[key] &&
typeof inputs.row[key] === "string"
) {
try {
inputs.row[key] = JSON.parse(inputs.row[key])
} catch (e) {
//Link is not an array or object, so continue
if (inputs?.row) {
for (let key in inputs.row) {
if (
inputs.schema?.[key]?.type === "link" &&
inputs.row[key] &&
typeof inputs.row[key] === "string"
) {
try {
inputs.row[key] = JSON.parse(inputs.row[key])
} catch (e) {
//Link is not an array or object, so continue
}
}
}
}
@ -267,7 +272,7 @@ export function stringSplit(value: string | string[]) {
return value.split(",")
}
export function typecastForLooping(input: LoopInput) {
export function typecastForLooping(input: LoopStepInputs) {
if (!input || !input.binding) {
return null
}

View File

@ -5,14 +5,15 @@ type ObjValue = {
[key: string]: string | ObjValue
}
export function replaceFakeBindings(
originalStepInput: Record<string, any>,
export function replaceFakeBindings<T extends Record<string, any>>(
originalStepInput: T,
loopStepNumber: number
) {
): T {
const result: Record<string, any> = {}
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(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,7 +6,7 @@ import {
AutomationCustomIOType,
AutomationFeature,
AutomationIOType,
AutomationStepSchema,
AutomationStepDefinition,
AutomationStepType,
ExternalAppStepOutputs,
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.
*/
export const definition: AutomationStepSchema = {
export const definition: AutomationStepDefinition = {
deprecated: true,
name: "Outgoing webhook",
tagline: "Send a {{inputs.requestMethod}} request",

View File

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

View File

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

View File

@ -1,6 +1,6 @@
import {
AutomationActionStepId,
AutomationStepSchema,
AutomationStepDefinition,
AutomationStepType,
AutomationIOType,
AutomationFeature,
@ -14,7 +14,7 @@ import {
* 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",
tagline: "Console log a value in the backend",
icon: "Monitoring",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,14 +2,12 @@ import {
AutomationCustomIOType,
AutomationIOType,
AutomationStepType,
AutomationTriggerSchema,
AutomationTriggerStepId,
AutomationEventType,
Row,
AutomationTriggerDefinition,
} from "@budibase/types"
import { SearchFilters } from "aws-sdk/clients/elasticbeanstalk"
export const definition: AutomationTriggerSchema = {
export const definition: AutomationTriggerDefinition = {
name: "Row Created",
event: AutomationEventType.ROW_SAVE,
icon: "TableRowAddBottom",
@ -54,14 +52,3 @@ export const definition: AutomationTriggerSchema = {
},
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,
AutomationIOType,
AutomationStepType,
AutomationTriggerSchema,
AutomationTriggerDefinition,
AutomationTriggerStepId,
AutomationEventType,
Row,
SearchFilters,
} from "@budibase/types"
export const definition: AutomationTriggerSchema = {
export const definition: AutomationTriggerDefinition = {
name: "Row Updated",
event: AutomationEventType.ROW_UPDATE,
icon: "Refresh",
@ -61,14 +59,3 @@ export const definition: AutomationTriggerSchema = {
},
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,
AutomationIOType,
AutomationStepType,
AutomationTriggerSchema,
AutomationTriggerStepId,
AutomationEventType,
AutomationTriggerDefinition,
} from "@budibase/types"
export const definition: AutomationTriggerSchema = {
export const definition: AutomationTriggerDefinition = {
name: "Webhook",
event: AutomationEventType.WEBHOOK_TRIGGER,
icon: "Send",

View File

@ -54,9 +54,9 @@ async function queueRelevantRowAutomations(
return trigger && trigger.event === eventType && !automation.disabled
})
for (let automation of automations) {
let automationDef = automation.definition
let automationTrigger = automationDef?.trigger
for (const automation of automations) {
const automationDef = automation.definition
const automationTrigger = automationDef?.trigger
// 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
// be checked due to lazy evaluation (first always false)

View File

@ -44,7 +44,9 @@ describe("automationUtils", () => {
})
it("should handle null values", () => {
// 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(() =>
typecastForLooping({ option: LoopStepType.ARRAY, binding: "test" })
).toThrow()

View File

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

View File

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

View File

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

View File

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

View File

@ -23,13 +23,9 @@ import {
AutomationStatus,
AutomationStep,
AutomationStepStatus,
} from "@budibase/types"
import {
AutomationContext,
LoopInput,
LoopStep,
TriggerOutput,
} from "../definitions/automations"
} from "@budibase/types"
import { AutomationContext, TriggerOutput } from "../definitions/automations"
import { WorkerCallback } from "./definitions"
import { context, logging } from "@budibase/backend-core"
import { processObject } from "@budibase/string-templates"
@ -40,8 +36,6 @@ import env from "../environment"
import tracer from "dd-trace"
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 STOPPED_STATUS = { success: true, status: AutomationStatus.STOPPED }
@ -256,7 +250,7 @@ class Orchestrator {
this._context.env = await sdkUtils.getEnvironmentVariables()
let automation = this._automation
let stopped = false
let loopStep: LoopStep | undefined = undefined
let loopStep: LoopStep | undefined
let stepCount = 0
let currentLoopStepIndex: number = 0
@ -276,7 +270,7 @@ class Orchestrator {
}
}
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", {
childOf: span,
})
@ -293,7 +287,7 @@ class Orchestrator {
},
})
let input: LoopInput | undefined,
let input,
iterations = 1,
iterationCount = 0
@ -310,8 +304,8 @@ class Orchestrator {
}
stepCount++
if (step.stepId === LOOP_STEP_ID) {
loopStep = step as LoopStep
if (step.stepId === AutomationActionStepId.LOOP) {
loopStep = step
currentLoopStepIndex = stepCount
continue
}
@ -331,7 +325,7 @@ class Orchestrator {
}
try {
loopStep.inputs.binding = automationUtils.typecastForLooping(
loopStep.inputs as LoopInput
loopStep.inputs
)
} catch (err) {
this.updateContextAndOutput(
@ -368,7 +362,7 @@ class Orchestrator {
if (
stepIndex === env.AUTOMATION_MAX_ITERATIONS ||
(loopStep.inputs.iterations &&
stepIndex === parseInt(loopStep.inputs.iterations))
stepIndex === loopStep.inputs.iterations)
) {
this.updateContextAndOutput(
currentLoopStepIndex,
@ -443,7 +437,10 @@ class Orchestrator {
this._context.steps[stepCount] = outputs
// 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
if (step.stepId === FILTER_STEP_ID && !outputs.result) {
if (
step.stepId === AutomationActionStepId.FILTER &&
!outputs.result
) {
stopped = true
this.updateExecutionOutput(
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 { Row } from "../row"
import { Table } from "../table"
import { AutomationStep, AutomationTrigger } from "./schema"
export enum AutomationIOType {
OBJECT = "object",
@ -71,6 +72,7 @@ export enum AutomationActionStepId {
COLLECT = "COLLECT",
OPENAI = "OPENAI",
TRIGGER_AUTOMATION_RUN = "TRIGGER_AUTOMATION_RUN",
BRANCH = "BRANCH",
// these used to be lowercase step IDs, maintain for backwards compat
discord = "discord",
slack = "slack",
@ -164,44 +166,10 @@ export interface InputOutputBlock {
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 {
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 {
NO_ITERATIONS = "no_iterations",
}

View File

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

View File

@ -1,14 +1,57 @@
import { SortOrder } from "../../../api"
import { EmptyFilterOption, Hosting, SearchFilters } from "../../../sdk"
import { HttpMethod } from "../query"
import { Row } from "../row"
import { Hosting } from "../../../sdk"
import {
AutomationActionStepId,
AutomationResults,
EmailAttachment,
LoopStepType,
ActionImplementation,
AutomationStepType,
AutomationFeature,
InputOutputBlock,
AutomationTriggerStepId,
} 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> = {
[AutomationActionStepId.COLLECT]: ActionImplementation<
@ -27,7 +70,6 @@ export type ActionImplementations<T extends Hosting> = {
DeleteRowStepInputs,
DeleteRowStepOutputs
>
[AutomationActionStepId.EXECUTE_QUERY]: ActionImplementation<
ExecuteQueryStepInputs,
ExecuteQueryStepOutputs
@ -72,7 +114,6 @@ export type ActionImplementations<T extends Hosting> = {
SlackStepInputs,
ExternalAppStepOutputs
>
[AutomationActionStepId.zapier]: ActionImplementation<
ZapierStepInputs,
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 =
| CollectStepOutputs
| CreateRowStepOutputs
@ -116,223 +175,204 @@ export type AutomationStepOutputs =
| UpdateRowStepOutputs
| ZapierStepOutputs
export type BaseAutomationOutputs = {
success?: boolean
response?: {
[key: string]: any
message?: string
}
}
export type AutomationStepInputs<T extends AutomationActionStepId> =
T extends AutomationActionStepId.COLLECT
? CollectStepInputs
: T extends AutomationActionStepId.CREATE_ROW
? 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 = {
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
export interface AutomationStepSchema<TStep extends AutomationActionStepId>
extends AutomationStepSchemaBase {
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 & {
row?: Row
export type CollectStep = AutomationStepSchema<AutomationActionStepId.COLLECT>
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 = {
url: string
username?: string
avatar_url?: string
content: string
export type AutomationTriggerDefinition = Omit<
AutomationTrigger,
"id" | "inputs"
> & {
inputs: EmptyInputs
}
export type ExecuteQueryStepInputs = {
query: {
queryId: string
}
export type AutomationTriggerInputs<T extends AutomationTriggerStepId> =
T extends AutomationTriggerStepId.APP
? 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 & {
info?: any
}
export type AutomationTrigger =
| AppActionTrigger
| CronTrigger
| RowActionTrigger
| RowDeletedTrigger
| RowSavedTrigger
| RowUpdatedTrigger
| WebhookTrigger
export type ExecuteScriptStepInputs = {
code: string
}
export type AppActionTrigger =
AutomationTriggerSchema<AutomationTriggerStepId.APP>
export type ExecuteScriptStepOutputs = BaseAutomationOutputs & {
value?: string
}
export type CronTrigger = AutomationTriggerSchema<AutomationTriggerStepId.CRON>
export type FilterStepInputs = {
field: any
condition: string
value: any
}
export type RowActionTrigger =
AutomationTriggerSchema<AutomationTriggerStepId.ROW_ACTION>
export type FilterStepOutputs = BaseAutomationOutputs & {
result: boolean
refValue?: any
comparisonValue?: any
}
export type RowDeletedTrigger =
AutomationTriggerSchema<AutomationTriggerStepId.ROW_DELETED>
export type LoopStepInputs = {
option: LoopStepType
binding: any
iterations?: number
failure?: string
}
export type RowSavedTrigger =
AutomationTriggerSchema<AutomationTriggerStepId.ROW_SAVED>
export type LoopStepOutputs = {
items: AutomationStepOutputs[]
success: boolean
iterations: number
}
export type RowUpdatedTrigger =
AutomationTriggerSchema<AutomationTriggerStepId.ROW_UPDATED>
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 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 WebhookTrigger =
AutomationTriggerSchema<AutomationTriggerStepId.WEBHOOK>