diff --git a/packages/server/src/constants/index.js b/packages/server/src/constants/index.js index 60f3c981d6..3aa5b2fb7b 100644 --- a/packages/server/src/constants/index.js +++ b/packages/server/src/constants/index.js @@ -208,5 +208,10 @@ exports.AutomationErrors = { FAILURE_CONDITION: "FAILURE_CONDITION_MET", } +exports.LoopStepTypes = { + ARRAY: "Array", + STRING: "String", +} + // pass through the list from the auth/core lib exports.ObjectStoreBuckets = ObjectStoreBuckets diff --git a/packages/server/src/threads/automation.js b/packages/server/src/threads/automation.js index 98c45e4af3..4ca490affd 100644 --- a/packages/server/src/threads/automation.js +++ b/packages/server/src/threads/automation.js @@ -8,7 +8,7 @@ const { DocumentTypes } = require("../db/utils") const { doInTenant } = require("@budibase/backend-core/tenancy") const { definitions: triggerDefs } = require("../automations/triggerInfo") 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 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 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. * It handles the context of the automation and makes sure each step gets the correct @@ -107,7 +142,9 @@ class Orchestrator { let loopSteps = [] for (let step of automation.definition.steps) { stepCount++ - let input + let input, + iterations = 1, + iterationCount = 0 if (step.stepId === LOOP_STEP_ID) { loopStep = step loopStepNumber = stepCount @@ -116,13 +153,9 @@ class Orchestrator { if (loopStep) { 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++) { let originalStepInput = cloneDeep(step.inputs) @@ -132,18 +165,11 @@ class Orchestrator { loopStep.inputs, cloneDeep(this._context) ) - newInput = automationUtils.cleanInputValues( - newInput, - loopStep.schema.inputs - ) let tempOutput = { items: loopSteps, iterations: iterationCount } - if ( - (loopStep.inputs.option === "Array" && - !Array.isArray(newInput.binding)) || - (loopStep.inputs.option === "String" && - typeof newInput.binding !== "string") - ) { + try { + newInput.binding = typecastForLooping(loopStep, newInput) + } catch (err) { this.updateContextAndOutput(loopStepNumber, step, tempOutput, { status: AutomationErrors.INCORRECT_TYPE, success: false, @@ -205,21 +231,13 @@ class Orchestrator { } let isFailure = false - if ( - typeof this._context.steps[loopStepNumber]?.currentItem === "object" - ) { - isFailure = Object.keys( - this._context.steps[loopStepNumber].currentItem - ).some(value => { - return ( - this._context.steps[loopStepNumber].currentItem[value] === - loopStep.inputs.failure - ) + const currentItem = this._context.steps[loopStepNumber]?.currentItem + if (currentItem && typeof currentItem === "object") { + isFailure = Object.keys(currentItem).some(value => { + return currentItem[value] === loopStep.inputs.failure }) } else { - isFailure = - this._context.steps[loopStepNumber]?.currentItem === - loopStep.inputs.failure + isFailure = currentItem && currentItem === loopStep.inputs.failure } if (isFailure) { diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index f9b414118f..dd402c4361 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -1014,10 +1014,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@1.0.147": - version "1.0.147" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.147.tgz#fc93a803e97e304170b33bd28b4f5deda32bec18" - integrity sha512-0GcF9G/tTAsko9g352MB8K3EtMTVb7v7Av6RdsYyKBdVtOD42HKFzSOD0n/RQUL4v70YByiu99zJAB6z1m1Pcg== +"@budibase/backend-core@1.0.150": + version "1.0.150" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.150.tgz#a48beb1e38f5e3e09473235fd2b7f35a05a15342" + integrity sha512-ceQVPnypKFurQMJgghky+MxtiF3x4b7rIzYhXv6Y+QfIb7IdY6wvv9o+dD3GPwNZJCRFSFizPR4YLBXdL+2+yw== dependencies: "@techpass/passport-openidconnect" "^0.3.0" aws-sdk "^2.901.0" @@ -1091,12 +1091,12 @@ svelte-flatpickr "^3.2.3" svelte-portal "^1.0.0" -"@budibase/pro@1.0.147": - version "1.0.147" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.147.tgz#c1ec9d92ffaa40285a83c57ddbec1f32802cd9a1" - integrity sha512-e4im6Byqeeit/QFHkVhVdRmgIlJJY/W06cQrOuiG/1ngO4PGnXeJqtjQjHVfvchD5Xi3y1MgQ4AtH2Bzb5LEhw== +"@budibase/pro@1.0.150": + version "1.0.150" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.150.tgz#06384561b67130bb2a93f0119921bd35d2be245c" + integrity sha512-Wlam8b5nhSeuNEj5Bb3ro1DKGX81mxxsjKMmxT4rbTqZrzuHSlTkxJ59IvUQqKuyp+cqfP6Rq/i4UXrhK0cg9Q== dependencies: - "@budibase/backend-core" "1.0.147" + "@budibase/backend-core" "1.0.150" node-fetch "^2.6.1" "@budibase/standard-components@^0.9.139": diff --git a/packages/worker/yarn.lock b/packages/worker/yarn.lock index 80f2996bec..a58efbdbcb 100644 --- a/packages/worker/yarn.lock +++ b/packages/worker/yarn.lock @@ -293,10 +293,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@1.0.147": - version "1.0.147" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.147.tgz#fc93a803e97e304170b33bd28b4f5deda32bec18" - integrity sha512-0GcF9G/tTAsko9g352MB8K3EtMTVb7v7Av6RdsYyKBdVtOD42HKFzSOD0n/RQUL4v70YByiu99zJAB6z1m1Pcg== +"@budibase/backend-core@1.0.150": + version "1.0.150" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.150.tgz#a48beb1e38f5e3e09473235fd2b7f35a05a15342" + integrity sha512-ceQVPnypKFurQMJgghky+MxtiF3x4b7rIzYhXv6Y+QfIb7IdY6wvv9o+dD3GPwNZJCRFSFizPR4YLBXdL+2+yw== dependencies: "@techpass/passport-openidconnect" "^0.3.0" aws-sdk "^2.901.0" @@ -321,12 +321,12 @@ uuid "^8.3.2" zlib "^1.0.5" -"@budibase/pro@1.0.147": - version "1.0.147" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.147.tgz#c1ec9d92ffaa40285a83c57ddbec1f32802cd9a1" - integrity sha512-e4im6Byqeeit/QFHkVhVdRmgIlJJY/W06cQrOuiG/1ngO4PGnXeJqtjQjHVfvchD5Xi3y1MgQ4AtH2Bzb5LEhw== +"@budibase/pro@1.0.150": + version "1.0.150" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.150.tgz#06384561b67130bb2a93f0119921bd35d2be245c" + integrity sha512-Wlam8b5nhSeuNEj5Bb3ro1DKGX81mxxsjKMmxT4rbTqZrzuHSlTkxJ59IvUQqKuyp+cqfP6Rq/i4UXrhK0cg9Q== dependencies: - "@budibase/backend-core" "1.0.147" + "@budibase/backend-core" "1.0.150" node-fetch "^2.6.1" "@cspotcode/source-map-consumer@0.8.0":