Refactored the selectedAutomation derived store to include tree metadata and moved the core doc to a new data attribute. All refs to selectedAutomation updated. Moved the id migration into the selectedAutomation store. Remove bindings from AutomationSetupBlock, moved to StepNode

This commit is contained in:
Dean 2024-10-13 22:34:17 +01:00
parent 61188fcc70
commit 9cfc3d499d
17 changed files with 145 additions and 133 deletions

View File

@ -3,8 +3,8 @@
import Flowchart from "./FlowChart/FlowChart.svelte"
</script>
{#if $selectedAutomation}
{#key $selectedAutomation._id}
<Flowchart automation={$selectedAutomation} />
{#if $selectedAutomation?.data}
{#key $selectedAutomation.data._id}
<Flowchart automation={$selectedAutomation.data} />
{/key}
{/if}

View File

@ -27,10 +27,13 @@
ActionStepID.TRIGGER_AUTOMATION_RUN,
]
$: blockRef = $automationStore.blocks?.[block.id]
$: blockRef = $selectedAutomation.blockRefs?.[block.id]
$: lastStep = blockRef?.terminating
$: pathSteps = block.id
? automationStore.actions.getPathSteps(blockRef.pathTo, $selectedAutomation)
? automationStore.actions.getPathSteps(
blockRef.pathTo,
$selectedAutomation?.data
)
: []
$: collectBlockExists = pathSteps?.some(
@ -81,7 +84,7 @@
// Filter out Collect block if not App Action or Webhook
if (
!collectBlockAllowedSteps.includes(
$selectedAutomation.definition.trigger.stepId
$selectedAutomation.data.definition.trigger.stepId
)
) {
delete acc.COLLECT

View File

@ -28,6 +28,7 @@
export let step
export let isLast
export let bindings
export let automation
let drawer
let condition
@ -85,13 +86,14 @@
<div class="flow-item">
<div class={`block branch-node hoverable`} class:selected={false}>
<FlowItemHeader
{automation}
{open}
itemName={branch.name}
block={step}
deleteStep={async () => {
await automationStore.actions.deleteBranch(
branchBlockRef.pathTo,
$selectedAutomation
$selectedAutomation.data
)
}}
on:update={async e => {
@ -103,7 +105,7 @@
const updatedAuto = automationStore.actions.updateStep(
pathTo,
$selectedAutomation,
$selectedAutomation.data,
stepUpdate
)
await automationStore.actions.save(updatedAuto)
@ -115,7 +117,7 @@
on:click={() => {
automationStore.actions.branchLeft(
branchBlockRef.pathTo,
$selectedAutomation,
$selectedAutomation.data,
step
)
}}
@ -130,7 +132,7 @@
on:click={() => {
automationStore.actions.branchRight(
branchBlockRef.pathTo,
$selectedAutomation,
$selectedAutomation.data,
step
)
}}

View File

@ -1,8 +1,8 @@
<script>
import {
automationStore,
selectedAutomation,
automationHistoryStore,
selectedAutomation,
} from "stores/builder"
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
import TestDataModal from "./TestDataModal.svelte"
@ -12,8 +12,6 @@
import StepNode from "./StepNode.svelte"
import { memo } from "@budibase/frontend-core"
import { sdk } from "@budibase/shared-core"
import { migrateReferencesInObject } from "dataBinding"
import { cloneDeep } from "lodash/fp"
import { onMount } from "svelte"
export let automation
@ -26,7 +24,7 @@
let blockRefs = {}
let treeEle
// Memo auto
// Memo auto - selectedAutomation
$: memoAutomation.set(automation)
// Parse the automation tree state
@ -37,49 +35,20 @@
)
$: isRowAction = sdk.automations.isRowAction($memoAutomation)
const refresh = auto => {
automationStore.update(state => {
return {
...state,
blocks: {},
}
})
// Traverse the automation and build metadata
automationStore.actions.traverse(auto)
blockRefs = $automationStore.blocks
const refresh = () => {
// Build global automation bindings.
const environmentBindings =
automationStore.actions.buildEnvironmentBindings()
// Push common bindings globally
automationStore.update(state => ({
...state,
bindings: [...environmentBindings],
}))
// Get all processed block references
blockRefs = $selectedAutomation.blockRefs
// Parse the steps for references to sequential binding
const updatedAuto = cloneDeep(auto)
// Parse and migrate all bindings
Object.values(blockRefs)
.filter(blockRef => {
// Pulls out all distinct terminating nodes
return blockRef.terminating
})
.forEach(blockRef => {
automationStore.actions
.getPathSteps(blockRef.pathTo, updatedAuto)
.forEach((step, idx, steps) => {
migrateReferencesInObject({
obj: step,
originalIndex: idx,
steps,
})
})
})
automationStore.update(state => {
return {
...state,
bindings: [...environmentBindings],
}
})
}
const getBlocks = automation => {
@ -93,7 +62,7 @@
const deleteAutomation = async () => {
try {
await automationStore.actions.delete($selectedAutomation)
await automationStore.actions.delete(automation)
} catch (error) {
notifications.error("Error deleting automation")
}
@ -127,7 +96,7 @@
</div>
<div class="controls">
<div
class:disabled={!$selectedAutomation?.definition?.trigger}
class:disabled={!automation?.definition?.trigger}
on:click={() => {
testDataModal.show()
}}
@ -155,7 +124,7 @@
automation._id,
automation.disabled
)}
disabled={!$selectedAutomation?.definition?.trigger}
disabled={!automation?.definition?.trigger}
value={!automation.disabled}
/>
</div>

View File

@ -1,9 +1,9 @@
<script>
import {
automationStore,
selectedAutomation,
permissions,
selectedAutomationDisplayData,
selectedAutomation,
} from "stores/builder"
import {
Icon,
@ -86,7 +86,7 @@
}
async function removeLooping() {
let loopBlockRef = $automationStore.blocks[blockRef.looped]
let loopBlockRef = $selectedAutomation.blockRefs[blockRef.looped]
await automationStore.actions.deleteAutomationBlock(loopBlockRef.pathTo)
}
@ -173,6 +173,7 @@
{/if}
<FlowItemHeader
{automation}
{open}
{block}
{testDataModal}
@ -224,8 +225,8 @@
{block}
on:branch={() => {
automationStore.actions.branchAutomation(
$automationStore.blocks[block.id].pathTo,
$selectedAutomation
$selectedAutomation.blockRefs[block.id].pathTo,
automation
)
}}
/>

View File

@ -14,14 +14,15 @@
export let deleteStep
export let enableNaming = true
export let itemName
export let automation
let validRegex = /^[A-Za-z0-9_\s]+$/
let typing = false
let editing = false
const dispatch = createEventDispatcher()
$: stepNames = $selectedAutomation?.definition.stepNames
$: allSteps = $selectedAutomation?.definition.steps || []
$: stepNames = automation?.definition.stepNames
$: allSteps = automation?.definition.steps || []
$: automationName = itemName || stepNames?.[block.id] || block?.name || ""
$: automationNameError = getAutomationNameError(automationName)
$: status = updateStatus(testResult)
@ -36,7 +37,7 @@
}
}
$: blockRef = $automationStore.blocks[block.id]
$: blockRef = $selectedAutomation.blockRefs[block.id]
$: isLooped = blockRef?.looped
async function onSelect(block) {

View File

@ -4,7 +4,9 @@
import { AutomationActionStepId } from "@budibase/types"
import { ActionButton } from "@budibase/bbui"
import { automationStore } from "stores/builder"
import { environment } from "stores/portal"
import { cloneDeep } from "lodash"
import { memo } from "@budibase/frontend-core"
export let step = {}
export let stepIdx
@ -12,6 +14,9 @@
export let blocks
export let isLast = false
const memoEnvVariables = memo($environment.variables)
$: memoEnvVariables.set($environment.variables)
$: blockRef = blocks?.[step.id]
$: pathToCurrentNode = blockRef?.pathTo
$: isBranch = step.stepId === AutomationActionStepId.BRANCH
@ -23,8 +28,12 @@
automation
)
// Fetch the env bindings
$: environmentBindings =
automationStore.actions.buildEnvironmentBindings($memoEnvVariables)
// Combine all bindings for the step
$: bindings = [...availableBindings, ...($automationStore.bindings || [])]
$: bindings = [...availableBindings, ...environmentBindings]
</script>
{#if isBranch}
@ -51,6 +60,7 @@
>
<div class="branch-node">
<BranchNode
{automation}
{step}
{bindings}
pathTo={pathToCurrentNode}

View File

@ -28,7 +28,7 @@
* @todo Parse *all* data for each trigger type and relay adequate feedback
*/
const parseTestData = testData => {
const autoTrigger = $selectedAutomation?.definition?.trigger
const autoTrigger = $selectedAutomation.data?.definition?.trigger
const { tableId } = autoTrigger?.inputs || {}
// Ensure the tableId matches the trigger table for row trigger automations
@ -62,12 +62,12 @@
return true
}
const memoTestData = memo(parseTestData($selectedAutomation.testData))
$: memoTestData.set(parseTestData($selectedAutomation.testData))
const memoTestData = memo(parseTestData($selectedAutomation.data.testData))
$: memoTestData.set(parseTestData($selectedAutomation.data.testData))
$: {
// clone the trigger so we're not mutating the reference
trigger = cloneDeep($selectedAutomation.definition.trigger)
trigger = cloneDeep($selectedAutomation.data.definition.trigger)
// get the outputs so we can define the fields
let schema = Object.entries(trigger.schema?.outputs?.properties || {})
@ -110,7 +110,10 @@
const testAutomation = async () => {
try {
await automationStore.actions.test($selectedAutomation, $memoTestData)
await automationStore.actions.test(
$selectedAutomation.data,
$memoTestData
)
$automationStore.showTestPanel = true
} catch (error) {
notifications.error(error)
@ -158,7 +161,7 @@
{#if selectedJSON}
<div class="text-area-container">
<TextArea
value={JSON.stringify($selectedAutomation.testData, null, 2)}
value={JSON.stringify($selectedAutomation.data.testData, null, 2)}
error={failedParse}
on:change={e => parseTestJSON(e)}
/>

View File

@ -3,7 +3,7 @@
import FlowItemHeader from "./FlowChart/FlowItemHeader.svelte"
import { ActionStepID } from "constants/backend/automations"
import { JsonView } from "@zerodevx/svelte-json-view"
import { automationStore } from "stores/builder"
import { automationStore, selectedAutomation } from "stores/builder"
import { AutomationActionStepId } from "@budibase/types"
export let automation
@ -45,7 +45,8 @@
: []
} else if (automation) {
const terminatingStep = filteredResults.at(-1)
const terminatingBlockRef = $automationStore.blocks[terminatingStep.id]
const terminatingBlockRef =
$selectedAutomation.blockRefs[terminatingStep.id]
const pathSteps = automationStore.actions.getPathSteps(
terminatingBlockRef.pathTo,
automation
@ -67,6 +68,7 @@
<div class="block" style={width ? `width: ${width}` : ""}>
{#if block.stepId !== ActionStepID.LOOP}
<FlowItemHeader
{automation}
enableNaming={false}
itemName={block.stepId === AutomationActionStepId.BRANCH
? getBranchName(block, filteredResults?.[idx].outputs?.branchId)

View File

@ -115,7 +115,7 @@
? "var(--spectrum-global-color-gray-600)"
: "var(--spectrum-global-color-gray-900)"}
text={automation.displayName}
selected={automation._id === $selectedAutomation?._id}
selected={automation._id === $selectedAutomation?.data?._id}
hovering={automation._id === $contextMenuStore.id}
on:click={() => automationStore.actions.select(automation._id)}
selectedBy={$userSelectedResourceMap[automation._id]}

View File

@ -65,6 +65,7 @@
export let testData
export let schemaProperties
export let isTestModal = false
export let bindings = []
// Stop unnecessary rendering
const memoBlock = memo(block)
@ -103,14 +104,6 @@
$: tempFilters = cloneDeep(filters)
$: stepId = $memoBlock.stepId
$: automationBindings = automationStore.actions.getPathBindings(
$memoBlock.id,
automation
)
$: environmentBindings =
automationStore.actions.buildEnvironmentBindings($memoEnvVariables)
$: bindings = [...automationBindings, ...environmentBindings]
$: getInputData(testData, $memoBlock.inputs)
$: tableId = inputData ? inputData.tableId : null
$: table = tableId

View File

@ -29,7 +29,7 @@
$: filteredAutomations = $automationStore.automations.filter(
automation =>
automation.definition.trigger.stepId === TriggerStepID.APP &&
automation._id !== $selectedAutomation._id
automation._id !== $selectedAutomation.data._id
)
</script>

View File

@ -11,7 +11,7 @@
let schemaURL
let propCount = 0
$: automation = $selectedAutomation
$: automation = $selectedAutomation.data
onMount(async () => {
if (!automation?.definition?.trigger?.inputs.schemaUrl) {

View File

@ -13,7 +13,7 @@
selectedAutomation,
} from "stores/builder"
$: automationId = $selectedAutomation?._id
$: automationId = $selectedAutomation?.data?._id
$: builderStore.selectResource(automationId)
// Keep URL and state in sync for selected screen ID
@ -68,7 +68,7 @@
{#if $automationStore.showTestPanel}
<div class="setup">
<TestPanel automation={$selectedAutomation} />
<TestPanel automation={$selectedAutomation.data} />
</div>
{/if}
<Modal bind:this={modal}>

View File

@ -6,7 +6,7 @@ import { createHistoryStore } from "stores/builder/history"
import { licensing } from "stores/portal"
import { tables } from "stores/builder"
import { notifications } from "@budibase/bbui"
import { getEnvironmentBindings } from "dataBinding"
import { getEnvironmentBindings, migrateReferencesInObject } from "dataBinding"
import {
AutomationTriggerStepId,
AutomationEventType,
@ -16,10 +16,7 @@ import { ActionStepID } from "constants/backend/automations"
import { FIELDS } from "constants/backend"
import { sdk } from "@budibase/shared-core"
import { rowActions } from "./rowActions"
import {
updateBindingsInSteps,
getNewStepName,
} from "helpers/automations/nameHelpers"
import { getNewStepName } from "helpers/automations/nameHelpers"
import { QueryUtils } from "@budibase/frontend-core"
const initialAutomationState = {
@ -76,29 +73,23 @@ const automationActions = store => ({
* @param {Object} block
* @param {Array<Object>} pathTo
*/
registerBlock: (block, pathTo, terminating) => {
store.update(state => {
state.blocks = {
...state.blocks,
[block.id]: {
...state.blocks[block.id],
pathTo,
bindings: [],
terminating: terminating || false,
// Register the looped block
...(block.blockToLoop ? { blockToLoop: block.blockToLoop } : {}),
},
}
registerBlock: (blocks, block, pathTo, terminating) => {
// Directly mutate the `blocks` object without reassigning
blocks[block.id] = {
...(blocks[block.id] || {}),
pathTo,
bindings: [],
terminating: terminating || false,
...(block.blockToLoop ? { blockToLoop: block.blockToLoop } : {}),
}
// If this is a loop block, add a reference to the block being looped
if (block.blockToLoop) {
state.blocks[block.blockToLoop] = {
...(state.blocks[block.blockToLoop] || {}),
looped: block.id,
}
// If this is a loop block, add a reference to the block being looped
if (block.blockToLoop) {
blocks[block.blockToLoop] = {
...(blocks[block.blockToLoop] || {}),
looped: block.id,
}
return state
})
}
},
/**
* Build a sequential list of all steps on the step path provided
@ -204,7 +195,7 @@ const automationActions = store => ({
const block = get(store).blocks[id]
const bindings = store.actions.getAvailableBindings(
block,
get(selectedAutomation)
get(selectedAutomation).data
)
return bindings
@ -217,7 +208,7 @@ const automationActions = store => ({
*
* @param {Object} automation
*/
traverse: automation => {
traverse: (blockRefs, automation) => {
let blocks = []
if (automation.definition.trigger) {
blocks.push(automation.definition.trigger)
@ -245,22 +236,19 @@ const automationActions = store => ({
})
store.actions.registerBlock(
blockRefs,
block,
pathToCurrentNode,
terminating && !branches.length
)
}
// Purge refs
store.update(state => {
state.blocks = {}
return state
})
// Traverse the entire tree.
blocks.forEach((block, idx, array) => {
treeTraverse(block, null, idx, null, array.length - 1 === idx)
})
return blockRefs
},
/**
@ -599,7 +587,7 @@ const automationActions = store => ({
})
// Create new modified automation
const automation = get(selectedAutomation)
const automation = get(selectedAutomation).data
const newAutomation = store.actions.getUpdatedDefinition(
automation,
newBlock
@ -685,7 +673,7 @@ const automationActions = store => ({
})
},
addTestDataToAutomation: async data => {
let newAutomation = cloneDeep(get(selectedAutomation))
let newAutomation = cloneDeep(get(selectedAutomation).data)
newAutomation.testData = {
...newAutomation.testData,
...data,
@ -701,7 +689,7 @@ const automationActions = store => ({
type,
id: generate(),
}
newName = getNewStepName(get(selectedAutomation), newStep)
newName = getNewStepName(get(selectedAutomation).data, newStep)
newStep.name = newName
return newStep
},
@ -729,7 +717,7 @@ const automationActions = store => ({
* @param {Array<Object>} pathWay location of insert point
*/
addBlockToAutomation: async (block, pathWay) => {
const automation = get(selectedAutomation)
const automation = get(selectedAutomation).data
let newAutomation = cloneDeep(automation)
const steps = [
@ -754,7 +742,7 @@ const automationActions = store => ({
if (!cache) {
if (final) {
// Offset path to accommodate the
// Offset path to accommodate the trigger
insertBlock(newAutomation.definition.steps, stepIdx - 1)
cache = block
} else {
@ -869,7 +857,7 @@ const automationActions = store => ({
return createBranch(`Branch ${idx + 1}`)
})
// Init the branch children. Shift all steps following the new branch
// Init the branch children. Shift all steps following the new branch step
// into the 0th branch.
newBranch.inputs.children = newBranch.inputs.branches.reduce(
(acc, branch, idx) => {
@ -1030,7 +1018,7 @@ const automationActions = store => ({
},
saveAutomationName: async (blockId, name) => {
const automation = get(selectedAutomation)
const automation = get(selectedAutomation).data
let newAutomation = cloneDeep(automation)
if (!newAutomation) {
return
@ -1046,7 +1034,7 @@ const automationActions = store => ({
await store.actions.save(newAutomation)
},
deleteAutomationName: async blockId => {
const automation = get(selectedAutomation)
const automation = get(selectedAutomation).data
let newAutomation = cloneDeep(automation)
if (!automation) {
return
@ -1065,7 +1053,7 @@ const automationActions = store => ({
* @param {Array<Object>} pathTo
*/
deleteAutomationBlock: async pathTo => {
const automation = get(selectedAutomation)
const automation = get(selectedAutomation).data
let newAutomation = cloneDeep(automation)
const steps = [
@ -1196,15 +1184,45 @@ export const selectedAutomation = derived(automationStore, $automationStore => {
x => x._id === $automationStore.selectedAutomationId
)
return selected
// Traverse the entire tree and record all nodes found
// Also store any info relevant to the UX
const blockRefs = {}
automationStore.actions.traverse(blockRefs, selected)
// Parse the steps for references to sequential binding
// Replace all bindings with id based alternatives
const updatedAuto = cloneDeep(selected)
Object.values(blockRefs)
.filter(blockRef => {
// Pulls out all distinct terminating nodes
return blockRef.terminating
})
.forEach(blockRef => {
automationStore.actions
.getPathSteps(blockRef.pathTo, updatedAuto)
.forEach((step, idx, steps) => {
migrateReferencesInObject({
obj: step,
originalIndex: idx,
steps,
})
})
})
return {
data: updatedAuto,
blockRefs,
}
})
export const selectedAutomationDisplayData = derived(
[automationStore, selectedAutomation],
([$automationStore, $selectedAutomation]) => {
if (!$selectedAutomation?._id) {
if (!$selectedAutomation?.data?._id) {
return null
}
return $automationStore.automationDisplayData[$selectedAutomation._id]
return $automationStore.automationDisplayData[
$selectedAutomation?.data?._id
]
}
)

View File

@ -218,7 +218,7 @@
targetGroup.filters.push({
valueType: FilterValueType.VALUE,
...(builderType === "condition"
? { operator: OperatorOptions.Equals.value }
? { operator: OperatorOptions.Equals.value, type: FieldType.STRING }
: {}),
})
} else if (group) {
@ -240,6 +240,11 @@
filters: [
{
valueType: FilterValueType.VALUE,
...(builderType === "condition"
? {
operator: OperatorOptions.Equals.value,
}
: {}),
},
],
})
@ -391,7 +396,12 @@
? "Edit binding"
: null}
{allowBindings}
{filter}
filter={{
...filter,
...(builderType === "condition"
? { type: FieldType.STRING }
: {}),
}}
{schemaFields}
{bindings}
{panel}

View File

@ -232,7 +232,7 @@
<div class="binding-control">
<!-- needs field, operator -->
{#if !disabled && allowBindings && filter.field && !filter.noValue}
{#if !disabled && allowBindings && !filter.noValue}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div