Automation overhaul to Typescript, plus type updates.
This commit is contained in:
parent
eeebd0fe70
commit
a3bb2e0d77
|
@ -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
|
||||
|
|
|
@ -39,7 +39,7 @@ export function createQueue<T>(
|
|||
return queue
|
||||
}
|
||||
|
||||
exports.shutdown = async () => {
|
||||
export async function shutdown() {
|
||||
if (QUEUES.length) {
|
||||
clearInterval(cleanupInterval)
|
||||
for (let queue of QUEUES) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<any>
|
||||
> = {
|
||||
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<string, AutomationStep> = {
|
||||
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
|
|
@ -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<string, any>, 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<Object>} 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")
|
|
@ -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
|
|
@ -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()
|
||||
}
|
|
@ -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
|
|
@ -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]
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
|
@ -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,
|
||||
|
|
|
@ -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
|
|
@ -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,
|
||||
},
|
|
@ -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,
|
|
@ -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
|
|
@ -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, {
|
|
@ -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: {
|
|
@ -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) {
|
|
@ -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"])
|
||||
) {
|
|
@ -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 = "<h1>No content</h1>"
|
|
@ -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 {
|
|
@ -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",
|
|
@ -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,
|
|
@ -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
|
||||
},
|
||||
}
|
|
@ -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
|
|
@ -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: {
|
|
@ -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 (<b>{{inputs.cron}}</b>)",
|
||||
description: "Triggers automation on a cron schedule.",
|
||||
stepId: "CRON",
|
||||
stepId: AutomationTriggerStepId.CRON,
|
||||
inputs: {},
|
||||
schema: {
|
||||
inputs: {
|
|
@ -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,
|
||||
}
|
|
@ -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,
|
||||
}
|
|
@ -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: {
|
|
@ -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: {
|
|
@ -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: {
|
|
@ -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: {
|
|
@ -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<string, any> },
|
||||
{ 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
|
|
@ -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
|
|
@ -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(
|
||||
|
|
|
@ -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<string, any>
|
||||
context: Record<string, any>
|
||||
emitter: EventEmitter
|
||||
appId: string
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue