in libs/designer/src/lib/core/utils/loops.ts [384:551]
loopSource: equals(repetitionReference.actionType, Constants.NODE.TYPE.FOREACH) ? repetitionReference.actionName : undefined,
};
}
}
}
return {
expression: areOutputsManifestBased
? `@${getTokenExpressionValueForManifestBasedOperation(
parentArrayTokenInfo.key,
!!parentArrayTokenInfo.arrayDetails,
parentArrayTokenInfo.arrayDetails?.loopSource,
tokenOwnerActionName,
!!parentArrayTokenInfo.required
)}`
: `@${getTokenExpressionValue(parentArrayTokenInfo)}`,
output: parentArrayOutput,
token: parentArrayTokenInfo,
};
};
const checkArrayInRepetition = (
actionName: string | undefined,
repetitionValue: string,
tokenKey: string | undefined,
tokenValue: string | undefined,
outputInfo: OutputInfo | undefined,
areOutputsManifestBased: boolean
): boolean => {
if (equals(repetitionValue, tokenValue)) {
return true;
}
if (areOutputsManifestBased && tokenKey) {
const method = getTokenExpressionMethodFromKey(tokenKey, actionName, outputInfo?.source);
const sanitizedValue = `@${generateExpressionFromKey(
method,
tokenKey,
actionName,
!!outputInfo?.isInsideArray,
!!outputInfo?.required
)}`;
return equals(repetitionValue, sanitizedValue);
}
return false;
};
// Directly checking the node type, because cannot make async calls while adding token from picker to editor.
// TODO - See if this can be made async and looked at manifest.
export const isLoopingNode = (nodeId: string, operationInfos: Record<string, NodeOperation>, ignoreUntil: boolean): boolean => {
const nodeType = getRecordEntry(operationInfos, nodeId)?.type;
return equals(nodeType, Constants.NODE.TYPE.FOREACH) || (!ignoreUntil && equals(nodeType, Constants.NODE.TYPE.UNTIL));
};
export const getForeachActionName = (
context: RepetitionContext,
foreachExpressionPath: string,
repetitionStep: string | undefined
): string | undefined => {
const sanitizedPath = sanitizeKey(foreachExpressionPath);
const foreachAction = first(
(item) =>
equals(item.actionType, Constants.NODE.TYPE.FOREACH) &&
equals(normalizeKeyPath(item.repetitionPath), normalizeKeyPath(sanitizedPath)) &&
item.repetitionStep === repetitionStep,
context.repetitionReferences
);
return foreachAction?.actionName;
};
export const isForeachActionNameForLoopsource = (
nodeId: string,
expression: string,
nodes: Record<string, Partial<NodeDataWithOperationMetadata>>,
operations: Operations,
nodesMetadata: NodesMetadata
): boolean => {
const operationInfos: Record<string, NodeOperation> = {};
for (const operationId of Object.keys(operations)) {
operationInfos[operationId] = {
type: operations[operationId]?.type,
kind: operations[operationId]?.kind,
connectorId: '',
operationId: '',
};
}
const repetitionNodeIds = getRepetitionNodeIds(nodeId, nodesMetadata, operationInfos);
const sanitizedPath = sanitizeKey(expression);
const foreachAction = first((item) => {
const operation = operationInfos[item];
const parameter = nodes[item]?.nodeInputs ? getParameterFromName(nodes[item].nodeInputs as NodeInputs, 'foreach') : undefined;
const foreachValue =
operation && (operation as any).foreach
? (operation as any).foreach
: parameter && parameter.value.length === 1 && parameter.value[0].token?.key;
return equals(operation?.type, Constants.NODE.TYPE.FOREACH) && equals(foreachValue, sanitizedPath);
}, repetitionNodeIds);
return !!foreachAction;
};
const getRepetitionReference = async (
nodeId: string,
operationInfos: Record<string, NodeOperation>,
allInputs: Record<string, NodeInputs>,
idReplacements?: Record<string, string>
): Promise<RepetitionReference | undefined> => {
const operationInfo = getRecordEntry(operationInfos, nodeId);
if (!operationInfo) {
return undefined;
}
const service = OperationManifestService();
if (service.isSupported(operationInfo.type, operationInfo.kind)) {
const manifest = await getOperationManifest(operationInfo);
const parameterName = manifest.properties.repetition?.loopParameter;
const nodeInputs = getRecordEntry(allInputs, nodeId) ?? { parameterGroups: {} };
const parameter = parameterName ? getParameterFromName(nodeInputs, parameterName) : undefined;
if (parameter) {
const repetitionValue = getJSONValueFromString(
parameterValueToString(
parameter,
/* isDefinitionValue */ true,
idReplacements,
shouldEncodeParameterValueForOperationBasedOnMetadata(operationInfo)
),
parameter.type
);
return {
actionName: nodeId,
actionType: operationInfo.type,
repetitionValue,
};
}
}
return undefined;
};
interface Foreach {
step?: string;
path?: string;
fullPath?: string;
}
export const parseForeach = (repetitionValue: string, repetitionContext: RepetitionContext): Foreach => {
const foreach: Foreach = {};
if (repetitionValue) {
let foreachExpression: Expression | undefined;
let splitOnExpression: Expression | undefined;
try {
foreachExpression = ExpressionParser.parseTemplateExpression(repetitionValue);
} catch {
// for invalid case, we just ignore it and return empty object
}
if (repetitionContext.splitOn) {
try {
splitOnExpression = ExpressionParser.parseTemplateExpression(repetitionContext.splitOn);
} catch {
// for invalid case, we just ignore it
}
}
if (foreachExpression) {
switch (foreachExpression.type) {
case ExpressionType.Function: {