Refactor the binding generation to ensure types are processed correctly. Rows were being misrepresented as 'Multi Select'

This commit is contained in:
Dean 2025-02-03 09:06:51 +00:00
parent ef2a2b8bd0
commit a280039dcb
1 changed files with 63 additions and 63 deletions

View File

@ -1,4 +1,4 @@
import { derived, get, Readable } from "svelte/store" import { derived, get, readable, Readable } from "svelte/store"
import { API } from "@/api" import { API } from "@/api"
import { cloneDeep } from "lodash/fp" import { cloneDeep } from "lodash/fp"
import { generate } from "shortid" import { generate } from "shortid"
@ -39,9 +39,10 @@ import {
TriggerTestOutputs, TriggerTestOutputs,
RowActionTriggerOutputs, RowActionTriggerOutputs,
WebhookTriggerOutputs, WebhookTriggerOutputs,
AutomationCustomIOType,
} from "@budibase/types" } from "@budibase/types"
import { ActionStepID, TriggerStepID } from "@/constants/backend/automations" import { ActionStepID, TriggerStepID } from "@/constants/backend/automations"
import { FIELDS } from "@/constants/backend" import { FIELDS as COLUMNS } from "@/constants/backend"
import { sdk } from "@budibase/shared-core" import { sdk } from "@budibase/shared-core"
import { rowActions } from "./rowActions" import { rowActions } from "./rowActions"
import { getNewStepName } from "@/helpers/automations/nameHelpers" import { getNewStepName } from "@/helpers/automations/nameHelpers"
@ -101,11 +102,7 @@ const automationActions = (store: AutomationStore) => ({
* *
* @returns {Readable<AutomationContext>} * @returns {Readable<AutomationContext>}
*/ */
generateContext: (): Readable<AutomationContext> | undefined => { generateContext: (): Readable<AutomationContext> => {
if (!organisation || !store.selected || !environment || !tables) {
console.error("Automations: Required context stores are uninitialised")
return
}
return derived( return derived(
[organisation, store.selected, environment, tables], [organisation, store.selected, environment, tables],
([$organisation, $selectedAutomation, $env, $tables]) => { ([$organisation, $selectedAutomation, $env, $tables]) => {
@ -653,7 +650,7 @@ const automationActions = (store: AutomationStore) => ({
let bindings: any[] = [] let bindings: any[] = []
const addBinding = ( const addBinding = (
name: string, name: string,
value: any, schema: any,
icon: string, icon: string,
idx: number, idx: number,
isLoopBlock: boolean, isLoopBlock: boolean,
@ -661,6 +658,7 @@ const automationActions = (store: AutomationStore) => ({
bindingName: string bindingName: string
) => { ) => {
if (!name) return if (!name) return
const runtimeBinding = store.actions.determineRuntimeBinding( const runtimeBinding = store.actions.determineRuntimeBinding(
name, name,
idx, idx,
@ -670,6 +668,11 @@ const automationActions = (store: AutomationStore) => ({
pathSteps pathSteps
) )
// Skip binding if its invalid
if (!runtimeBinding) {
return
}
const readableBinding = store.actions.determineReadableBinding( const readableBinding = store.actions.determineReadableBinding(
name, name,
pathBlock pathBlock
@ -681,20 +684,38 @@ const automationActions = (store: AutomationStore) => ({
bindingName, bindingName,
loopBlockCount loopBlockCount
) )
bindings.push(
store.actions.createBindingObject( const isStep = !isLoopBlock && idx !== 0
name, const defaultReadable =
value, bindingName && isStep ? `steps.${bindingName}.${name}` : runtimeBinding
icon,
idx, // Check if the schema matches any column types.
loopBlockCount, const column = Object.values(COLUMNS).find(
isLoopBlock, col =>
runtimeBinding, col.type === schema.type &&
categoryName, ("subtype" in col ? col.subtype === schema.subtype : true)
bindingName,
readableBinding
)
) )
// Automation types and column types can collide e.g. "array"
// Exclude where necessary
const ignoreColumnType = schema.customType === AutomationCustomIOType.ROWS
// Shown in the bindable menus
const displayType = ignoreColumnType ? schema.type : column?.name
bindings.push({
readableBinding: readableBinding || defaultReadable,
runtimeBinding,
type: schema.type,
description: schema.description,
icon,
category: categoryName,
display: {
type: displayType,
name,
rank: isLoopBlock ? idx + 1 : idx - loopBlockCount,
},
})
} }
let loopBlockCount = 0 let loopBlockCount = 0
@ -766,6 +787,7 @@ const automationActions = (store: AutomationStore) => ({
console.error("Loop block missing.") console.error("Loop block missing.")
} }
} }
Object.entries(schema).forEach(([name, value]) => { Object.entries(schema).forEach(([name, value]) => {
addBinding( addBinding(
name, name,
@ -826,7 +848,7 @@ const automationActions = (store: AutomationStore) => ({
currentBlock: AutomationStep | AutomationTrigger | undefined, currentBlock: AutomationStep | AutomationTrigger | undefined,
pathSteps: (AutomationStep | AutomationTrigger)[] pathSteps: (AutomationStep | AutomationTrigger)[]
) => { ) => {
let runtimeName: string | null let runtimeName: string
// Legacy support for EXECUTE_SCRIPT steps // Legacy support for EXECUTE_SCRIPT steps
const isJSScript = const isJSScript =
@ -863,14 +885,14 @@ const automationActions = (store: AutomationStore) => ({
const stepId = pathSteps[idx].id const stepId = pathSteps[idx].id
if (!stepId) { if (!stepId) {
notifications.error("Error generating binding: Step ID not found.") notifications.error("Error generating binding: Step ID not found.")
return null return
} }
runtimeName = `steps["${stepId}"].${name}` runtimeName = `steps["${stepId}"].${name}`
} else { } else {
const stepId = pathSteps[idx].id const stepId = pathSteps[idx].id
if (!stepId) { if (!stepId) {
notifications.error("Error generating binding: Step ID not found.") notifications.error("Error generating binding: Step ID not found.")
return null return
} }
runtimeName = `steps.${stepId}.${name}` runtimeName = `steps.${stepId}.${name}`
} }
@ -891,44 +913,6 @@ const automationActions = (store: AutomationStore) => ({
: `Step ${idx - loopBlockCount} outputs` : `Step ${idx - loopBlockCount} outputs`
}, },
createBindingObject: (
name: string,
value: any,
icon: string,
idx: number,
loopBlockCount: number,
isLoopBlock: boolean,
runtimeBinding: string | null,
categoryName: string,
bindingName?: string,
readableBinding?: string
) => {
const field = Object.values(FIELDS).find(
field =>
field.type === value.type &&
("subtype" in field ? field.subtype === value.subtype : true)
)
const readableBindingDefault =
bindingName && !isLoopBlock && idx !== 0
? `steps.${bindingName}.${name}`
: runtimeBinding
return {
readableBinding: readableBinding || readableBindingDefault,
runtimeBinding,
type: value.type,
description: value.description,
icon,
category: categoryName,
display: {
type: field?.name || value.type,
name,
rank: isLoopBlock ? idx + 1 : idx - loopBlockCount,
},
}
},
processBlockInputs: async ( processBlockInputs: async (
block: AutomationStep, block: AutomationStep,
data: Record<string, any> data: Record<string, any>
@ -1706,4 +1690,20 @@ export const automationStore = new AutomationStore()
export const automationHistoryStore = automationStore.history export const automationHistoryStore = automationStore.history
export const selectedAutomation = automationStore.selected export const selectedAutomation = automationStore.selected
export const evaluationContext = automationStore.context
// Define an empty evaluate context at the start
const emptyContext: AutomationContext = {
user: {},
steps: {},
env: {},
settings: {},
}
// Page layout kicks off initialisation, subscription happens within the page
export const evaluationContext: Readable<AutomationContext> = readable(
emptyContext,
set => {
const unsubscribe = automationStore.context?.subscribe(set) ?? (() => {})
return () => unsubscribe()
}
)