Integrate actions into new global context and greatly improve performance by only enriching when required

This commit is contained in:
Andrew Kingston 2023-09-15 11:52:35 +01:00
parent 3839e26758
commit f78cc26a68
2 changed files with 89 additions and 86 deletions

View File

@ -35,6 +35,10 @@
findHBSBlocks, findHBSBlocks,
isJSBinding, isJSBinding,
} from "@budibase/string-templates" } from "@budibase/string-templates"
import {
getActionContextKey,
getActionDependentContextKeys,
} from "../utils/buttonActions.js"
export let instance = {} export let instance = {}
export let isLayout = false export let isLayout = false
@ -165,9 +169,6 @@
hasMissingRequiredSettings) hasMissingRequiredSettings)
$: emptyState = empty && showEmptyState $: emptyState = empty && showEmptyState
// Enrich component settings
// $: enrichComponentSettings($context, settingsDefinitionMap)
// Evaluate conditional UI settings and store any component setting changes // Evaluate conditional UI settings and store any component setting changes
// which need to be made // which need to be made
$: evaluateConditions(conditions) $: evaluateConditions(conditions)
@ -296,18 +297,42 @@
} }
}) })
// The known context key map is built up at runtime, as changes to keys are
// encountered. We manually seed this to the required action keys as these
// are not encountered at runtime and so need computed in advance.
knownContextKeyMap = generateActionKeyMap(instance, settingsDefinition)
bindingString = bindings.join(" ")
// Run any migrations // Run any migrations
runMigrations(instance, settingsDefinition) runMigrations(instance, settingsDefinition)
// Force an initial enrichment of the new settings // Force an initial enrichment of the new settings
enrichComponentSettings(get(context), settingsDefinitionMap, { enrichComponentSettings(get(context), settingsDefinitionMap)
force: true, }
})
bindingString = bindings.join(" ")
knownContextKeyMap = {}
// Force an initial enrichment of the new settings // Extracts a map of all context keys which are required by action settings
enrichComponentSettings($context, settingsDefinitionMap) // to provide the functions to evaluate at runtime. This needs done manually
// as the action definitions themselves do not specify bindings for action
// keys, meaning we cannot do this while doing the other normal bindings.
const generateActionKeyMap = (instance, settingsDefinition) => {
let map = {}
settingsDefinition.forEach(setting => {
if (setting.type === "event") {
instance[setting.key]?.forEach(action => {
// We depend on the actual action key
const actionKey = getActionContextKey(action)
if (actionKey) {
map[actionKey] = true
}
// We also depend on any manually declared context keys
getActionDependentContextKeys(action)?.forEach(key => {
map[key] = true
})
})
}
})
return map
} }
const runMigrations = (instance, settingsDefinition) => { const runMigrations = (instance, settingsDefinition) => {

View File

@ -17,6 +17,54 @@ import { ActionTypes } from "constants"
import { enrichDataBindings } from "./enrichDataBinding" import { enrichDataBindings } from "./enrichDataBinding"
import { Helpers } from "@budibase/bbui" import { Helpers } from "@budibase/bbui"
// Default action handler, which extracts an action from context that was
// provided by another component and executes it with all action parameters
const contextActionHandler = async (action, context) => {
const key = getActionContextKey(action)
const fn = context[key]
if (fn) {
return await fn(action.parameters)
}
}
// Generates the context key, which is the key that this action depends on in
// context to provide the function it will run. This is broken out as a util
// because we reuse this inside the core Component.svelte file to determine
// what the required action context keys are for all action settings.
export const getActionContextKey = action => {
const type = action?.["##eventHandlerType"]
const key = (componentId, type) => `${componentId}_${type}`
switch (type) {
case "Scroll To Field":
return key(action.parameters.componentId, ActionTypes.ScrollTo)
case "Update Field Value":
return key(action.parameters.componentId, ActionTypes.UpdateFieldValue)
case "Validate Form":
return key(action.parameters.componentId, ActionTypes.ValidateForm)
case "Refresh Data Provider":
return key(action.parameters.componentId, ActionTypes.RefreshDatasource)
case "Clear Form":
return key(action.parameters.componentId, ActionTypes.ClearForm)
case "Change Form Step":
return key(action.parameters.componentId, ActionTypes.ChangeFormStep)
default:
return null
}
}
// If button actions depend on context, they must declare which keys they need
export const getActionDependentContextKeys = action => {
const type = action?.["##eventHandlerType"]
switch (type) {
case "Save Row":
case "Duplicate Row":
if (action.parameters?.providerId) {
return [action.parameters.providerId]
}
}
return []
}
const saveRowHandler = async (action, context) => { const saveRowHandler = async (action, context) => {
const { fields, providerId, tableId, notificationOverride } = const { fields, providerId, tableId, notificationOverride } =
action.parameters action.parameters
@ -189,17 +237,6 @@ const navigationHandler = action => {
routeStore.actions.navigate(url, peek, externalNewTab) routeStore.actions.navigate(url, peek, externalNewTab)
} }
const scrollHandler = async (action, context) => {
return await executeActionHandler(
context,
action.parameters.componentId,
ActionTypes.ScrollTo,
{
field: action.parameters.field,
}
)
}
const queryExecutionHandler = async action => { const queryExecutionHandler = async action => {
const { datasourceId, queryId, queryParams, notificationOverride } = const { datasourceId, queryId, queryParams, notificationOverride } =
action.parameters action.parameters
@ -235,47 +272,6 @@ const queryExecutionHandler = async action => {
} }
} }
const executeActionHandler = async (
context,
componentId,
actionType,
params
) => {
const fn = context[`${componentId}_${actionType}`]
if (fn) {
return await fn(params)
}
}
const updateFieldValueHandler = async (action, context) => {
return await executeActionHandler(
context,
action.parameters.componentId,
ActionTypes.UpdateFieldValue,
{
type: action.parameters.type,
field: action.parameters.field,
value: action.parameters.value,
}
)
}
const validateFormHandler = async (action, context) => {
return await executeActionHandler(
context,
action.parameters.componentId,
ActionTypes.ValidateForm
)
}
const refreshDataProviderHandler = async (action, context) => {
return await executeActionHandler(
context,
action.parameters.componentId,
ActionTypes.RefreshDatasource
)
}
const logoutHandler = async action => { const logoutHandler = async action => {
await authStore.actions.logOut() await authStore.actions.logOut()
let redirectUrl = "/builder/auth/login" let redirectUrl = "/builder/auth/login"
@ -292,23 +288,6 @@ const logoutHandler = async action => {
} }
} }
const clearFormHandler = async (action, context) => {
return await executeActionHandler(
context,
action.parameters.componentId,
ActionTypes.ClearForm
)
}
const changeFormStepHandler = async (action, context) => {
return await executeActionHandler(
context,
action.parameters.componentId,
ActionTypes.ChangeFormStep,
action.parameters
)
}
const closeScreenModalHandler = action => { const closeScreenModalHandler = action => {
let url let url
if (action?.parameters) { if (action?.parameters) {
@ -416,16 +395,10 @@ const handlerMap = {
["Duplicate Row"]: duplicateRowHandler, ["Duplicate Row"]: duplicateRowHandler,
["Delete Row"]: deleteRowHandler, ["Delete Row"]: deleteRowHandler,
["Navigate To"]: navigationHandler, ["Navigate To"]: navigationHandler,
["Scroll To Field"]: scrollHandler,
["Execute Query"]: queryExecutionHandler, ["Execute Query"]: queryExecutionHandler,
["Trigger Automation"]: triggerAutomationHandler, ["Trigger Automation"]: triggerAutomationHandler,
["Validate Form"]: validateFormHandler,
["Update Field Value"]: updateFieldValueHandler,
["Refresh Data Provider"]: refreshDataProviderHandler,
["Log Out"]: logoutHandler, ["Log Out"]: logoutHandler,
["Clear Form"]: clearFormHandler,
["Close Screen Modal"]: closeScreenModalHandler, ["Close Screen Modal"]: closeScreenModalHandler,
["Change Form Step"]: changeFormStepHandler,
["Update State"]: updateStateHandler, ["Update State"]: updateStateHandler,
["Upload File to S3"]: s3UploadHandler, ["Upload File to S3"]: s3UploadHandler,
["Export Data"]: exportDataHandler, ["Export Data"]: exportDataHandler,
@ -460,7 +433,12 @@ export const enrichButtonActions = (actions, context) => {
return actions return actions
} }
const handlers = actions.map(def => handlerMap[def["##eventHandlerType"]]) // Get handlers for each action. If no bespoke handler is configured, fall
// back to simply executing this action from context.
const handlers = actions.map(def => {
return handlerMap[def["##eventHandlerType"]] || contextActionHandler
})
return async eventContext => { return async eventContext => {
// Button context is built up as actions are executed. // Button context is built up as actions are executed.
// Inherit any previous button context which may have come from actions // Inherit any previous button context which may have come from actions