diff --git a/packages/backend/src/helpers/compute-parameters.js b/packages/backend/src/helpers/compute-parameters.js index d5a5ade0..2125e70d 100644 --- a/packages/backend/src/helpers/compute-parameters.js +++ b/packages/backend/src/helpers/compute-parameters.js @@ -2,67 +2,137 @@ import get from 'lodash.get'; const variableRegExp = /({{step\.[\da-zA-Z-]+(?:\.[^.}{]+)+}})/g; -export default function computeParameters(parameters, executionSteps) { - const entries = Object.entries(parameters); - return entries.reduce((result, [key, value]) => { - if (typeof value === 'string') { - const parts = value.split(variableRegExp); +function getParameterEntries(parameters) { + return Object.entries(parameters); +} - const computedValue = parts - .map((part) => { - const isVariable = part.match(variableRegExp); - - if (isVariable) { - const stepIdAndKeyPath = part.replace(/{{step.|}}/g, ''); - const [stepId, ...keyPaths] = stepIdAndKeyPath.split('.'); - const keyPath = keyPaths.join('.'); - const executionStep = executionSteps.find((executionStep) => { - return executionStep.stepId === stepId; - }); - const data = executionStep?.dataOut; - const dataValue = get(data, keyPath); - - // Covers both arrays and objects - if (typeof dataValue === 'object') { - return JSON.stringify(dataValue); - } - - return dataValue; - } - - return part; - }).join(''); - - // challenge the input to see if it is stringifies object or array - try { - const parsedValue = JSON.parse(computedValue); - - if (typeof parsedValue === 'number') { - throw new Error('Use original unparsed value.'); - } - - return { - ...result, - [key]: parsedValue, - }; - } catch (error) { - return { - ...result, - [key]: computedValue, - }; - } - } - - if (Array.isArray(value)) { - return { - ...result, - [key]: value.map((item) => computeParameters(item, executionSteps)), - }; - } +function computeParameterEntries(parameterEntries, executionSteps) { + const defaultComputedParameters = {}; + return parameterEntries.reduce((result, [key, value]) => { + const parameterComputedValue = computeParameter(value, executionSteps); return { ...result, - [key]: value, - }; - }, {}); + [key]: parameterComputedValue, + } + }, defaultComputedParameters); +} + +function computeParameter(value, executionSteps) { + if (typeof value === 'string') { + const computedStringParameter = computeStringParameter(value, executionSteps); + return computedStringParameter; + } + + if (Array.isArray(value)) { + const computedArrayParameter = computeArrayParameter(value, executionSteps); + return computedArrayParameter; + } + + return value; +} + +function splitByVariable(stringValue) { + const parts = stringValue.split(variableRegExp); + + return parts; +} + +function isVariable(stringValue) { + return stringValue.match(variableRegExp); +} + +function splitVariableByStepIdAndKeyPath(variableValue) { + const stepIdAndKeyPath = variableValue.replace(/{{step.|}}/g, ''); + const [stepId, ...keyPaths] = stepIdAndKeyPath.split('.'); + const keyPath = keyPaths.join('.'); + + return { + stepId, + keyPath + } +} + +function getVariableStepId(variableValue) { + const { stepId } = splitVariableByStepIdAndKeyPath(variableValue); + + return stepId; +} + +function getVariableKeyPath(variableValue) { + const { keyPath } = splitVariableByStepIdAndKeyPath(variableValue); + + return keyPath +} + +function getVariableExecutionStep(variableValue, executionSteps) { + const stepId = getVariableStepId(variableValue); + + const executionStep = executionSteps.find((executionStep) => { + return executionStep.stepId === stepId; + }); + + return executionStep; +} + +function computeVariable(variable, executionSteps) { + const keyPath = getVariableKeyPath(variable); + const executionStep = getVariableExecutionStep(variable, executionSteps); + const data = executionStep?.dataOut; + const computedVariable = get(data, keyPath); + + /** + * Inline both arrays and objects. Otherwise, variables resolving to + * them would be resolved as `[object Object]` or lose their shape. + */ + if (typeof computedVariable === 'object') { + return JSON.stringify(computedVariable); + } + + return computedVariable; +} + +function autoParseComputedVariable(computedVariable) { + // challenge the input to see if it is stringified object or array + try { + const parsedValue = JSON.parse(computedVariable); + + if (typeof parsedValue === 'number') { + throw new Error('Use original unparsed value.'); + } + + return parsedValue; + } catch (error) { + return computedVariable; + } +} + +function computeStringParameter(stringValue, executionSteps) { + const parts = splitByVariable(stringValue); + + const computedValue = parts + .map((part) => { + const variable = isVariable(part); + + if (variable) { + return computeVariable(part, executionSteps); + } + + return part; + }) + .join(''); + + const autoParsedValue = autoParseComputedVariable(computedValue); + + return autoParsedValue; +} + +function computeArrayParameter(arrayValue, executionSteps) { + return arrayValue.map((item) => computeParameters(item, executionSteps)); +} + +export default function computeParameters(parameters, executionSteps) { + const parameterEntries = getParameterEntries(parameters); + + return computeParameterEntries(parameterEntries, executionSteps); }