def METHOD_NAME(
    env: Dict[CollectionAddress, "GraphTask"], end_nodes: List[CollectionAddress]
) -> GraphRepr:
    """
    Builds a representation of the current graph built for a privacy request (that includes its edges)
    for caching in Redis.
    Requires the results of traversal.traverse():
        - the modified `env`
        - and the outputted end_nodes, which are the final nodes without children
    Maps collections to their upstream dependencies and associated edges. The root is stored as having
    no upstream collections and the terminator collection has no incoming edges.
    Example:
    {
       <collection>: {
          <upstream collection>: [edge between upstream and current],
          <another upstream collection>: [edge between another upstream and current]
       },
       <root collection>: {},
       <terminator collection>: {
            <end collection>: [],
            <end collection>: []
       }
    }
    """
    graph_repr: GraphRepr = {
        collection.value: {
            upstream_collection_address.value: [str(edge) for edge in edge_list]
            for upstream_collection_address, edge_list in g_task.incoming_edges_by_collection.items()
        }
        for collection, g_task in env.items()
    }
    graph_repr[ROOT_COLLECTION_ADDRESS.value] = {}
    graph_repr[TERMINATOR_ADDRESS.value] = {
        end_node.value: [] for end_node in end_nodes
    }
    return graph_repr