Refactor automation store to properly handle errors

This commit is contained in:
Andrew Kingston 2022-01-25 15:44:08 +00:00
parent 45547f1efc
commit 9d52841219
12 changed files with 242 additions and 197 deletions

View File

@ -22,109 +22,86 @@ export const getAutomationStore = () => {
const automationActions = store => ({ const automationActions = store => ({
fetch: async () => { fetch: async () => {
try { const responses = await Promise.all([
const responses = await Promise.all([ API.getAutomations(),
API.getAutomations(), API.getAutomationDefinitions(),
API.getAutomationDefinitions(), ])
]) store.update(state => {
store.update(state => { let selected = state.selectedAutomation?.automation
let selected = state.selectedAutomation?.automation state.automations = responses[0]
state.automations = responses[0] state.blockDefinitions = {
state.blockDefinitions = { TRIGGER: responses[1].trigger,
TRIGGER: responses[1].trigger, ACTION: responses[1].action,
ACTION: responses[1].action, }
} // If previously selected find the new obj and select it
// If previously selected find the new obj and select it if (selected) {
if (selected) { selected = responses[0].filter(
selected = responses[0].filter( automation => automation._id === selected._id
automation => automation._id === selected._id )
) state.selectedAutomation = new Automation(selected[0])
state.selectedAutomation = new Automation(selected[0]) }
} return state
return state })
})
} catch (error) {
notifications.error("Error fetching automations")
store.set(initialAutomationState)
}
}, },
create: async ({ name }) => { create: async ({ name }) => {
try { const automation = {
const automation = { name,
name, type: "automation",
type: "automation", definition: {
definition: { steps: [],
steps: [], },
},
}
const response = await API.createAutomation(automation)
store.update(state => {
state.automations = [...state.automations, response.automation]
store.actions.select(response.automation)
return state
})
} catch (error) {
notifications.error("Error creating automation")
} }
const response = await API.createAutomation(automation)
store.update(state => {
state.automations = [...state.automations, response.automation]
store.actions.select(response.automation)
return state
})
}, },
save: async automation => { save: async automation => {
try { const response = await API.updateAutomation(automation)
const response = await API.updateAutomation(automation) store.update(state => {
store.update(state => { const updatedAutomation = response.automation
const updatedAutomation = response.automation const existingIdx = state.automations.findIndex(
const existingIdx = state.automations.findIndex( existing => existing._id === automation._id
existing => existing._id === automation._id )
) if (existingIdx !== -1) {
if (existingIdx !== -1) { state.automations.splice(existingIdx, 1, updatedAutomation)
state.automations.splice(existingIdx, 1, updatedAutomation) state.automations = [...state.automations]
state.automations = [...state.automations] store.actions.select(updatedAutomation)
store.actions.select(updatedAutomation) return state
return state }
} })
})
notifications.success("Automation saved successfully")
} catch (error) {
notifications.error("Error saving automation")
}
}, },
delete: async automation => { delete: async automation => {
try { await API.deleteAutomation({
await API.deleteAutomation({ automationId: automation?._id,
automationId: automation?._id, automationRev: automation?._rev,
automationRev: automation?._rev, })
}) store.update(state => {
store.update(state => { const existingIdx = state.automations.findIndex(
const existingIdx = state.automations.findIndex( existing => existing._id === automation?._id
existing => existing._id === automation?._id )
) state.automations.splice(existingIdx, 1)
state.automations.splice(existingIdx, 1) state.automations = [...state.automations]
state.automations = [...state.automations] state.selectedAutomation = null
state.selectedAutomation = null state.selectedBlock = null
state.selectedBlock = null return state
return state })
})
notifications.success("Automation deleted successfully")
} catch (error) {
notifications.error("Error deleting automation")
}
}, },
test: async (automation, testData) => { test: async (automation, testData) => {
try { store.update(state => {
const result = await API.testAutomation({ state.selectedAutomation.testResults = null
automationId: automation?._id, return state
testData, })
}) const result = await API.testAutomation({
store.update(state => { automationId: automation?._id,
state.selectedAutomation.testResults = result testData,
return state })
}) store.update(state => {
} catch (error) { state.selectedAutomation.testResults = result
notifications.error("Error testing automation") return state
store.update(state => { })
state.selectedAutomation.testResults = null
return state
})
}
}, },
select: automation => { select: automation => {
store.update(state => { store.update(state => {

View File

@ -6,6 +6,7 @@
Body, Body,
Icon, Icon,
Tooltip, Tooltip,
notifications,
} from "@budibase/bbui" } from "@budibase/bbui"
import { automationStore } from "builderStore" import { automationStore } from "builderStore"
import { admin } from "stores/portal" import { admin } from "stores/portal"
@ -47,15 +48,19 @@
} }
async function addBlockToAutomation() { async function addBlockToAutomation() {
const newBlock = $automationStore.selectedAutomation.constructBlock( try {
"ACTION", const newBlock = $automationStore.selectedAutomation.constructBlock(
actionVal.stepId, "ACTION",
actionVal actionVal.stepId,
) actionVal
automationStore.actions.addBlockToAutomation(newBlock, blockIdx + 1) )
await automationStore.actions.save( automationStore.actions.addBlockToAutomation(newBlock, blockIdx + 1)
$automationStore.selectedAutomation?.automation await automationStore.actions.save(
) $automationStore.selectedAutomation?.automation
)
} catch (error) {
notifications.error("Error saving automation")
}
} }
</script> </script>

View File

@ -30,9 +30,13 @@
} }
async function deleteAutomation() { async function deleteAutomation() {
await automationStore.actions.delete( try {
$automationStore.selectedAutomation?.automation await automationStore.actions.delete(
) $automationStore.selectedAutomation?.automation
)
} catch (error) {
notifications.error("Error deleting automation")
}
} }
</script> </script>

View File

@ -10,6 +10,7 @@
Button, Button,
StatusLight, StatusLight,
ActionButton, ActionButton,
notifications,
} from "@budibase/bbui" } from "@budibase/bbui"
import AutomationBlockSetup from "../../SetupPanel/AutomationBlockSetup.svelte" import AutomationBlockSetup from "../../SetupPanel/AutomationBlockSetup.svelte"
import CreateWebhookModal from "components/automation/Shared/CreateWebhookModal.svelte" import CreateWebhookModal from "components/automation/Shared/CreateWebhookModal.svelte"
@ -54,10 +55,14 @@
).every(x => block?.inputs[x]) ).every(x => block?.inputs[x])
async function deleteStep() { async function deleteStep() {
automationStore.actions.deleteAutomationBlock(block) try {
await automationStore.actions.save( automationStore.actions.deleteAutomationBlock(block)
$automationStore.selectedAutomation?.automation await automationStore.actions.save(
) $automationStore.selectedAutomation?.automation
)
} catch (error) {
notifications.error("Error saving notification")
}
} }
</script> </script>

View File

@ -1,5 +1,12 @@
<script> <script>
import { ModalContent, Tabs, Tab, TextArea, Label } from "@budibase/bbui" import {
ModalContent,
Tabs,
Tab,
TextArea,
Label,
notifications,
} from "@budibase/bbui"
import { automationStore } from "builderStore" import { automationStore } from "builderStore"
import AutomationBlockSetup from "../../SetupPanel/AutomationBlockSetup.svelte" import AutomationBlockSetup from "../../SetupPanel/AutomationBlockSetup.svelte"
import { cloneDeep } from "lodash/fp" import { cloneDeep } from "lodash/fp"
@ -37,6 +44,17 @@
failedParse = "Invalid JSON" failedParse = "Invalid JSON"
} }
} }
const testAutomation = async () => {
try {
await automationStore.actions.test(
$automationStore.selectedAutomation?.automation,
testData
)
} catch (error) {
notifications.error("Error testing notification")
}
}
</script> </script>
<ModalContent <ModalContent
@ -44,12 +62,7 @@
confirmText="Test" confirmText="Test"
showConfirmButton={true} showConfirmButton={true}
disabled={isError} disabled={isError}
onConfirm={() => { onConfirm={testAutomation}
automationStore.actions.test(
$automationStore.selectedAutomation?.automation,
testData
)
}}
cancelText="Cancel" cancelText="Cancel"
> >
<Tabs selected="Form" quiet <Tabs selected="Form" quiet

View File

@ -4,10 +4,16 @@
import { automationStore } from "builderStore" import { automationStore } from "builderStore"
import NavItem from "components/common/NavItem.svelte" import NavItem from "components/common/NavItem.svelte"
import EditAutomationPopover from "./EditAutomationPopover.svelte" import EditAutomationPopover from "./EditAutomationPopover.svelte"
import { notifications } from "@budibase/bbui"
$: selectedAutomationId = $automationStore.selectedAutomation?.automation?._id $: selectedAutomationId = $automationStore.selectedAutomation?.automation?._id
onMount(() => {
automationStore.actions.fetch() onMount(async () => {
try {
await automationStore.actions.fetch()
} catch (error) {
notifications.error("Error getting automations list")
}
}) })
function selectAutomation(automation) { function selectAutomation(automation) {

View File

@ -24,29 +24,33 @@
nameTouched && !name ? "Please specify a name for the automation." : null nameTouched && !name ? "Please specify a name for the automation." : null
async function createAutomation() { async function createAutomation() {
await automationStore.actions.create({ try {
name, await automationStore.actions.create({
instanceId, name,
}) instanceId,
const newBlock = $automationStore.selectedAutomation.constructBlock( })
"TRIGGER", const newBlock = $automationStore.selectedAutomation.constructBlock(
triggerVal.stepId, "TRIGGER",
triggerVal triggerVal.stepId,
) triggerVal
)
automationStore.actions.addBlockToAutomation(newBlock) automationStore.actions.addBlockToAutomation(newBlock)
if (triggerVal.stepId === "WEBHOOK") { if (triggerVal.stepId === "WEBHOOK") {
webhookModal.show webhookModal.show
}
await automationStore.actions.save(
$automationStore.selectedAutomation?.automation
)
notifications.success(`Automation ${name} created`)
$goto(`./${$automationStore.selectedAutomation.automation._id}`)
analytics.captureEvent(Events.AUTOMATION.CREATED, { name })
} catch (error) {
notifications.error("Error creating automation")
} }
await automationStore.actions.save(
$automationStore.selectedAutomation?.automation
)
notifications.success(`Automation ${name} created.`)
$goto(`./${$automationStore.selectedAutomation.automation._id}`)
analytics.captureEvent(Events.AUTOMATION.CREATED, { name })
} }
$: triggers = Object.entries($automationStore.blockDefinitions.TRIGGER) $: triggers = Object.entries($automationStore.blockDefinitions.TRIGGER)

View File

@ -11,9 +11,13 @@
let updateAutomationDialog let updateAutomationDialog
async function deleteAutomation() { async function deleteAutomation() {
await automationStore.actions.delete(automation) try {
notifications.success("Automation deleted.") await automationStore.actions.delete(automation)
$goto("../automate") notifications.success("Automation deleted successfully")
$goto("../automate")
} catch (error) {
notifications.error("Error deleting automation")
}
} }
</script> </script>

View File

@ -20,14 +20,18 @@
} }
async function saveAutomation() { async function saveAutomation() {
const updatedAutomation = { try {
...automation, const updatedAutomation = {
name, ...automation,
name,
}
await automationStore.actions.save(updatedAutomation)
notifications.success(`Automation ${name} updated successfully`)
analytics.captureEvent(Events.AUTOMATION.SAVED, { name })
hide()
} catch (error) {
notifications.error("Error saving automation")
} }
await automationStore.actions.save(updatedAutomation)
notifications.success(`Automation ${name} updated successfully.`)
analytics.captureEvent(Events.AUTOMATION.SAVED, { name })
hide()
} }
function checkValid(evt) { function checkValid(evt) {

View File

@ -11,6 +11,7 @@
Drawer, Drawer,
Modal, Modal,
Detail, Detail,
notifications,
} from "@budibase/bbui" } from "@budibase/bbui"
import CreateWebhookModal from "components/automation/Shared/CreateWebhookModal.svelte" import CreateWebhookModal from "components/automation/Shared/CreateWebhookModal.svelte"
@ -54,28 +55,32 @@
$: schemaFields = table ? Object.values(table.schema) : [] $: schemaFields = table ? Object.values(table.schema) : []
const onChange = debounce(async function (e, key) { const onChange = debounce(async function (e, key) {
if (isTestModal) { try {
// Special case for webhook, as it requires a body, but the schema already brings back the body's contents if (isTestModal) {
if (stepId === "WEBHOOK") { // Special case for webhook, as it requires a body, but the schema already brings back the body's contents
if (stepId === "WEBHOOK") {
automationStore.actions.addTestDataToAutomation({
body: {
[key]: e.detail,
...$automationStore.selectedAutomation.automation.testData.body,
},
})
}
automationStore.actions.addTestDataToAutomation({ automationStore.actions.addTestDataToAutomation({
body: { [key]: e.detail,
[key]: e.detail,
...$automationStore.selectedAutomation.automation.testData.body,
},
}) })
testData[key] = e.detail
await automationStore.actions.save(
$automationStore.selectedAutomation?.automation
)
} else {
block.inputs[key] = e.detail
await automationStore.actions.save(
$automationStore.selectedAutomation?.automation
)
} }
automationStore.actions.addTestDataToAutomation({ } catch (error) {
[key]: e.detail, notifications.error("Error saving automation")
})
testData[key] = e.detail
await automationStore.actions.save(
$automationStore.selectedAutomation?.automation
)
} else {
block.inputs[key] = e.detail
await automationStore.actions.save(
$automationStore.selectedAutomation?.automation
)
} }
}, 800) }, 800)

View File

@ -1,5 +1,5 @@
<script> <script>
import { Icon } from "@budibase/bbui" import { Icon, notifications } from "@budibase/bbui"
import { automationStore } from "builderStore" import { automationStore } from "builderStore"
import WebhookDisplay from "./WebhookDisplay.svelte" import WebhookDisplay from "./WebhookDisplay.svelte"
import { ModalContent } from "@budibase/bbui" import { ModalContent } from "@budibase/bbui"
@ -16,15 +16,24 @@
onMount(async () => { onMount(async () => {
if (!automation?.definition?.trigger?.inputs.schemaUrl) { if (!automation?.definition?.trigger?.inputs.schemaUrl) {
// save the automation initially // save the automation initially
await automationStore.actions.save(automation) try {
await automationStore.actions.save(automation)
} catch (error) {
notifications.error("Error saving automation")
}
} }
interval = setInterval(async () => { interval = setInterval(async () => {
await automationStore.actions.fetch() try {
const outputs = automation?.definition?.trigger.schema.outputs?.properties await automationStore.actions.fetch()
// always one prop for the "body" const outputs =
if (Object.keys(outputs).length > 1) { automation?.definition?.trigger.schema.outputs?.properties
propCount = Object.keys(outputs).length - 1 // always one prop for the "body"
finished = true if (Object.keys(outputs).length > 1) {
propCount = Object.keys(outputs).length - 1
finished = true
}
} catch (error) {
notifications.error("Error getting automations list")
} }
}, POLL_RATE_MS) }, POLL_RATE_MS)
schemaURL = automation?.definition?.trigger?.inputs.schemaUrl schemaURL = automation?.definition?.trigger?.inputs.schemaUrl

View File

@ -36,28 +36,37 @@
// called by the parent modal when actions are saved // called by the parent modal when actions are saved
const createAutomation = async parameters => { const createAutomation = async parameters => {
if (parameters.automationId || !parameters.newAutomationName) return if (parameters.automationId || !parameters.newAutomationName) {
await automationStore.actions.create({ name: parameters.newAutomationName }) return
const appActionDefinition = $automationStore.blockDefinitions.TRIGGER.APP
const newBlock = $automationStore.selectedAutomation.constructBlock(
"TRIGGER",
"APP",
appActionDefinition
)
newBlock.inputs = {
fields: Object.keys(parameters.fields).reduce((fields, key) => {
fields[key] = "string"
return fields
}, {}),
} }
try {
await automationStore.actions.create({
name: parameters.newAutomationName,
})
const appActionDefinition = $automationStore.blockDefinitions.TRIGGER.APP
const newBlock = $automationStore.selectedAutomation.constructBlock(
"TRIGGER",
"APP",
appActionDefinition
)
automationStore.actions.addBlockToAutomation(newBlock) newBlock.inputs = {
await automationStore.actions.save( fields: Object.keys(parameters.fields).reduce((fields, key) => {
$automationStore.selectedAutomation?.automation fields[key] = "string"
) return fields
parameters.automationId = $automationStore.selectedAutomation.automation._id }, {}),
delete parameters.newAutomationName }
automationStore.actions.addBlockToAutomation(newBlock)
await automationStore.actions.save(
$automationStore.selectedAutomation?.automation
)
parameters.automationId =
$automationStore.selectedAutomation.automation._id
delete parameters.newAutomationName
} catch (error) {
notifications.error("Error creating automation")
}
} }
</script> </script>