forked from tsai/budibase
12 changed files with 549 additions and 497 deletions
@ -1,330 +0,0 @@ |
|||||
let accessLevels = require("../../../utilities/accessLevels") |
|
||||
let conditions = require("../../../workflows/logic").LogicConditions |
|
||||
|
|
||||
const ACTION = { |
|
||||
SAVE_RECORD: { |
|
||||
name: "Save Record", |
|
||||
tagline: "Save a {{inputs.enriched.model.name}} record", |
|
||||
icon: "ri-save-3-fill", |
|
||||
description: "Save a record to your database", |
|
||||
type: "ACTION", |
|
||||
inputs: {}, |
|
||||
schema: { |
|
||||
inputs: { |
|
||||
properties: { |
|
||||
record: { |
|
||||
type: "object", |
|
||||
properties: { |
|
||||
modelId: { |
|
||||
type: "string", |
|
||||
customType: "model", |
|
||||
title: "Table", |
|
||||
}, |
|
||||
}, |
|
||||
customType: "record", |
|
||||
title: "Table", |
|
||||
default: {}, |
|
||||
required: ["modelId"], |
|
||||
}, |
|
||||
}, |
|
||||
required: ["record"], |
|
||||
}, |
|
||||
outputs: { |
|
||||
properties: { |
|
||||
response: { |
|
||||
type: "object", |
|
||||
description: "The response from the table", |
|
||||
}, |
|
||||
success: { |
|
||||
type: "boolean", |
|
||||
description: "Whether the action was successful", |
|
||||
}, |
|
||||
id: { |
|
||||
type: "string", |
|
||||
description: "The identifier of the new record", |
|
||||
}, |
|
||||
revision: { |
|
||||
type: "string", |
|
||||
description: "The revision of the new record", |
|
||||
}, |
|
||||
}, |
|
||||
required: ["success", "id", "revision"], |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
DELETE_RECORD: { |
|
||||
description: "Delete a record from your database", |
|
||||
icon: "ri-delete-bin-line", |
|
||||
name: "Delete Record", |
|
||||
tagline: "Delete a {{inputs.enriched.model.name}} record", |
|
||||
type: "ACTION", |
|
||||
inputs: {}, |
|
||||
schema: { |
|
||||
inputs: { |
|
||||
properties: { |
|
||||
modelId: { |
|
||||
type: "string", |
|
||||
customType: "model", |
|
||||
title: "Table", |
|
||||
}, |
|
||||
id: { |
|
||||
type: "string", |
|
||||
title: "Record ID", |
|
||||
}, |
|
||||
revision: { |
|
||||
type: "string", |
|
||||
title: "Record Revision", |
|
||||
}, |
|
||||
}, |
|
||||
required: ["modelId", "id", "revision"], |
|
||||
}, |
|
||||
outputs: { |
|
||||
properties: { |
|
||||
record: { |
|
||||
type: "object", |
|
||||
customType: "record", |
|
||||
description: "The deleted record", |
|
||||
}, |
|
||||
response: { |
|
||||
type: "object", |
|
||||
description: "The response from the table", |
|
||||
}, |
|
||||
success: { |
|
||||
type: "boolean", |
|
||||
description: "Whether the action was successful", |
|
||||
}, |
|
||||
}, |
|
||||
required: ["record", "success"], |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
CREATE_USER: { |
|
||||
description: "Create a new user", |
|
||||
tagline: "Create user {{inputs.username}}", |
|
||||
icon: "ri-user-add-fill", |
|
||||
name: "Create User", |
|
||||
type: "ACTION", |
|
||||
inputs: {}, |
|
||||
schema: { |
|
||||
inputs: { |
|
||||
properties: { |
|
||||
username: { |
|
||||
type: "string", |
|
||||
title: "Username", |
|
||||
}, |
|
||||
password: { |
|
||||
type: "string", |
|
||||
customType: "password", |
|
||||
title: "Password", |
|
||||
}, |
|
||||
accessLevelId: { |
|
||||
type: "string", |
|
||||
title: "Access Level", |
|
||||
default: accessLevels.POWERUSER_LEVEL_ID, |
|
||||
enum: accessLevels.ACCESS_LEVELS, |
|
||||
}, |
|
||||
}, |
|
||||
required: ["username", "password", "accessLevelId"], |
|
||||
}, |
|
||||
outputs: { |
|
||||
properties: { |
|
||||
id: { |
|
||||
type: "string", |
|
||||
description: "The identifier of the new user", |
|
||||
}, |
|
||||
revision: { |
|
||||
type: "string", |
|
||||
description: "The revision of the new user", |
|
||||
}, |
|
||||
response: { |
|
||||
type: "object", |
|
||||
description: "The response from the user table", |
|
||||
}, |
|
||||
success: { |
|
||||
type: "boolean", |
|
||||
description: "Whether the action was successful", |
|
||||
}, |
|
||||
}, |
|
||||
required: ["id", "revision", "success"], |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
SEND_EMAIL: { |
|
||||
description: "Send an email.", |
|
||||
tagline: "Send email to {{inputs.to}}", |
|
||||
icon: "ri-mail-open-fill", |
|
||||
name: "Send Email", |
|
||||
type: "ACTION", |
|
||||
inputs: {}, |
|
||||
schema: { |
|
||||
inputs: { |
|
||||
properties: { |
|
||||
to: { |
|
||||
type: "string", |
|
||||
title: "Send To", |
|
||||
}, |
|
||||
from: { |
|
||||
type: "string", |
|
||||
title: "Send From", |
|
||||
}, |
|
||||
subject: { |
|
||||
type: "string", |
|
||||
title: "Email Subject", |
|
||||
}, |
|
||||
contents: { |
|
||||
type: "string", |
|
||||
title: "Email Contents", |
|
||||
}, |
|
||||
}, |
|
||||
required: ["to", "from", "subject", "contents"], |
|
||||
}, |
|
||||
outputs: { |
|
||||
properties: { |
|
||||
success: { |
|
||||
type: "boolean", |
|
||||
description: "Whether the email was sent", |
|
||||
}, |
|
||||
response: { |
|
||||
type: "object", |
|
||||
description: |
|
||||
"A response from the email client, this may be an error", |
|
||||
}, |
|
||||
}, |
|
||||
required: ["success"], |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
} |
|
||||
|
|
||||
const LOGIC = { |
|
||||
FILTER: { |
|
||||
name: "Filter", |
|
||||
tagline: "{{inputs.filter}} {{inputs.condition}} {{inputs.value}}", |
|
||||
icon: "ri-git-branch-line", |
|
||||
description: "Filter any workflows which do not meet certain conditions", |
|
||||
type: "LOGIC", |
|
||||
inputs: {}, |
|
||||
schema: { |
|
||||
inputs: { |
|
||||
properties: { |
|
||||
filter: { |
|
||||
type: "string", |
|
||||
title: "Reference Value", |
|
||||
}, |
|
||||
condition: { |
|
||||
type: "string", |
|
||||
title: "Condition", |
|
||||
enum: conditions, |
|
||||
default: "equals", |
|
||||
}, |
|
||||
value: { |
|
||||
type: "string", |
|
||||
title: "Comparison Value", |
|
||||
}, |
|
||||
}, |
|
||||
required: ["filter", "condition", "value"], |
|
||||
}, |
|
||||
outputs: { |
|
||||
properties: { |
|
||||
success: { |
|
||||
type: "boolean", |
|
||||
description: "Whether the logic block passed", |
|
||||
}, |
|
||||
}, |
|
||||
required: ["success"], |
|
||||
}, |
|
||||
}, |
|
||||
}, |
|
||||
DELAY: { |
|
||||
name: "Delay", |
|
||||
icon: "ri-time-fill", |
|
||||
tagline: "Delay for {{inputs.time}} milliseconds", |
|
||||
description: "Delay the workflow until an amount of time has passed", |
|
||||
inputs: {}, |
|
||||
schema: { |
|
||||
inputs: { |
|
||||
properties: { |
|
||||
time: { |
|
||||
type: "number", |
|
||||
title: "Delay in milliseconds", |
|
||||
}, |
|
||||
}, |
|
||||
required: ["time"], |
|
||||
}, |
|
||||
}, |
|
||||
type: "LOGIC", |
|
||||
}, |
|
||||
} |
|
||||
|
|
||||
const TRIGGER = { |
|
||||
RECORD_SAVED: { |
|
||||
name: "Record Saved", |
|
||||
event: "record:save", |
|
||||
icon: "ri-save-line", |
|
||||
tagline: "Record is added to {{inputs.enriched.model.name}}", |
|
||||
description: "Fired when a record is saved to your database", |
|
||||
inputs: {}, |
|
||||
schema: { |
|
||||
inputs: { |
|
||||
properties: { |
|
||||
modelId: { |
|
||||
type: "string", |
|
||||
customType: "model", |
|
||||
title: "Table", |
|
||||
}, |
|
||||
}, |
|
||||
required: ["modelId"], |
|
||||
}, |
|
||||
outputs: { |
|
||||
properties: { |
|
||||
record: { |
|
||||
type: "object", |
|
||||
customType: "record", |
|
||||
description: "The new record that was saved", |
|
||||
}, |
|
||||
}, |
|
||||
required: ["record"], |
|
||||
}, |
|
||||
}, |
|
||||
type: "TRIGGER", |
|
||||
}, |
|
||||
RECORD_DELETED: { |
|
||||
name: "Record Deleted", |
|
||||
event: "record:delete", |
|
||||
icon: "ri-delete-bin-line", |
|
||||
tagline: "Record is deleted from {{inputs.enriched.model.name}}", |
|
||||
description: "Fired when a record is deleted from your database", |
|
||||
inputs: {}, |
|
||||
schema: { |
|
||||
inputs: { |
|
||||
properties: { |
|
||||
modelId: { |
|
||||
type: "string", |
|
||||
customType: "model", |
|
||||
title: "Table", |
|
||||
}, |
|
||||
}, |
|
||||
required: ["modelId"], |
|
||||
}, |
|
||||
outputs: { |
|
||||
properties: { |
|
||||
record: { |
|
||||
type: "object", |
|
||||
customType: "record", |
|
||||
description: "The record that was deleted", |
|
||||
}, |
|
||||
}, |
|
||||
required: ["record"], |
|
||||
}, |
|
||||
}, |
|
||||
type: "TRIGGER", |
|
||||
}, |
|
||||
} |
|
||||
|
|
||||
// This contains the definitions for the steps and triggers that make up a workflow, a workflow comprises
|
|
||||
// of many steps and a single trigger
|
|
||||
module.exports = { |
|
||||
ACTION, |
|
||||
LOGIC, |
|
||||
TRIGGER, |
|
||||
} |
|
||||
@ -1,31 +1,20 @@ |
|||||
const wait = ms => new Promise(resolve => setTimeout(resolve, ms)) |
let filter = require("./steps/filter") |
||||
|
let delay = require("./steps/delay") |
||||
|
|
||||
let LOGIC = { |
let BUILTIN_LOGIC = { |
||||
DELAY: async function delay(inputs) { |
DELAY: delay.run, |
||||
await wait(inputs.time) |
FILTER: filter.run, |
||||
}, |
} |
||||
|
|
||||
FILTER: async function filter(inputs) { |
let BUILTIN_DEFINITIONS = { |
||||
const { field, condition, value } = inputs |
DELAY: delay.definition, |
||||
switch (condition) { |
FILTER: filter.definition, |
||||
case "equals": |
|
||||
if (field !== value) return |
|
||||
break |
|
||||
default: |
|
||||
return |
|
||||
} |
|
||||
}, |
|
||||
} |
} |
||||
|
|
||||
module.exports.getLogic = function(logicName) { |
module.exports.getLogic = function(logicName) { |
||||
if (LOGIC[logicName] != null) { |
if (BUILTIN_LOGIC[logicName] != null) { |
||||
return LOGIC[logicName] |
return BUILTIN_LOGIC[logicName] |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
module.exports.LogicConditions = [ |
module.exports.BUILTIN_DEFINITIONS = BUILTIN_DEFINITIONS |
||||
"Equals", |
|
||||
"Not equals", |
|
||||
"Greater than", |
|
||||
"Less than", |
|
||||
] |
|
||||
|
|||||
@ -0,0 +1,82 @@ |
|||||
|
const userController = require("../../api/controllers/user") |
||||
|
let accessLevels = require("../../utilities/accessLevels") |
||||
|
|
||||
|
module.exports.definition = { |
||||
|
description: "Create a new user", |
||||
|
tagline: "Create user {{inputs.username}}", |
||||
|
icon: "ri-user-add-fill", |
||||
|
name: "Create User", |
||||
|
type: "ACTION", |
||||
|
inputs: {}, |
||||
|
schema: { |
||||
|
inputs: { |
||||
|
properties: { |
||||
|
username: { |
||||
|
type: "string", |
||||
|
title: "Username", |
||||
|
}, |
||||
|
password: { |
||||
|
type: "string", |
||||
|
customType: "password", |
||||
|
title: "Password", |
||||
|
}, |
||||
|
accessLevelId: { |
||||
|
type: "string", |
||||
|
title: "Access Level", |
||||
|
default: accessLevels.POWERUSER_LEVEL_ID, |
||||
|
enum: accessLevels.ACCESS_LEVELS, |
||||
|
}, |
||||
|
}, |
||||
|
required: ["username", "password", "accessLevelId"], |
||||
|
}, |
||||
|
outputs: { |
||||
|
properties: { |
||||
|
id: { |
||||
|
type: "string", |
||||
|
description: "The identifier of the new user", |
||||
|
}, |
||||
|
revision: { |
||||
|
type: "string", |
||||
|
description: "The revision of the new user", |
||||
|
}, |
||||
|
response: { |
||||
|
type: "object", |
||||
|
description: "The response from the user table", |
||||
|
}, |
||||
|
success: { |
||||
|
type: "boolean", |
||||
|
description: "Whether the action was successful", |
||||
|
}, |
||||
|
}, |
||||
|
required: ["id", "revision", "success"], |
||||
|
}, |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
module.exports.run = async function({ inputs, instanceId }) { |
||||
|
const { username, password, accessLevelId } = inputs |
||||
|
const ctx = { |
||||
|
user: { |
||||
|
instanceId: instanceId, |
||||
|
}, |
||||
|
request: { |
||||
|
body: { username, password, accessLevelId }, |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
await userController.create(ctx) |
||||
|
return { |
||||
|
response: ctx.body, |
||||
|
id: ctx.body._id, |
||||
|
revision: ctx.body._rev, |
||||
|
success: ctx.status === 200, |
||||
|
} |
||||
|
} catch (err) { |
||||
|
console.error(err) |
||||
|
return { |
||||
|
success: false, |
||||
|
response: err, |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
const wait = ms => new Promise(resolve => setTimeout(resolve, ms)) |
||||
|
|
||||
|
module.exports.definition = { |
||||
|
name: "Delay", |
||||
|
icon: "ri-time-fill", |
||||
|
tagline: "Delay for {{inputs.time}} milliseconds", |
||||
|
description: "Delay the workflow until an amount of time has passed", |
||||
|
inputs: {}, |
||||
|
schema: { |
||||
|
inputs: { |
||||
|
properties: { |
||||
|
time: { |
||||
|
type: "number", |
||||
|
title: "Delay in milliseconds", |
||||
|
}, |
||||
|
}, |
||||
|
required: ["time"], |
||||
|
}, |
||||
|
}, |
||||
|
type: "LOGIC", |
||||
|
} |
||||
|
|
||||
|
module.exports.run = async function delay({ inputs }) { |
||||
|
await wait(inputs.time) |
||||
|
} |
||||
@ -0,0 +1,77 @@ |
|||||
|
const recordController = require("../../api/controllers/record") |
||||
|
|
||||
|
module.exports.definition = { |
||||
|
description: "Delete a record from your database", |
||||
|
icon: "ri-delete-bin-line", |
||||
|
name: "Delete Record", |
||||
|
tagline: "Delete a {{inputs.enriched.model.name}} record", |
||||
|
type: "ACTION", |
||||
|
inputs: {}, |
||||
|
schema: { |
||||
|
inputs: { |
||||
|
properties: { |
||||
|
modelId: { |
||||
|
type: "string", |
||||
|
customType: "model", |
||||
|
title: "Table", |
||||
|
}, |
||||
|
id: { |
||||
|
type: "string", |
||||
|
title: "Record ID", |
||||
|
}, |
||||
|
revision: { |
||||
|
type: "string", |
||||
|
title: "Record Revision", |
||||
|
}, |
||||
|
}, |
||||
|
required: ["modelId", "id", "revision"], |
||||
|
}, |
||||
|
outputs: { |
||||
|
properties: { |
||||
|
record: { |
||||
|
type: "object", |
||||
|
customType: "record", |
||||
|
description: "The deleted record", |
||||
|
}, |
||||
|
response: { |
||||
|
type: "object", |
||||
|
description: "The response from the table", |
||||
|
}, |
||||
|
success: { |
||||
|
type: "boolean", |
||||
|
description: "Whether the action was successful", |
||||
|
}, |
||||
|
}, |
||||
|
required: ["record", "success"], |
||||
|
}, |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
module.exports.run = async function({ inputs, instanceId }) { |
||||
|
// TODO: better logging of when actions are missed due to missing parameters
|
||||
|
if (inputs.id == null || inputs.revision == null) { |
||||
|
return |
||||
|
} |
||||
|
let ctx = { |
||||
|
params: { |
||||
|
modelId: inputs.modelId, |
||||
|
recordId: inputs.id, |
||||
|
revId: inputs.revision, |
||||
|
}, |
||||
|
user: { instanceId }, |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
await recordController.destroy(ctx) |
||||
|
return { |
||||
|
response: ctx.body, |
||||
|
success: ctx.status === 200, |
||||
|
} |
||||
|
} catch (err) { |
||||
|
console.error(err) |
||||
|
return { |
||||
|
success: false, |
||||
|
response: err, |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,71 @@ |
|||||
|
const LogicConditions = { |
||||
|
EQUALS: "Equals", |
||||
|
NOT_EQUALS: "Not equals", |
||||
|
GREATER_THAN: "Greater than", |
||||
|
LESS_THAN: "Less than", |
||||
|
} |
||||
|
|
||||
|
module.exports.definition = { |
||||
|
name: "Filter", |
||||
|
tagline: "{{inputs.filter}} {{inputs.condition}} {{inputs.value}}", |
||||
|
icon: "ri-git-branch-line", |
||||
|
description: "Filter any workflows which do not meet certain conditions", |
||||
|
type: "LOGIC", |
||||
|
inputs: {}, |
||||
|
schema: { |
||||
|
inputs: { |
||||
|
properties: { |
||||
|
filter: { |
||||
|
type: "string", |
||||
|
title: "Reference Value", |
||||
|
}, |
||||
|
condition: { |
||||
|
type: "string", |
||||
|
title: "Condition", |
||||
|
enum: Object.values(LogicConditions), |
||||
|
default: "equals", |
||||
|
}, |
||||
|
value: { |
||||
|
type: "string", |
||||
|
title: "Comparison Value", |
||||
|
}, |
||||
|
}, |
||||
|
required: ["filter", "condition", "value"], |
||||
|
}, |
||||
|
outputs: { |
||||
|
properties: { |
||||
|
success: { |
||||
|
type: "boolean", |
||||
|
description: "Whether the logic block passed", |
||||
|
}, |
||||
|
}, |
||||
|
required: ["success"], |
||||
|
}, |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
module.exports.run = async function filter({ inputs }) { |
||||
|
const { field, condition, value } = inputs |
||||
|
let success |
||||
|
if (typeof field !== "object" && typeof value !== "object") { |
||||
|
switch (condition) { |
||||
|
case LogicConditions.EQUALS: |
||||
|
success = field === value |
||||
|
break |
||||
|
case LogicConditions.NOT_EQUALS: |
||||
|
success = field !== value |
||||
|
break |
||||
|
case LogicConditions.GREATER_THAN: |
||||
|
success = field > value |
||||
|
break |
||||
|
case LogicConditions.LESS_THAN: |
||||
|
success = field < value |
||||
|
break |
||||
|
default: |
||||
|
return |
||||
|
} |
||||
|
} else { |
||||
|
success = false |
||||
|
} |
||||
|
return { success } |
||||
|
} |
||||
@ -0,0 +1,85 @@ |
|||||
|
const recordController = require("../../api/controllers/record") |
||||
|
|
||||
|
module.exports.definition = { |
||||
|
name: "Save Record", |
||||
|
tagline: "Save a {{inputs.enriched.model.name}} record", |
||||
|
icon: "ri-save-3-fill", |
||||
|
description: "Save a record to your database", |
||||
|
type: "ACTION", |
||||
|
inputs: {}, |
||||
|
schema: { |
||||
|
inputs: { |
||||
|
properties: { |
||||
|
record: { |
||||
|
type: "object", |
||||
|
properties: { |
||||
|
modelId: { |
||||
|
type: "string", |
||||
|
customType: "model", |
||||
|
title: "Table", |
||||
|
}, |
||||
|
}, |
||||
|
customType: "record", |
||||
|
title: "The record to be written", |
||||
|
default: {}, |
||||
|
required: ["modelId"], |
||||
|
}, |
||||
|
}, |
||||
|
required: ["record"], |
||||
|
}, |
||||
|
outputs: { |
||||
|
properties: { |
||||
|
response: { |
||||
|
type: "object", |
||||
|
description: "The response from the table", |
||||
|
}, |
||||
|
success: { |
||||
|
type: "boolean", |
||||
|
description: "Whether the action was successful", |
||||
|
}, |
||||
|
id: { |
||||
|
type: "string", |
||||
|
description: "The identifier of the new record", |
||||
|
}, |
||||
|
revision: { |
||||
|
type: "string", |
||||
|
description: "The revision of the new record", |
||||
|
}, |
||||
|
}, |
||||
|
required: ["success", "id", "revision"], |
||||
|
}, |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
module.exports.run = async function({ inputs, instanceId }) { |
||||
|
// TODO: better logging of when actions are missed due to missing parameters
|
||||
|
if (inputs.record == null || inputs.record.modelId == null) { |
||||
|
return |
||||
|
} |
||||
|
// have to clean up the record, remove the model from it
|
||||
|
const ctx = { |
||||
|
params: { |
||||
|
modelId: inputs.record.modelId, |
||||
|
}, |
||||
|
request: { |
||||
|
body: inputs.record, |
||||
|
}, |
||||
|
user: { instanceId }, |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
await recordController.save(ctx) |
||||
|
return { |
||||
|
response: ctx.body, |
||||
|
id: ctx.body._id, |
||||
|
revision: ctx.body._rev, |
||||
|
success: ctx.status === 200, |
||||
|
} |
||||
|
} catch (err) { |
||||
|
console.error(err) |
||||
|
return { |
||||
|
success: false, |
||||
|
response: err, |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,70 @@ |
|||||
|
const sgMail = require("@sendgrid/mail") |
||||
|
sgMail.setApiKey(process.env.SENDGRID_API_KEY) |
||||
|
|
||||
|
module.exports.definition = { |
||||
|
description: "Send an email.", |
||||
|
tagline: "Send email to {{inputs.to}}", |
||||
|
icon: "ri-mail-open-fill", |
||||
|
name: "Send Email", |
||||
|
type: "ACTION", |
||||
|
inputs: {}, |
||||
|
schema: { |
||||
|
inputs: { |
||||
|
properties: { |
||||
|
to: { |
||||
|
type: "string", |
||||
|
title: "Send To", |
||||
|
}, |
||||
|
from: { |
||||
|
type: "string", |
||||
|
title: "Send From", |
||||
|
}, |
||||
|
subject: { |
||||
|
type: "string", |
||||
|
title: "Email Subject", |
||||
|
}, |
||||
|
contents: { |
||||
|
type: "string", |
||||
|
title: "Email Contents", |
||||
|
}, |
||||
|
}, |
||||
|
required: ["to", "from", "subject", "contents"], |
||||
|
}, |
||||
|
outputs: { |
||||
|
properties: { |
||||
|
success: { |
||||
|
type: "boolean", |
||||
|
description: "Whether the email was sent", |
||||
|
}, |
||||
|
response: { |
||||
|
type: "object", |
||||
|
description: "A response from the email client, this may be an error", |
||||
|
}, |
||||
|
}, |
||||
|
required: ["success"], |
||||
|
}, |
||||
|
}, |
||||
|
} |
||||
|
|
||||
|
module.exports.run = async function({ inputs }) { |
||||
|
const msg = { |
||||
|
to: inputs.to, |
||||
|
from: inputs.from, |
||||
|
subject: inputs.subject, |
||||
|
text: inputs.text, |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
let response = await sgMail.send(msg) |
||||
|
return { |
||||
|
success: true, |
||||
|
response, |
||||
|
} |
||||
|
} catch (err) { |
||||
|
console.error(err) |
||||
|
return { |
||||
|
success: false, |
||||
|
response: err, |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue