From a3bb2e0d770fe0c66f0382aebef86faa2bbcc696 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 25 Nov 2022 19:57:07 +0000 Subject: [PATCH] Automation overhaul to Typescript, plus type updates. --- packages/backend-core/src/environment.ts | 6 +- packages/backend-core/src/queue/queue.ts | 2 +- .../server/src/api/controllers/automation.ts | 4 +- .../automations/{actions.js => actions.ts} | 49 ++++++----- ...{automationUtils.js => automationUtils.ts} | 25 +++--- packages/server/src/automations/bullboard.js | 39 --------- packages/server/src/automations/bullboard.ts | 38 +++++++++ packages/server/src/automations/index.js | 29 ------- packages/server/src/automations/index.ts | 26 ++++++ .../automations/steps/{bash.js => bash.ts} | 21 +++-- .../server/src/automations/steps/createRow.ts | 11 ++- .../automations/steps/{delay.js => delay.ts} | 13 ++- .../server/src/automations/steps/deleteRow.ts | 11 ++- .../steps/{discord.js => discord.ts} | 15 ++-- .../{executeQuery.js => executeQuery.ts} | 19 +++-- .../{executeScript.js => executeScript.ts} | 24 ++++-- .../steps/{filter.js => filter.ts} | 21 +++-- .../steps/{integromat.js => integromat.ts} | 15 ++-- .../automations/steps/{loop.js => loop.ts} | 6 +- ...{outgoingWebhook.js => outgoingWebhook.ts} | 31 ++++--- .../steps/{queryRows.js => queryRows.ts} | 84 +++++++++++-------- .../{sendSmtpEmail.js => sendSmtpEmail.ts} | 15 ++-- .../steps/{serverLog.js => serverLog.ts} | 12 ++- .../automations/steps/{slack.js => slack.ts} | 15 ++-- .../steps/{updateRow.js => updateRow.ts} | 19 +++-- .../automations/steps/{utils.js => utils.ts} | 14 +++- .../steps/{zapier.js => zapier.ts} | 16 ++-- .../triggerInfo/{app.js => app.ts} | 6 +- .../triggerInfo/{cron.js => cron.ts} | 6 +- .../src/automations/triggerInfo/index.js | 15 ---- .../src/automations/triggerInfo/index.ts | 15 ++++ .../{rowDeleted.js => rowDeleted.ts} | 6 +- .../triggerInfo/{rowSaved.js => rowSaved.ts} | 6 +- .../{rowUpdated.js => rowUpdated.ts} | 6 +- .../triggerInfo/{webhook.js => webhook.ts} | 6 +- .../automations/{triggers.js => triggers.ts} | 51 ++++++----- .../src/{environment.js => environment.ts} | 15 ++-- .../server/src/utilities/workerRequests.ts | 2 +- .../types/src/documents/app/automation.ts | 20 ++++- packages/worker/src/environment.ts | 10 ++- 40 files changed, 442 insertions(+), 302 deletions(-) rename packages/server/src/automations/{actions.js => actions.ts} (58%) rename packages/server/src/automations/{automationUtils.js => automationUtils.ts} (86%) delete mode 100644 packages/server/src/automations/bullboard.js create mode 100644 packages/server/src/automations/bullboard.ts delete mode 100644 packages/server/src/automations/index.js create mode 100644 packages/server/src/automations/index.ts rename packages/server/src/automations/steps/{bash.js => bash.ts} (72%) rename packages/server/src/automations/steps/{delay.js => delay.ts} (69%) rename packages/server/src/automations/steps/{discord.js => discord.ts} (84%) rename packages/server/src/automations/steps/{executeQuery.js => executeQuery.ts} (78%) rename packages/server/src/automations/steps/{executeScript.js => executeScript.ts} (70%) rename packages/server/src/automations/steps/{filter.js => filter.ts} (86%) rename packages/server/src/automations/steps/{integromat.js => integromat.ts} (84%) rename packages/server/src/automations/steps/{loop.js => loop.ts} (87%) rename packages/server/src/automations/steps/{outgoingWebhook.js => outgoingWebhook.ts} (84%) rename packages/server/src/automations/steps/{queryRows.js => queryRows.ts} (61%) rename packages/server/src/automations/steps/{sendSmtpEmail.js => sendSmtpEmail.ts} (81%) rename packages/server/src/automations/steps/{serverLog.js => serverLog.ts} (80%) rename packages/server/src/automations/steps/{slack.js => slack.ts} (79%) rename packages/server/src/automations/steps/{updateRow.js => updateRow.ts} (82%) rename packages/server/src/automations/steps/{utils.js => utils.ts} (76%) rename packages/server/src/automations/steps/{zapier.js => zapier.ts} (83%) rename packages/server/src/automations/triggerInfo/{app.js => app.ts} (80%) rename packages/server/src/automations/triggerInfo/{cron.js => cron.ts} (78%) delete mode 100644 packages/server/src/automations/triggerInfo/index.js create mode 100644 packages/server/src/automations/triggerInfo/index.ts rename packages/server/src/automations/triggerInfo/{rowDeleted.js => rowDeleted.ts} (79%) rename packages/server/src/automations/triggerInfo/{rowSaved.js => rowSaved.ts} (83%) rename packages/server/src/automations/triggerInfo/{rowUpdated.js => rowUpdated.ts} (83%) rename packages/server/src/automations/triggerInfo/{webhook.js => webhook.ts} (82%) rename packages/server/src/automations/{triggers.js => triggers.ts} (77%) rename packages/server/src/{environment.js => environment.ts} (93%) diff --git a/packages/backend-core/src/environment.ts b/packages/backend-core/src/environment.ts index 2443287d5a..5d010f1421 100644 --- a/packages/backend-core/src/environment.ts +++ b/packages/backend-core/src/environment.ts @@ -25,7 +25,7 @@ const DefaultBucketName = { PLUGINS: "plugins", } -const env = { +const environment = { isTest, isDev, JS_BCRYPT: process.env.JS_BCRYPT, @@ -80,7 +80,7 @@ const env = { } // clean up any environment variable edge cases -for (let [key, value] of Object.entries(env)) { +for (let [key, value] of Object.entries(environment)) { // handle the edge case of "0" to disable an environment variable if (value === "0") { // @ts-ignore @@ -88,4 +88,4 @@ for (let [key, value] of Object.entries(env)) { } } -export = env +export = environment diff --git a/packages/backend-core/src/queue/queue.ts b/packages/backend-core/src/queue/queue.ts index b4eeeb31aa..b34d46e463 100644 --- a/packages/backend-core/src/queue/queue.ts +++ b/packages/backend-core/src/queue/queue.ts @@ -39,7 +39,7 @@ export function createQueue( return queue } -exports.shutdown = async () => { +export async function shutdown() { if (QUEUES.length) { clearInterval(cleanupInterval) for (let queue of QUEUES) { diff --git a/packages/server/src/api/controllers/automation.ts b/packages/server/src/api/controllers/automation.ts index 185da80216..0e6624d125 100644 --- a/packages/server/src/api/controllers/automation.ts +++ b/packages/server/src/api/controllers/automation.ts @@ -1,5 +1,5 @@ -import actions from "../../automations/actions" -import triggers from "../../automations/triggers" +import * as actions from "../../automations/actions" +import * as triggers from "../../automations/triggers" import { getAutomationParams, generateAutomationID, diff --git a/packages/server/src/automations/actions.js b/packages/server/src/automations/actions.ts similarity index 58% rename from packages/server/src/automations/actions.js rename to packages/server/src/automations/actions.ts index e1ebff560a..7c1ef719f7 100644 --- a/packages/server/src/automations/actions.js +++ b/packages/server/src/automations/actions.ts @@ -1,22 +1,26 @@ -const sendSmtpEmail = require("./steps/sendSmtpEmail") -const createRow = require("./steps/createRow") -const updateRow = require("./steps/updateRow") -const deleteRow = require("./steps/deleteRow") -const executeScript = require("./steps/executeScript") -const executeQuery = require("./steps/executeQuery") -const outgoingWebhook = require("./steps/outgoingWebhook") -const serverLog = require("./steps/serverLog") -const discord = require("./steps/discord") -const slack = require("./steps/slack") -const zapier = require("./steps/zapier") -const integromat = require("./steps/integromat") -let filter = require("./steps/filter") -let delay = require("./steps/delay") -let queryRow = require("./steps/queryRows") -let loop = require("./steps/loop") -const env = require("../environment") +import * as sendSmtpEmail from "./steps/sendSmtpEmail" +import * as createRow from "./steps/createRow" +import * as updateRow from "./steps/updateRow" +import * as deleteRow from "./steps/deleteRow" +import * as executeScript from "./steps/executeScript" +import * as executeQuery from "./steps/executeQuery" +import * as outgoingWebhook from "./steps/outgoingWebhook" +import * as serverLog from "./steps/serverLog" +import * as discord from "./steps/discord" +import * as slack from "./steps/slack" +import * as zapier from "./steps/zapier" +import * as integromat from "./steps/integromat" +import * as filter from "./steps/filter" +import * as delay from "./steps/delay" +import * as queryRow from "./steps/queryRows" +import * as loop from "./steps/loop" +import env from "../environment" +import { AutomationStep, AutomationStepInput } from "@budibase/types" -const ACTION_IMPLS = { +const ACTION_IMPLS: Record< + string, + (opts: AutomationStepInput) => Promise +> = { SEND_EMAIL_SMTP: sendSmtpEmail.run, CREATE_ROW: createRow.run, UPDATE_ROW: updateRow.run, @@ -28,14 +32,13 @@ const ACTION_IMPLS = { DELAY: delay.run, FILTER: filter.run, QUERY_ROWS: queryRow.run, - LOOP: loop.run, // these used to be lowercase step IDs, maintain for backwards compat discord: discord.run, slack: slack.run, zapier: zapier.run, integromat: integromat.run, } -const ACTION_DEFINITIONS = { +export const ACTION_DEFINITIONS: Record = { SEND_EMAIL_SMTP: sendSmtpEmail.definition, CREATE_ROW: createRow.definition, UPDATE_ROW: updateRow.definition, @@ -60,15 +63,15 @@ const ACTION_DEFINITIONS = { // ran at all if (env.SELF_HOSTED) { const bash = require("./steps/bash") + // @ts-ignore ACTION_IMPLS["EXECUTE_BASH"] = bash.run + // @ts-ignore ACTION_DEFINITIONS["EXECUTE_BASH"] = bash.definition } /* istanbul ignore next */ -exports.getAction = async function (actionName) { +export async function getAction(actionName: string) { if (ACTION_IMPLS[actionName] != null) { return ACTION_IMPLS[actionName] } } - -exports.ACTION_DEFINITIONS = ACTION_DEFINITIONS diff --git a/packages/server/src/automations/automationUtils.js b/packages/server/src/automations/automationUtils.ts similarity index 86% rename from packages/server/src/automations/automationUtils.js rename to packages/server/src/automations/automationUtils.ts index 12f7566b6d..8a75de83dd 100644 --- a/packages/server/src/automations/automationUtils.js +++ b/packages/server/src/automations/automationUtils.ts @@ -1,9 +1,10 @@ -const { +import { decodeJSBinding, isJSBinding, encodeJSBinding, -} = require("@budibase/string-templates") -const sdk = require("../sdk") +} from "@budibase/string-templates" +import sdk from "../sdk" +import { Row } from "@budibase/types" /** * When values are input to the system generally they will be of type string as this is required for template strings. @@ -21,7 +22,7 @@ const sdk = require("../sdk") * @returns {object} The inputs object which has had all the various types supported by this function converted to their * primitive types. */ -exports.cleanInputValues = (inputs, schema) => { +export function cleanInputValues(inputs: Record, schema: any) { if (schema == null) { return inputs } @@ -62,12 +63,12 @@ exports.cleanInputValues = (inputs, schema) => { * @param {object} row The input row structure which requires clean-up after having been through template statements. * @returns {Promise} The cleaned up rows object, will should now have all the required primitive types. */ -exports.cleanUpRow = async (tableId, row) => { +export async function cleanUpRow(tableId: string, row: Row) { let table = await sdk.tables.getTable(tableId) - return exports.cleanInputValues(row, { properties: table.schema }) + return cleanInputValues(row, { properties: table.schema }) } -exports.getError = err => { +export function getError(err: any) { if (err == null) { return "No error provided." } @@ -80,13 +81,13 @@ exports.getError = err => { return typeof err !== "string" ? err.toString() : err } -exports.substituteLoopStep = (hbsString, substitute) => { +export function substituteLoopStep(hbsString: string, substitute: string) { let checkForJS = isJSBinding(hbsString) let substitutedHbsString = "" let open = checkForJS ? `$("` : "{{" let closed = checkForJS ? `")` : "}}" if (checkForJS) { - hbsString = decodeJSBinding(hbsString) + hbsString = decodeJSBinding(hbsString) as string } let pointer = 0, openPointer = 0, @@ -111,9 +112,9 @@ exports.substituteLoopStep = (hbsString, substitute) => { return substitutedHbsString } -exports.stringSplit = value => { - if (value == null) { - return [] +export function stringSplit(value: string | string[]) { + if (value == null || Array.isArray(value)) { + return value || [] } if (value.split("\n").length > 1) { value = value.split("\n") diff --git a/packages/server/src/automations/bullboard.js b/packages/server/src/automations/bullboard.js deleted file mode 100644 index dd4a6aa383..0000000000 --- a/packages/server/src/automations/bullboard.js +++ /dev/null @@ -1,39 +0,0 @@ -const { createBullBoard } = require("@bull-board/api") -const { BullAdapter } = require("@bull-board/api/bullAdapter") -const { KoaAdapter } = require("@bull-board/koa") -const { queue } = require("@budibase/backend-core") -const automation = require("../threads/automation") -const { backups } = require("@budibase/pro") - -let automationQueue = queue.createQueue( - queue.JobQueue.AUTOMATION, - automation.removeStalled -) - -const PATH_PREFIX = "/bulladmin" - -exports.init = async () => { - // Set up queues for bull board admin - const backupQueue = await backups.getBackupQueue() - const queues = [automationQueue] - if (backupQueue) { - queues.push(backupQueue) - } - const adapters = [] - const serverAdapter = new KoaAdapter() - for (let queue of queues) { - adapters.push(new BullAdapter(queue)) - } - createBullBoard({ - queues: adapters, - serverAdapter, - }) - serverAdapter.setBasePath(PATH_PREFIX) - return serverAdapter.registerPlugin() -} - -exports.shutdown = async () => { - await queue.shutdown() -} - -exports.automationQueue = automationQueue diff --git a/packages/server/src/automations/bullboard.ts b/packages/server/src/automations/bullboard.ts new file mode 100644 index 0000000000..df784eacff --- /dev/null +++ b/packages/server/src/automations/bullboard.ts @@ -0,0 +1,38 @@ +import { BullAdapter } from "@bull-board/api/bullAdapter" +import { KoaAdapter } from "@bull-board/koa" +import { queue } from "@budibase/backend-core" +import * as automation from "../threads/automation" +import { backups } from "@budibase/pro" +import { createBullBoard } from "@bull-board/api" +import BullQueue from "bull" + +export const automationQueue: BullQueue.Queue = queue.createQueue( + queue.JobQueue.AUTOMATION, + { removeStalledCb: automation.removeStalled } +) + +const PATH_PREFIX = "/bulladmin" + +export async function init() { + // Set up queues for bull board admin + const backupQueue = await backups.getBackupQueue() + const queues = [automationQueue] + if (backupQueue) { + queues.push(backupQueue) + } + const adapters = [] + const serverAdapter: any = new KoaAdapter() + for (let queue of queues) { + adapters.push(new BullAdapter(queue)) + } + createBullBoard({ + queues: adapters, + serverAdapter, + }) + serverAdapter.setBasePath(PATH_PREFIX) + return serverAdapter.registerPlugin() +} + +export async function shutdown() { + await queue.shutdown() +} diff --git a/packages/server/src/automations/index.js b/packages/server/src/automations/index.js deleted file mode 100644 index 521991dd2c..0000000000 --- a/packages/server/src/automations/index.js +++ /dev/null @@ -1,29 +0,0 @@ -const { processEvent } = require("./utils") -const { automationQueue, shutdown } = require("./bullboard") -const { TRIGGER_DEFINITIONS, rebootTrigger } = require("./triggers") -const { ACTION_DEFINITIONS } = require("./actions") - -/** - * This module is built purely to kick off the worker farm and manage the inputs/outputs - */ -exports.init = async function () { - // this promise will not complete - const promise = automationQueue.process(async job => { - await processEvent(job) - }) - // on init we need to trigger any reboot automations - await rebootTrigger() - return promise -} - -exports.getQueues = () => { - return [automationQueue] -} - -exports.shutdown = () => { - return shutdown() -} - -exports.automationQueue = automationQueue -exports.TRIGGER_DEFINITIONS = TRIGGER_DEFINITIONS -exports.ACTION_DEFINITIONS = ACTION_DEFINITIONS diff --git a/packages/server/src/automations/index.ts b/packages/server/src/automations/index.ts new file mode 100644 index 0000000000..e46500d33e --- /dev/null +++ b/packages/server/src/automations/index.ts @@ -0,0 +1,26 @@ +import { processEvent } from "./utils" +import { automationQueue } from "./bullboard" +import { rebootTrigger } from "./triggers" +import BullQueue from "bull" + +export { automationQueue } from "./bullboard" +export { shutdown } from "./bullboard" +export { TRIGGER_DEFINITIONS } from "./triggers" +export { ACTION_DEFINITIONS } from "./actions" + +/** + * This module is built purely to kick off the worker farm and manage the inputs/outputs + */ +export async function init() { + // this promise will not complete + const promise = automationQueue.process(async job => { + await processEvent(job) + }) + // on init we need to trigger any reboot automations + await rebootTrigger() + return promise +} + +export function getQueues(): BullQueue.Queue[] { + return [automationQueue] +} diff --git a/packages/server/src/automations/steps/bash.js b/packages/server/src/automations/steps/bash.ts similarity index 72% rename from packages/server/src/automations/steps/bash.js rename to packages/server/src/automations/steps/bash.ts index efa4295e35..5bdf482e70 100644 --- a/packages/server/src/automations/steps/bash.js +++ b/packages/server/src/automations/steps/bash.ts @@ -1,16 +1,21 @@ -const { execSync } = require("child_process") -const { processStringSync } = require("@budibase/string-templates") -const automationUtils = require("../automationUtils") -const environment = require("../../environment") +import { execSync } from "child_process" +import { processStringSync } from "@budibase/string-templates" +import automationUtils from "../automationUtils" +import environment from "../../environment" +import { + AutomationActionStepId, + AutomationStep, + AutomationStepInput, +} from "@budibase/types" -exports.definition = { +export const definition: AutomationStep = { name: "Bash Scripting", tagline: "Execute a bash command", icon: "JourneyEvent", description: "Run a bash script", type: "ACTION", internal: true, - stepId: "EXECUTE_BASH", + stepId: AutomationActionStepId.EXECUTE_BASH, inputs: {}, schema: { inputs: { @@ -39,7 +44,7 @@ exports.definition = { }, } -exports.run = async function ({ inputs, context }) { +export async function run({ inputs, context }: AutomationStepInput) { if (inputs.code == null) { return { stdout: "Budibase bash automation failed: Invalid inputs", @@ -55,7 +60,7 @@ exports.run = async function ({ inputs, context }) { stdout = execSync(command, { timeout: environment.QUERY_THREAD_TIMEOUT || 500, }).toString() - } catch (err) { + } catch (err: any) { stdout = err.message success = false } diff --git a/packages/server/src/automations/steps/createRow.ts b/packages/server/src/automations/steps/createRow.ts index 906a1529ea..a8a139e11c 100644 --- a/packages/server/src/automations/steps/createRow.ts +++ b/packages/server/src/automations/steps/createRow.ts @@ -1,15 +1,20 @@ import { save } from "../../api/controllers/row" import { cleanUpRow, getError } from "../automationUtils" import { buildCtx } from "./utils" +import { + AutomationActionStepId, + AutomationStep, + AutomationStepInput, +} from "@budibase/types" -export const definition = { +export const definition: AutomationStep = { name: "Create Row", tagline: "Create a {{inputs.enriched.table.name}} row", icon: "TableRowAddBottom", description: "Add a row to your database", type: "ACTION", internal: true, - stepId: "CREATE_ROW", + stepId: AutomationActionStepId.CREATE_ROW, inputs: {}, schema: { inputs: { @@ -58,7 +63,7 @@ export const definition = { }, } -export async function run({ inputs, appId, emitter }: any) { +export async function run({ inputs, appId, emitter }: AutomationStepInput) { if (inputs.row == null || inputs.row.tableId == null) { return { success: false, diff --git a/packages/server/src/automations/steps/delay.js b/packages/server/src/automations/steps/delay.ts similarity index 69% rename from packages/server/src/automations/steps/delay.js rename to packages/server/src/automations/steps/delay.ts index ad59df54c5..17a07d5699 100644 --- a/packages/server/src/automations/steps/delay.js +++ b/packages/server/src/automations/steps/delay.ts @@ -1,11 +1,16 @@ -let { wait } = require("../../utilities") +import { wait } from "../../utilities" +import { + AutomationActionStepId, + AutomationStep, + AutomationStepInput, +} from "@budibase/types" -exports.definition = { +export const definition: AutomationStep = { name: "Delay", icon: "Clock", tagline: "Delay for {{inputs.time}} milliseconds", description: "Delay the automation until an amount of time has passed", - stepId: "DELAY", + stepId: AutomationActionStepId.DELAY, internal: true, inputs: {}, schema: { @@ -31,7 +36,7 @@ exports.definition = { type: "LOGIC", } -exports.run = async function delay({ inputs }) { +export async function run({ inputs }: AutomationStepInput) { await wait(inputs.time) return { success: true, diff --git a/packages/server/src/automations/steps/deleteRow.ts b/packages/server/src/automations/steps/deleteRow.ts index 0537d60de7..92978987b2 100644 --- a/packages/server/src/automations/steps/deleteRow.ts +++ b/packages/server/src/automations/steps/deleteRow.ts @@ -1,14 +1,19 @@ import { destroy } from "../../api/controllers/row" import { buildCtx } from "./utils" import { getError } from "../automationUtils" +import { + AutomationActionStepId, + AutomationStep, + AutomationStepInput, +} from "@budibase/types" -export const definition = { +export const definition: AutomationStep = { description: "Delete a row from your database", icon: "TableRowRemoveCenter", name: "Delete Row", tagline: "Delete a {{inputs.enriched.table.name}} row", type: "ACTION", - stepId: "DELETE_ROW", + stepId: AutomationActionStepId.DELETE_ROW, internal: true, inputs: {}, schema: { @@ -47,7 +52,7 @@ export const definition = { }, } -export async function run({ inputs, appId, emitter }: any) { +export async function run({ inputs, appId, emitter }: AutomationStepInput) { if (inputs.id == null) { return { success: false, diff --git a/packages/server/src/automations/steps/discord.js b/packages/server/src/automations/steps/discord.ts similarity index 84% rename from packages/server/src/automations/steps/discord.js rename to packages/server/src/automations/steps/discord.ts index a0b732b11f..75899961a6 100644 --- a/packages/server/src/automations/steps/discord.js +++ b/packages/server/src/automations/steps/discord.ts @@ -1,15 +1,20 @@ -const fetch = require("node-fetch") -const { getFetchResponse } = require("./utils") +import fetch from "node-fetch" +import { getFetchResponse } from "./utils" +import { + AutomationActionStepId, + AutomationStep, + AutomationStepInput, +} from "@budibase/types" const DEFAULT_USERNAME = "Budibase Automate" const DEFAULT_AVATAR_URL = "https://i.imgur.com/a1cmTKM.png" -exports.definition = { +export const definition: AutomationStep = { name: "Discord Message", tagline: "Send a message to a Discord server", description: "Send a message to a Discord server", icon: "ri-discord-line", - stepId: "discord", + stepId: AutomationActionStepId.discord, type: "ACTION", internal: false, inputs: {}, @@ -54,7 +59,7 @@ exports.definition = { }, } -exports.run = async function ({ inputs }) { +export async function run({ inputs }: AutomationStepInput) { let { url, username, avatar_url, content } = inputs if (!username) { username = DEFAULT_USERNAME diff --git a/packages/server/src/automations/steps/executeQuery.js b/packages/server/src/automations/steps/executeQuery.ts similarity index 78% rename from packages/server/src/automations/steps/executeQuery.js rename to packages/server/src/automations/steps/executeQuery.ts index 93cc8e0fb8..d4408441dd 100644 --- a/packages/server/src/automations/steps/executeQuery.js +++ b/packages/server/src/automations/steps/executeQuery.ts @@ -1,14 +1,19 @@ -const queryController = require("../../api/controllers/query") -const { buildCtx } = require("./utils") -const automationUtils = require("../automationUtils") +import * as queryController from "../../api/controllers/query" +import { buildCtx } from "./utils" +import automationUtils from "../automationUtils" +import { + AutomationActionStepId, + AutomationStep, + AutomationStepInput, +} from "@budibase/types" -exports.definition = { +export const definition: AutomationStep = { name: "External Data Connector", tagline: "Execute Data Connector", icon: "Data", description: "Execute a query in an external data connector", type: "ACTION", - stepId: "EXECUTE_QUERY", + stepId: AutomationActionStepId.EXECUTE_QUERY, internal: true, inputs: {}, schema: { @@ -50,7 +55,7 @@ exports.definition = { }, } -exports.run = async function ({ inputs, appId, emitter }) { +export async function run({ inputs, appId, emitter }: AutomationStepInput) { if (inputs.query == null) { return { success: false, @@ -62,7 +67,7 @@ exports.run = async function ({ inputs, appId, emitter }) { const { queryId, ...rest } = inputs.query - const ctx = buildCtx(appId, emitter, { + const ctx: any = buildCtx(appId, emitter, { body: { parameters: rest, }, diff --git a/packages/server/src/automations/steps/executeScript.js b/packages/server/src/automations/steps/executeScript.ts similarity index 70% rename from packages/server/src/automations/steps/executeScript.js rename to packages/server/src/automations/steps/executeScript.ts index 7a7296014b..05e4c9a196 100644 --- a/packages/server/src/automations/steps/executeScript.js +++ b/packages/server/src/automations/steps/executeScript.ts @@ -1,15 +1,20 @@ -const scriptController = require("../../api/controllers/script") -const { buildCtx } = require("./utils") -const automationUtils = require("../automationUtils") +import * as scriptController from "../../api/controllers/script" +import { buildCtx } from "./utils" +import automationUtils from "../automationUtils" +import { + AutomationActionStepId, + AutomationStep, + AutomationStepInput, +} from "@budibase/types" -exports.definition = { +export const definition: AutomationStep = { name: "JS Scripting", tagline: "Execute JavaScript Code", icon: "Code", description: "Run a piece of JavaScript code in your automation", type: "ACTION", internal: true, - stepId: "EXECUTE_SCRIPT", + stepId: AutomationActionStepId.EXECUTE_SCRIPT, inputs: {}, schema: { inputs: { @@ -38,7 +43,12 @@ exports.definition = { }, } -exports.run = async function ({ inputs, appId, context, emitter }) { +export async function run({ + inputs, + appId, + context, + emitter, +}: AutomationStepInput) { if (inputs.code == null) { return { success: false, @@ -48,7 +58,7 @@ exports.run = async function ({ inputs, appId, context, emitter }) { } } - const ctx = buildCtx(appId, emitter, { + const ctx: any = buildCtx(appId, emitter, { body: { script: inputs.code, context, diff --git a/packages/server/src/automations/steps/filter.js b/packages/server/src/automations/steps/filter.ts similarity index 86% rename from packages/server/src/automations/steps/filter.js rename to packages/server/src/automations/steps/filter.ts index 566bb60f22..9eeaefd69d 100644 --- a/packages/server/src/automations/steps/filter.js +++ b/packages/server/src/automations/steps/filter.ts @@ -1,21 +1,24 @@ -const FilterConditions = { +import { + AutomationActionStepId, + AutomationStep, + AutomationStepInput, +} from "@budibase/types" + +export const FilterConditions = { EQUAL: "EQUAL", NOT_EQUAL: "NOT_EQUAL", GREATER_THAN: "GREATER_THAN", LESS_THAN: "LESS_THAN", } -const PrettyFilterConditions = { +export const PrettyFilterConditions = { [FilterConditions.EQUAL]: "Equals", [FilterConditions.NOT_EQUAL]: "Not equals", [FilterConditions.GREATER_THAN]: "Greater than", [FilterConditions.LESS_THAN]: "Less than", } -exports.FilterConditions = FilterConditions -exports.PrettyFilterConditions = PrettyFilterConditions - -exports.definition = { +export const definition: AutomationStep = { name: "Condition", tagline: "{{inputs.field}} {{inputs.condition}} {{inputs.value}}", icon: "Branch2", @@ -23,9 +26,9 @@ exports.definition = { "Conditionally halt automations which do not meet certain conditions", type: "LOGIC", internal: true, - stepId: "FILTER", + stepId: AutomationActionStepId.FILTER, inputs: { - condition: FilterConditions.EQUALS, + condition: FilterConditions.EQUAL, }, schema: { inputs: { @@ -63,7 +66,7 @@ exports.definition = { }, } -exports.run = async function filter({ inputs }) { +export async function run({ inputs }: AutomationStepInput) { try { let { field, condition, value } = inputs // coerce types so that we can use them diff --git a/packages/server/src/automations/steps/integromat.js b/packages/server/src/automations/steps/integromat.ts similarity index 84% rename from packages/server/src/automations/steps/integromat.js rename to packages/server/src/automations/steps/integromat.ts index e7ea03efca..9677d3e06a 100644 --- a/packages/server/src/automations/steps/integromat.js +++ b/packages/server/src/automations/steps/integromat.ts @@ -1,13 +1,18 @@ -const fetch = require("node-fetch") -const { getFetchResponse } = require("./utils") +import fetch from "node-fetch" +import { getFetchResponse } from "./utils" +import { + AutomationActionStepId, + AutomationStep, + AutomationStepInput, +} from "@budibase/types" -exports.definition = { +export const definition: AutomationStep = { name: "Integromat Integration", tagline: "Trigger an Integromat scenario", description: "Performs a webhook call to Integromat and gets the response (if configured)", icon: "ri-shut-down-line", - stepId: "integromat", + stepId: AutomationActionStepId.integromat, type: "ACTION", internal: false, inputs: {}, @@ -61,7 +66,7 @@ exports.definition = { }, } -exports.run = async function ({ inputs }) { +export async function run({ inputs }: AutomationStepInput) { const { url, value1, value2, value3, value4, value5 } = inputs const response = await fetch(url, { diff --git a/packages/server/src/automations/steps/loop.js b/packages/server/src/automations/steps/loop.ts similarity index 87% rename from packages/server/src/automations/steps/loop.js rename to packages/server/src/automations/steps/loop.ts index c77def022a..552d6df6e7 100644 --- a/packages/server/src/automations/steps/loop.js +++ b/packages/server/src/automations/steps/loop.ts @@ -1,9 +1,11 @@ -exports.definition = { +import { AutomationActionStepId, AutomationStep } from "@budibase/types" + +export const definition: AutomationStep = { name: "Looping", icon: "Reuse", tagline: "Loop the block", description: "Loop", - stepId: "LOOP", + stepId: AutomationActionStepId.LOOP, internal: true, inputs: {}, schema: { diff --git a/packages/server/src/automations/steps/outgoingWebhook.js b/packages/server/src/automations/steps/outgoingWebhook.ts similarity index 84% rename from packages/server/src/automations/steps/outgoingWebhook.js rename to packages/server/src/automations/steps/outgoingWebhook.ts index 01d1e8d6be..42eb0342b0 100644 --- a/packages/server/src/automations/steps/outgoingWebhook.js +++ b/packages/server/src/automations/steps/outgoingWebhook.ts @@ -1,13 +1,18 @@ -const fetch = require("node-fetch") -const { getFetchResponse } = require("./utils") -const automationUtils = require("../automationUtils") +import fetch from "node-fetch" +import { getFetchResponse } from "./utils" +import automationUtils from "../automationUtils" +import { + AutomationActionStepId, + AutomationStep, + AutomationStepInput, +} from "@budibase/types" -const RequestType = { - POST: "POST", - GET: "GET", - PUT: "PUT", - DELETE: "DELETE", - PATCH: "PATCH", +enum RequestType { + POST = "POST", + GET = "GET", + PUT = "PUT", + DELETE = "DELETE", + PATCH = "PATCH", } const BODY_REQUESTS = [RequestType.POST, RequestType.PUT, RequestType.PATCH] @@ -16,7 +21,7 @@ const BODY_REQUESTS = [RequestType.POST, RequestType.PUT, RequestType.PATCH] * NOTE: this functionality is deprecated - it no longer should be used. */ -exports.definition = { +export const definition: AutomationStep = { deprecated: true, name: "Outgoing webhook", tagline: "Send a {{inputs.requestMethod}} request", @@ -24,7 +29,7 @@ exports.definition = { description: "Send a request of specified method to a URL", type: "ACTION", internal: true, - stepId: "OUTGOING_WEBHOOK", + stepId: AutomationActionStepId.OUTGOING_WEBHOOK, inputs: { requestMethod: "POST", url: "http://", @@ -76,12 +81,12 @@ exports.definition = { }, } -exports.run = async function ({ inputs }) { +export async function run({ inputs }: AutomationStepInput) { let { requestMethod, url, requestBody, headers } = inputs if (!url.startsWith("http")) { url = `http://${url}` } - const request = { + const request: any = { method: requestMethod, } if (headers) { diff --git a/packages/server/src/automations/steps/queryRows.js b/packages/server/src/automations/steps/queryRows.ts similarity index 61% rename from packages/server/src/automations/steps/queryRows.js rename to packages/server/src/automations/steps/queryRows.ts index 23a7ce55fa..1c9d1f54e0 100644 --- a/packages/server/src/automations/steps/queryRows.js +++ b/packages/server/src/automations/steps/queryRows.ts @@ -1,36 +1,43 @@ -const rowController = require("../../api/controllers/row") -const tableController = require("../../api/controllers/table") -const { FieldTypes } = require("../../constants") -const { buildCtx } = require("./utils") -const automationUtils = require("../automationUtils") +import * as rowController from "../../api/controllers/row" +import * as tableController from "../../api/controllers/table" +import { FieldTypes } from "../../constants" +import { buildCtx } from "./utils" +import automationUtils from "../automationUtils" +import { + AutomationActionStepId, + AutomationStep, + AutomationStepInput, + SearchFilters, + Table, +} from "@budibase/types" -const SortOrders = { - ASCENDING: "ascending", - DESCENDING: "descending", +enum SortOrder { + ASCENDING = "ascending", + DESCENDING = "descending", } -const SortOrdersPretty = { - [SortOrders.ASCENDING]: "Ascending", - [SortOrders.DESCENDING]: "Descending", +const SortOrderPretty = { + [SortOrder.ASCENDING]: "Ascending", + [SortOrder.DESCENDING]: "Descending", } -const EmptyFilterOptions = { - RETURN_ALL: "all", - RETURN_NONE: "none", +enum EmptyFilterOption { + RETURN_ALL = "all", + RETURN_NONE = "none", } -const EmptyFilterOptionsPretty = { - [EmptyFilterOptions.RETURN_ALL]: "Return all table rows", - [EmptyFilterOptions.RETURN_NONE]: "Return no rows", +const EmptyFilterOptionPretty = { + [EmptyFilterOption.RETURN_ALL]: "Return all table rows", + [EmptyFilterOption.RETURN_NONE]: "Return no rows", } -exports.definition = { +export const definition: AutomationStep = { description: "Query rows from the database", icon: "Search", name: "Query rows", tagline: "Query rows from {{inputs.enriched.table.name}} table", type: "ACTION", - stepId: "QUERY_ROWS", + stepId: AutomationActionStepId.QUERY_ROWS, internal: true, inputs: {}, schema: { @@ -54,8 +61,8 @@ exports.definition = { sortOrder: { type: "string", title: "Sort Order", - enum: Object.values(SortOrders), - pretty: Object.values(SortOrdersPretty), + enum: Object.values(SortOrder), + pretty: Object.values(SortOrderPretty), }, limit: { type: "number", @@ -63,8 +70,8 @@ exports.definition = { customType: "queryLimit", }, onEmptyFilter: { - pretty: Object.values(EmptyFilterOptionsPretty), - enum: Object.values(EmptyFilterOptions), + pretty: Object.values(EmptyFilterOptionPretty), + enum: Object.values(EmptyFilterOption), type: "string", title: "When Filter Empty", }, @@ -88,8 +95,8 @@ exports.definition = { }, } -async function getTable(appId, tableId) { - const ctx = buildCtx(appId, null, { +async function getTable(appId: string, tableId: string) { + const ctx: any = buildCtx(appId, null, { params: { tableId, }, @@ -98,20 +105,22 @@ async function getTable(appId, tableId) { return ctx.body } -function typeCoercion(filters, table) { +function typeCoercion(filters: SearchFilters, table: Table) { if (!filters || !table) { return filters } for (let key of Object.keys(filters)) { - if (typeof filters[key] === "object") { - for (let [property, value] of Object.entries(filters[key])) { + // @ts-ignore + const searchParam = filters[key] + if (typeof searchParam === "object") { + for (let [property, value] of Object.entries(searchParam)) { const column = table.schema[property] // convert string inputs if (!column || typeof value !== "string") { continue } if (column.type === FieldTypes.NUMBER) { - filters[key][property] = parseFloat(value) + searchParam[property] = parseFloat(value) } } } @@ -119,11 +128,14 @@ function typeCoercion(filters, table) { return filters } -const hasNullFilters = filters => - filters.length === 0 || - filters.some(filter => filter.value === null || filter.value === "") +function hasNullFilters(filters: any[]) { + return ( + filters.length === 0 || + filters.some(filter => filter.value === null || filter.value === "") + ) +} -exports.run = async function ({ inputs, appId }) { +export async function run({ inputs, appId }: AutomationStepInput) { const { tableId, filters, sortColumn, sortOrder, limit } = inputs if (!tableId) { return { @@ -140,7 +152,7 @@ exports.run = async function ({ inputs, appId }) { sortType = fieldType === FieldTypes.NUMBER ? FieldTypes.NUMBER : FieldTypes.STRING } - const ctx = buildCtx(appId, null, { + const ctx: any = buildCtx(appId, null, { params: { tableId, }, @@ -150,7 +162,7 @@ exports.run = async function ({ inputs, appId }) { sort: sortColumn, query: typeCoercion(filters || {}, table), // default to ascending, like data tab - sortOrder: sortOrder || SortOrders.ASCENDING, + sortOrder: sortOrder || SortOrder.ASCENDING, }, version: "1", }) @@ -158,7 +170,7 @@ exports.run = async function ({ inputs, appId }) { let rows if ( - inputs.onEmptyFilter === EmptyFilterOptions.RETURN_NONE && + inputs.onEmptyFilter === EmptyFilterOption.RETURN_NONE && inputs["filters-def"] && hasNullFilters(inputs["filters-def"]) ) { diff --git a/packages/server/src/automations/steps/sendSmtpEmail.js b/packages/server/src/automations/steps/sendSmtpEmail.ts similarity index 81% rename from packages/server/src/automations/steps/sendSmtpEmail.js rename to packages/server/src/automations/steps/sendSmtpEmail.ts index 423363701b..a2680a05cb 100644 --- a/packages/server/src/automations/steps/sendSmtpEmail.js +++ b/packages/server/src/automations/steps/sendSmtpEmail.ts @@ -1,14 +1,19 @@ -const { sendSmtpEmail } = require("../../utilities/workerRequests") -const automationUtils = require("../automationUtils") +import { sendSmtpEmail } from "../../utilities/workerRequests" +import automationUtils from "../automationUtils" +import { + AutomationActionStepId, + AutomationStep, + AutomationStepInput, +} from "@budibase/types" -exports.definition = { +export const definition: AutomationStep = { description: "Send an email using SMTP", tagline: "Send SMTP email to {{inputs.to}}", icon: "Email", name: "Send Email (SMTP)", type: "ACTION", internal: true, - stepId: "SEND_EMAIL_SMTP", + stepId: AutomationActionStepId.SEND_EMAIL_SMTP, inputs: {}, schema: { inputs: { @@ -56,7 +61,7 @@ exports.definition = { }, } -exports.run = async function ({ inputs }) { +export async function run({ inputs }: AutomationStepInput) { let { to, from, subject, contents, cc, bcc } = inputs if (!contents) { contents = "

No content

" diff --git a/packages/server/src/automations/steps/serverLog.js b/packages/server/src/automations/steps/serverLog.ts similarity index 80% rename from packages/server/src/automations/steps/serverLog.js rename to packages/server/src/automations/steps/serverLog.ts index 19bbc36abf..04eba3e6d4 100644 --- a/packages/server/src/automations/steps/serverLog.js +++ b/packages/server/src/automations/steps/serverLog.ts @@ -1,17 +1,23 @@ +import { + AutomationActionStepId, + AutomationStep, + AutomationStepInput, +} from "@budibase/types" + /** * Note, there is some functionality in this that is not currently exposed as it * is complex and maybe better to be opinionated here. * GET/DELETE requests cannot handle body elements so they will not be sent if configured. */ -exports.definition = { +export const definition: AutomationStep = { name: "Backend log", tagline: "Console log a value in the backend", icon: "Monitoring", description: "Logs the given text to the server (using console.log)", type: "ACTION", internal: true, - stepId: "SERVER_LOG", + stepId: AutomationActionStepId.SERVER_LOG, inputs: { text: "", }, @@ -41,7 +47,7 @@ exports.definition = { }, } -exports.run = async function ({ inputs, appId }) { +export async function run({ inputs, appId }: AutomationStepInput) { const message = `App ${appId} - ${inputs.text}` console.log(message) return { diff --git a/packages/server/src/automations/steps/slack.js b/packages/server/src/automations/steps/slack.ts similarity index 79% rename from packages/server/src/automations/steps/slack.js rename to packages/server/src/automations/steps/slack.ts index ec6341a26f..ddd26c98e2 100644 --- a/packages/server/src/automations/steps/slack.js +++ b/packages/server/src/automations/steps/slack.ts @@ -1,12 +1,17 @@ -const fetch = require("node-fetch") -const { getFetchResponse } = require("./utils") +import fetch from "node-fetch" +import { getFetchResponse } from "./utils" +import { + AutomationActionStepId, + AutomationStep, + AutomationStepInput, +} from "@budibase/types" -exports.definition = { +export const definition: AutomationStep = { name: "Slack Message", tagline: "Send a message to Slack", description: "Send a message to Slack", icon: "ri-slack-line", - stepId: "slack", + stepId: AutomationActionStepId.slack, type: "ACTION", internal: false, inputs: {}, @@ -43,7 +48,7 @@ exports.definition = { }, } -exports.run = async function ({ inputs }) { +export async function run({ inputs }: AutomationStepInput) { let { url, text } = inputs const response = await fetch(url, { method: "post", diff --git a/packages/server/src/automations/steps/updateRow.js b/packages/server/src/automations/steps/updateRow.ts similarity index 82% rename from packages/server/src/automations/steps/updateRow.js rename to packages/server/src/automations/steps/updateRow.ts index 5a2c158c5f..ed8e8a02ee 100644 --- a/packages/server/src/automations/steps/updateRow.js +++ b/packages/server/src/automations/steps/updateRow.ts @@ -1,15 +1,20 @@ -const rowController = require("../../api/controllers/row") -const automationUtils = require("../automationUtils") -const { buildCtx } = require("./utils") +import * as rowController from "../../api/controllers/row" +import automationUtils from "../automationUtils" +import { buildCtx } from "./utils" +import { + AutomationActionStepId, + AutomationStep, + AutomationStepInput, +} from "@budibase/types" -exports.definition = { +export const definition: AutomationStep = { name: "Update Row", tagline: "Update a {{inputs.enriched.table.name}} row", icon: "Refresh", description: "Update a row in your database", type: "ACTION", internal: true, - stepId: "UPDATE_ROW", + stepId: AutomationActionStepId.UPDATE_ROW, inputs: {}, schema: { inputs: { @@ -55,7 +60,7 @@ exports.definition = { }, } -exports.run = async function ({ inputs, appId, emitter }) { +export async function run({ inputs, appId, emitter }: AutomationStepInput) { if (inputs.rowId == null || inputs.row == null) { return { success: false, @@ -74,7 +79,7 @@ exports.run = async function ({ inputs, appId, emitter }) { } // have to clean up the row, remove the table from it - const ctx = buildCtx(appId, emitter, { + const ctx: any = buildCtx(appId, emitter, { body: { ...inputs.row, _id: inputs.rowId, diff --git a/packages/server/src/automations/steps/utils.js b/packages/server/src/automations/steps/utils.ts similarity index 76% rename from packages/server/src/automations/steps/utils.js rename to packages/server/src/automations/steps/utils.ts index ed9a441499..02b0f4d34c 100644 --- a/packages/server/src/automations/steps/utils.js +++ b/packages/server/src/automations/steps/utils.ts @@ -1,4 +1,6 @@ -exports.getFetchResponse = async fetched => { +import { EventEmitter } from "events" + +export async function getFetchResponse(fetched: any) { let status = fetched.status, message const contentType = fetched.headers.get("content-type") @@ -18,12 +20,16 @@ exports.getFetchResponse = async fetched => { // throw added to them, so that controllers don't // throw a ctx.throw undefined when error occurs // opts can contain, body, params and version -exports.buildCtx = (appId, emitter, opts = {}) => { - const ctx = { +export async function buildCtx( + appId: string, + emitter?: EventEmitter | null, + opts: any = {} +) { + const ctx: any = { appId, user: { appId }, eventEmitter: emitter, - throw: (code, error) => { + throw: (code: string, error: any) => { throw error }, } diff --git a/packages/server/src/automations/steps/zapier.js b/packages/server/src/automations/steps/zapier.ts similarity index 83% rename from packages/server/src/automations/steps/zapier.js rename to packages/server/src/automations/steps/zapier.ts index bec90497cd..96bc90c4d0 100644 --- a/packages/server/src/automations/steps/zapier.js +++ b/packages/server/src/automations/steps/zapier.ts @@ -1,14 +1,20 @@ -const fetch = require("node-fetch") -const { getFetchResponse } = require("./utils") +import fetch from "node-fetch" +import { getFetchResponse } from "./utils" +import { + AutomationActionStepId, + AutomationStep, + AutomationStepInput, +} from "@budibase/types" -exports.definition = { +export const definition: AutomationStep = { name: "Zapier Webhook", - stepId: "zapier", + stepId: AutomationActionStepId.zapier, type: "ACTION", internal: false, description: "Trigger a Zapier Zap via webhooks", tagline: "Trigger a Zapier Zap", icon: "ri-flashlight-line", + inputs: {}, schema: { inputs: { properties: { @@ -54,7 +60,7 @@ exports.definition = { }, } -exports.run = async function ({ inputs }) { +export async function run({ inputs }: AutomationStepInput) { const { url, value1, value2, value3, value4, value5 } = inputs // send the platform to make sure zaps always work, even diff --git a/packages/server/src/automations/triggerInfo/app.js b/packages/server/src/automations/triggerInfo/app.ts similarity index 80% rename from packages/server/src/automations/triggerInfo/app.js rename to packages/server/src/automations/triggerInfo/app.ts index b1b07e9d5b..fe1413e88e 100644 --- a/packages/server/src/automations/triggerInfo/app.js +++ b/packages/server/src/automations/triggerInfo/app.ts @@ -1,10 +1,12 @@ -exports.definition = { +import { AutomationTrigger, AutomationTriggerStepId } from "@budibase/types" + +export const definition: AutomationTrigger = { name: "App Action", event: "app:trigger", icon: "Apps", tagline: "Automation fired from the frontend", description: "Trigger an automation from an action inside your app", - stepId: "APP", + stepId: AutomationTriggerStepId.APP, inputs: {}, schema: { inputs: { diff --git a/packages/server/src/automations/triggerInfo/cron.js b/packages/server/src/automations/triggerInfo/cron.ts similarity index 78% rename from packages/server/src/automations/triggerInfo/cron.js rename to packages/server/src/automations/triggerInfo/cron.ts index 9547da69fa..fc40d33683 100644 --- a/packages/server/src/automations/triggerInfo/cron.js +++ b/packages/server/src/automations/triggerInfo/cron.ts @@ -1,10 +1,12 @@ -exports.definition = { +import { AutomationTrigger, AutomationTriggerStepId } from "@budibase/types" + +export const definition: AutomationTrigger = { name: "Cron Trigger", event: "cron:trigger", icon: "Clock", tagline: "Cron Trigger ({{inputs.cron}})", description: "Triggers automation on a cron schedule.", - stepId: "CRON", + stepId: AutomationTriggerStepId.CRON, inputs: {}, schema: { inputs: { diff --git a/packages/server/src/automations/triggerInfo/index.js b/packages/server/src/automations/triggerInfo/index.js deleted file mode 100644 index 066993d324..0000000000 --- a/packages/server/src/automations/triggerInfo/index.js +++ /dev/null @@ -1,15 +0,0 @@ -const app = require("./app") -const cron = require("./cron") -const rowDeleted = require("./rowDeleted") -const rowSaved = require("./rowSaved") -const rowUpdated = require("./rowUpdated") -const webhook = require("./webhook") - -exports.definitions = { - ROW_SAVED: rowSaved.definition, - ROW_UPDATED: rowUpdated.definition, - ROW_DELETED: rowDeleted.definition, - WEBHOOK: webhook.definition, - APP: app.definition, - CRON: cron.definition, -} diff --git a/packages/server/src/automations/triggerInfo/index.ts b/packages/server/src/automations/triggerInfo/index.ts new file mode 100644 index 0000000000..50089c98e9 --- /dev/null +++ b/packages/server/src/automations/triggerInfo/index.ts @@ -0,0 +1,15 @@ +import app from "./app" +import cron from "./cron" +import rowDeleted from "./rowDeleted" +import rowSaved from "./rowSaved" +import rowUpdated from "./rowUpdated" +import webhook from "./webhook" + +export const definitions = { + ROW_SAVED: rowSaved.definition, + ROW_UPDATED: rowUpdated.definition, + ROW_DELETED: rowDeleted.definition, + WEBHOOK: webhook.definition, + APP: app.definition, + CRON: cron.definition, +} diff --git a/packages/server/src/automations/triggerInfo/rowDeleted.js b/packages/server/src/automations/triggerInfo/rowDeleted.ts similarity index 79% rename from packages/server/src/automations/triggerInfo/rowDeleted.js rename to packages/server/src/automations/triggerInfo/rowDeleted.ts index 7a7de13b11..ab238800ea 100644 --- a/packages/server/src/automations/triggerInfo/rowDeleted.js +++ b/packages/server/src/automations/triggerInfo/rowDeleted.ts @@ -1,10 +1,12 @@ -exports.definition = { +import { AutomationTrigger, AutomationTriggerStepId } from "@budibase/types" + +export const definition: AutomationTrigger = { name: "Row Deleted", event: "row:delete", icon: "TableRowRemoveCenter", tagline: "Row is deleted from {{inputs.enriched.table.name}}", description: "Fired when a row is deleted from your database", - stepId: "ROW_DELETED", + stepId: AutomationTriggerStepId.ROW_DELETED, inputs: {}, schema: { inputs: { diff --git a/packages/server/src/automations/triggerInfo/rowSaved.js b/packages/server/src/automations/triggerInfo/rowSaved.ts similarity index 83% rename from packages/server/src/automations/triggerInfo/rowSaved.js rename to packages/server/src/automations/triggerInfo/rowSaved.ts index d763ca0a8a..55cf60c594 100644 --- a/packages/server/src/automations/triggerInfo/rowSaved.js +++ b/packages/server/src/automations/triggerInfo/rowSaved.ts @@ -1,10 +1,12 @@ -exports.definition = { +import { AutomationTrigger, AutomationTriggerStepId } from "@budibase/types" + +export const definition: AutomationTrigger = { name: "Row Created", event: "row:save", icon: "TableRowAddBottom", tagline: "Row is added to {{inputs.enriched.table.name}}", description: "Fired when a row is added to your database", - stepId: "ROW_SAVED", + stepId: AutomationTriggerStepId.ROW_SAVED, inputs: {}, schema: { inputs: { diff --git a/packages/server/src/automations/triggerInfo/rowUpdated.js b/packages/server/src/automations/triggerInfo/rowUpdated.ts similarity index 83% rename from packages/server/src/automations/triggerInfo/rowUpdated.js rename to packages/server/src/automations/triggerInfo/rowUpdated.ts index 182c7c810e..6cc6d303c9 100644 --- a/packages/server/src/automations/triggerInfo/rowUpdated.js +++ b/packages/server/src/automations/triggerInfo/rowUpdated.ts @@ -1,10 +1,12 @@ -exports.definition = { +import { AutomationTrigger, AutomationTriggerStepId } from "@budibase/types" + +export const definition: AutomationTrigger = { name: "Row Updated", event: "row:update", icon: "Refresh", tagline: "Row is updated in {{inputs.enriched.table.name}}", description: "Fired when a row is updated in your database", - stepId: "ROW_UPDATED", + stepId: AutomationTriggerStepId.ROW_UPDATED, inputs: {}, schema: { inputs: { diff --git a/packages/server/src/automations/triggerInfo/webhook.js b/packages/server/src/automations/triggerInfo/webhook.ts similarity index 82% rename from packages/server/src/automations/triggerInfo/webhook.js rename to packages/server/src/automations/triggerInfo/webhook.ts index 56e139311c..762b871193 100644 --- a/packages/server/src/automations/triggerInfo/webhook.js +++ b/packages/server/src/automations/triggerInfo/webhook.ts @@ -1,10 +1,12 @@ -exports.definition = { +import { AutomationTrigger, AutomationTriggerStepId } from "@budibase/types" + +export const definition: AutomationTrigger = { name: "Webhook", event: "web:trigger", icon: "Send", tagline: "Webhook endpoint is hit", description: "Trigger an automation when a HTTP POST webhook is hit", - stepId: "WEBHOOK", + stepId: AutomationTriggerStepId.WEBHOOK, inputs: {}, schema: { inputs: { diff --git a/packages/server/src/automations/triggers.js b/packages/server/src/automations/triggers.ts similarity index 77% rename from packages/server/src/automations/triggers.js rename to packages/server/src/automations/triggers.ts index 4f865d4df8..f4b34bc9e8 100644 --- a/packages/server/src/automations/triggers.js +++ b/packages/server/src/automations/triggers.ts @@ -1,16 +1,17 @@ -const emitter = require("../events/index") -const { getAutomationParams } = require("../db/utils") -const { coerce } = require("../utilities/rowProcessor") -const { definitions } = require("./triggerInfo") -const { isDevAppID } = require("../db/utils") +import emitter from "../events/index" +import { getAutomationParams } from "../db/utils" +import { coerce } from "../utilities/rowProcessor" +import { definitions } from "./triggerInfo" +import { isDevAppID } from "../db/utils" // need this to call directly, so we can get a response -const { automationQueue } = require("./bullboard") -const { checkTestFlag } = require("../utilities/redis") -const utils = require("./utils") -const env = require("../environment") -const { context, db: dbCore } = require("@budibase/backend-core") +import { automationQueue } from "./bullboard" +import { checkTestFlag } from "../utilities/redis" +import * as utils from "./utils" +import env from "../environment" +import { context, db as dbCore } from "@budibase/backend-core" +import { Automation, Row } from "@budibase/types" -const TRIGGER_DEFINITIONS = definitions +export const TRIGGER_DEFINITIONS = definitions const JOB_OPTS = { removeOnComplete: true, removeOnFail: true, @@ -24,12 +25,15 @@ async function getAllAutomations() { return automations.rows.map(row => row.doc) } -async function queueRelevantRowAutomations(event, eventType) { +async function queueRelevantRowAutomations( + event: { appId: string; row: Row }, + eventType: string +) { if (event.appId == null) { throw `No appId specified for ${eventType} - check event emitters.` } - context.doInAppContext(event.appId, async () => { + await context.doInAppContext(event.appId, async () => { let automations = await getAllAutomations() // filter down to the correct event type @@ -85,20 +89,20 @@ emitter.on("row:delete", async function (event) { await queueRelevantRowAutomations(event, "row:delete") }) -exports.externalTrigger = async function ( - automation, - params, - { getResponses } = {} +export async function externalTrigger( + automation: Automation, + params: { fields: Record }, + { getResponses }: { getResponses?: boolean } = {} ) { if ( automation.definition != null && automation.definition.trigger != null && automation.definition.trigger.stepId === definitions.APP.stepId && automation.definition.trigger.stepId === "APP" && - !(await checkTestFlag(automation._id)) + !(await checkTestFlag(automation._id!)) ) { // values are likely to be submitted as strings, so we shall convert to correct type - const coercedFields = {} + const coercedFields: any = {} const fields = automation.definition.trigger.inputs.fields for (let key of Object.keys(fields || {})) { coercedFields[key] = coerce(params.fields[key], fields[key]) @@ -113,7 +117,7 @@ exports.externalTrigger = async function ( } } -exports.rebootTrigger = async () => { +export async function rebootTrigger() { // reboot cron option is only available on the main thread at // startup and only usable in self host and single tenant environments if (env.isInThread() || !env.SELF_HOSTED || env.MULTI_TENANCY) { @@ -121,7 +125,10 @@ exports.rebootTrigger = async () => { } // iterate through all production apps, find the reboot crons // and trigger events for them - const appIds = await dbCore.getAllApps({ dev: false, idsOnly: true }) + const appIds = (await dbCore.getAllApps({ + dev: false, + idsOnly: true, + })) as string[] for (let prodAppId of appIds) { await context.doInAppContext(prodAppId, async () => { let automations = await getAllAutomations() @@ -142,5 +149,3 @@ exports.rebootTrigger = async () => { }) } } - -exports.TRIGGER_DEFINITIONS = TRIGGER_DEFINITIONS diff --git a/packages/server/src/environment.js b/packages/server/src/environment.ts similarity index 93% rename from packages/server/src/environment.js rename to packages/server/src/environment.ts index 72baab9167..d3b884c12c 100644 --- a/packages/server/src/environment.js +++ b/packages/server/src/environment.ts @@ -1,4 +1,4 @@ -const { join } = require("path") +import { join } from "path" function isTest() { return isCypress() || isJest() @@ -28,7 +28,7 @@ if (!LOADED && isDev() && !isTest()) { LOADED = true } -function parseIntSafe(number) { +function parseIntSafe(number?: string) { if (number) { return parseInt(number) } @@ -36,7 +36,7 @@ function parseIntSafe(number) { let inThread = false -module.exports = { +const environment = { // important - prefer app port to generic port PORT: process.env.APP_PORT || process.env.PORT, JWT_SECRET: process.env.JWT_SECRET, @@ -86,7 +86,7 @@ module.exports = { SELF_HOSTED: process.env.SELF_HOSTED, // old CLIENT_ID: process.env.CLIENT_ID, - _set(key, value) { + _set(key: string, value: any) { process.env[key] = value module.exports[key] = value }, @@ -108,13 +108,16 @@ module.exports = { // threading can cause memory issues with node-ts in development if (isDev() && module.exports.DISABLE_THREADING == null) { - module.exports._set("DISABLE_THREADING", "1") + environment._set("DISABLE_THREADING", "1") } // clean up any environment variable edge cases for (let [key, value] of Object.entries(module.exports)) { // handle the edge case of "0" to disable an environment variable if (value === "0") { - module.exports[key] = 0 + // @ts-ignore + environment[key] = 0 } } + +export = environment diff --git a/packages/server/src/utilities/workerRequests.ts b/packages/server/src/utilities/workerRequests.ts index b5554bbe6f..d1fd467025 100644 --- a/packages/server/src/utilities/workerRequests.ts +++ b/packages/server/src/utilities/workerRequests.ts @@ -62,7 +62,7 @@ export async function sendSmtpEmail( contents: string, cc: string, bcc: string, - automation: Automation + automation: boolean ) { // tenant ID will be set in header const response = await fetch( diff --git a/packages/types/src/documents/app/automation.ts b/packages/types/src/documents/app/automation.ts index d7450e4b0d..ba718aa8d5 100644 --- a/packages/types/src/documents/app/automation.ts +++ b/packages/types/src/documents/app/automation.ts @@ -1,4 +1,5 @@ import { Document } from "../document" +import { EventEmitter } from "events" export enum AutomationTriggerStepId { ROW_SAVED = "ROW_SAVED", @@ -14,6 +15,7 @@ export enum AutomationActionStepId { CREATE_ROW = "CREATE_ROW", UPDATE_ROW = "UPDATE_ROW", DELETE_ROW = "DELETE_ROW", + EXECUTE_BASH = "EXECUTE_BASH", OUTGOING_WEBHOOK = "OUTGOING_WEBHOOK", EXECUTE_SCRIPT = "EXECUTE_SCRIPT", EXECUTE_QUERY = "EXECUTE_QUERY", @@ -40,7 +42,14 @@ export interface Automation extends Document { } export interface AutomationStep { - id: string + id?: string + name: string + tagline: string + icon: string + description: string + type: string + internal?: boolean + deprecated?: boolean stepId: AutomationTriggerStepId | AutomationActionStepId inputs: { [key: string]: any @@ -52,10 +61,12 @@ export interface AutomationStep { outputs: { [key: string]: any } + required?: string[] } } export interface AutomationTrigger extends AutomationStep { + event?: string cronJobId?: string } @@ -91,3 +102,10 @@ export interface AutomationLogPage { hasNextPage: boolean nextPage?: string } + +export type AutomationStepInput = { + inputs: Record + context: Record + emitter: EventEmitter + appId: string +} diff --git a/packages/worker/src/environment.ts b/packages/worker/src/environment.ts index c9eabdfdb4..c67679395f 100644 --- a/packages/worker/src/environment.ts +++ b/packages/worker/src/environment.ts @@ -26,7 +26,7 @@ function parseIntSafe(number: any) { } } -const env = { +const environment = { // auth MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY, MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY, @@ -77,8 +77,10 @@ const env = { } // if some var haven't been set, define them -if (!env.APPS_URL) { - env.APPS_URL = isDev() ? "http://localhost:4001" : "http://app-service:4002" +if (!environment.APPS_URL) { + environment.APPS_URL = isDev() + ? "http://localhost:4001" + : "http://app-service:4002" } // clean up any environment variable edge cases @@ -90,4 +92,4 @@ for (let [key, value] of Object.entries(module.exports)) { } } -export = env +export = environment