From 554cefe997dbb1579e3f92b7785b41ffead99658 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Fri, 25 Mar 2022 09:26:55 +0000 Subject: [PATCH] Automation foreach block --- .../FlowChart/ActionModal.svelte | 1 + .../FlowChart/FlowItem.svelte | 180 +++++++++++++----- .../SetupPanel/AutomationBlockSetup.svelte | 14 ++ packages/server/src/automations/actions.js | 3 + packages/server/src/automations/steps/loop.js | 47 +++++ packages/server/src/threads/automation.js | 103 ++++++---- 6 files changed, 270 insertions(+), 78 deletions(-) create mode 100644 packages/server/src/automations/steps/loop.js diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ActionModal.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ActionModal.svelte index 4e1e5e1103..caf8835b86 100644 --- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ActionModal.svelte +++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/ActionModal.svelte @@ -39,6 +39,7 @@ if (v.internal) { acc[k] = v } + delete acc.LOOP return acc }, {}) diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte index 69dd67724a..5bd250572e 100644 --- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte +++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte @@ -9,8 +9,8 @@ Modal, Button, StatusLight, - ActionButton, Select, + Label, notifications, } from "@budibase/bbui" import AutomationBlockSetup from "../../SetupPanel/AutomationBlockSetup.svelte" @@ -25,7 +25,6 @@ let webhookModal let actionModal let resultsModal - let setupToggled let blockComplete $: rowControl = $automationStore.selectedAutomation.automation.rowControl @@ -52,6 +51,8 @@ block.schema?.inputs?.properties || {} ).every(x => block?.inputs[x]) + $: loopingSelected = !!block.llop + $: showLooping = false async function deleteStep() { try { automationStore.actions.deleteAutomationBlock(block) @@ -76,6 +77,37 @@ ) } + /* + async function removeLooping() { + loopingSelected = false + const idx = + $automationStore.selectedAutomation.automation.definition.steps.findIndex( + x => x.id === block.id + ) + + delete $automationStore.selectedAutomation.automation.definition.steps[idx] + .loop + + await automationStore.actions.save( + $automationStore.selectedAutomation?.automation + ) + }*/ + async function addLooping() { + loopingSelected = true + const loopDefinition = $automationStore.blockDefinitions.ACTION.LOOP + + const loopBlock = $automationStore.selectedAutomation.constructBlock( + "ACTION", + "LOOP", + loopDefinition + ) + loopBlock.blockToLoop = block.id + automationStore.actions.addBlockToAutomation(loopBlock, blockIdx - 1) + await automationStore.actions.save( + $automationStore.selectedAutomation?.automation + ) + } + async function onSelect(block) { await automationStore.update(state => { state.selectedBlock = block @@ -84,13 +116,61 @@ } -
{ - onSelect(block) - }} -> +
{}}> + {#if loopingSelected} +
+
{ + showLooping = !showLooping + }} + class="splitHeader" + > +
+ + + +
+ Looping +
+
+ +
+
{ + onSelect(block) + }} + > + +
+
+
+
+ + {#if !showLooping} +
+ + + +
+ + {/if} + {/if} +
{ @@ -127,34 +207,42 @@ {block?.name?.toUpperCase() || ""}
- {#if testResult && testResult[0]} - resultsModal.show()}> - View response - - {/if} +
+ {#if testResult && testResult[0]} +
resultsModal.show()}> + View response +
+ {/if} +
{ + onSelect(block) + }} + > + +
+
{#if !blockComplete}
-
- { - onSelect(block) - setupToggled = !setupToggled - }} - quiet - icon={setupToggled ? "ChevronDown" : "ChevronRight"} - > - Setup - - {#if !isTrigger} + {#if !isTrigger} +
+ {#if !loopingSelected} +
addLooping()}> + +
+ +
+
+ {/if} {#if showBindingPicker}
onChange(e, key)} + autoWidth + value={inputData[key]} + options={["Array", "String"]} + defaultValue={"Array"} + /> {:else if value.type === "string" || value.type === "number" || value.type === "integer"} {#if isTestModal} { - return stepFn({ - inputs: step.inputs, - appId: this._appId, - emitter: this._emitter, - context: this._context, - }) - }) - this._context.steps.push(outputs) - // if filter causes us to stop execution don't break the loop, set a var - // so that we can finish iterating through the steps and record that it stopped - if (step.stepId === FILTER_STEP_ID && !outputs.success) { - stopped = true - this.updateExecutionOutput(step.id, step.stepId, step.inputs, { - ...outputs, - ...STOPPED_STATUS, - }) + + let iterations = loopStep ? loopStep.inputs.iterations : 1 + for (let index = 0; index < iterations; index++) { + // execution stopped, record state for that + if (stopped) { + this.updateExecutionOutput(step.id, step.stepId, {}, STOPPED_STATUS) continue } - this.updateExecutionOutput(step.id, step.stepId, step.inputs, outputs) - } catch (err) { - console.error(`Automation error - ${step.stepId} - ${err}`) - return err + + // If it's a loop step, we need to manually add the bindings to the context + if (loopStep) { + this._context.steps[loopStepNumber] = { + currentItem: loopStep.inputs.binding.split(",")[index], + } + } + + let stepFn = await this.getStepFunctionality(step.stepId) + console.log(step.inputs) + + step.inputs = await processObject(step.inputs, this._context) + step.inputs = automationUtils.cleanInputValues( + step.inputs, + step.schema.inputs + ) + console.log(step.inputs) + try { + // appId is always passed + let tenantId = app.tenantId || DEFAULT_TENANT_ID + const outputs = await doInTenant(tenantId, () => { + return stepFn({ + inputs: step.inputs, + appId: this._appId, + emitter: this._emitter, + context: this._context, + }) + }) + this._context.steps[stepCount] = outputs + // if filter causes us to stop execution don't break the loop, set a var + // so that we can finish iterating through the steps and record that it stopped + if (step.stepId === FILTER_STEP_ID && !outputs.success) { + stopped = true + this.updateExecutionOutput(step.id, step.stepId, step.inputs, { + ...outputs, + ...STOPPED_STATUS, + }) + continue + } + // THE OUTPUTS GET SET IN THE CONSTRUCTOR SO WE NEED TO RESET THEM + + this.updateExecutionOutput(step.id, step.stepId, step.inputs, outputs) + console.log(this.executionOutput.input) + } catch (err) { + console.error(`Automation error - ${step.stepId} - ${err}`) + return err + } + if (index === iterations - 1) { + loopStep = null + break + } } } - // Increment quota for automation runs if (!env.SELF_HOSTED && !isDevAppID(this._appId)) { await usage.update(usage.Properties.AUTOMATION, 1) } + // make that we don't loop the next step if we have already been looping (loop block only has one step) + return this.executionOutput } }