Merge pull request #5817 from Budibase/fix/loop-hbs-usage

Fix HBS usage in automation looping action
This commit is contained in:
Michael Drury 2022-05-11 12:40:58 +01:00 committed by GitHub
commit f572afeb08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 54 additions and 31 deletions

View File

@ -208,5 +208,10 @@ exports.AutomationErrors = {
FAILURE_CONDITION: "FAILURE_CONDITION_MET", FAILURE_CONDITION: "FAILURE_CONDITION_MET",
} }
exports.LoopStepTypes = {
ARRAY: "Array",
STRING: "String",
}
// pass through the list from the auth/core lib // pass through the list from the auth/core lib
exports.ObjectStoreBuckets = ObjectStoreBuckets exports.ObjectStoreBuckets = ObjectStoreBuckets

View File

@ -8,7 +8,7 @@ const { DocumentTypes } = require("../db/utils")
const { doInTenant } = require("@budibase/backend-core/tenancy") const { doInTenant } = require("@budibase/backend-core/tenancy")
const { definitions: triggerDefs } = require("../automations/triggerInfo") const { definitions: triggerDefs } = require("../automations/triggerInfo")
const { doInAppContext, getAppDB } = require("@budibase/backend-core/context") const { doInAppContext, getAppDB } = require("@budibase/backend-core/context")
const { AutomationErrors } = require("../constants") const { AutomationErrors, LoopStepTypes } = require("../constants")
const FILTER_STEP_ID = actions.ACTION_DEFINITIONS.FILTER.stepId const FILTER_STEP_ID = actions.ACTION_DEFINITIONS.FILTER.stepId
const LOOP_STEP_ID = actions.ACTION_DEFINITIONS.LOOP.stepId const LOOP_STEP_ID = actions.ACTION_DEFINITIONS.LOOP.stepId
@ -17,6 +17,41 @@ const STOPPED_STATUS = { success: false, status: "STOPPED" }
const { cloneDeep } = require("lodash/fp") const { cloneDeep } = require("lodash/fp")
const env = require("../environment") const env = require("../environment")
function typecastForLooping(loopStep, input) {
if (!input || !input.binding) {
return null
}
const isArray = Array.isArray(input.binding),
isString = typeof input.binding === "string"
try {
switch (loopStep.inputs.option) {
case LoopStepTypes.ARRAY:
if (isString) {
return JSON.parse(input.binding)
}
break
case LoopStepTypes.STRING:
if (isArray) {
return input.binding.join(",")
}
break
}
} catch (err) {
throw new Error("Unable to cast to correct type")
}
return input.binding
}
function getLoopIterations(loopStep, input) {
const binding = typecastForLooping(loopStep, input)
if (!loopStep || !binding) {
return 1
}
return Array.isArray(binding)
? binding.length
: automationUtils.stringSplit(binding).length
}
/** /**
* The automation orchestrator is a class responsible for executing automations. * The automation orchestrator is a class responsible for executing automations.
* It handles the context of the automation and makes sure each step gets the correct * It handles the context of the automation and makes sure each step gets the correct
@ -107,7 +142,9 @@ class Orchestrator {
let loopSteps = [] let loopSteps = []
for (let step of automation.definition.steps) { for (let step of automation.definition.steps) {
stepCount++ stepCount++
let input let input,
iterations = 1,
iterationCount = 0
if (step.stepId === LOOP_STEP_ID) { if (step.stepId === LOOP_STEP_ID) {
loopStep = step loopStep = step
loopStepNumber = stepCount loopStepNumber = stepCount
@ -116,13 +153,9 @@ class Orchestrator {
if (loopStep) { if (loopStep) {
input = await processObject(loopStep.inputs, this._context) input = await processObject(loopStep.inputs, this._context)
iterations = getLoopIterations(loopStep, input)
} }
let iterations = loopStep
? Array.isArray(input.binding)
? input.binding.length
: automationUtils.stringSplit(input.binding).length
: 1
let iterationCount = 0
for (let index = 0; index < iterations; index++) { for (let index = 0; index < iterations; index++) {
let originalStepInput = cloneDeep(step.inputs) let originalStepInput = cloneDeep(step.inputs)
@ -132,18 +165,11 @@ class Orchestrator {
loopStep.inputs, loopStep.inputs,
cloneDeep(this._context) cloneDeep(this._context)
) )
newInput = automationUtils.cleanInputValues(
newInput,
loopStep.schema.inputs
)
let tempOutput = { items: loopSteps, iterations: iterationCount } let tempOutput = { items: loopSteps, iterations: iterationCount }
if ( try {
(loopStep.inputs.option === "Array" && newInput.binding = typecastForLooping(loopStep, newInput)
!Array.isArray(newInput.binding)) || } catch (err) {
(loopStep.inputs.option === "String" &&
typeof newInput.binding !== "string")
) {
this.updateContextAndOutput(loopStepNumber, step, tempOutput, { this.updateContextAndOutput(loopStepNumber, step, tempOutput, {
status: AutomationErrors.INCORRECT_TYPE, status: AutomationErrors.INCORRECT_TYPE,
success: false, success: false,
@ -205,21 +231,13 @@ class Orchestrator {
} }
let isFailure = false let isFailure = false
if ( const currentItem = this._context.steps[loopStepNumber]?.currentItem
typeof this._context.steps[loopStepNumber]?.currentItem === "object" if (currentItem && typeof currentItem === "object") {
) { isFailure = Object.keys(currentItem).some(value => {
isFailure = Object.keys( return currentItem[value] === loopStep.inputs.failure
this._context.steps[loopStepNumber].currentItem
).some(value => {
return (
this._context.steps[loopStepNumber].currentItem[value] ===
loopStep.inputs.failure
)
}) })
} else { } else {
isFailure = isFailure = currentItem && currentItem === loopStep.inputs.failure
this._context.steps[loopStepNumber]?.currentItem ===
loopStep.inputs.failure
} }
if (isFailure) { if (isFailure) {