diff --git a/packages/bbui/src/Form/TextArea.svelte b/packages/bbui/src/Form/TextArea.svelte
index 15e91d72c1..c803643e6e 100644
--- a/packages/bbui/src/Form/TextArea.svelte
+++ b/packages/bbui/src/Form/TextArea.svelte
@@ -1,21 +1,21 @@
-
@@ -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/backend/DatasourceNavigator/modals/ImportRestQueriesModal.svelte b/packages/builder/src/components/backend/DatasourceNavigator/modals/ImportRestQueriesModal.svelte
deleted file mode 100644
index 37610061ac..0000000000
--- a/packages/builder/src/components/backend/DatasourceNavigator/modals/ImportRestQueriesModal.svelte
+++ /dev/null
@@ -1,132 +0,0 @@
-
-
-
importQueries()}
- {onCancel}
- confirmText={"Import"}
- cancelText="Back"
- size="L"
->
-
- Import
- Import your rest collection using one of the options below
-
-
-
-
- {
- $data.file = e.detail?.[0]
- lastTouched = "file"
- }}
- fileTags={[
- "OpenAPI 3.0",
- "OpenAPI 2.0",
- "Swagger 2.0",
- "cURL",
- "YAML",
- "JSON",
- ]}
- maximum={1}
- />
-
-
-
-
-
-
diff --git a/packages/builder/src/components/backend/RoleEditor/RoleNode.svelte b/packages/builder/src/components/backend/RoleEditor/RoleNode.svelte
index c01ae43362..6183246487 100644
--- a/packages/builder/src/components/backend/RoleEditor/RoleNode.svelte
+++ b/packages/builder/src/components/backend/RoleEditor/RoleNode.svelte
@@ -43,7 +43,7 @@
const validateDescription = description => {
if (!description?.length) {
- return "Please enter a name"
+ return "Please enter a description"
}
return null
}
diff --git a/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte b/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte
index 0f4bc64e2a..0127e31def 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/DrawerBindableCodeEditorField.svelte b/packages/builder/src/components/common/bindings/DrawerBindableCodeEditorField.svelte
new file mode 100644
index 0000000000..82f566ad92
--- /dev/null
+++ b/packages/builder/src/components/common/bindings/DrawerBindableCodeEditorField.svelte
@@ -0,0 +1,68 @@
+
+
+
+
{
+ value = e.detail
+ dispatch("change", value)
+ }}
+ >
+
+ (value = e.detail)}
+ on:blur={() => dispatch("change", value)}
+ />
+
+
+
+
+
diff --git a/packages/builder/src/components/common/bindings/DrawerBindableSlot.svelte b/packages/builder/src/components/common/bindings/DrawerBindableSlot.svelte
index 6fda04480c..617b6aa590 100644
--- a/packages/builder/src/components/common/bindings/DrawerBindableSlot.svelte
+++ b/packages/builder/src/components/common/bindings/DrawerBindableSlot.svelte
@@ -22,6 +22,8 @@
export let updateOnChange = true
export let type
export let schema
+ export let allowHBS = true
+ export let context = {}
const dispatch = createEventDispatcher()
let bindingDrawer
@@ -147,7 +149,7 @@
- {#if !isValid(value)}
+ {#if !isValid(value) && !$$slots.default}
{/if}
- {#if !disabled && type !== "formula" && !disabled && !attachmentTypes.includes(type)}
+ {#if !disabled && type !== "formula" && !attachmentTypes.includes(type)}
{
@@ -187,7 +189,6 @@
on:drawerShow
bind:this={bindingDrawer}
title={title ?? placeholder ?? "Bindings"}
- forceModal={true}
>
(tempValue = event.detail)}
{bindings}
{allowJS}
+ {allowHBS}
{allowHelpers}
+ {context}
/>
@@ -208,22 +211,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/ButtonActionEditor/actions/ClearRowSelection.svelte b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/ClearRowSelection.svelte
index cd289e3f61..758cf0dab8 100644
--- a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/ClearRowSelection.svelte
+++ b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/ClearRowSelection.svelte
@@ -2,6 +2,7 @@
import { Label, Select, Body } from "@budibase/bbui"
import { findAllMatchingComponents } from "@/helpers/components"
import { selectedScreen } from "@/stores/builder"
+ import { InlineAlert } from "@budibase/bbui"
export let parameters
@@ -27,6 +28,12 @@
+
diff --git a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/index.js b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/index.js
index b171b34111..2ad41076f2 100644
--- a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/index.js
+++ b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/index.js
@@ -26,3 +26,4 @@ export { default as CloseModal } from "./CloseModal.svelte"
export { default as ClearRowSelection } from "./ClearRowSelection.svelte"
export { default as DownloadFile } from "./DownloadFile.svelte"
export { default as RowAction } from "./RowAction.svelte"
+export { default as CopyToClipboard } from "./CopyToClipboard.svelte"
diff --git a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/manifest.json b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/manifest.json
index 631e3119e8..2bc8599057 100644
--- a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/manifest.json
+++ b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/manifest.json
@@ -183,6 +183,17 @@
"name": "Row Action",
"type": "data",
"component": "RowAction"
+ },
+ {
+ "name": "Copy To Clipboard",
+ "type": "data",
+ "component": "CopyToClipboard",
+ "context": [
+ {
+ "label": "Copied text",
+ "value": "copied"
+ }
+ ]
}
]
}
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()
+
+ // Init the binding evaluation context
+ automationStore.actions.initContext()
+
$automationStore.showTestPanel = false
})
diff --git a/packages/builder/src/pages/builder/app/[application]/data/datasource/[datasourceId]/_components/panels/Queries/RestImportQueriesModal.svelte b/packages/builder/src/pages/builder/app/[application]/data/datasource/[datasourceId]/_components/panels/Queries/RestImportQueriesModal.svelte
index dd90acf367..b144ed64b9 100644
--- a/packages/builder/src/pages/builder/app/[application]/data/datasource/[datasourceId]/_components/panels/Queries/RestImportQueriesModal.svelte
+++ b/packages/builder/src/pages/builder/app/[application]/data/datasource/[datasourceId]/_components/panels/Queries/RestImportQueriesModal.svelte
@@ -1,4 +1,4 @@
-
diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js
index a467cc81ed..192b69061d 100644
--- a/packages/client/src/utils/buttonActions.js
+++ b/packages/client/src/utils/buttonActions.js
@@ -421,6 +421,28 @@ const showNotificationHandler = action => {
const promptUserHandler = () => {}
+const copyToClipboardHandler = async action => {
+ const { textToCopy, showNotification, notificationMessage } =
+ action.parameters
+
+ if (!textToCopy) {
+ return
+ }
+
+ try {
+ await navigator.clipboard.writeText(textToCopy)
+ if (showNotification) {
+ const message = notificationMessage || "Copied to clipboard"
+ notificationStore.actions.success(message, true, 3000)
+ }
+ } catch (err) {
+ console.error("Failed to copy text: ", err)
+ notificationStore.actions.error("Failed to copy to clipboard")
+ }
+
+ return { copied: textToCopy }
+}
+
const openSidePanelHandler = action => {
const { id } = action.parameters
if (id) {
@@ -514,6 +536,7 @@ const handlerMap = {
["Close Modal"]: closeModalHandler,
["Download File"]: downloadFileHandler,
["Row Action"]: rowActionHandler,
+ ["Copy To Clipboard"]: copyToClipboardHandler,
}
const confirmTextMap = {
diff --git a/packages/frontend-core/src/components/ConditionField.svelte b/packages/frontend-core/src/components/ConditionField.svelte
index 58dffc8dea..310be4acea 100644
--- a/packages/frontend-core/src/components/ConditionField.svelte
+++ b/packages/frontend-core/src/components/ConditionField.svelte
@@ -10,6 +10,7 @@
export let drawerTitle
export let toReadable
export let toRuntime
+ export let evaluationContext = {}
const dispatch = createEventDispatcher()
@@ -66,7 +67,6 @@
>
Confirm
-
diff --git a/packages/frontend-core/src/components/CoreFilterBuilder.svelte b/packages/frontend-core/src/components/CoreFilterBuilder.svelte
index 2bf40bda37..932298fc7d 100644
--- a/packages/frontend-core/src/components/CoreFilterBuilder.svelte
+++ b/packages/frontend-core/src/components/CoreFilterBuilder.svelte
@@ -42,6 +42,7 @@
export let panel
export let toReadable
export let toRuntime
+ export let evaluationContext = {}
$: editableFilters = migrateFilters(filters)
$: {
@@ -385,6 +386,7 @@
{panel}
{toReadable}
{toRuntime}
+ {evaluationContext}
on:change={e => {
const updated = {
...filter,
@@ -423,6 +425,7 @@
{panel}
{toReadable}
{toRuntime}
+ {evaluationContext}
on:change={e => {
onFilterFieldUpdate(
{ ...filter, ...e.detail },
diff --git a/packages/frontend-core/src/components/FilterField.svelte b/packages/frontend-core/src/components/FilterField.svelte
index 861df9c2fc..7c201a7e64 100644
--- a/packages/frontend-core/src/components/FilterField.svelte
+++ b/packages/frontend-core/src/components/FilterField.svelte
@@ -24,6 +24,7 @@
export let drawerTitle
export let toReadable
export let toRuntime
+ export let evaluationContext = {}
const dispatch = createEventDispatcher()
const { OperatorOptions, FilterValueType } = Constants
@@ -156,6 +157,7 @@
allowHBS
on:change={drawerOnChange}
{bindings}
+ context={evaluationContext}
/>
diff --git a/packages/server/src/api/controllers/automation.ts b/packages/server/src/api/controllers/automation.ts
index a6c91d6753..95a6f250eb 100644
--- a/packages/server/src/api/controllers/automation.ts
+++ b/packages/server/src/api/controllers/automation.ts
@@ -3,7 +3,13 @@ import { sdk as coreSdk } from "@budibase/shared-core"
import { DocumentType } from "../../db/utils"
import { updateTestHistory } from "../../automations/utils"
import { withTestFlag } from "../../utilities/redis"
-import { context, cache, events, db as dbCore } from "@budibase/backend-core"
+import {
+ context,
+ cache,
+ events,
+ db as dbCore,
+ HTTPError,
+} from "@budibase/backend-core"
import { automations, features } from "@budibase/pro"
import {
App,
@@ -28,6 +34,7 @@ import {
TriggerAutomationResponse,
TestAutomationRequest,
TestAutomationResponse,
+ Table,
} from "@budibase/types"
import { getActionDefinitions as actionDefs } from "../../automations/actions"
import sdk from "../../sdk"
@@ -231,14 +238,22 @@ export async function test(
const { request, appId } = ctx
const { body } = request
+ let table: Table | undefined
+ if (coreSdk.automations.isRowAction(automation) && body.row?.tableId) {
+ table = await sdk.tables.getTable(body.row?.tableId)
+ if (!table) {
+ throw new HTTPError("Table not found", 404)
+ }
+ }
+
ctx.body = await withTestFlag(automation._id!, async () => {
const occurredAt = new Date().getTime()
await updateTestHistory(appId, automation, { ...body, occurredAt })
-
+ const input = prepareTestInput(body)
const user = sdk.users.getUserContextBindings(ctx.user)
return await triggers.externalTrigger(
automation,
- { ...prepareTestInput(body), appId, user },
+ { ...{ ...input, ...(table ? { table } : {}) }, appId, user },
{ getResponses: true }
)
})
diff --git a/packages/server/src/api/routes/tests/automation.spec.ts b/packages/server/src/api/routes/tests/automation.spec.ts
index 662d6ebffb..c41746cb2d 100644
--- a/packages/server/src/api/routes/tests/automation.spec.ts
+++ b/packages/server/src/api/routes/tests/automation.spec.ts
@@ -11,6 +11,7 @@ import {
import { configs, context, events } from "@budibase/backend-core"
import sdk from "../../../sdk"
import {
+ AutomationResults,
ConfigType,
FieldType,
FilterCondition,
@@ -621,7 +622,7 @@ describe("/automations", () => {
})
)
- const res = await config.api.automation.test(automation._id!, {
+ const response = await config.api.automation.test(automation._id!, {
fields: {},
oldRow: {
City: oldCity,
@@ -631,12 +632,14 @@ describe("/automations", () => {
},
})
- if (isDidNotTriggerResponse(res)) {
+ if (isDidNotTriggerResponse(response)) {
throw new Error("Automation did not trigger")
}
+ const results: AutomationResults = response as AutomationResults
+
const expectedResult = oldCity === newCity
- expect(res.steps[1].outputs.result).toEqual(expectedResult)
+ expect(results.steps[1].outputs.result).toEqual(expectedResult)
}
)
})
@@ -723,7 +726,8 @@ describe("/automations", () => {
if (isDidNotTriggerResponse(res)) {
expect(expectToRun).toEqual(false)
} else {
- expect(res.steps[1].outputs.success).toEqual(expectToRun)
+ const results: AutomationResults = res as AutomationResults
+ expect(results.steps[1].outputs.success).toEqual(expectToRun)
}
}
)
diff --git a/packages/server/src/automations/tests/triggers/webhook.spec.ts b/packages/server/src/automations/tests/triggers/webhook.spec.ts
index 77d63a7ffa..ad14ea9d9c 100644
--- a/packages/server/src/automations/tests/triggers/webhook.spec.ts
+++ b/packages/server/src/automations/tests/triggers/webhook.spec.ts
@@ -12,7 +12,7 @@ describe("Webhook trigger test", () => {
async function createWebhookAutomation() {
const { automation } = await createAutomationBuilder(config)
- .onWebhook({ fields: { parameter: "string" } })
+ .onWebhook({ body: { parameter: "string" } })
.createRow({
row: { tableId: table._id!, name: "{{ trigger.parameter }}" },
})
diff --git a/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts b/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts
index 2ebd58b1cf..2a62903b7c 100644
--- a/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts
+++ b/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts
@@ -4,6 +4,7 @@ import { TRIGGER_DEFINITIONS } from "../../triggers"
import {
Automation,
AutomationActionStepId,
+ AutomationResults,
AutomationStep,
AutomationStepInputs,
AutomationTrigger,
@@ -213,10 +214,11 @@ class AutomationRunner
{
throw new Error(response.message)
}
+ const results: AutomationResults = response as AutomationResults
// Remove the trigger step from the response.
- response.steps.shift()
+ results.steps.shift()
- return response
+ return results
}
async trigger(
diff --git a/packages/server/src/automations/triggers.ts b/packages/server/src/automations/triggers.ts
index 16d5246a91..5fd3c51d3e 100644
--- a/packages/server/src/automations/triggers.ts
+++ b/packages/server/src/automations/triggers.ts
@@ -22,6 +22,7 @@ import {
UserBindings,
AutomationResults,
DidNotTriggerResponse,
+ Table,
} from "@budibase/types"
import { executeInThread } from "../threads/automation"
import { dataFilters, sdk } from "@budibase/shared-core"
@@ -154,6 +155,7 @@ interface AutomationTriggerParams {
timeout?: number
appId?: string
user?: UserBindings
+ table?: Table
}
export async function externalTrigger(
diff --git a/packages/server/src/integrations/s3.ts b/packages/server/src/integrations/s3.ts
index 88d00724f1..32a44c838b 100644
--- a/packages/server/src/integrations/s3.ts
+++ b/packages/server/src/integrations/s3.ts
@@ -168,6 +168,7 @@ class S3Integration implements IntegrationBase {
secretAccessKey: config.secretAccessKey,
},
region: config.region,
+ endpoint: config.endpoint,
}
if (config.endpoint) {
this.config.forcePathStyle = true
diff --git a/packages/types/src/api/web/app/automation.ts b/packages/types/src/api/web/app/automation.ts
index 6b6c63a261..23293586a7 100644
--- a/packages/types/src/api/web/app/automation.ts
+++ b/packages/types/src/api/web/app/automation.ts
@@ -1,3 +1,4 @@
+import { AutomationJob } from "../../../sdk/automations"
import {
Automation,
AutomationActionStepId,
@@ -78,10 +79,25 @@ export interface TestAutomationRequest {
row?: Row
oldRow?: Row
}
-export type TestAutomationResponse = AutomationResults | DidNotTriggerResponse
export function isDidNotTriggerResponse(
response: TestAutomationResponse
): response is DidNotTriggerResponse {
return !!("message" in response && response.message)
}
+
+export function isAutomationResults(
+ response: TestAutomationResponse
+): response is AutomationResults {
+ return !!(
+ "steps" in response &&
+ response.steps &&
+ "trigger" in response &&
+ response.trigger
+ )
+}
+
+export type TestAutomationResponse =
+ | AutomationResults
+ | DidNotTriggerResponse
+ | AutomationJob
diff --git a/packages/types/src/api/web/app/query.ts b/packages/types/src/api/web/app/query.ts
index 75cc37e1a9..14f39ba267 100644
--- a/packages/types/src/api/web/app/query.ts
+++ b/packages/types/src/api/web/app/query.ts
@@ -11,7 +11,7 @@ export interface SaveQueryRequest extends Query {}
export interface SaveQueryResponse extends Query {}
export interface ImportRestQueryRequest {
- datasourceId: string
+ datasourceId?: string
data: string
datasource: Datasource
}
diff --git a/packages/types/src/documents/app/automation/StepInputsOutputs.ts b/packages/types/src/documents/app/automation/StepInputsOutputs.ts
index 52b07ae17f..fb4dd676f2 100644
--- a/packages/types/src/documents/app/automation/StepInputsOutputs.ts
+++ b/packages/types/src/documents/app/automation/StepInputsOutputs.ts
@@ -1,3 +1,4 @@
+import { Table } from "@budibase/types"
import { SortOrder } from "../../../api"
import {
SearchFilters,
@@ -305,6 +306,7 @@ export type RowUpdatedTriggerOutputs = {
row: Row
id: string
revision?: string
+ oldRow?: Row
}
export type WebhookTriggerInputs = {
@@ -312,6 +314,17 @@ export type WebhookTriggerInputs = {
triggerUrl: string
}
-export type WebhookTriggerOutputs = {
- fields: Record
+export type WebhookTriggerOutputs = Record & {
+ body: Record
+}
+
+export type RowActionTriggerInputs = {
+ tableId: string
+}
+
+export type RowActionTriggerOutputs = {
+ row: Row
+ id: string
+ revision?: string
+ table: Table
}
diff --git a/packages/types/src/documents/app/automation/automation.ts b/packages/types/src/documents/app/automation/automation.ts
index e5dad2d177..1682c53e0a 100644
--- a/packages/types/src/documents/app/automation/automation.ts
+++ b/packages/types/src/documents/app/automation/automation.ts
@@ -136,15 +136,7 @@ export interface Automation extends Document {
internal?: boolean
type?: string
disabled?: boolean
- testData?: {
- row?: Row
- meta: {
- [key: string]: unknown
- }
- id: string
- revision: string
- oldRow?: Row
- }
+ testData?: AutomationTriggerResultOutputs
}
export interface BaseIOStructure {