add ability to trigger synchronous automation from button action

This commit is contained in:
Peter Clement 2023-05-09 12:10:20 +01:00
parent ac57a849ce
commit 1539bf234b
11 changed files with 139 additions and 17 deletions

View File

@ -48,7 +48,6 @@
}
return acc
}, {})
console.log(plugins)
const selectAction = action => {
actionVal = action

View File

@ -184,7 +184,7 @@
{#if !isTrigger}
<div>
<div class="block-options">
{#if !loopBlock}
{#if block?.canLoop}
<ActionButton on:click={() => addLooping()} icon="Reuse">
Add Looping
</ActionButton>

View File

@ -126,8 +126,7 @@
}
const getAllBindings = (bindings, eventContextBindings, actions) => {
let allBindings = eventContextBindings.concat(bindings)
let allBindings = []
if (!actions) {
return []
}
@ -145,14 +144,37 @@
.forEach(action => {
// Check we have a binding for this action, and generate one if not
const stateBinding = makeStateBinding(action.parameters.key)
const hasKey = allBindings.some(binding => {
const hasKey = bindings.some(binding => {
return binding.runtimeBinding === stateBinding.runtimeBinding
})
if (!hasKey) {
allBindings.push(stateBinding)
bindings.push(stateBinding)
}
})
// Get which indexes are asynchronous automations as we want to filter them out from the bindings
const asynchronousAutomationIndexes = actions
.map((action, index) => {
if (
action[EVENT_TYPE_KEY] === "Trigger Automation" &&
!action.parameters?.synchronous
) {
return index
}
})
.filter(index => index !== undefined)
// Based on the above, filter out the asynchronous automations from the bindings
if (asynchronousAutomationIndexes) {
allBindings = eventContextBindings
.filter((binding, index) => {
return !asynchronousAutomationIndexes.includes(index)
})
.concat(bindings)
} else {
allBindings = eventContextBindings.concat(bindings)
}
return allBindings
}
</script>

View File

@ -1,12 +1,15 @@
<script>
import { Select, Label, Input, Checkbox } from "@budibase/bbui"
import { Select, Label, Input, Checkbox, Icon } from "@budibase/bbui"
import { automationStore } from "builderStore"
import SaveFields from "./SaveFields.svelte"
import { TriggerStepID } from "constants/backend/automations"
import { AutomationActionStepId } from "../../../../../../../../types/src/documents"
export let parameters = {}
export let bindings = []
let synchronous = parameters.synchronous
const AUTOMATION_STATUS = {
NEW: "new",
EXISTING: "existing",
@ -16,6 +19,11 @@
? AUTOMATION_STATUS.EXISTING
: AUTOMATION_STATUS.NEW
$: {
if (automationStatus === AUTOMATION_STATUS.NEW) {
synchronous = false
}
}
$: automations = $automationStore.automations
.filter(a => a.definition.trigger?.stepId === TriggerStepID.APP)
.map(automation => {
@ -23,10 +31,15 @@
automation.definition.trigger.inputs.fields || {}
).map(([name, type]) => ({ name, type }))
let hasCollectBlock = automation.definition.steps.some(
step => step.stepId === AutomationActionStepId.COLLECT
)
return {
name: automation.name,
_id: automation._id,
schema,
synchronous: hasCollectBlock,
}
})
$: hasAutomations = automations && automations.length > 0
@ -57,6 +70,15 @@
parameters.fields = {}
parameters.automationId = automations[0]?._id
}
const onChange = value => {
let automationId = value.detail
synchronous = automations.find(
automation => automation._id === automationId
).synchronous
parameters.automationId = automationId
parameters.synchronous = synchronous
}
</script>
<div class="root">
@ -85,6 +107,7 @@
{#if automationStatus === AUTOMATION_STATUS.EXISTING}
<Select
on:change={onChange}
bind:value={parameters.automationId}
placeholder="Choose automation"
options={automations}
@ -98,6 +121,19 @@
/>
{/if}
{#if synchronous}
<Label small />
<div class="synchronous-info">
<Icon name="Info" />
<div>
<i
>This automation will run synchronously due to the existence of a
Collect block</i
>
</div>
</div>
{/if}
<Label small />
<Checkbox
text="Do not display default notification"
@ -142,6 +178,11 @@
align-items: center;
}
.synchronous-info {
display: flex;
gap: var(--spacing-s);
}
.fields {
margin-top: var(--spacing-l);
display: grid;

View File

@ -57,7 +57,13 @@
{
"name": "Trigger Automation",
"type": "application",
"component": "TriggerAutomation"
"component": "TriggerAutomation",
"context": [
{
"label": "Automation Result",
"value": "result"
}
]
},
{
"name": "Update Field Value",

View File

@ -122,15 +122,24 @@ const deleteRowHandler = async action => {
}
const triggerAutomationHandler = async action => {
const { fields, notificationOverride } = action.parameters
const { fields, notificationOverride, synchronous } = action.parameters
if (fields) {
try {
await API.triggerAutomation({
automationId: action.parameters.automationId,
fields,
})
if (!notificationOverride) {
notificationStore.actions.success("Automation triggered")
if (synchronous) {
const result = await API.triggerSynchronousAutomation({
automationId: action.parameters.automationId,
fields,
})
console.log(typeof result)
return { result }
} else {
await API.triggerAutomation({
automationId: action.parameters.automationId,
fields,
})
if (!notificationOverride) {
notificationStore.actions.success("Automation triggered")
}
}
} catch (error) {
// Abort next actions
@ -138,7 +147,6 @@ const triggerAutomationHandler = async action => {
}
}
}
const navigationHandler = action => {
const { url, peek, externalNewTab } = action.parameters
routeStore.actions.navigate(url, peek, externalNewTab)

View File

@ -11,6 +11,13 @@ export const buildAutomationEndpoints = API => ({
})
},
triggerSynchronousAutomation: async ({ automationId, fields }) => {
return await API.post({
url: `/api/automations/${automationId}/triggerSynchronous`,
body: { fields },
})
},
/**
* Tests an automation with data.
* @param automationId the ID of the automation to test

View File

@ -15,7 +15,12 @@ import { MetadataTypes } from "../../constants"
import { setTestFlag, clearTestFlag } from "../../utilities/redis"
import { context, cache, events } from "@budibase/backend-core"
import { automations } from "@budibase/pro"
import { Automation, BBContext } from "@budibase/types"
import {
Automation,
AutomationActionStepId,
AutomationResults,
BBContext,
} from "@budibase/types"
import { getActionDefinitions as actionDefs } from "../../automations/actions"
async function getActionDefinitions() {
@ -267,6 +272,24 @@ export async function trigger(ctx: BBContext) {
}
}
export async function triggerSynchronous(ctx: BBContext) {
const db = context.getAppDB()
let automation = await db.get(ctx.params.id)
const response: AutomationResults = await triggers.externalTrigger(
automation,
{
...ctx.request.body,
appId: ctx.appId,
},
{ getResponses: true }
)
let collectedValue = response.steps.find(
step => step.stepId === AutomationActionStepId.COLLECT
)
ctx.body = collectedValue?.outputs
}
function prepareTestInput(input: any) {
// prepare the test parameters
if (input.id && input.row) {

View File

@ -73,6 +73,17 @@ router
),
controller.trigger
)
.post(
"/api/automations/:id/triggerSynchronous",
appInfoMiddleware({ appType: AppType.PROD }),
paramResource("id"),
authorized(
permissions.PermissionType.AUTOMATION,
permissions.PermissionLevel.EXECUTE
),
controller.triggerSynchronous
)
.post(
"/api/automations/:id/test",
appInfoMiddleware({ appType: AppType.DEV }),

View File

@ -14,6 +14,7 @@ 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 * as collect from "./steps/collect"
import env from "../environment"
import {
AutomationStepSchema,
@ -39,6 +40,7 @@ const ACTION_IMPLS: Record<
DELAY: delay.run,
FILTER: filter.run,
QUERY_ROWS: queryRow.run,
COLLECT: collect.run,
// these used to be lowercase step IDs, maintain for backwards compat
discord: discord.run,
slack: slack.run,
@ -59,6 +61,7 @@ export const BUILTIN_ACTION_DEFINITIONS: Record<string, AutomationStepSchema> =
FILTER: filter.definition,
QUERY_ROWS: queryRow.definition,
LOOP: loop.definition,
COLLECT: collect.definition,
// these used to be lowercase step IDs, maintain for backwards compat
discord: discord.definition,
slack: slack.definition,

View File

@ -56,6 +56,7 @@ export enum AutomationActionStepId {
FILTER = "FILTER",
QUERY_ROWS = "QUERY_ROWS",
LOOP = "LOOP",
COLLECT = "COLLECT",
// these used to be lowercase step IDs, maintain for backwards compat
discord = "discord",
slack = "slack",
@ -120,6 +121,7 @@ export interface AutomationStepSchema {
outputs: InputOutputBlock
}
custom?: boolean
canLoop?: boolean
}
export interface AutomationStep extends AutomationStepSchema {