diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/BranchNode.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/BranchNode.svelte
index 779eaf415a..cdf0f82225 100644
--- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/BranchNode.svelte
+++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/BranchNode.svelte
@@ -18,8 +18,12 @@
import AutomationBindingPanel from "@/components/common/bindings/ServerBindingPanel.svelte"
import FlowItemHeader from "./FlowItemHeader.svelte"
import FlowItemActions from "./FlowItemActions.svelte"
- import { automationStore, selectedAutomation } from "@/stores/builder"
- import { QueryUtils, Utils } from "@budibase/frontend-core"
+ import {
+ automationStore,
+ selectedAutomation,
+ evaluationContext,
+ } from "@/stores/builder"
+ import { QueryUtils, Utils, memo } from "@budibase/frontend-core"
import { cloneDeep } from "lodash/fp"
import { createEventDispatcher, getContext } from "svelte"
import DragZone from "./DragZone.svelte"
@@ -34,11 +38,14 @@
export let automation
const view = getContext("draggableView")
+ const memoContext = memo({})
let drawer
let open = true
let confirmDeleteModal
+ $: memoContext.set($evaluationContext)
+
$: branch = step.inputs?.branches?.[branchIdx]
$: editableConditionUI = branch.conditionUI || {}
@@ -100,6 +107,7 @@
allowOnEmpty={false}
builderType={"condition"}
docsURL={null}
+ evaluationContext={$memoContext}
/>
diff --git a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte
index 16183ea59a..9470afdce6 100644
--- a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte
+++ b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte
@@ -21,7 +21,7 @@
} from "@budibase/bbui"
import CreateWebhookModal from "@/components/automation/Shared/CreateWebhookModal.svelte"
- import { automationStore, tables } from "@/stores/builder"
+ import { automationStore, tables, evaluationContext } from "@/stores/builder"
import { environment } from "@/stores/portal"
import WebhookDisplay from "../Shared/WebhookDisplay.svelte"
import {
@@ -62,6 +62,8 @@
} from "@budibase/types"
import PropField from "./PropField.svelte"
import { utils } from "@budibase/shared-core"
+ import { encodeJSBinding } from "@budibase/string-templates"
+ import CodeEditorField from "@/components/common/bindings/CodeEditorField.svelte"
export let automation
export let block
@@ -74,6 +76,7 @@
// Stop unnecessary rendering
const memoBlock = memo(block)
+ const memoContext = memo({})
const rowTriggers = [
TriggerStepID.ROW_UPDATED,
@@ -97,6 +100,7 @@
let stepLayouts = {}
$: memoBlock.set(block)
+ $: memoContext.set($evaluationContext)
$: filters = lookForFilters(schemaProperties)
$: filterCount =
@@ -140,6 +144,7 @@
? [hbAutocomplete([...bindingsToCompletions(bindings, codeMode)])]
: []
+ // TODO: check if it inputData != newInputData (memo)
const getInputData = (testData, blockInputs) => {
// Test data is not cloned for reactivity
let newInputData = testData || cloneDeep(blockInputs)
@@ -156,6 +161,7 @@
}
const setDefaultEnumValues = () => {
+ // TODO: Update this for memoisation
for (const [key, value] of schemaProperties) {
if (value.type === "string" && value.enum && inputData[key] == null) {
inputData[key] = value.enum[0]
@@ -200,7 +206,6 @@
onChange({ ["revision"]: e.detail })
},
updateOnChange: false,
- forceModal: true,
},
},
]
@@ -228,7 +233,6 @@
onChange({ [rowIdentifier]: e.detail })
},
updateOnChange: false,
- forceModal: true,
},
},
]
@@ -476,6 +480,10 @@
...update,
})
+ if (!updatedAutomation) {
+ return
+ }
+
// Exclude default or invalid data from the test data
let updatedFields = {}
for (const key of Object.keys(block?.inputs?.fields || {})) {
@@ -547,7 +555,7 @@
...newTestData,
body: {
...update,
- ...automation.testData?.body,
+ ...(automation?.testData?.body || {}),
},
}
}
@@ -668,6 +676,7 @@
{...config.props}
{bindings}
on:change={config.props.onChange}
+ context={$memoContext}
/>
{:else}
@@ -676,6 +685,7 @@
{...config.props}
{bindings}
on:change={config.props.onChange}
+ context={$memoContext}
/>
{/if}
{/each}
@@ -800,6 +810,7 @@
: "Add signature"}
keyPlaceholder={"URL"}
valuePlaceholder={"Filename"}
+ context={$memoContext}
/>
{:else if isTestModal}
{/if}
@@ -853,6 +865,7 @@
panel={AutomationBindingPanel}
showFilterEmptyDropdown={!rowTriggers.includes(stepId)}
on:change={e => (tempFilters = e.detail)}
+ evaluationContext={$memoContext}
/>
@@ -895,7 +908,39 @@
on:change={e => onChange({ [key]: e.detail })}
value={inputData[key]}
/>
- {:else if value.customType === "code"}
+ {:else if value.customType === "code" && stepId === ActionStepID.EXECUTE_SCRIPT_V2}
+
+
onChange({ [key]: e.detail })}
+ value={inputData[key]}
+ {bindings}
+ allowJS={true}
+ allowHBS={false}
+ updateOnChange={false}
+ context={$memoContext}
+ >
+
+
+ onChange({ [key]: encodeJSBinding(e.detail) })}
+ />
+
+
+
+ {:else if value.customType === "code" && stepId === ActionStepID.EXECUTE_SCRIPT}
+
{
// Push any pending changes when the window closes
@@ -977,6 +1022,7 @@
? queryLimit
: ""}
drawerLeft="260px"
+ context={$memoContext}
/>
{/if}
@@ -1044,4 +1090,23 @@
flex: 3;
margin-top: calc((var(--spacing-xl) * -1) + 1px);
}
+
+ .field-wrap :global(.cm-editor),
+ .field-wrap :global(.cm-scroller) {
+ border-radius: 4px;
+ }
+ .field-wrap {
+ box-sizing: border-box;
+ border: 1px solid var(--spectrum-global-color-gray-400);
+ border-radius: 4px;
+ }
+ .field-wrap.code-editor {
+ height: 180px;
+ }
+ .scriptv2-wrapper :global(.icon.slot-icon) {
+ top: 1px;
+ border-bottom-left-radius: var(--spectrum-alias-border-radius-regular);
+ border-right: 0px;
+ border-bottom: 1px solid var(--spectrum-alias-border-color);
+ }
diff --git a/packages/builder/src/components/automation/SetupPanel/RowSelector.svelte b/packages/builder/src/components/automation/SetupPanel/RowSelector.svelte
index cc3315a4ab..d859f86f9c 100644
--- a/packages/builder/src/components/automation/SetupPanel/RowSelector.svelte
+++ b/packages/builder/src/components/automation/SetupPanel/RowSelector.svelte
@@ -25,6 +25,7 @@
export let meta
export let bindings
export let isTestModal
+ export let context = {}
const typeToField = Object.values(FIELDS).reduce((acc, field) => {
acc[field.type] = field
@@ -58,7 +59,7 @@
$: parsedBindings = bindings.map(binding => {
let clone = Object.assign({}, binding)
- clone.icon = "ShareAndroid"
+ clone.icon = clone.icon ?? "ShareAndroid"
return clone
})
@@ -258,6 +259,7 @@
fields: editableFields,
}}
{onChange}
+ {context}
/>
{:else}
onChange(change)}
/>
diff --git a/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte b/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte
index 86666660af..aefbf02303 100644
--- a/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte
+++ b/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte
@@ -25,12 +25,13 @@
export let meta
export let bindings
export let isTestModal
+ export let context
$: fieldData = value[field]
$: parsedBindings = bindings.map(binding => {
let clone = Object.assign({}, binding)
- clone.icon = "ShareAndroid"
+ clone.icon = clone.icon ?? "ShareAndroid"
return clone
})
@@ -232,6 +233,7 @@
actionButtonDisabled={(schema.type === FieldType.ATTACHMENT_SINGLE ||
schema.type === FieldType.SIGNATURE_SINGLE) &&
fieldData}
+ {context}
/>
{:else}
diff --git a/packages/builder/src/components/automation/SetupPanel/SchemaSetup.svelte b/packages/builder/src/components/automation/SetupPanel/SchemaSetup.svelte
index 24b04abf31..d829bbe65a 100644
--- a/packages/builder/src/components/automation/SetupPanel/SchemaSetup.svelte
+++ b/packages/builder/src/components/automation/SetupPanel/SchemaSetup.svelte
@@ -1,18 +1,11 @@
@@ -69,7 +93,7 @@
- {#each fieldsArray as field}
+ {#each fieldsArray as field, idx (field.id)}
removeField(field.name)}
+ on:click={() => {
+ removeField(idx)
+ }}
/>
{/each}
@@ -115,4 +141,12 @@
align-items: center;
gap: var(--spacing-m);
}
+
+ .remove-field {
+ cursor: pointer;
+ }
+
+ .remove-field:hover {
+ color: var(--spectrum-global-color-gray-900);
+ }
diff --git a/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte b/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte
index bc88f0f981..46907cd71c 100644
--- a/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte
+++ b/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte
@@ -1,3 +1,10 @@
+
+
+
+
+
+ {#key jsCompletions}
+
+ {/key}
+
+
+
+
diff --git a/packages/builder/src/components/common/bindings/DrawerBindableSlot.svelte b/packages/builder/src/components/common/bindings/DrawerBindableSlot.svelte
index 6fda04480c..8c10239b49 100644
--- a/packages/builder/src/components/common/bindings/DrawerBindableSlot.svelte
+++ b/packages/builder/src/components/common/bindings/DrawerBindableSlot.svelte
@@ -23,6 +23,9 @@
export let type
export let schema
+ export let allowHBS = true
+ export let context = {}
+
const dispatch = createEventDispatcher()
let bindingDrawer
let currentVal = value
@@ -147,7 +150,7 @@
- {#if !isValid(value)}
+ {#if !isValid(value) && !$$slots.default}
(tempValue = event.detail)}
{bindings}
{allowJS}
+ {allowHBS}
{allowHelpers}
+ {context}
/>
@@ -208,22 +212,22 @@
}
.slot-icon {
- right: 31px !important;
+ right: 31px;
border-right: 1px solid var(--spectrum-alias-border-color);
- border-top-right-radius: 0px !important;
- border-bottom-right-radius: 0px !important;
+ border-top-right-radius: 0px;
+ border-bottom-right-radius: 0px;
}
.text-area-slot-icon {
border-bottom: 1px solid var(--spectrum-alias-border-color);
- border-bottom-right-radius: 0px !important;
- top: 1px !important;
+ border-bottom-right-radius: 0px;
+ top: 1px;
}
.json-slot-icon {
border-bottom: 1px solid var(--spectrum-alias-border-color);
- border-bottom-right-radius: 0px !important;
- top: 1px !important;
- right: 0px !important;
+ border-bottom-right-radius: 0px;
+ top: 1px;
+ right: 0px;
}
.icon {
diff --git a/packages/builder/src/components/common/bindings/ServerBindingPanel.svelte b/packages/builder/src/components/common/bindings/ServerBindingPanel.svelte
index 19a466db6b..14c8de89a3 100644
--- a/packages/builder/src/components/common/bindings/ServerBindingPanel.svelte
+++ b/packages/builder/src/components/common/bindings/ServerBindingPanel.svelte
@@ -5,6 +5,7 @@
export let bindings = []
export let value = ""
export let allowJS = false
+ export let allowHBS = true
export let context = null
$: enrichedBindings = enrichBindings(bindings)
@@ -22,8 +23,10 @@
diff --git a/packages/builder/src/components/design/settings/controls/FilterEditor/FilterBuilder.svelte b/packages/builder/src/components/design/settings/controls/FilterEditor/FilterBuilder.svelte
index 010216cb4d..cf45fad78a 100644
--- a/packages/builder/src/components/design/settings/controls/FilterEditor/FilterBuilder.svelte
+++ b/packages/builder/src/components/design/settings/controls/FilterEditor/FilterBuilder.svelte
@@ -16,6 +16,7 @@
export let datasource
export let builderType
export let docsURL
+ export let evaluationContext = {}
diff --git a/packages/builder/src/components/integration/KeyValueBuilder.svelte b/packages/builder/src/components/integration/KeyValueBuilder.svelte
index e42703f9c6..7ea7a935c8 100644
--- a/packages/builder/src/components/integration/KeyValueBuilder.svelte
+++ b/packages/builder/src/components/integration/KeyValueBuilder.svelte
@@ -39,6 +39,7 @@
export let allowJS = false
export let actionButtonDisabled = false
export let compare = (option, value) => option === value
+ export let context = null
let fields = Object.entries(object || {}).map(([name, value]) => ({
name,
@@ -132,6 +133,7 @@
{allowJS}
{allowHelpers}
drawerLeft={bindingDrawerLeft}
+ {context}
/>
{:else}
@@ -158,6 +160,7 @@
{allowJS}
{allowHelpers}
drawerLeft={bindingDrawerLeft}
+ {context}
/>
{:else}
{
+ onMount(async () => {
+ await automationStore.actions.initAppSelf()
+
$automationStore.showTestPanel = false
})
diff --git a/packages/builder/src/stores/builder/automations.ts b/packages/builder/src/stores/builder/automations.ts
index 25930e953b..d977625edf 100644
--- a/packages/builder/src/stores/builder/automations.ts
+++ b/packages/builder/src/stores/builder/automations.ts
@@ -1,9 +1,9 @@
-import { derived, get } from "svelte/store"
+import { derived, get, Readable, Writable } from "svelte/store"
import { API } from "@/api"
import { cloneDeep } from "lodash/fp"
import { generate } from "shortid"
import { createHistoryStore } from "@/stores/builder/history"
-import { licensing } from "@/stores/portal"
+import { licensing, organisation, environment, auth } from "@/stores/portal"
import { tables, appStore } from "@/stores/builder"
import { notifications } from "@budibase/bbui"
import {
@@ -32,8 +32,10 @@ import {
BlockDefinitions,
GetAutomationTriggerDefinitionsResponse,
GetAutomationActionDefinitionsResponse,
+ AppSelfResponse,
+ TestAutomationResponse,
} from "@budibase/types"
-import { ActionStepID } from "@/constants/backend/automations"
+import { ActionStepID, TriggerStepID } from "@/constants/backend/automations"
import { FIELDS } from "@/constants/backend"
import { sdk } from "@budibase/shared-core"
import { rowActions } from "./rowActions"
@@ -43,10 +45,11 @@ import { BudiStore, DerivedBudiStore } from "@/stores/BudiStore"
interface AutomationState {
automations: Automation[]
- testResults: any | null
+ testResults?: TestAutomationResponse
showTestPanel: boolean
blockDefinitions: BlockDefinitions
selectedAutomationId: string | null
+ appSelf?: AppSelfResponse
}
interface DerivedAutomationState extends AutomationState {
@@ -56,7 +59,6 @@ interface DerivedAutomationState extends AutomationState {
const initialAutomationState: AutomationState = {
automations: [],
- testResults: null,
showTestPanel: false,
blockDefinitions: {
TRIGGER: {},
@@ -88,6 +90,20 @@ const getFinalDefinitions = (
}
const automationActions = (store: AutomationStore) => ({
+ /**
+ * Fetches the app user context used for live evaluation
+ * This matches the context used on the server
+ * @returns {AppSelfResponse | null}
+ */
+ initAppSelf: async (): Promise => {
+ // Fetch and update the app self if it hasn't been set
+ const appSelfResponse = await API.fetchSelf()
+ store.update(state => ({
+ ...state,
+ appSelf: appSelfResponse,
+ }))
+ return appSelfResponse
+ },
/**
* Move a given block from one location on the tree to another.
*
@@ -284,9 +300,12 @@ const automationActions = (store: AutomationStore) => ({
* Build a sequential list of all steps on the step path provided
*
* @param {Array