Skip to content

Commit 58e7257

Browse files
committed
stacks: update build_stack_graph to use new endpoint (bug 1787516)
- use updated response from differential.revision.search endpoint - translate `stackGraph` field to phids and edges - update `tests.mocks` such that revision response includes new field
1 parent c1be7fe commit 58e7257

File tree

2 files changed

+65
-34
lines changed

2 files changed

+65
-34
lines changed

landoapi/stacks.py

Lines changed: 14 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,6 @@ def build_stack_graph(
2727
) -> Tuple[Set[str], Set[Tuple[str, str]]]:
2828
"""Return a graph representation of a revision stack.
2929
30-
This function is expensive and can make up to approximately
31-
n/2 calls to phabricator for a linear stack where n is the
32-
number of revisions in the stack.
33-
3430
Args:
3531
phab: A landoapi.phabricator.PhabricatorClient.
3632
revision_phid: String PHID of a revision in the stack.
@@ -42,35 +38,20 @@ def build_stack_graph(
4238
between two nodes. `child` and `parent` are also string
4339
PHIDs.
4440
"""
45-
phids = set()
46-
new_phids = {revision_phid}
47-
edges = []
48-
49-
# Repeatedly request all related edges, adding connected revisions
50-
# each time until no new revisions are found.
51-
while new_phids:
52-
phids.update(new_phids)
53-
edges = phab.call_conduit(
54-
"edge.search",
55-
types=["revision.parent", "revision.child"],
56-
sourcePHIDs=[phid for phid in phids],
57-
limit=10000,
58-
)
59-
edges = phab.expect(edges, "data")
60-
new_phids = set()
61-
for edge in edges:
62-
new_phids.add(edge["sourcePHID"])
63-
new_phids.add(edge["destinationPHID"])
64-
65-
new_phids = new_phids - phids
66-
67-
# Treat the stack like a commit DAG, we only care about edges going
68-
# from child to parent. This is enough to represent the graph.
69-
edges = {
70-
(edge["sourcePHID"], edge["destinationPHID"])
71-
for edge in edges
72-
if edge["edgeType"] == "revision.parent"
73-
}
41+
result = phab.call_conduit(
42+
"differential.revision.search",
43+
constraints={"phids": [revision_phid]},
44+
limit=1,
45+
)
46+
47+
revision = PhabricatorClient.single(result, "data")
48+
stack_graph = PhabricatorClient.expect(revision, "fields", "stackGraph")
49+
phids = set(stack_graph.keys())
50+
edges = set()
51+
52+
for source_node, destination_nodes in stack_graph.items():
53+
for predecessor_node in destination_nodes:
54+
edges.add((source_node, predecessor_node))
7455

7556
return phids, edges
7657

tests/mocks.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import hashlib
55
import json
66
from copy import deepcopy
7+
from collections import defaultdict
78

89
from landoapi.phabricator import (
910
PhabricatorAPIException,
@@ -96,6 +97,51 @@ def validate_change(change):
9697
return True
9798

9899

100+
def get_stack(_phid, phabdouble):
101+
phids = set()
102+
new_phids = {_phid}
103+
edges = []
104+
105+
# Repeatedly request all related edges, adding connected revisions
106+
# each time until no new revisions are found.
107+
# NOTE: this was adapted from previous implementation of build_stack_graph.
108+
while new_phids:
109+
phids.update(new_phids)
110+
edges = [
111+
e
112+
for e in phabdouble._edges
113+
if e["sourcePHID"] in phids
114+
and e["edgeType"] in ("revision.parent", "revision.child")
115+
]
116+
new_phids = set()
117+
for edge in edges:
118+
new_phids.add(edge["sourcePHID"])
119+
new_phids.add(edge["destinationPHID"])
120+
121+
new_phids = new_phids - phids
122+
123+
# Treat the stack like a commit DAG, we only care about edges going
124+
# from child to parent. This is enough to represent the graph.
125+
edges = {
126+
(edge["sourcePHID"], edge["destinationPHID"])
127+
for edge in edges
128+
if edge["edgeType"] == "revision.parent"
129+
}
130+
131+
stack_graph = defaultdict(list)
132+
sources = [edge[0] for edge in edges]
133+
for source, dest in edges:
134+
# Check that destination phid has a corresponding source phid.
135+
if dest not in sources:
136+
# We are at a root node.
137+
stack_graph[dest] = []
138+
stack_graph[source].append(dest)
139+
if not stack_graph:
140+
# There is only one node, the root node.
141+
stack_graph[_phid] = []
142+
return dict(stack_graph)
143+
144+
99145
class PhabricatorDouble:
100146
"""Phabricator test double.
101147
@@ -898,6 +944,7 @@ def to_response(i):
898944
"fields": {
899945
"title": i["title"],
900946
"authorPHID": i["authorPHID"],
947+
"stackGraph": i["stack_graph"],
901948
"status": {
902949
"value": i["status"].value,
903950
"name": i["status"].output_name,
@@ -948,7 +995,10 @@ def to_response(i):
948995

949996
return deepcopy(resp)
950997

951-
items = [r for r in self._revisions]
998+
items = []
999+
for r in self._revisions:
1000+
r["stack_graph"] = get_stack(r["phid"], self)
1001+
items.append(r)
9521002

9531003
if constraints and "ids" in constraints:
9541004
items = [i for i in items if i["id"] in constraints["ids"]]

0 commit comments

Comments
 (0)