const CouchDB = require("../db") const emitter = require("../events/index") const InMemoryQueue = require("../utilities/queue/inMemoryQueue") const { getAutomationParams } = require("../db/utils") let automationQueue = new InMemoryQueue("automationQueue") const FAKE_STRING = "TEST" const FAKE_BOOL = false const FAKE_NUMBER = 1 const FAKE_DATETIME = "1970-01-01T00:00:00.000Z" const BUILTIN_DEFINITIONS = { ROW_SAVED: { name: "Row Saved", event: "row:save", icon: "ri-save-line", tagline: "Row is added to {{inputs.enriched.table.name}}", description: "Fired when a row is saved 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 saved", }, 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", "id"], }, }, type: "TRIGGER", }, } async function queueRelevantRowAutomations(event, eventType) { if (event.instanceId == null) { throw `No instanceId specified for ${eventType} - check event emitters.` } const db = new CouchDB(event.instanceId) let automations = await db.allDocs( getAutomationParams(null, { include_docs: true }) ) // filter down to the correct event type automations = automations.rows .map(automation => automation.doc) .filter(automation => { const trigger = automation.definition.trigger return trigger && trigger.event === eventType }) for (let automation of automations) { let automationDef = automation.definition let automationTrigger = automationDef ? automationDef.trigger : {} if ( !automation.live || !automationTrigger.inputs || automationTrigger.inputs.tableId !== event.row.tableId ) { continue } automationQueue.add({ automation, event }) } } emitter.on("row:save", async function(event) { if (!event || !event.row || !event.row.tableId) { return } await queueRelevantRowAutomations(event, "row:save") }) emitter.on("row:delete", async function(event) { if (!event || !event.row || !event.row.tableId) { return } await queueRelevantRowAutomations(event, "row:delete") }) async function fillRowOutput(automation, params) { let triggerSchema = automation.definition.trigger let tableId = triggerSchema.inputs.tableId const db = new CouchDB(params.instanceId) try { let table = await db.get(tableId) let row = {} for (let schemaKey of Object.keys(table.schema)) { if (params[schemaKey] != null) { continue } let propSchema = table.schema[schemaKey] switch (propSchema.constraints.type) { case "string": row[schemaKey] = FAKE_STRING break case "boolean": row[schemaKey] = FAKE_BOOL break case "number": row[schemaKey] = FAKE_NUMBER break case "datetime": row[schemaKey] = FAKE_DATETIME break } } params.row = row } catch (err) { 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 && automation.definition.trigger.inputs.tableId != null ) { params = await fillRowOutput(automation, params) } automationQueue.add({ automation, event: params }) } module.exports.automationQueue = automationQueue module.exports.BUILTIN_DEFINITIONS = BUILTIN_DEFINITIONS