Return a dag representation of the nodes passed.

    This is equally used for pipelines and pipeline runs.

    Params:
        nodes: an instance of `Operation` | `OperationRun` the nodes to represent en dag.
        downstream_fn: a function that returns the downstream nodes of the a node.

    Returns:
         tuple: (dag, dict(node_id: node))

def get_dag(nodes, downstream_fn) -> Tuple[Dict, Dict]:
    """Return a dag representation of the nodes passed.

    This is equally used for pipelines and pipeline runs.

    Params:
        nodes: an instance of `Operation` | `OperationRun` the nodes to represent en dag.
        downstream_fn: a function that returns the downstream nodes of the a node.

    Returns:
         tuple: (dag, dict(node_id: node))
    """
    dag = {}
    node_by_ids = {}
    for node in nodes:
        downstream_ops = downstream_fn(node)
        dag[node.id] = set(downstream_ops)
        node_by_ids[node.id] = node

    return dag, node_by_ids