loopSource: equals()

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: {