Cleanup, prepping for automation history, some refactoring to get rid of concept of builtin.

This commit is contained in:
mike12345567 2021-09-06 17:53:02 +01:00
parent 5cc5bd4533
commit 2531d9a681
30 changed files with 290 additions and 331 deletions

View File

@ -5,8 +5,8 @@ const triggers = require("../../automations/triggers")
const webhooks = require("./webhook") const webhooks = require("./webhook")
const { getAutomationParams, generateAutomationID } = require("../../db/utils") const { getAutomationParams, generateAutomationID } = require("../../db/utils")
const WH_STEP_ID = triggers.BUILTIN_DEFINITIONS.WEBHOOK.stepId const WH_STEP_ID = triggers.TRIGGER_DEFINITIONS.WEBHOOK.stepId
const CRON_STEP_ID = triggers.BUILTIN_DEFINITIONS.CRON.stepId const CRON_STEP_ID = triggers.TRIGGER_DEFINITIONS.CRON.stepId
/************************* /*************************
* * * *
@ -242,22 +242,22 @@ exports.destroy = async function (ctx) {
} }
exports.getActionList = async function (ctx) { exports.getActionList = async function (ctx) {
ctx.body = actions.DEFINITIONS ctx.body = actions.ACTION_DEFINITIONS
} }
exports.getTriggerList = async function (ctx) { exports.getTriggerList = async function (ctx) {
ctx.body = triggers.BUILTIN_DEFINITIONS ctx.body = triggers.TRIGGER_DEFINITIONS
} }
exports.getLogicList = async function (ctx) { exports.getLogicList = async function (ctx) {
ctx.body = logic.BUILTIN_DEFINITIONS ctx.body = logic.LOGIC_DEFINITIONS
} }
module.exports.getDefinitionList = async function (ctx) { module.exports.getDefinitionList = async function (ctx) {
ctx.body = { ctx.body = {
logic: logic.BUILTIN_DEFINITIONS, logic: logic.LOGIC_DEFINITIONS,
trigger: triggers.BUILTIN_DEFINITIONS, trigger: triggers.TRIGGER_DEFINITIONS,
action: actions.DEFINITIONS, action: actions.ACTION_DEFINITIONS,
} }
} }

View File

@ -44,7 +44,7 @@ describe("/automations", () => {
ACTION_DEFINITIONS = res.body ACTION_DEFINITIONS = res.body
}) })
it("returns a list of definitions for triggers", async () => { it("returns a list of definitions for triggerInfo", async () => {
const res = await request const res = await request
.get(`/api/automations/trigger/list`) .get(`/api/automations/trigger/list`)
.set(config.defaultHeaders()) .set(config.defaultHeaders())

View File

@ -13,7 +13,7 @@ const discord = require("./steps/discord")
const zapier = require("./steps/zapier") const zapier = require("./steps/zapier")
const integromat = require("./steps/integromat") const integromat = require("./steps/integromat")
const BUILTIN_ACTIONS = { const ACTION_IMPLS = {
SEND_EMAIL: sendgridEmail.run, SEND_EMAIL: sendgridEmail.run,
SEND_EMAIL_SMTP: sendSmtpEmail.run, SEND_EMAIL_SMTP: sendSmtpEmail.run,
CREATE_ROW: createRow.run, CREATE_ROW: createRow.run,
@ -29,7 +29,7 @@ const BUILTIN_ACTIONS = {
zapier: zapier.run, zapier: zapier.run,
integromat: integromat.run, integromat: integromat.run,
} }
const BUILTIN_DEFINITIONS = { const ACTION_DEFINITIONS = {
SEND_EMAIL: sendgridEmail.definition, SEND_EMAIL: sendgridEmail.definition,
SEND_EMAIL_SMTP: sendSmtpEmail.definition, SEND_EMAIL_SMTP: sendSmtpEmail.definition,
CREATE_ROW: createRow.definition, CREATE_ROW: createRow.definition,
@ -47,12 +47,10 @@ const BUILTIN_DEFINITIONS = {
} }
/* istanbul ignore next */ /* istanbul ignore next */
module.exports.getAction = async function (actionName) { exports.getAction = async function (actionName) {
if (BUILTIN_ACTIONS[actionName] != null) { if (ACTION_IMPLS[actionName] != null) {
return BUILTIN_ACTIONS[actionName] return ACTION_IMPLS[actionName]
} }
} }
// definitions will have downloaded ones added to it, while builtin won't exports.ACTION_DEFINITIONS = ACTION_DEFINITIONS
module.exports.DEFINITIONS = BUILTIN_DEFINITIONS
module.exports.BUILTIN_DEFINITIONS = BUILTIN_DEFINITIONS

View File

@ -1,20 +1,20 @@
let filter = require("./steps/filter") let filter = require("./steps/filter")
let delay = require("./steps/delay") let delay = require("./steps/delay")
let BUILTIN_LOGIC = { let LOGIC_IMPLS = {
DELAY: delay.run, DELAY: delay.run,
FILTER: filter.run, FILTER: filter.run,
} }
let BUILTIN_DEFINITIONS = { let LOGIC_DEFINITIONS = {
DELAY: delay.definition, DELAY: delay.definition,
FILTER: filter.definition, FILTER: filter.definition,
} }
module.exports.getLogic = function (logicName) { exports.getLogic = function (logicName) {
if (BUILTIN_LOGIC[logicName] != null) { if (LOGIC_IMPLS[logicName] != null) {
return BUILTIN_LOGIC[logicName] return LOGIC_IMPLS[logicName]
} }
} }
module.exports.BUILTIN_DEFINITIONS = BUILTIN_DEFINITIONS exports.LOGIC_DEFINITIONS = LOGIC_DEFINITIONS

View File

@ -1,7 +1,7 @@
const { execSync } = require("child_process") const { execSync } = require("child_process")
const { processStringSync } = require("@budibase/string-templates") const { processStringSync } = require("@budibase/string-templates")
module.exports.definition = { exports.definition = {
name: "Bash Scripting", name: "Bash Scripting",
tagline: "Execute a bash command", tagline: "Execute a bash command",
icon: "ri-terminal-box-line", icon: "ri-terminal-box-line",
@ -32,7 +32,7 @@ module.exports.definition = {
}, },
} }
module.exports.run = async function ({ inputs, context }) { exports.run = async function ({ inputs, context }) {
if (inputs.code == null) { if (inputs.code == null) {
return { return {
stdout: "Budibase bash automation failed: Invalid inputs", stdout: "Budibase bash automation failed: Invalid inputs",

View File

@ -3,7 +3,7 @@ const automationUtils = require("../automationUtils")
const env = require("../../environment") const env = require("../../environment")
const usage = require("../../utilities/usageQuota") const usage = require("../../utilities/usageQuota")
module.exports.definition = { exports.definition = {
name: "Create Row", name: "Create Row",
tagline: "Create a {{inputs.enriched.table.name}} row", tagline: "Create a {{inputs.enriched.table.name}} row",
icon: "ri-save-3-line", icon: "ri-save-3-line",
@ -58,7 +58,7 @@ module.exports.definition = {
}, },
} }
module.exports.run = async function ({ inputs, appId, apiKey, emitter }) { exports.run = async function ({ inputs, appId, apiKey, emitter }) {
if (inputs.row == null || inputs.row.tableId == null) { if (inputs.row == null || inputs.row.tableId == null) {
return { return {
success: false, success: false,

View File

@ -1,6 +1,6 @@
let { wait } = require("../../utilities") let { wait } = require("../../utilities")
module.exports.definition = { exports.definition = {
name: "Delay", name: "Delay",
icon: "ri-time-line", icon: "ri-time-line",
tagline: "Delay for {{inputs.time}} milliseconds", tagline: "Delay for {{inputs.time}} milliseconds",
@ -21,6 +21,6 @@ module.exports.definition = {
type: "LOGIC", type: "LOGIC",
} }
module.exports.run = async function delay({ inputs }) { exports.run = async function delay({ inputs }) {
await wait(inputs.time) await wait(inputs.time)
} }

View File

@ -2,7 +2,7 @@ const rowController = require("../../api/controllers/row")
const env = require("../../environment") const env = require("../../environment")
const usage = require("../../utilities/usageQuota") const usage = require("../../utilities/usageQuota")
module.exports.definition = { exports.definition = {
description: "Delete a row from your database", description: "Delete a row from your database",
icon: "ri-delete-bin-line", icon: "ri-delete-bin-line",
name: "Delete Row", name: "Delete Row",
@ -50,7 +50,7 @@ module.exports.definition = {
}, },
} }
module.exports.run = async function ({ inputs, appId, apiKey, emitter }) { exports.run = async function ({ inputs, appId, apiKey, emitter }) {
if (inputs.id == null || inputs.revision == null) { if (inputs.id == null || inputs.revision == null) {
return { return {
success: false, success: false,

View File

@ -1,6 +1,6 @@
const fetch = require("node-fetch") const fetch = require("node-fetch")
module.exports.definition = { exports.definition = {
name: "Discord Message", name: "Discord Message",
tagline: "Send a message to a Discord server", tagline: "Send a message to a Discord server",
description: "Send a message to a Discord server", description: "Send a message to a Discord server",
@ -44,7 +44,7 @@ module.exports.definition = {
}, },
} }
module.exports.run = async function ({ inputs }) { exports.run = async function ({ inputs }) {
const { url, username, avatar_url, content } = inputs const { url, username, avatar_url, content } = inputs
const response = await fetch(url, { const response = await fetch(url, {

View File

@ -1,6 +1,6 @@
const queryController = require("../../api/controllers/query") const queryController = require("../../api/controllers/query")
module.exports.definition = { exports.definition = {
name: "External Data Connector", name: "External Data Connector",
tagline: "Execute Data Connector", tagline: "Execute Data Connector",
icon: "ri-database-2-line", icon: "ri-database-2-line",
@ -42,7 +42,7 @@ module.exports.definition = {
}, },
} }
module.exports.run = async function ({ inputs, appId, emitter }) { exports.run = async function ({ inputs, appId, emitter }) {
if (inputs.query == null) { if (inputs.query == null) {
return { return {
success: false, success: false,

View File

@ -1,6 +1,6 @@
const scriptController = require("../../api/controllers/script") const scriptController = require("../../api/controllers/script")
module.exports.definition = { exports.definition = {
name: "JS Scripting", name: "JS Scripting",
tagline: "Execute JavaScript Code", tagline: "Execute JavaScript Code",
icon: "ri-terminal-box-line", icon: "ri-terminal-box-line",
@ -36,7 +36,7 @@ module.exports.definition = {
}, },
} }
module.exports.run = async function ({ inputs, appId, context, emitter }) { exports.run = async function ({ inputs, appId, context, emitter }) {
if (inputs.code == null) { if (inputs.code == null) {
return { return {
success: false, success: false,

View File

@ -12,10 +12,10 @@ const PrettyLogicConditions = {
[LogicConditions.LESS_THAN]: "Less than", [LogicConditions.LESS_THAN]: "Less than",
} }
module.exports.LogicConditions = LogicConditions exports.LogicConditions = LogicConditions
module.exports.PrettyLogicConditions = PrettyLogicConditions exports.PrettyLogicConditions = PrettyLogicConditions
module.exports.definition = { exports.definition = {
name: "Filter", name: "Filter",
tagline: "{{inputs.field}} {{inputs.condition}} {{inputs.value}}", tagline: "{{inputs.field}} {{inputs.condition}} {{inputs.value}}",
icon: "ri-git-branch-line", icon: "ri-git-branch-line",
@ -57,7 +57,7 @@ module.exports.definition = {
}, },
} }
module.exports.run = async function filter({ inputs }) { exports.run = async function filter({ inputs }) {
let { field, condition, value } = inputs let { field, condition, value } = inputs
// coerce types so that we can use them // coerce types so that we can use them
if (!isNaN(value) && !isNaN(field)) { if (!isNaN(value) && !isNaN(field)) {

View File

@ -1,6 +1,6 @@
const fetch = require("node-fetch") const fetch = require("node-fetch")
module.exports.definition = { exports.definition = {
name: "Integromat Integration", name: "Integromat Integration",
tagline: "Trigger an Integromat scenario", tagline: "Trigger an Integromat scenario",
description: description:
@ -55,7 +55,7 @@ module.exports.definition = {
}, },
} }
module.exports.run = async function ({ inputs }) { exports.run = async function ({ inputs }) {
const { url, value1, value2, value3, value4, value5 } = inputs const { url, value1, value2, value3, value4, value5 } = inputs
const response = await fetch(url, { const response = await fetch(url, {

View File

@ -16,7 +16,7 @@ const BODY_REQUESTS = [RequestType.POST, RequestType.PUT, RequestType.PATCH]
* GET/DELETE requests cannot handle body elements so they will not be sent if configured. * GET/DELETE requests cannot handle body elements so they will not be sent if configured.
*/ */
module.exports.definition = { exports.definition = {
name: "Outgoing webhook", name: "Outgoing webhook",
tagline: "Send a {{inputs.requestMethod}} request", tagline: "Send a {{inputs.requestMethod}} request",
icon: "ri-send-plane-line", icon: "ri-send-plane-line",
@ -70,7 +70,7 @@ module.exports.definition = {
}, },
} }
module.exports.run = async function ({ inputs }) { exports.run = async function ({ inputs }) {
let { requestMethod, url, requestBody, headers } = inputs let { requestMethod, url, requestBody, headers } = inputs
if (!url.startsWith("http")) { if (!url.startsWith("http")) {
url = `http://${url}` url = `http://${url}`

View File

@ -1,6 +1,6 @@
const { sendSmtpEmail } = require("../../utilities/workerRequests") const { sendSmtpEmail } = require("../../utilities/workerRequests")
module.exports.definition = { exports.definition = {
description: "Send an email using SMTP", description: "Send an email using SMTP",
tagline: "Send SMTP email to {{inputs.to}}", tagline: "Send SMTP email to {{inputs.to}}",
icon: "ri-mail-open-line", icon: "ri-mail-open-line",
@ -46,7 +46,7 @@ module.exports.definition = {
}, },
} }
module.exports.run = async function ({ inputs }) { exports.run = async function ({ inputs }) {
let { to, from, subject, contents } = inputs let { to, from, subject, contents } = inputs
if (!contents) { if (!contents) {
contents = "<h1>No content</h1>" contents = "<h1>No content</h1>"

View File

@ -1,4 +1,4 @@
module.exports.definition = { exports.definition = {
description: "Send an email using SendGrid", description: "Send an email using SendGrid",
tagline: "Send email to {{inputs.to}}", tagline: "Send email to {{inputs.to}}",
icon: "ri-mail-open-line", icon: "ri-mail-open-line",
@ -48,7 +48,7 @@ module.exports.definition = {
}, },
} }
module.exports.run = async function ({ inputs }) { exports.run = async function ({ inputs }) {
const sgMail = require("@sendgrid/mail") const sgMail = require("@sendgrid/mail")
sgMail.setApiKey(inputs.apiKey) sgMail.setApiKey(inputs.apiKey)
const msg = { const msg = {

View File

@ -4,7 +4,7 @@
* GET/DELETE requests cannot handle body elements so they will not be sent if configured. * GET/DELETE requests cannot handle body elements so they will not be sent if configured.
*/ */
module.exports.definition = { exports.definition = {
name: "Backend log", name: "Backend log",
tagline: "Console log a value in the backend", tagline: "Console log a value in the backend",
icon: "ri-server-line", icon: "ri-server-line",
@ -36,6 +36,6 @@ module.exports.definition = {
}, },
} }
module.exports.run = async function ({ inputs, appId }) { exports.run = async function ({ inputs, appId }) {
console.log(`App ${appId} - ${inputs.text}`) console.log(`App ${appId} - ${inputs.text}`)
} }

View File

@ -1,7 +1,7 @@
const rowController = require("../../api/controllers/row") const rowController = require("../../api/controllers/row")
const automationUtils = require("../automationUtils") const automationUtils = require("../automationUtils")
module.exports.definition = { exports.definition = {
name: "Update Row", name: "Update Row",
tagline: "Update a {{inputs.enriched.table.name}} row", tagline: "Update a {{inputs.enriched.table.name}} row",
icon: "ri-refresh-line", icon: "ri-refresh-line",
@ -53,7 +53,7 @@ module.exports.definition = {
}, },
} }
module.exports.run = async function ({ inputs, appId, emitter }) { exports.run = async function ({ inputs, appId, emitter }) {
if (inputs.rowId == null || inputs.row == null) { if (inputs.rowId == null || inputs.row == null) {
return { return {
success: false, success: false,

View File

@ -1,6 +1,6 @@
const fetch = require("node-fetch") const fetch = require("node-fetch")
module.exports.definition = { exports.definition = {
name: "Zapier Webhook", name: "Zapier Webhook",
stepId: "zapier", stepId: "zapier",
type: "ACTION", type: "ACTION",
@ -52,7 +52,7 @@ module.exports.definition = {
}, },
} }
module.exports.run = async function ({ inputs }) { exports.run = async function ({ inputs }) {
const { url, value1, value2, value3, value4, value5 } = inputs const { url, value1, value2, value3, value4, value5 } = inputs
// send the platform to make sure zaps always work, even // send the platform to make sure zaps always work, even

View File

@ -56,5 +56,5 @@ exports.runStep = async function runStep(stepId, inputs) {
exports.apiKey = "test" exports.apiKey = "test"
exports.actions = actions.BUILTIN_DEFINITIONS exports.actions = actions.ACTION_DEFINITIONS
exports.logic = logic.BUILTIN_DEFINITIONS exports.logic = logic.LOGIC_DEFINITIONS

View File

@ -8,7 +8,7 @@ const CouchDB = require("../db")
const { DocumentTypes } = require("../db/utils") const { DocumentTypes } = require("../db/utils")
const { doInTenant } = require("@budibase/auth/tenancy") const { doInTenant } = require("@budibase/auth/tenancy")
const FILTER_STEP_ID = logic.BUILTIN_DEFINITIONS.FILTER.stepId const FILTER_STEP_ID = logic.LOGIC_DEFINITIONS.FILTER.stepId
/** /**
* The automation orchestrator is a class responsible for executing automations. * The automation orchestrator is a class responsible for executing automations.

View File

@ -0,0 +1,31 @@
exports.definition = {
name: "App Action",
event: "app:trigger",
icon: "ri-window-fill",
tagline: "Automation fired from the frontend",
description: "Trigger an automation from an action inside your app",
stepId: "APP",
inputs: {},
schema: {
inputs: {
properties: {
fields: {
type: "object",
customType: "triggerSchema",
title: "Fields",
},
},
required: [],
},
outputs: {
properties: {
fields: {
type: "object",
description: "Fields submitted from the app frontend",
},
},
required: ["fields"],
},
},
type: "TRIGGER",
}

View File

@ -0,0 +1,31 @@
exports.defintion = {
name: "Cron Trigger",
event: "cron:trigger",
icon: "ri-timer-line",
tagline: "Cron Trigger (<b>{{inputs.cron}}</b>)",
description: "Triggers automation on a cron schedule.",
stepId: "CRON",
inputs: {},
schema: {
inputs: {
properties: {
cron: {
type: "string",
customType: "cron",
title: "Expression",
},
},
required: ["cron"],
},
outputs: {
properties: {
timestamp: {
type: "number",
description: "Timestamp the cron was executed",
},
},
required: ["timestamp"],
},
},
type: "TRIGGER",
}

View File

@ -0,0 +1,15 @@
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.defintion,
}

View File

@ -0,0 +1,32 @@
exports.definition = {
name: "Row Deleted",
event: "row:delete",
icon: "ri-delete-bin-line",
tagline: "Row is deleted from {{inputs.enriched.table.name}}",
description: "Fired when a row is deleted from your database",
stepId: "ROW_DELETED",
inputs: {},
schema: {
inputs: {
properties: {
tableId: {
type: "string",
customType: "table",
title: "Table",
},
},
required: ["tableId"],
},
outputs: {
properties: {
row: {
type: "object",
customType: "row",
description: "The row that was deleted",
},
},
required: ["row"],
},
},
type: "TRIGGER",
}

View File

@ -0,0 +1,40 @@
exports.definition = {
name: "Row Created",
event: "row:save",
icon: "ri-save-line",
tagline: "Row is added to {{inputs.enriched.table.name}}",
description: "Fired when a row is added to your database",
stepId: "ROW_SAVED",
inputs: {},
schema: {
inputs: {
properties: {
tableId: {
type: "string",
customType: "table",
title: "Table",
},
},
required: ["tableId"],
},
outputs: {
properties: {
row: {
type: "object",
customType: "row",
description: "The new row that was created",
},
id: {
type: "string",
description: "Row ID - can be used for updating",
},
revision: {
type: "string",
description: "Revision of row",
},
},
required: ["row", "id"],
},
},
type: "TRIGGER",
}

View File

@ -0,0 +1,40 @@
exports.definition = {
name: "Row Updated",
event: "row:update",
icon: "ri-refresh-line",
tagline: "Row is updated in {{inputs.enriched.table.name}}",
description: "Fired when a row is updated in your database",
stepId: "ROW_UPDATED",
inputs: {},
schema: {
inputs: {
properties: {
tableId: {
type: "string",
customType: "table",
title: "Table",
},
},
required: ["tableId"],
},
outputs: {
properties: {
row: {
type: "object",
customType: "row",
description: "The row that was updated",
},
id: {
type: "string",
description: "Row ID - can be used for updating",
},
revision: {
type: "string",
description: "Revision of row",
},
},
required: ["row", "id"],
},
},
type: "TRIGGER",
}

View File

@ -0,0 +1,36 @@
exports.definition = {
name: "Webhook",
event: "web:trigger",
icon: "ri-global-line",
tagline: "Webhook endpoint is hit",
description: "Trigger an automation when a HTTP POST webhook is hit",
stepId: "WEBHOOK",
inputs: {},
schema: {
inputs: {
properties: {
schemaUrl: {
type: "string",
customType: "webhookUrl",
title: "Schema URL",
},
triggerUrl: {
type: "string",
customType: "webhookUrl",
title: "Trigger URL",
},
},
required: ["schemaUrl", "triggerUrl"],
},
outputs: {
properties: {
body: {
type: "object",
description: "Body of the request which hit the webhook",
},
},
required: ["body"],
},
},
type: "TRIGGER",
}

View File

@ -8,232 +8,12 @@ const { getAutomationParams } = require("../db/utils")
const { coerce } = require("../utilities/rowProcessor") const { coerce } = require("../utilities/rowProcessor")
const { utils } = require("@budibase/auth/redis") const { utils } = require("@budibase/auth/redis")
const { JobQueues } = require("../constants") const { JobQueues } = require("../constants")
const { const { definitions } = require("./triggerInfo")
isExternalTable,
breakExternalTableId,
} = require("../integrations/utils")
const { getExternalTable } = require("../api/controllers/table/utils")
const { opts } = utils.getRedisOptions() const { opts } = utils.getRedisOptions()
let automationQueue = new Queue(JobQueues.AUTOMATIONS, { redis: opts }) let automationQueue = new Queue(JobQueues.AUTOMATIONS, { redis: opts })
const FAKE_STRING = "TEST" const TRIGGER_DEFINITIONS = definitions
const FAKE_BOOL = false
const FAKE_NUMBER = 1
const FAKE_DATETIME = "1970-01-01T00:00:00.000Z"
const BUILTIN_DEFINITIONS = {
ROW_SAVED: {
name: "Row Created",
event: "row:save",
icon: "ri-save-line",
tagline: "Row is added to {{inputs.enriched.table.name}}",
description: "Fired when a row is added to your database",
stepId: "ROW_SAVED",
inputs: {},
schema: {
inputs: {
properties: {
tableId: {
type: "string",
customType: "table",
title: "Table",
},
},
required: ["tableId"],
},
outputs: {
properties: {
row: {
type: "object",
customType: "row",
description: "The new row that was created",
},
id: {
type: "string",
description: "Row ID - can be used for updating",
},
revision: {
type: "string",
description: "Revision of row",
},
},
required: ["row", "id"],
},
},
type: "TRIGGER",
},
ROW_UPDATED: {
name: "Row Updated",
event: "row:update",
icon: "ri-refresh-line",
tagline: "Row is updated in {{inputs.enriched.table.name}}",
description: "Fired when a row is updated in your database",
stepId: "ROW_UPDATED",
inputs: {},
schema: {
inputs: {
properties: {
tableId: {
type: "string",
customType: "table",
title: "Table",
},
},
required: ["tableId"],
},
outputs: {
properties: {
row: {
type: "object",
customType: "row",
description: "The row that was updated",
},
id: {
type: "string",
description: "Row ID - can be used for updating",
},
revision: {
type: "string",
description: "Revision of row",
},
},
required: ["row", "id"],
},
},
type: "TRIGGER",
},
ROW_DELETED: {
name: "Row Deleted",
event: "row:delete",
icon: "ri-delete-bin-line",
tagline: "Row is deleted from {{inputs.enriched.table.name}}",
description: "Fired when a row is deleted from your database",
stepId: "ROW_DELETED",
inputs: {},
schema: {
inputs: {
properties: {
tableId: {
type: "string",
customType: "table",
title: "Table",
},
},
required: ["tableId"],
},
outputs: {
properties: {
row: {
type: "object",
customType: "row",
description: "The row that was deleted",
},
},
required: ["row"],
},
},
type: "TRIGGER",
},
WEBHOOK: {
name: "Webhook",
event: "web:trigger",
icon: "ri-global-line",
tagline: "Webhook endpoint is hit",
description: "Trigger an automation when a HTTP POST webhook is hit",
stepId: "WEBHOOK",
inputs: {},
schema: {
inputs: {
properties: {
schemaUrl: {
type: "string",
customType: "webhookUrl",
title: "Schema URL",
},
triggerUrl: {
type: "string",
customType: "webhookUrl",
title: "Trigger URL",
},
},
required: ["schemaUrl", "triggerUrl"],
},
outputs: {
properties: {
body: {
type: "object",
description: "Body of the request which hit the webhook",
},
},
required: ["body"],
},
},
type: "TRIGGER",
},
APP: {
name: "App Action",
event: "app:trigger",
icon: "ri-window-fill",
tagline: "Automation fired from the frontend",
description: "Trigger an automation from an action inside your app",
stepId: "APP",
inputs: {},
schema: {
inputs: {
properties: {
fields: {
type: "object",
customType: "triggerSchema",
title: "Fields",
},
},
required: [],
},
outputs: {
properties: {
fields: {
type: "object",
description: "Fields submitted from the app frontend",
},
},
required: ["fields"],
},
},
type: "TRIGGER",
},
CRON: {
name: "Cron Trigger",
event: "cron:trigger",
icon: "ri-timer-line",
tagline: "Cron Trigger (<b>{{inputs.cron}}</b>)",
description: "Triggers automation on a cron schedule.",
stepId: "CRON",
inputs: {},
schema: {
inputs: {
properties: {
cron: {
type: "string",
customType: "cron",
title: "Expression",
},
},
required: ["cron"],
},
outputs: {
properties: {
timestamp: {
type: "number",
description: "Timestamp the cron was executed",
},
},
required: ["timestamp"],
},
},
type: "TRIGGER",
},
}
async function queueRelevantRowAutomations(event, eventType) { async function queueRelevantRowAutomations(event, eventType) {
if (event.appId == null) { if (event.appId == null) {
@ -262,7 +42,7 @@ async function queueRelevantRowAutomations(event, eventType) {
) { ) {
continue continue
} }
automationQueue.add({ automation, event }) await automationQueue.add({ automation, event })
} }
} }
@ -290,51 +70,8 @@ emitter.on("row:delete", async function (event) {
await queueRelevantRowAutomations(event, "row:delete") await queueRelevantRowAutomations(event, "row:delete")
}) })
async function fillRowOutput(automation, params) { exports.externalTrigger = async function (automation, params) {
let triggerSchema = automation.definition.trigger
let tableId = triggerSchema.inputs.tableId
try {
let table
if (!isExternalTable(tableId)) {
const db = new CouchDB(params.appId)
table = await db.get(tableId)
} else {
const { datasourceId, tableName } = breakExternalTableId(tableId)
table = await getExternalTable(params.appId, datasourceId, tableName)
}
let row = {}
for (let schemaKey of Object.keys(table.schema)) {
const paramValue = params[schemaKey]
let propSchema = table.schema[schemaKey]
switch (propSchema.constraints.type) {
case "string":
row[schemaKey] = paramValue || FAKE_STRING
break
case "boolean":
row[schemaKey] = paramValue || FAKE_BOOL
break
case "number":
row[schemaKey] = paramValue || FAKE_NUMBER
break
case "datetime":
row[schemaKey] = paramValue || FAKE_DATETIME
break
}
}
params.row = row
} catch (err) {
/* istanbul ignore next */
throw "Failed to find table for trigger"
}
return params
}
module.exports.externalTrigger = async function (automation, params) {
// TODO: replace this with allowing user in builder to input values in future
if (automation.definition != null && automation.definition.trigger != null) { if (automation.definition != null && automation.definition.trigger != null) {
if (automation.definition.trigger.inputs.tableId != null) {
params = await fillRowOutput(automation, params)
}
if (automation.definition.trigger.stepId === "APP") { if (automation.definition.trigger.stepId === "APP") {
// values are likely to be submitted as strings, so we shall convert to correct type // values are likely to be submitted as strings, so we shall convert to correct type
const coercedFields = {} const coercedFields = {}
@ -346,13 +83,12 @@ module.exports.externalTrigger = async function (automation, params) {
} }
} }
automationQueue.add({ automation, event: params }) await automationQueue.add({ automation, event: params })
} }
module.exports.getQueues = () => { exports.getQueues = () => {
return [automationQueue] return [automationQueue]
} }
module.exports.fillRowOutput = fillRowOutput exports.automationQueue = automationQueue
module.exports.automationQueue = automationQueue
module.exports.BUILTIN_DEFINITIONS = BUILTIN_DEFINITIONS exports.TRIGGER_DEFINITIONS = TRIGGER_DEFINITIONS

View File

@ -14,7 +14,7 @@ const DOMAIN_MAP = {
views: usageQuota.Properties.VIEW, views: usageQuota.Properties.VIEW,
users: usageQuota.Properties.USER, users: usageQuota.Properties.USER,
// this will not be updated by endpoint calls // this will not be updated by endpoint calls
// instead it will be updated by triggers // instead it will be updated by triggerInfo
automationRuns: usageQuota.Properties.AUTOMATION, automationRuns: usageQuota.Properties.AUTOMATION,
} }