add ability to trigger synchronous automation from button action
This commit is contained in:
parent
ac57a849ce
commit
1539bf234b
|
@ -48,7 +48,6 @@
|
||||||
}
|
}
|
||||||
return acc
|
return acc
|
||||||
}, {})
|
}, {})
|
||||||
console.log(plugins)
|
|
||||||
|
|
||||||
const selectAction = action => {
|
const selectAction = action => {
|
||||||
actionVal = action
|
actionVal = action
|
||||||
|
|
|
@ -184,7 +184,7 @@
|
||||||
{#if !isTrigger}
|
{#if !isTrigger}
|
||||||
<div>
|
<div>
|
||||||
<div class="block-options">
|
<div class="block-options">
|
||||||
{#if !loopBlock}
|
{#if block?.canLoop}
|
||||||
<ActionButton on:click={() => addLooping()} icon="Reuse">
|
<ActionButton on:click={() => addLooping()} icon="Reuse">
|
||||||
Add Looping
|
Add Looping
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
|
|
|
@ -126,8 +126,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const getAllBindings = (bindings, eventContextBindings, actions) => {
|
const getAllBindings = (bindings, eventContextBindings, actions) => {
|
||||||
let allBindings = eventContextBindings.concat(bindings)
|
let allBindings = []
|
||||||
|
|
||||||
if (!actions) {
|
if (!actions) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
@ -145,14 +144,37 @@
|
||||||
.forEach(action => {
|
.forEach(action => {
|
||||||
// Check we have a binding for this action, and generate one if not
|
// Check we have a binding for this action, and generate one if not
|
||||||
const stateBinding = makeStateBinding(action.parameters.key)
|
const stateBinding = makeStateBinding(action.parameters.key)
|
||||||
const hasKey = allBindings.some(binding => {
|
const hasKey = bindings.some(binding => {
|
||||||
return binding.runtimeBinding === stateBinding.runtimeBinding
|
return binding.runtimeBinding === stateBinding.runtimeBinding
|
||||||
})
|
})
|
||||||
if (!hasKey) {
|
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
|
return allBindings
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
<script>
|
<script>
|
||||||
import { Select, Label, Input, Checkbox } from "@budibase/bbui"
|
import { Select, Label, Input, Checkbox, Icon } from "@budibase/bbui"
|
||||||
import { automationStore } from "builderStore"
|
import { automationStore } from "builderStore"
|
||||||
import SaveFields from "./SaveFields.svelte"
|
import SaveFields from "./SaveFields.svelte"
|
||||||
import { TriggerStepID } from "constants/backend/automations"
|
import { TriggerStepID } from "constants/backend/automations"
|
||||||
|
import { AutomationActionStepId } from "../../../../../../../../types/src/documents"
|
||||||
|
|
||||||
export let parameters = {}
|
export let parameters = {}
|
||||||
export let bindings = []
|
export let bindings = []
|
||||||
|
|
||||||
|
let synchronous = parameters.synchronous
|
||||||
|
|
||||||
const AUTOMATION_STATUS = {
|
const AUTOMATION_STATUS = {
|
||||||
NEW: "new",
|
NEW: "new",
|
||||||
EXISTING: "existing",
|
EXISTING: "existing",
|
||||||
|
@ -16,6 +19,11 @@
|
||||||
? AUTOMATION_STATUS.EXISTING
|
? AUTOMATION_STATUS.EXISTING
|
||||||
: AUTOMATION_STATUS.NEW
|
: AUTOMATION_STATUS.NEW
|
||||||
|
|
||||||
|
$: {
|
||||||
|
if (automationStatus === AUTOMATION_STATUS.NEW) {
|
||||||
|
synchronous = false
|
||||||
|
}
|
||||||
|
}
|
||||||
$: automations = $automationStore.automations
|
$: automations = $automationStore.automations
|
||||||
.filter(a => a.definition.trigger?.stepId === TriggerStepID.APP)
|
.filter(a => a.definition.trigger?.stepId === TriggerStepID.APP)
|
||||||
.map(automation => {
|
.map(automation => {
|
||||||
|
@ -23,10 +31,15 @@
|
||||||
automation.definition.trigger.inputs.fields || {}
|
automation.definition.trigger.inputs.fields || {}
|
||||||
).map(([name, type]) => ({ name, type }))
|
).map(([name, type]) => ({ name, type }))
|
||||||
|
|
||||||
|
let hasCollectBlock = automation.definition.steps.some(
|
||||||
|
step => step.stepId === AutomationActionStepId.COLLECT
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: automation.name,
|
name: automation.name,
|
||||||
_id: automation._id,
|
_id: automation._id,
|
||||||
schema,
|
schema,
|
||||||
|
synchronous: hasCollectBlock,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
$: hasAutomations = automations && automations.length > 0
|
$: hasAutomations = automations && automations.length > 0
|
||||||
|
@ -57,6 +70,15 @@
|
||||||
parameters.fields = {}
|
parameters.fields = {}
|
||||||
parameters.automationId = automations[0]?._id
|
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>
|
</script>
|
||||||
|
|
||||||
<div class="root">
|
<div class="root">
|
||||||
|
@ -85,6 +107,7 @@
|
||||||
|
|
||||||
{#if automationStatus === AUTOMATION_STATUS.EXISTING}
|
{#if automationStatus === AUTOMATION_STATUS.EXISTING}
|
||||||
<Select
|
<Select
|
||||||
|
on:change={onChange}
|
||||||
bind:value={parameters.automationId}
|
bind:value={parameters.automationId}
|
||||||
placeholder="Choose automation"
|
placeholder="Choose automation"
|
||||||
options={automations}
|
options={automations}
|
||||||
|
@ -98,6 +121,19 @@
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/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 />
|
<Label small />
|
||||||
<Checkbox
|
<Checkbox
|
||||||
text="Do not display default notification"
|
text="Do not display default notification"
|
||||||
|
@ -142,6 +178,11 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.synchronous-info {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--spacing-s);
|
||||||
|
}
|
||||||
|
|
||||||
.fields {
|
.fields {
|
||||||
margin-top: var(--spacing-l);
|
margin-top: var(--spacing-l);
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|
|
@ -57,7 +57,13 @@
|
||||||
{
|
{
|
||||||
"name": "Trigger Automation",
|
"name": "Trigger Automation",
|
||||||
"type": "application",
|
"type": "application",
|
||||||
"component": "TriggerAutomation"
|
"component": "TriggerAutomation",
|
||||||
|
"context": [
|
||||||
|
{
|
||||||
|
"label": "Automation Result",
|
||||||
|
"value": "result"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Update Field Value",
|
"name": "Update Field Value",
|
||||||
|
|
|
@ -122,15 +122,24 @@ const deleteRowHandler = async action => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const triggerAutomationHandler = async action => {
|
const triggerAutomationHandler = async action => {
|
||||||
const { fields, notificationOverride } = action.parameters
|
const { fields, notificationOverride, synchronous } = action.parameters
|
||||||
if (fields) {
|
if (fields) {
|
||||||
try {
|
try {
|
||||||
await API.triggerAutomation({
|
if (synchronous) {
|
||||||
automationId: action.parameters.automationId,
|
const result = await API.triggerSynchronousAutomation({
|
||||||
fields,
|
automationId: action.parameters.automationId,
|
||||||
})
|
fields,
|
||||||
if (!notificationOverride) {
|
})
|
||||||
notificationStore.actions.success("Automation triggered")
|
console.log(typeof result)
|
||||||
|
return { result }
|
||||||
|
} else {
|
||||||
|
await API.triggerAutomation({
|
||||||
|
automationId: action.parameters.automationId,
|
||||||
|
fields,
|
||||||
|
})
|
||||||
|
if (!notificationOverride) {
|
||||||
|
notificationStore.actions.success("Automation triggered")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Abort next actions
|
// Abort next actions
|
||||||
|
@ -138,7 +147,6 @@ const triggerAutomationHandler = async action => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const navigationHandler = action => {
|
const navigationHandler = action => {
|
||||||
const { url, peek, externalNewTab } = action.parameters
|
const { url, peek, externalNewTab } = action.parameters
|
||||||
routeStore.actions.navigate(url, peek, externalNewTab)
|
routeStore.actions.navigate(url, peek, externalNewTab)
|
||||||
|
|
|
@ -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.
|
* Tests an automation with data.
|
||||||
* @param automationId the ID of the automation to test
|
* @param automationId the ID of the automation to test
|
||||||
|
|
|
@ -15,7 +15,12 @@ import { MetadataTypes } from "../../constants"
|
||||||
import { setTestFlag, clearTestFlag } from "../../utilities/redis"
|
import { setTestFlag, clearTestFlag } from "../../utilities/redis"
|
||||||
import { context, cache, events } from "@budibase/backend-core"
|
import { context, cache, events } from "@budibase/backend-core"
|
||||||
import { automations } from "@budibase/pro"
|
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"
|
import { getActionDefinitions as actionDefs } from "../../automations/actions"
|
||||||
|
|
||||||
async function getActionDefinitions() {
|
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) {
|
function prepareTestInput(input: any) {
|
||||||
// prepare the test parameters
|
// prepare the test parameters
|
||||||
if (input.id && input.row) {
|
if (input.id && input.row) {
|
||||||
|
|
|
@ -73,6 +73,17 @@ router
|
||||||
),
|
),
|
||||||
controller.trigger
|
controller.trigger
|
||||||
)
|
)
|
||||||
|
.post(
|
||||||
|
"/api/automations/:id/triggerSynchronous",
|
||||||
|
appInfoMiddleware({ appType: AppType.PROD }),
|
||||||
|
paramResource("id"),
|
||||||
|
authorized(
|
||||||
|
permissions.PermissionType.AUTOMATION,
|
||||||
|
permissions.PermissionLevel.EXECUTE
|
||||||
|
),
|
||||||
|
controller.triggerSynchronous
|
||||||
|
)
|
||||||
|
|
||||||
.post(
|
.post(
|
||||||
"/api/automations/:id/test",
|
"/api/automations/:id/test",
|
||||||
appInfoMiddleware({ appType: AppType.DEV }),
|
appInfoMiddleware({ appType: AppType.DEV }),
|
||||||
|
|
|
@ -14,6 +14,7 @@ import * as filter from "./steps/filter"
|
||||||
import * as delay from "./steps/delay"
|
import * as delay from "./steps/delay"
|
||||||
import * as queryRow from "./steps/queryRows"
|
import * as queryRow from "./steps/queryRows"
|
||||||
import * as loop from "./steps/loop"
|
import * as loop from "./steps/loop"
|
||||||
|
import * as collect from "./steps/collect"
|
||||||
import env from "../environment"
|
import env from "../environment"
|
||||||
import {
|
import {
|
||||||
AutomationStepSchema,
|
AutomationStepSchema,
|
||||||
|
@ -39,6 +40,7 @@ const ACTION_IMPLS: Record<
|
||||||
DELAY: delay.run,
|
DELAY: delay.run,
|
||||||
FILTER: filter.run,
|
FILTER: filter.run,
|
||||||
QUERY_ROWS: queryRow.run,
|
QUERY_ROWS: queryRow.run,
|
||||||
|
COLLECT: collect.run,
|
||||||
// these used to be lowercase step IDs, maintain for backwards compat
|
// these used to be lowercase step IDs, maintain for backwards compat
|
||||||
discord: discord.run,
|
discord: discord.run,
|
||||||
slack: slack.run,
|
slack: slack.run,
|
||||||
|
@ -59,6 +61,7 @@ export const BUILTIN_ACTION_DEFINITIONS: Record<string, AutomationStepSchema> =
|
||||||
FILTER: filter.definition,
|
FILTER: filter.definition,
|
||||||
QUERY_ROWS: queryRow.definition,
|
QUERY_ROWS: queryRow.definition,
|
||||||
LOOP: loop.definition,
|
LOOP: loop.definition,
|
||||||
|
COLLECT: collect.definition,
|
||||||
// these used to be lowercase step IDs, maintain for backwards compat
|
// these used to be lowercase step IDs, maintain for backwards compat
|
||||||
discord: discord.definition,
|
discord: discord.definition,
|
||||||
slack: slack.definition,
|
slack: slack.definition,
|
||||||
|
|
|
@ -56,6 +56,7 @@ export enum AutomationActionStepId {
|
||||||
FILTER = "FILTER",
|
FILTER = "FILTER",
|
||||||
QUERY_ROWS = "QUERY_ROWS",
|
QUERY_ROWS = "QUERY_ROWS",
|
||||||
LOOP = "LOOP",
|
LOOP = "LOOP",
|
||||||
|
COLLECT = "COLLECT",
|
||||||
// these used to be lowercase step IDs, maintain for backwards compat
|
// these used to be lowercase step IDs, maintain for backwards compat
|
||||||
discord = "discord",
|
discord = "discord",
|
||||||
slack = "slack",
|
slack = "slack",
|
||||||
|
@ -120,6 +121,7 @@ export interface AutomationStepSchema {
|
||||||
outputs: InputOutputBlock
|
outputs: InputOutputBlock
|
||||||
}
|
}
|
||||||
custom?: boolean
|
custom?: boolean
|
||||||
|
canLoop?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AutomationStep extends AutomationStepSchema {
|
export interface AutomationStep extends AutomationStepSchema {
|
||||||
|
|
Loading…
Reference in New Issue