From 0e3689548c349b188bdd779638d4da93550cb747 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 14 Jan 2025 14:45:37 +0000 Subject: [PATCH 1/9] Initial work to make a new JS script block in automations. --- .../src/constants/backend/automations.ts | 1 + .../builder/src/stores/builder/automations.ts | 5 +- packages/server/src/automations/actions.ts | 3 + .../src/automations/steps/executeScriptV2.ts | 82 +++++++++++++++++++ .../tests/utilities/AutomationTestBuilder.ts | 15 ++++ .../documents/app/automation/automation.ts | 1 + .../src/documents/app/automation/schema.ts | 10 +++ 7 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 packages/server/src/automations/steps/executeScriptV2.ts diff --git a/packages/builder/src/constants/backend/automations.ts b/packages/builder/src/constants/backend/automations.ts index 37ae35aea0..3c8ac8d362 100644 --- a/packages/builder/src/constants/backend/automations.ts +++ b/packages/builder/src/constants/backend/automations.ts @@ -15,6 +15,7 @@ export const ActionStepID = { DELETE_ROW: "DELETE_ROW", OUTGOING_WEBHOOK: "OUTGOING_WEBHOOK", EXECUTE_SCRIPT: "EXECUTE_SCRIPT", + EXECUTE_SCRIPT_V2: "EXECUTE_SCRIPT_V2", EXECUTE_QUERY: "EXECUTE_QUERY", SERVER_LOG: "SERVER_LOG", DELAY: "DELAY", diff --git a/packages/builder/src/stores/builder/automations.ts b/packages/builder/src/stores/builder/automations.ts index 9b20b4cd03..c44bad9eeb 100644 --- a/packages/builder/src/stores/builder/automations.ts +++ b/packages/builder/src/stores/builder/automations.ts @@ -676,7 +676,10 @@ const automationActions = (store: AutomationStore) => ({ runtimeName = `loop.${name}` } else if (idx === 0) { runtimeName = `trigger.${name}` - } else if (currentBlock?.stepId === AutomationActionStepId.EXECUTE_SCRIPT) { + } else if ( + currentBlock?.stepId === AutomationActionStepId.EXECUTE_SCRIPT || + currentBlock?.stepId === AutomationActionStepId.EXECUTE_SCRIPT_V2 + ) { const stepId = pathSteps[idx].id if (!stepId) { notifications.error("Error generating binding: Step ID not found.") diff --git a/packages/server/src/automations/actions.ts b/packages/server/src/automations/actions.ts index 537b6befc3..c50a4482fc 100644 --- a/packages/server/src/automations/actions.ts +++ b/packages/server/src/automations/actions.ts @@ -3,6 +3,7 @@ import * as createRow from "./steps/createRow" import * as updateRow from "./steps/updateRow" import * as deleteRow from "./steps/deleteRow" import * as executeScript from "./steps/executeScript" +import * as executeScriptV2 from "./steps/executeScriptV2" import * as executeQuery from "./steps/executeQuery" import * as outgoingWebhook from "./steps/outgoingWebhook" import * as serverLog from "./steps/serverLog" @@ -42,6 +43,7 @@ const ACTION_IMPLS: ActionImplType = { DELETE_ROW: deleteRow.run, OUTGOING_WEBHOOK: outgoingWebhook.run, EXECUTE_SCRIPT: executeScript.run, + EXECUTE_SCRIPT_V2: executeScriptV2.run, EXECUTE_QUERY: executeQuery.run, SERVER_LOG: serverLog.run, DELAY: delay.run, @@ -68,6 +70,7 @@ export const BUILTIN_ACTION_DEFINITIONS: Record< DELETE_ROW: deleteRow.definition, OUTGOING_WEBHOOK: outgoingWebhook.definition, EXECUTE_SCRIPT: executeScript.definition, + EXECUTE_SCRIPT_V2: executeScriptV2.definition, EXECUTE_QUERY: executeQuery.definition, SERVER_LOG: serverLog.definition, DELAY: delay.definition, diff --git a/packages/server/src/automations/steps/executeScriptV2.ts b/packages/server/src/automations/steps/executeScriptV2.ts new file mode 100644 index 0000000000..e58ee244d4 --- /dev/null +++ b/packages/server/src/automations/steps/executeScriptV2.ts @@ -0,0 +1,82 @@ +import * as automationUtils from "../automationUtils" +import { + AutomationActionStepId, + AutomationCustomIOType, + AutomationFeature, + AutomationIOType, + AutomationStepDefinition, + AutomationStepType, + ExecuteScriptStepInputs, + ExecuteScriptStepOutputs, +} from "@budibase/types" +import { processStringSync } from "@budibase/string-templates" + +export const definition: AutomationStepDefinition = { + name: "JS Scripting", + tagline: "Execute JavaScript Code", + icon: "Code", + description: "Run a piece of JavaScript code in your automation", + type: AutomationStepType.ACTION, + internal: true, + stepId: AutomationActionStepId.EXECUTE_SCRIPT_V2, + inputs: {}, + features: { + [AutomationFeature.LOOPING]: true, + }, + schema: { + inputs: { + properties: { + code: { + type: AutomationIOType.STRING, + customType: AutomationCustomIOType.CODE, + title: "Code", + }, + }, + required: ["code"], + }, + outputs: { + properties: { + value: { + type: AutomationIOType.STRING, + description: "The result of the return statement", + }, + success: { + type: AutomationIOType.BOOLEAN, + description: "Whether the action was successful", + }, + }, + required: ["success"], + }, + }, +} + +export async function run({ + inputs, + context, +}: { + inputs: ExecuteScriptStepInputs + context: Record +}): Promise { + if (inputs.code == null) { + return { + success: false, + response: { + message: "Invalid inputs", + }, + } + } + + const js = Buffer.from(inputs.code, "utf-8").toString("base64") + + try { + return { + success: true, + value: processStringSync(`{{ js "${js}" }}`, context), + } + } catch (err) { + return { + success: false, + response: automationUtils.getError(err), + } + } +} diff --git a/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts b/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts index acc35a0eeb..840c107b3e 100644 --- a/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts +++ b/packages/server/src/automations/tests/utilities/AutomationTestBuilder.ts @@ -208,6 +208,9 @@ class BaseStepBuilder { ) } + /** + * @deprecated Use `executeScriptV2` instead + */ executeScript( input: ExecuteScriptStepInputs, opts?: { stepName?: string; stepId?: string } @@ -220,6 +223,18 @@ class BaseStepBuilder { ) } + executeScriptV2( + input: ExecuteScriptStepInputs, + opts?: { stepName?: string; stepId?: string } + ): this { + return this.step( + AutomationActionStepId.EXECUTE_SCRIPT_V2, + BUILTIN_ACTION_DEFINITIONS.EXECUTE_SCRIPT_V2, + input, + opts + ) + } + filter(input: FilterStepInputs): this { return this.step( AutomationActionStepId.FILTER, diff --git a/packages/types/src/documents/app/automation/automation.ts b/packages/types/src/documents/app/automation/automation.ts index d56f0de879..0bb3750c5c 100644 --- a/packages/types/src/documents/app/automation/automation.ts +++ b/packages/types/src/documents/app/automation/automation.ts @@ -63,6 +63,7 @@ export enum AutomationActionStepId { EXECUTE_BASH = "EXECUTE_BASH", OUTGOING_WEBHOOK = "OUTGOING_WEBHOOK", EXECUTE_SCRIPT = "EXECUTE_SCRIPT", + EXECUTE_SCRIPT_V2 = "EXECUTE_SCRIPT_V2", EXECUTE_QUERY = "EXECUTE_QUERY", SERVER_LOG = "SERVER_LOG", DELAY = "DELAY", diff --git a/packages/types/src/documents/app/automation/schema.ts b/packages/types/src/documents/app/automation/schema.ts index 84bfebf6bf..677d25d4ce 100644 --- a/packages/types/src/documents/app/automation/schema.ts +++ b/packages/types/src/documents/app/automation/schema.ts @@ -79,6 +79,10 @@ export type ActionImplementations = { ExecuteScriptStepInputs, ExecuteScriptStepOutputs > + [AutomationActionStepId.EXECUTE_SCRIPT_V2]: ActionImplementation< + ExecuteScriptStepInputs, + ExecuteScriptStepOutputs + > [AutomationActionStepId.FILTER]: ActionImplementation< FilterStepInputs, FilterStepOutputs @@ -190,6 +194,8 @@ export type AutomationStepInputs = ? ExecuteQueryStepInputs : T extends AutomationActionStepId.EXECUTE_SCRIPT ? ExecuteScriptStepInputs + : T extends AutomationActionStepId.EXECUTE_SCRIPT_V2 + ? ExecuteScriptStepInputs : T extends AutomationActionStepId.FILTER ? FilterStepInputs : T extends AutomationActionStepId.QUERY_ROWS @@ -247,6 +253,9 @@ export type ExecuteQueryStep = export type ExecuteScriptStep = AutomationStepSchema +export type ExecuteScriptV2Step = + AutomationStepSchema + export type FilterStep = AutomationStepSchema export type QueryRowsStep = @@ -293,6 +302,7 @@ export type AutomationStep = | DeleteRowStep | ExecuteQueryStep | ExecuteScriptStep + | ExecuteScriptV2Step | FilterStep | QueryRowsStep | SendEmailSmtpStep From d92a82f44d3a452877af5cc31c4d7302db69089b Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 14 Jan 2025 15:04:41 +0000 Subject: [PATCH 2/9] Temporarily rename new step. --- packages/server/src/automations/steps/executeScriptV2.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/automations/steps/executeScriptV2.ts b/packages/server/src/automations/steps/executeScriptV2.ts index e58ee244d4..20a0ed0805 100644 --- a/packages/server/src/automations/steps/executeScriptV2.ts +++ b/packages/server/src/automations/steps/executeScriptV2.ts @@ -12,7 +12,7 @@ import { import { processStringSync } from "@budibase/string-templates" export const definition: AutomationStepDefinition = { - name: "JS Scripting", + name: "JS Scripting V2", tagline: "Execute JavaScript Code", icon: "Code", description: "Run a piece of JavaScript code in your automation", From bf281fdff5658228ce28c2e0e34a6c9236ada2ff Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 14 Jan 2025 16:14:37 +0000 Subject: [PATCH 3/9] Assume code is already base64 encoded. --- packages/server/src/automations/steps/executeScriptV2.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/server/src/automations/steps/executeScriptV2.ts b/packages/server/src/automations/steps/executeScriptV2.ts index 20a0ed0805..d568245f4d 100644 --- a/packages/server/src/automations/steps/executeScriptV2.ts +++ b/packages/server/src/automations/steps/executeScriptV2.ts @@ -66,12 +66,10 @@ export async function run({ } } - const js = Buffer.from(inputs.code, "utf-8").toString("base64") - try { return { success: true, - value: processStringSync(`{{ js "${js}" }}`, context), + value: processStringSync(inputs.code, context), } } catch (err) { return { From 96869c2b4d2f3ec3f057649d5a9561c6a24d239d Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 14 Jan 2025 17:09:35 +0000 Subject: [PATCH 4/9] Deprecate old JS script block, mark new one as... well, new. --- .../FlowChart/ActionModal.svelte | 6 ++++++ .../src/automations/steps/executeScript.ts | 1 + .../src/automations/steps/executeScriptV2.ts | 18 ++++++++++++++++-- .../src/documents/app/automation/schema.ts | 1 + 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ActionModal.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ActionModal.svelte index 706c196fff..4400c3fcf3 100644 --- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ActionModal.svelte +++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ActionModal.svelte @@ -186,6 +186,12 @@ {:else if isDisabled} + {:else if action.new} +
+ + New + +
{/if} diff --git a/packages/server/src/automations/steps/executeScript.ts b/packages/server/src/automations/steps/executeScript.ts index 97145f3e14..58ddb332c1 100644 --- a/packages/server/src/automations/steps/executeScript.ts +++ b/packages/server/src/automations/steps/executeScript.ts @@ -15,6 +15,7 @@ import { EventEmitter } from "events" export const definition: AutomationStepDefinition = { name: "JS Scripting", + deprecated: true, tagline: "Execute JavaScript Code", icon: "Code", description: "Run a piece of JavaScript code in your automation", diff --git a/packages/server/src/automations/steps/executeScriptV2.ts b/packages/server/src/automations/steps/executeScriptV2.ts index d568245f4d..f5edf20f7d 100644 --- a/packages/server/src/automations/steps/executeScriptV2.ts +++ b/packages/server/src/automations/steps/executeScriptV2.ts @@ -12,12 +12,13 @@ import { import { processStringSync } from "@budibase/string-templates" export const definition: AutomationStepDefinition = { - name: "JS Scripting V2", + name: "JS Scripting", tagline: "Execute JavaScript Code", icon: "Code", description: "Run a piece of JavaScript code in your automation", type: AutomationStepType.ACTION, internal: true, + new: true, stepId: AutomationActionStepId.EXECUTE_SCRIPT_V2, inputs: {}, features: { @@ -57,7 +58,9 @@ export async function run({ inputs: ExecuteScriptStepInputs context: Record }): Promise { - if (inputs.code == null) { + let { code } = inputs + + if (code == null) { return { success: false, response: { @@ -66,6 +69,17 @@ export async function run({ } } + code = code.trim() + + if (!code.startsWith("{{ js ")) { + return { + success: false, + response: { + message: "Expected code to be a {{ js }} template block", + }, + } + } + try { return { success: true, diff --git a/packages/types/src/documents/app/automation/schema.ts b/packages/types/src/documents/app/automation/schema.ts index 677d25d4ce..a712b9e45b 100644 --- a/packages/types/src/documents/app/automation/schema.ts +++ b/packages/types/src/documents/app/automation/schema.ts @@ -154,6 +154,7 @@ export interface AutomationStepSchemaBase { type: AutomationStepType internal?: boolean deprecated?: boolean + new?: boolean blockToLoop?: string schema: { inputs: InputOutputBlock From 6dafe79e671f838ad6e5761e20ae077a8923d89f Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 15 Jan 2025 12:24:33 +0000 Subject: [PATCH 5/9] Tweak styling of new component, add deprecation warning to old component. --- packages/bbui/src/Tags/Tag.svelte | 7 +++++ .../FlowChart/ActionModal.svelte | 19 +++++++------ .../FlowChart/FlowItemHeader.svelte | 21 ++++++++++++-- .../builder/src/stores/builder/automations.ts | 21 ++++++++------ .../server/src/api/controllers/automation.ts | 18 ++++-------- .../src/api/routes/tests/automation.spec.ts | 9 ++---- .../src/automations/steps/executeScriptV2.ts | 4 +-- packages/server/src/automations/utils.ts | 28 +------------------ packages/types/src/ui/stores/automations.ts | 11 ++++++-- 9 files changed, 68 insertions(+), 70 deletions(-) diff --git a/packages/bbui/src/Tags/Tag.svelte b/packages/bbui/src/Tags/Tag.svelte index 0cdd6c385d..b54bd234c5 100644 --- a/packages/bbui/src/Tags/Tag.svelte +++ b/packages/bbui/src/Tags/Tag.svelte @@ -6,6 +6,7 @@ export let icon = "" export let avatar = "" export let invalid = false + export let emphasized = false export let disabled = false export let closable = false @@ -13,6 +14,7 @@
@@ -40,4 +42,9 @@ margin-bottom: 0; margin-top: 0; } + + .is-emphasized { + border-color: var(--spectrum-global-color-blue-700); + color: var(--spectrum-global-color-blue-700); + } diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ActionModal.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ActionModal.svelte index 4400c3fcf3..d9f5ad1ef6 100644 --- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ActionModal.svelte +++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ActionModal.svelte @@ -23,9 +23,8 @@ let collectBlockAllowedSteps = [TriggerStepID.APP, TriggerStepID.WEBHOOK] let selectedAction let actions = Object.entries($automationStore.blockDefinitions.ACTION).filter( - entry => { - const [key] = entry - return key !== AutomationActionStepId.BRANCH + ([key, action]) => { + return key !== AutomationActionStepId.BRANCH && action.deprecated !== true } ) let lockedFeatures = [ @@ -187,11 +186,9 @@ {:else if isDisabled} {:else if action.new} -
- - New - -
+ + New + {/if}
@@ -233,6 +230,10 @@ grid-gap: var(--spectrum-alias-grid-baseline); } + .item :global(.spectrum-Tags-itemLabel) { + cursor: pointer; + } + .item { cursor: pointer; grid-gap: var(--spectrum-alias-grid-margin-xsmall); @@ -243,6 +244,8 @@ border-radius: 5px; box-sizing: border-box; border-width: 2px; + min-height: 3.5rem; + display: flex; } .item:not(.disabled):hover, .selected { diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte index e81d6d0f5c..f3dea4885f 100644 --- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte +++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte @@ -1,6 +1,13 @@