Add handling for failure conditions

This commit is contained in:
Peter Clement 2022-04-11 10:26:59 +01:00
parent d4ff168ae4
commit 999199dcf6
5 changed files with 205 additions and 56 deletions

View File

@ -92,7 +92,7 @@
)
loopBlock.blockToLoop = block.id
block.loopBlock = loopBlock.id
automationStore.actions.addBlockToAutomation(loopBlock, blockIdx - 1)
automationStore.actions.addBlockToAutomation(loopBlock, blockIdx)
await automationStore.actions.save(
$automationStore.selectedAutomation?.automation
)
@ -131,16 +131,6 @@
</div>
<div class="blockTitle">
{#if testResult && testResult[0]}
<div style="float: right;" on:click={() => resultsModal.show()}>
<StatusLight
positive={isTrigger || testResult[0].outputs?.success}
negative={!testResult[0].outputs?.success}
><Body size="XS">View response</Body></StatusLight
>
</div>
{/if}
<div
style="margin-left: 10px;"
on:click={() => {

View File

@ -1,5 +1,5 @@
<script>
import { ModalContent, Icon, Detail, TextArea } from "@budibase/bbui"
import { ModalContent, Icon, Detail, TextArea, Label } from "@budibase/bbui"
export let testResult
export let isTrigger
@ -10,7 +10,7 @@
<ModalContent
showCloseIcon={false}
showConfirmButton={false}
title="Test Automation"
title="Test Results"
cancelText="Close"
>
<div slot="header">
@ -26,7 +26,18 @@
{/if}
</div>
</div>
<span>
{#if testResult[0].outputs.iterations}
<div style="display: flex;">
<Icon name="Reuse" />
<div style="margin-left: 10px;">
<Label>
This loop ran {testResult[0].outputs.iterations} times.</Label
>
</div>
</div>
{/if}
</span>
<div
on:click={() => {
inputToggled = !inputToggled

View File

@ -30,6 +30,7 @@
import FilterDrawer from "components/design/PropertiesPanel/PropertyControls/FilterEditor/FilterDrawer.svelte"
import { LuceneUtils } from "@budibase/frontend-core"
import { getSchemaForTable } from "builderStore/dataBinding"
import { cloneDeep } from "lodash/fp"
export let block
export let testData
@ -88,34 +89,61 @@
if (!block || !automation) {
return []
}
// Find previous steps to the selected one
let allSteps = [...automation.steps]
if (automation.trigger) {
allSteps = [automation.trigger, ...allSteps]
}
const blockIdx = allSteps.findIndex(step => step.id === block.id)
let blockIdx = allSteps.findIndex(step => step.id === block.id)
// Extract all outputs from all previous steps as available bindings
let loopBlockIdx = cloneDeep(allSteps)
.splice(0, blockIdx)
.findIndex(x => x.stepId === "LOOP")
// if a loop stepId exists in previous steps, we need to decerement the blockIdx
if (loopBlockIdx > -1 && blockIdx > loopBlockIdx) {
blockIdx--
}
// Extract all outputs from all previous steps as available bindins
let bindings = []
for (let idx = 0; idx < blockIdx; idx++) {
const outputs = Object.entries(
allSteps[idx].schema?.outputs?.properties ?? {}
)
let isLoopBlock = allSteps[idx + 1]?.blockToLoop === block.id
let schema = allSteps[idx]?.schema?.outputs?.properties ?? {}
if (isLoopBlock) {
schema = {
currentItem: {
type: "string",
description: "the item currently being executed",
},
}
}
if (loopBlockIdx && allSteps[blockIdx - 1]?.stepId === "LOOP") {
schema = {
...schema,
...$automationStore.blockDefinitions.ACTION.LOOP.schema.outputs
.properties.properties,
}
}
const outputs = Object.entries(schema)
bindings = bindings.concat(
outputs.map(([name, value]) => {
let runtimeName =
$automationStore.selectedAutomation.automation.definition.steps.find(
x => block.id === x.blockToLoop
)
? `loop.${name}`
: `steps.${idx}.${name}`
let runtimeName = isLoopBlock
? `loop.${name}`
: `steps.${idx}.${name}`
const runtime = idx === 0 ? `trigger.${name}` : runtimeName
return {
label: runtime,
type: value.type,
description: value.description,
category: idx === 0 ? "Trigger outputs" : `Step ${idx} outputs`,
category:
idx === 0
? "Trigger outputs"
: isLoopBlock
? "Loop Outputs"
: `Step ${idx} outputs`,
path: runtime,
}
})

View File

@ -48,8 +48,3 @@ exports.definition = {
},
type: "LOGIC",
}
exports.run = async function filter({ inputs }) {
let currentItem = inputs.binding
return { currentItem }
}

View File

@ -86,26 +86,149 @@ class Orchestrator {
let stepCount = 0
let loopStepNumber
let loopSteps = []
let lastLoopStep
for (let step of automation.definition.steps) {
stepCount++
let input
if (step.stepId === LOOP_STEP_ID) {
loopStep = step
loopStepNumber = stepCount
continue
}
let iterations = loopStep ? loopStep.inputs.binding.split(",").length : 1
if (loopStep) {
input = await processObject(loopStep.inputs, this._context)
}
let iterations = loopStep ? input.binding.length : 1
let iterationCount = 0
for (let index = 0; index < iterations; index++) {
let originalStepInput = cloneDeep(step.inputs)
/*
if (step.stepId === LOOP_STEP_ID && index >= loopStep.inputs.iterations) {
this.executionOutput.steps[loopStepNumber].outputs.status = "Loop Broken"
break
// Handle if the user has set a max iteration count or if it reaches the max limit set by us
if (loopStep) {
// lets first of all handle the input
// if the input is array then use it, if it is a string then split it on every new line
let newInput = await processObject(
loopStep.inputs,
cloneDeep(this._context)
)
newInput = automationUtils.cleanInputValues(
newInput,
loopStep.schema.inputs
)
this._context.steps[loopStepNumber] = {
currentItem: newInput.binding[index],
}
let tempOutput = { items: loopSteps, iterations: iterationCount }
if (
loopStep.inputs.option === "Array" &&
!Array.isArray(newInput.binding)
) {
this.executionOutput.steps.splice(loopStepNumber, 0, {
id: step.id,
stepId: step.stepId,
outputs: {
...tempOutput,
success: true,
status: "INCORRECT_TYPE",
},
inputs: step.inputs,
})
this._context.steps.splice(loopStepNumber, 0, {
...tempOutput,
success: true,
status: "INCORRECT_TYPE",
})
loopSteps = null
loopStep = null
break
} else if (
loopStep.inputs.option === "String" &&
typeof newInput.binding !== "string"
) {
this.executionOutput.steps.splice(loopStepNumber, 0, {
id: step.id,
stepId: step.stepId,
outputs: {
...tempOutput,
success: false,
status: "INCORRECT_TYPE",
},
inputs: step.inputs,
})
this._context.steps.splice(loopStepNumber, 0, {
...tempOutput,
success: true,
status: "INCORRECT_TYPE",
})
loopSteps = null
loopStep = null
break
}
// The "Loop" binding in the front end is "fake", so replace it here so the context can understand it
for (let key in originalStepInput) {
if (key === "row") {
for (let test in originalStepInput["row"]) {
originalStepInput["row"][test] = originalStepInput["row"][
test
].replace(/loop/, `steps.${loopStepNumber}`)
}
} else {
originalStepInput[key] = originalStepInput[key].replace(
/loop/,
`steps.${loopStepNumber}`
)
}
}
if (index >= loopStep.inputs.iterations) {
this.executionOutput.steps.splice(loopStepNumber, 0, {
id: step.id,
stepId: step.stepId,
outputs: { ...tempOutput, success: true, status: "LOOP_BROKEN" },
inputs: step.inputs,
})
this._context.steps.splice(loopStepNumber, 0, {
...tempOutput,
success: true,
status: "LOOP_BROKEN",
})
loopSteps = null
loopStep = null
break
}
if (
this._context.steps[loopStepNumber]?.currentItem ===
loopStep.inputs.failure
) {
console.log("hello?????")
this.executionOutput.steps.splice(loopStepNumber, 0, {
id: step.id,
stepId: step.stepId,
outputs: {
...tempOutput,
success: false,
status: "FAILURE_CONDITION_MET",
},
inputs: step.inputs,
})
this._context.steps.splice(loopStepNumber, 0, {
...tempOutput,
success: false,
status: "FAILURE_CONDITION_MET",
})
loopSteps = null
loopStep = null
break
}
}
*/
// execution stopped, record state for that
if (stopped) {
this.updateExecutionOutput(step.id, step.stepId, {}, STOPPED_STATUS)
@ -113,11 +236,6 @@ class Orchestrator {
}
// 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)
let inputs = await processObject(originalStepInput, this._context)
inputs = automationUtils.cleanInputValues(inputs, step.schema.inputs)
@ -143,7 +261,6 @@ class Orchestrator {
})
continue
}
this._context.steps.splice(loopStepNumber, 1)
if (loopStep && loopSteps) {
loopSteps.push(outputs)
} else {
@ -158,26 +275,34 @@ class Orchestrator {
console.error(`Automation error - ${step.stepId} - ${err}`)
return err
}
if (index === iterations - 1) {
lastLoopStep = loopStep
loopStep = null
break
if (loopStep) {
iterationCount++
if (index === iterations - 1) {
loopStep = null
this._context.steps.splice(loopStepNumber, 1)
break
}
}
}
if (loopSteps && loopSteps.length) {
let tempOutput = { success: true, outputs: loopSteps }
this.executionOutput.steps.splice(loopStep, 0, {
id: lastLoopStep.id,
stepId: lastLoopStep.stepId,
let tempOutput = {
success: true,
items: loopSteps,
iterations: iterationCount,
}
this.executionOutput.steps.splice(loopStepNumber + 1, 0, {
id: step.id,
stepId: step.stepId,
outputs: tempOutput,
inputs: step.inputs,
})
this._context.steps.splice(loopStep, 0, tempOutput)
this._context.steps.splice(loopStepNumber, 0, tempOutput)
loopSteps = null
}
}
return this.executionOutput
}
}