From 7fd55fe27df9c0079295c072a7c98d5b1e89bd90 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Thu, 18 Jul 2024 10:38:15 +0100 Subject: [PATCH 1/2] Automation trigger filtering (#14123) * backend for triggering automation based on filters * frontend for handling triggering automations on filter / old row * lint and bug fix * fix issue with test header * make test data optional * improve safety on trigger gate * add support for running trigger with filter if no change happened but filter matches * update var naming to actually make sense * tests * fix lint * improve gating for shouldTrigger check * remove unecessary cast * unecessary tableId check * frontend text updates * resolving comments * pro * Update packages/types/src/documents/app/automation.ts Co-authored-by: Sam Rose * link out to docs for trigger filtering * fix pro * more pr comments * use getAppId --------- Co-authored-by: Sam Rose --- .../FlowChart/FlowItemHeader.svelte | 6 +- .../AutomationBuilder/TestDisplay.svelte | 44 +++++- .../SetupPanel/AutomationBlockSetup.svelte | 131 +++++++++++++----- .../FilterEditor/FilterBuilder.svelte | 3 +- .../builder/src/stores/builder/automations.js | 2 +- .../src/components/FilterBuilder.svelte | 4 +- .../src/api/routes/tests/automation.spec.ts | 93 ++++++++++++- .../src/automations/triggerInfo/rowSaved.ts | 5 + .../src/automations/triggerInfo/rowUpdated.ts | 5 + packages/server/src/automations/triggers.ts | 60 +++++++- .../server/src/tests/utilities/structures.ts | 59 +++++++- .../types/src/documents/app/automation.ts | 14 ++ packages/types/src/sdk/automations/index.ts | 4 +- 13 files changed, 364 insertions(+), 66 deletions(-) diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte index a409d85305..5533572511 100644 --- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte +++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte @@ -16,13 +16,12 @@ export let enableNaming = true let validRegex = /^[A-Za-z0-9_\s]+$/ let typing = false - const dispatch = createEventDispatcher() $: stepNames = $selectedAutomation?.definition.stepNames $: automationName = stepNames?.[block.id] || block?.name || "" $: automationNameError = getAutomationNameError(automationName) - $: status = updateStatus(testResult, isTrigger) + $: status = updateStatus(testResult) $: isHeaderTrigger = isTrigger || block.type === "TRIGGER" $: { @@ -43,7 +42,7 @@ }) } - function updateStatus(results, isTrigger) { + function updateStatus(results) { if (!results) { return {} } @@ -56,7 +55,6 @@ return { negative: true, message: "Error" } } } - const getAutomationNameError = name => { if (stepNames) { for (const [key, value] of Object.entries(stepNames)) { diff --git a/packages/builder/src/components/automation/AutomationBuilder/TestDisplay.svelte b/packages/builder/src/components/automation/AutomationBuilder/TestDisplay.svelte index 2cad22c820..8487b7a519 100644 --- a/packages/builder/src/components/automation/AutomationBuilder/TestDisplay.svelte +++ b/packages/builder/src/components/automation/AutomationBuilder/TestDisplay.svelte @@ -12,14 +12,31 @@ let blocks function prepTestResults(results) { - return results?.steps.filter(x => x.stepId !== ActionStepID.LOOP || []) + if (results.message) { + return [ + { + inputs: {}, + outputs: { + success: results.outputs?.success || false, + status: results.outputs?.status || "unknown", + message: results.message, + }, + }, + ] + } else { + return results?.steps?.filter(x => x.stepId !== ActionStepID.LOOP) || [] + } } $: filteredResults = prepTestResults(testResults) $: { - blocks = [] - if (automation) { + if (testResults.message) { + blocks = automation?.definition?.trigger + ? [automation.definition.trigger] + : [] + } else if (automation) { + blocks = [] if (automation.definition.trigger) { blocks.push(automation.definition.trigger) } @@ -46,7 +63,9 @@ open={!!openBlocks[block.id]} on:toggle={() => (openBlocks[block.id] = !openBlocks[block.id])} isTrigger={idx === 0} - testResult={filteredResults?.[idx]} + testResult={testResults.message + ? testResults + : filteredResults?.[idx]} showTestStatus {block} {idx} @@ -68,7 +87,9 @@
- {#if filteredResults?.[idx]?.inputs} + {#if testResults.message} + No input + {:else if filteredResults?.[idx]?.inputs} {:else} No input @@ -77,13 +98,22 @@
- {#if filteredResults?.[idx]?.outputs} + {#if testResults.message} + + {:else if filteredResults?.[idx]?.outputs} {:else} - No input + No output {/if}
diff --git a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte index 283bd14fc0..8a9d1e59ea 100644 --- a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte +++ b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte @@ -17,7 +17,9 @@ Helpers, Toggle, Divider, + Icon, } from "@budibase/bbui" + import CreateWebhookModal from "components/automation/Shared/CreateWebhookModal.svelte" import { automationStore, selectedAutomation, tables } from "stores/builder" import { environment, licensing } from "stores/portal" @@ -365,41 +367,74 @@ /** * Handler for row trigger automation updates. - @param {object} update - An automation block.inputs update object - @example - onRowTriggerUpdate({ - "tableId" : "ta_bb_employee" - }) + * @param {object} update - An automation block.inputs update object + * @param {string} [update.tableId] - The ID of the table + * @param {object} [update.filters] - Filter configuration for the row trigger + * @param {object} [update.filters-def] - Filter definitions for the row trigger + * @example + * // Example with tableId + * onRowTriggerUpdate({ + * "tableId" : "ta_bb_employee" + * }) + * @example + * // Example with filters + * onRowTriggerUpdate({ + * filters: { + * equal: { "1:Approved": "true" } + * }, + * "filters-def": [{ + * id: "oH1T4S49n", + * field: "1:Approved", + * operator: "equal", + * value: "true", + * valueType: "Value", + * type: "string" + * }] + * }) */ const onRowTriggerUpdate = async update => { if ( - Object.hasOwn(update, "tableId") && - $selectedAutomation.testData?.row?.tableId !== update.tableId + ["tableId", "filters", "meta"].some(key => Object.hasOwn(update, key)) ) { try { - const reqSchema = getSchemaForDatasourcePlus(update.tableId, { - searchableSchema: true, - }).schema + let updatedAutomation - // Parse the block inputs as usual - const updatedAutomation = - await automationStore.actions.processBlockInputs(block, { - schema: reqSchema, - ...update, - }) + if ( + Object.hasOwn(update, "tableId") && + $selectedAutomation.testData?.row?.tableId !== update.tableId + ) { + const reqSchema = getSchemaForDatasourcePlus(update.tableId, { + searchableSchema: true, + }).schema - // Save the entire automation and reset the testData - await automationStore.actions.save({ - ...updatedAutomation, - testData: { - // Reset Core fields - row: { tableId: update.tableId }, - oldRow: { tableId: update.tableId }, - meta: {}, - id: "", - revision: "", - }, - }) + updatedAutomation = await automationStore.actions.processBlockInputs( + block, + { + schema: reqSchema, + ...update, + } + ) + + // Reset testData when tableId changes + updatedAutomation = { + ...updatedAutomation, + testData: { + row: { tableId: update.tableId }, + oldRow: { tableId: update.tableId }, + meta: {}, + id: "", + revision: "", + }, + } + } else { + // For filters update, just process block inputs without resetting testData + updatedAutomation = await automationStore.actions.processBlockInputs( + block, + update + ) + } + + await automationStore.actions.save(updatedAutomation) return } catch (e) { @@ -408,7 +443,6 @@ } } } - /** * Handler for App trigger automation updates. * Ensure updates to the field list are reflected in testData @@ -743,6 +777,7 @@ value.customType !== "triggerSchema" && value.customType !== "automationFields" && value.customType !== "fields" && + value.customType !== "trigger_filter_setting" && value.type !== "signature_single" && value.type !== "attachment" && value.type !== "attachment_single" @@ -807,13 +842,23 @@ {@const label = getFieldLabel(key, value)}
{#if key !== "fields" && value.type !== "boolean" && shouldRenderField(value)} - +
+ + {#if value.customType === "trigger_filter"} + + window.open( + "https://docs.budibase.com/docs/row-trigger-filters", + "_blank" + )} + size="XS" + name="InfoOutline" + /> + {/if} +
{/if}
{#if value.type === "string" && value.enum && canShowField(key, value)} @@ -932,8 +977,12 @@ {/if}
- {:else if value.customType === "filters"} - Define filters + {:else if value.customType === "filters" || value.customType === "trigger_filter"} + {filters.length > 0 + ? "Update Filter" + : "No Filter set"}