From 0609201af4cd1559c5d5f1992d0f98696256867d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dandelion=20Man=C3=A9?=
Date: Mon, 30 Apr 2018 15:32:28 -0700
Subject: [PATCH] Remove Graph.{in,out}Edges (#174)
This method removes `Graph.inEdges` and `Graph.outEdges`. As a
replacement to these two functions, `graph.neighborhood` now takes an
optional `direction` flag, which can be set to `"IN" | "OUT" | "ANY"`.
This reduces the surface area of the Graph API, and means that the same
pattern can be used when requesting in or out neighbors as is used when
requesting all neighbors.
This change generates significant churn in the test files, and in some
cases the tests are less elegant / show historicity, as they were
written for the type signature of `{in,out}Edges`, which just returns an
array of edges, and now receive an array of neighbors. I think this is
acceptable, and it's not worth re-writing the test.
In many cases, replacing existing calls to `{in,out}Edges` in our actual
codebase resulted in cleaner code, as `neighborhood` successfully
abstracts over the common patterns that users of `{in,out}Edges` were
implementing.
As a fly-by refactor, I also changed the `neighborAddress` part of the
`neighborhood` return value to `neighbor`. It's a little less
descriptive, but it's more concise, and flow is there to help ensure
it's used correctly.
Test plan: Note that CI passes. Inspect the test changes, and verify
that they are appropriate transformations for consuming the new API.
---
src/core/graph.js | 126 ++++++------
src/core/graph.test.js | 183 +++++++++++-------
.../artifact/editor/ContributionList.test.js | 4 +-
.../editor/adapters/githubPluginAdapter.js | 11 +-
src/plugins/git/createGraph.test.js | 29 ++-
src/plugins/github/api.js | 4 +-
src/plugins/github/parser.test.js | 28 +--
7 files changed, 223 insertions(+), 162 deletions(-)
diff --git a/src/core/graph.js b/src/core/graph.js
index c72a476..1f4933e 100644
--- a/src/core/graph.js
+++ b/src/core/graph.js
@@ -169,78 +169,70 @@ export class Graph {
}
/**
- * Gets the array of all out-edges from the node at the given address.
- * The order of the resulting array is unspecified.
- */
- outEdges(
- nodeAddress: Address,
- typeOptions?: {+nodeType?: string, +edgeType?: string}
- ): Edge[] {
- if (nodeAddress == null) {
- throw new Error(`address is ${String(nodeAddress)}`);
- }
- let result = this._lookupEdges(this._outEdges, nodeAddress).map((e) =>
- this.edge(e)
- );
-
- if (typeOptions != null && typeOptions.edgeType != null) {
- const edgeType = typeOptions.edgeType;
- result = result.filter((e) => e.address.type === edgeType);
- }
- if (typeOptions != null && typeOptions.nodeType != null) {
- const nodeType = typeOptions.nodeType;
- result = result.filter((e) => e.dst.type === nodeType);
- }
- return result;
- }
-
- /**
- * Gets the array of all in-edges to the node at the given address.
- * The order of the resulting array is unspecified.
- */
- inEdges(
- nodeAddress: Address,
- typeOptions?: {+nodeType?: string, +edgeType?: string}
- ): Edge[] {
- if (nodeAddress == null) {
- throw new Error(`address is ${String(nodeAddress)}`);
- }
- let result = this._lookupEdges(this._inEdges, nodeAddress).map((e) =>
- this.edge(e)
- );
-
- if (typeOptions != null && typeOptions.edgeType != null) {
- const edgeType = typeOptions.edgeType;
- result = result.filter((e) => e.address.type === edgeType);
- }
- if (typeOptions != null && typeOptions.nodeType != null) {
- const nodeType = typeOptions.nodeType;
- result = result.filter((e) => e.src.type === nodeType);
- }
- return result;
- }
-
- /**
- * Find the neighborhood of a given nodeAddress
+ * Find the neighborhood of the node at the given address.
*
- * By neighborhood, we mean every `{edge, neighborAddress}` such that edge
- * has `nodeAddress` as either `src` or `dst`, and `neighborAddress` is the
+ * By neighborhood, we mean every `{edge, neighbor}` such that edge
+ * has `nodeAddress` as either `src` or `dst`, and `neighbor` is the
* address at the other end of the edge.
+ *
+ * The returned neighbors are filtered according to the `options`. Callers
+ * can filter by nodeType, edgeType, and whether it should be an "IN" edge
+ * (i.e. the provided node is the dst), an "OUT" edge (i.e. provided node is
+ * the src), or "ANY".
*/
neighborhood(
nodeAddress: Address,
- typeOptions?: {+nodeType?: string, +edgeType?: string}
- ): {+edge: Edge, +neighborAddress: Address}[] {
- const inNeighbors = this.inEdges(nodeAddress, typeOptions).map((e) => {
- return {edge: e, neighborAddress: e.src};
- });
- const outNeighbors = this.outEdges(nodeAddress, typeOptions)
- // If there are self-reference edges, avoid double counting them.
- .filter((e) => !deepEqual(e.src, e.dst))
- .map((e) => {
- return {edge: e, neighborAddress: e.dst};
- });
- return [].concat(inNeighbors, outNeighbors);
+ options?: {|
+ +nodeType?: string,
+ +edgeType?: string,
+ +direction?: "IN" | "OUT" | "ANY",
+ |}
+ ): {+edge: Edge, +neighbor: Address}[] {
+ if (nodeAddress == null) {
+ throw new Error(`address is ${String(nodeAddress)}`);
+ }
+
+ let result: {+edge: Edge, +neighbor: Address}[] = [];
+ const direction = (options != null && options.direction) || "ANY";
+
+ if (direction === "ANY" || direction === "IN") {
+ let inNeighbors = this._lookupEdges(this._inEdges, nodeAddress).map(
+ (e) => {
+ const edge = this.edge(e);
+ return {edge, neighbor: edge.src};
+ }
+ );
+
+ result = result.concat(inNeighbors);
+ }
+
+ if (direction === "ANY" || direction === "OUT") {
+ let outNeighbors = this._lookupEdges(this._outEdges, nodeAddress).map(
+ (e) => {
+ const edge = this.edge(e);
+ return {edge, neighbor: edge.dst};
+ }
+ );
+
+ if (direction === "ANY") {
+ // If direction is ANY, we already counted self-referencing edges as
+ // an inNeighbor
+ outNeighbors = outNeighbors.filter(
+ ({edge}) => !deepEqual(edge.src, edge.dst)
+ );
+ }
+
+ result = result.concat(outNeighbors);
+ }
+ if (options != null && options.edgeType != null) {
+ const edgeType = options.edgeType;
+ result = result.filter(({edge}) => edge.address.type === edgeType);
+ }
+ if (options != null && options.nodeType != null) {
+ const nodeType = options.nodeType;
+ result = result.filter(({neighbor}) => neighbor.type === nodeType);
+ }
+ return result;
}
/**
diff --git a/src/core/graph.test.js b/src/core/graph.test.js
index 4e0afbd..23a0154 100644
--- a/src/core/graph.test.js
+++ b/src/core/graph.test.js
@@ -65,13 +65,8 @@ describe("graph", () => {
`address is ${String(bad)}`
);
});
- it(`getting ${String(bad)} in-edges`, () => {
- expect(() => new Graph().inEdges((bad: any))).toThrow(
- `address is ${String(bad)}`
- );
- });
- it(`getting ${String(bad)} out-edges`, () => {
- expect(() => new Graph().outEdges((bad: any))).toThrow(
+ it(`getting ${String(bad)} neighborhood`, () => {
+ expect(() => new Graph().neighborhood((bad: any))).toThrow(
`address is ${String(bad)}`
);
});
@@ -298,12 +293,16 @@ describe("graph", () => {
it("allows creating self-loops", () => {
const g = demoData.simpleMealGraph();
g.addEdge(demoData.crabLoopEdge());
- expect(g.outEdges(demoData.crabNode().address)).toContainEqual(
- demoData.crabLoopEdge()
- );
- expect(g.inEdges(demoData.crabNode().address)).toContainEqual(
- demoData.crabLoopEdge()
- );
+ expect(
+ g
+ .neighborhood(demoData.crabNode().address, {direction: "OUT"})
+ .map(({edge}) => edge)
+ ).toContainEqual(demoData.crabLoopEdge());
+ expect(
+ g
+ .neighborhood(demoData.crabNode().address, {direction: "IN"})
+ .map(({edge}) => edge)
+ ).toContainEqual(demoData.crabLoopEdge());
const crabNeighbors = g.neighborhood(demoData.crabNode().address);
const crabLoops = crabNeighbors.filter(({edge}) =>
deepEqual(edge, demoData.crabLoopEdge())
@@ -315,7 +314,11 @@ describe("graph", () => {
const g = demoData.simpleMealGraph();
g.addEdge(demoData.duplicateCookEdge());
[demoData.cookEdge(), demoData.duplicateCookEdge()].forEach((e) => {
- expect(g.outEdges(demoData.mealNode().address)).toContainEqual(e);
+ expect(
+ g
+ .neighborhood(demoData.mealNode().address, {direction: "OUT"})
+ .map(({edge}) => edge)
+ ).toContainEqual(e);
expect(g.edge(e.address)).toEqual(e);
});
});
@@ -381,7 +384,7 @@ describe("graph", () => {
});
});
- describe("inEdges and outEdges", () => {
+ describe("neighborhood detection", () => {
describe("type filtering", () => {
class ExampleGraph {
graph: Graph<{}, {}>;
@@ -441,14 +444,26 @@ describe("graph", () => {
const exampleGraph = new ExampleGraph();
[
[
- "inEdges",
+ "neighborhood IN",
exampleGraph.inEdges,
- (opts) => exampleGraph.graph.inEdges(exampleGraph.root, opts),
+ (opts) =>
+ exampleGraph.graph
+ .neighborhood(exampleGraph.root, {
+ ...(opts || {}),
+ direction: "IN",
+ })
+ .map(({edge}) => edge),
],
[
- "outEdges",
+ "neighborhood OUT",
exampleGraph.outEdges,
- (opts) => exampleGraph.graph.outEdges(exampleGraph.root, opts),
+ (opts) =>
+ exampleGraph.graph
+ .neighborhood(exampleGraph.root, {
+ ...(opts || {}),
+ direction: "OUT",
+ })
+ .map(({edge}) => edge),
],
].forEach(([choice, {a1, a2, b1, b2}, edges]) => {
describe(choice, () => {
@@ -516,23 +531,23 @@ describe("graph", () => {
neighborhood: [
{
edge: demoData.eatEdge(),
- neighborAddress: demoData.mealNode().address,
+ neighbor: demoData.mealNode().address,
},
{
edge: demoData.pickEdge(),
- neighborAddress: demoData.bananasNode().address,
+ neighbor: demoData.bananasNode().address,
},
{
edge: demoData.grabEdge(),
- neighborAddress: demoData.crabNode().address,
+ neighbor: demoData.crabNode().address,
},
{
edge: demoData.cookEdge(),
- neighborAddress: demoData.mealNode().address,
+ neighbor: demoData.mealNode().address,
},
{
edge: demoData.duplicateCookEdge(),
- neighborAddress: demoData.mealNode().address,
+ neighbor: demoData.mealNode().address,
},
],
},
@@ -541,11 +556,11 @@ describe("graph", () => {
neighborhood: [
{
edge: demoData.pickEdge(),
- neighborAddress: demoData.heroNode().address,
+ neighbor: demoData.heroNode().address,
},
{
edge: demoData.bananasIngredientEdge(),
- neighborAddress: demoData.mealNode().address,
+ neighbor: demoData.mealNode().address,
},
],
},
@@ -554,15 +569,15 @@ describe("graph", () => {
neighborhood: [
{
edge: demoData.crabIngredientEdge(),
- neighborAddress: demoData.mealNode().address,
+ neighbor: demoData.mealNode().address,
},
{
edge: demoData.grabEdge(),
- neighborAddress: demoData.heroNode().address,
+ neighbor: demoData.heroNode().address,
},
{
edge: demoData.crabLoopEdge(),
- neighborAddress: demoData.crabNode().address,
+ neighbor: demoData.crabNode().address,
},
],
},
@@ -571,23 +586,23 @@ describe("graph", () => {
neighborhood: [
{
edge: demoData.bananasIngredientEdge(),
- neighborAddress: demoData.bananasNode().address,
+ neighbor: demoData.bananasNode().address,
},
{
edge: demoData.crabIngredientEdge(),
- neighborAddress: demoData.crabNode().address,
+ neighbor: demoData.crabNode().address,
},
{
edge: demoData.cookEdge(),
- neighborAddress: demoData.heroNode().address,
+ neighbor: demoData.heroNode().address,
},
{
edge: demoData.eatEdge(),
- neighborAddress: demoData.heroNode().address,
+ neighbor: demoData.heroNode().address,
},
{
edge: demoData.duplicateCookEdge(),
- neighborAddress: demoData.heroNode().address,
+ neighbor: demoData.heroNode().address,
},
],
},
@@ -617,7 +632,10 @@ describe("graph", () => {
],
];
nodeAndExpectedEdgePairs.forEach(([node, expectedEdges]) => {
- const actual = demoData.advancedMealGraph().outEdges(node.address);
+ const actual = demoData
+ .advancedMealGraph()
+ .neighborhood(node.address, {direction: "OUT"})
+ .map(({edge}) => edge);
expectSameSorted(actual, expectedEdges);
});
});
@@ -641,7 +659,10 @@ describe("graph", () => {
[demoData.mealNode(), [demoData.eatEdge()]],
];
nodeAndExpectedEdgePairs.forEach(([node, expectedEdges]) => {
- const actual = demoData.advancedMealGraph().inEdges(node.address);
+ const actual = demoData
+ .advancedMealGraph()
+ .neighborhood(node.address, {direction: "IN"})
+ .map(({edge}) => edge);
expectSameSorted(actual, expectedEdges);
});
});
@@ -649,14 +670,18 @@ describe("graph", () => {
it("gets empty out-edges for a nonexistent node", () => {
const result = demoData
.simpleMealGraph()
- .outEdges(demoData.makeAddress("hinox", "NPC"));
+ .neighborhood(demoData.makeAddress("hinox", "NPC"), {
+ direction: "OUT",
+ });
expect(result).toEqual([]);
});
it("gets empty in-edges for a nonexistent node", () => {
const result = demoData
.simpleMealGraph()
- .inEdges(demoData.makeAddress("hinox", "NPC"));
+ .neighborhood(demoData.makeAddress("hinox", "NPC"), {
+ direction: "IN",
+ });
expect(result).toEqual([]);
});
@@ -690,7 +715,9 @@ describe("graph", () => {
.addEdge(fullyDanglingEdge())
.removeNode(danglingSrc().address)
.removeNode(danglingDst().address);
- const inEdges = g.inEdges(fullyDanglingEdge().dst);
+ const inEdges = g
+ .neighborhood(fullyDanglingEdge().dst, {direction: "IN"})
+ .map(({edge}) => edge);
expect(inEdges).toEqual([fullyDanglingEdge()]);
});
@@ -702,7 +729,9 @@ describe("graph", () => {
.addEdge(fullyDanglingEdge())
.removeNode(danglingSrc().address)
.removeNode(danglingDst().address);
- const outEdges = g.outEdges(fullyDanglingEdge().src);
+ const outEdges = g
+ .neighborhood(fullyDanglingEdge().src, {direction: "OUT"})
+ .map(({edge}) => edge);
expect(outEdges).toEqual([fullyDanglingEdge()]);
});
@@ -713,8 +742,10 @@ describe("graph", () => {
.addNode(danglingDst())
.addEdge(fullyDanglingEdge())
.removeEdge(fullyDanglingEdge().address);
- const outEdges = g.inEdges(fullyDanglingEdge().dst);
- expect(outEdges).toEqual([]);
+ const inEdges = g.neighborhood(fullyDanglingEdge().dst, {
+ direction: "IN",
+ });
+ expect(inEdges).toEqual([]);
});
it("has lack of out-edges for deleted edge", () => {
@@ -724,19 +755,25 @@ describe("graph", () => {
.addNode(danglingDst())
.addEdge(fullyDanglingEdge())
.removeEdge(fullyDanglingEdge().address);
- const outEdges = g.outEdges(fullyDanglingEdge().src);
+ const outEdges = g.neighborhood(fullyDanglingEdge().src, {
+ direction: "OUT",
+ });
expect(outEdges).toEqual([]);
});
it("has in-edges for non-existent node with dangling edge", () => {
const g = demoData.simpleMealGraph().addEdge(fullyDanglingEdge());
- const inEdges = g.inEdges(fullyDanglingEdge().dst);
+ const inEdges = g
+ .neighborhood(fullyDanglingEdge().dst, {direction: "IN"})
+ .map(({edge}) => edge);
expect(inEdges).toEqual([fullyDanglingEdge()]);
});
it("has out-edges for non-existent node with dangling edge", () => {
const g = demoData.simpleMealGraph().addEdge(fullyDanglingEdge());
- const outEdges = g.outEdges(fullyDanglingEdge().src);
+ const outEdges = g
+ .neighborhood(fullyDanglingEdge().src, {direction: "OUT"})
+ .map(({edge}) => edge);
expect(outEdges).toEqual([fullyDanglingEdge()]);
});
@@ -745,7 +782,9 @@ describe("graph", () => {
.simpleMealGraph()
.addEdge(fullyDanglingEdge())
.addNode(danglingDst());
- const inEdges = g.inEdges(fullyDanglingEdge().dst);
+ const inEdges = g
+ .neighborhood(fullyDanglingEdge().dst, {direction: "IN"})
+ .map(({edge}) => edge);
expect(inEdges).toEqual([fullyDanglingEdge()]);
});
@@ -754,7 +793,9 @@ describe("graph", () => {
.simpleMealGraph()
.addEdge(fullyDanglingEdge())
.addNode(danglingSrc());
- const outEdges = g.outEdges(fullyDanglingEdge().src);
+ const outEdges = g
+ .neighborhood(fullyDanglingEdge().src, {direction: "OUT"})
+ .map(({edge}) => edge);
expect(outEdges).toEqual([fullyDanglingEdge()]);
});
}
@@ -777,15 +818,23 @@ describe("graph", () => {
it("is idempotent in terms of in-edges", () => {
const g1 = originalGraph();
const g2 = modifiedGraph();
- const e1 = g1.inEdges(taredge().address);
- const e2 = g2.inEdges(taredge().address);
+ const e1 = g1
+ .neighborhood(taredge().address, {direction: "IN"})
+ .map(({edge}) => edge);
+ const e2 = g2
+ .neighborhood(taredge().address, {direction: "IN"})
+ .map(({edge}) => edge);
expectSameSorted(e1, e2);
});
it("is idempotent in terms of out-edges", () => {
const g1 = originalGraph();
const g2 = modifiedGraph();
- const e1 = g1.outEdges(taredge().address);
- const e2 = g2.outEdges(taredge().address);
+ const e1 = g1
+ .neighborhood(taredge().address, {direction: "OUT"})
+ .map(({edge}) => edge);
+ const e2 = g2
+ .neighborhood(taredge().address, {direction: "OUT"})
+ .map(({edge}) => edge);
expectSameSorted(e1, e2);
});
});
@@ -848,21 +897,25 @@ describe("graph", () => {
return originalGraph.nodes().map((node) => {
const miniGraph = new Graph();
miniGraph.addNode(node);
- originalGraph.outEdges(node.address).forEach((edge) => {
- if (miniGraph.node(edge.dst) === undefined) {
- miniGraph.addNode(originalGraph.node(edge.dst));
- }
- miniGraph.addEdge(edge);
- });
- originalGraph.inEdges(node.address).forEach((edge) => {
- if (miniGraph.node(edge.src) === undefined) {
- miniGraph.addNode(originalGraph.node(edge.src));
- }
- if (miniGraph.edge(edge.address) === undefined) {
- // This check is necessary to prevent double-adding loops.
+ originalGraph
+ .neighborhood(node.address, {direction: "OUT"})
+ .forEach(({edge}) => {
+ if (miniGraph.node(edge.dst) === undefined) {
+ miniGraph.addNode(originalGraph.node(edge.dst));
+ }
miniGraph.addEdge(edge);
- }
- });
+ });
+ originalGraph
+ .neighborhood(node.address, {direction: "IN"})
+ .forEach(({edge}) => {
+ if (miniGraph.node(edge.src) === undefined) {
+ miniGraph.addNode(originalGraph.node(edge.src));
+ }
+ if (miniGraph.edge(edge.address) === undefined) {
+ // This check is necessary to prevent double-adding loops.
+ miniGraph.addEdge(edge);
+ }
+ });
return miniGraph;
});
}
diff --git a/src/plugins/artifact/editor/ContributionList.test.js b/src/plugins/artifact/editor/ContributionList.test.js
index b509b20..787eb55 100644
--- a/src/plugins/artifact/editor/ContributionList.test.js
+++ b/src/plugins/artifact/editor/ContributionList.test.js
@@ -90,7 +90,9 @@ function createTestData(): * {
}> {
render() {
const {graph, node} = this.props;
- const neighborCount = graph.outEdges(node.address).length;
+ const neighborCount = graph.neighborhood(node.address, {
+ direction: "OUT",
+ }).length;
return (
{node.address.id} has neighbor count{" "}
diff --git a/src/plugins/artifact/editor/adapters/githubPluginAdapter.js b/src/plugins/artifact/editor/adapters/githubPluginAdapter.js
index 4b12c30..ec79905 100644
--- a/src/plugins/artifact/editor/adapters/githubPluginAdapter.js
+++ b/src/plugins/artifact/editor/adapters/githubPluginAdapter.js
@@ -38,11 +38,12 @@ const adapter: PluginAdapter = {
// previously queried IDs.)
function extractParentTitles(node: Node): string[] {
return graph
- .inEdges(node.address)
- .filter((e) => e.address.type === CONTAINS_EDGE_TYPE)
- .map((e) => graph.node(e.src))
- .map((container) => {
- return adapter.extractTitle(graph, container);
+ .neighborhood(node.address, {
+ direction: "IN",
+ edgeType: CONTAINS_EDGE_TYPE,
+ })
+ .map(({neighbor}) => {
+ return adapter.extractTitle(graph, graph.node(neighbor));
});
}
function extractIssueOrPrTitle(
diff --git a/src/plugins/git/createGraph.test.js b/src/plugins/git/createGraph.test.js
index a166819..083da0a 100644
--- a/src/plugins/git/createGraph.test.js
+++ b/src/plugins/git/createGraph.test.js
@@ -62,26 +62,31 @@ describe("createGraph", () => {
id: hash,
};
- const entryChildren = graph.outEdges(address, {
+ const entryChildren = graph.neighborhood(address, {
nodeType: TREE_ENTRY_NODE_TYPE,
edgeType: INCLUDES_EDGE_TYPE,
+ direction: "OUT",
});
expect(entryChildren).toHaveLength(
Object.keys(data.trees[hash].entries).length
);
- expect(graph.outEdges(address)).toHaveLength(entryChildren.length);
+ expect(graph.neighborhood(address, {direction: "OUT"})).toHaveLength(
+ entryChildren.length
+ );
expect(graph.node(address)).toEqual({address, payload: {}});
- const owningCommits = graph.inEdges(address, {
+ const owningCommits = graph.neighborhood(address, {
nodeType: COMMIT_NODE_TYPE,
edgeType: HAS_TREE_EDGE_TYPE,
+ direction: "IN",
});
expect(owningCommits.length).toBeLessThanOrEqual(1);
- const parentTreeEntries = graph.inEdges(address, {
+ const parentTreeEntries = graph.neighborhood(address, {
nodeType: TREE_ENTRY_NODE_TYPE,
edgeType: HAS_CONTENTS_EDGE_TYPE,
+ direction: "IN",
});
- expect(graph.inEdges(address)).toHaveLength(
+ expect(graph.neighborhood(address, {direction: "IN"})).toHaveLength(
owningCommits.length + parentTreeEntries.length
);
});
@@ -110,14 +115,18 @@ describe("createGraph", () => {
id: treeEntryId(hash, name),
};
expect(
- graph.inEdges(entryAddress, {
+ graph.neighborhood(entryAddress, {
nodeType: TREE_NODE_TYPE,
edgeType: INCLUDES_EDGE_TYPE,
+ direction: "IN",
})
).toHaveLength(1);
const shouldHaveContents = tree.entries[name].type !== "commit";
expect(
- graph.outEdges(entryAddress, {edgeType: HAS_CONTENTS_EDGE_TYPE})
+ graph.neighborhood(entryAddress, {
+ edgeType: HAS_CONTENTS_EDGE_TYPE,
+ direction: "OUT",
+ })
).toHaveLength(shouldHaveContents ? 1 : 0);
expect(graph.neighborhood(entryAddress)).toHaveLength(
shouldHaveContents ? 2 : 1
@@ -142,7 +151,7 @@ describe("createGraph", () => {
.neighborhood(nodeAddress, filter)
.filter((x) => predicate(x));
expect(edges).toHaveLength(1);
- return edges[0].neighborAddress;
+ return edges[0].neighbor;
}
function uniqueTree(graph, commitAddress) {
@@ -218,7 +227,9 @@ describe("createGraph", () => {
expect(graph.node(treeEntryAddress)).toEqual(expect.anything());
// Submodule commits never have contents, because the commit nodes
// are from an unknown repository.
- expect(graph.outEdges(treeEntryAddress)).toHaveLength(0);
+ expect(
+ graph.neighborhood(treeEntryAddress, {direction: "OUT"})
+ ).toHaveLength(0);
});
});
});
diff --git a/src/plugins/github/api.js b/src/plugins/github/api.js
index a518a3c..4d7d6bd 100644
--- a/src/plugins/github/api.js
+++ b/src/plugins/github/api.js
@@ -113,7 +113,7 @@ class Post<
edgeType: AUTHORS_EDGE_TYPE,
nodeType: AUTHOR_NODE_TYPE,
})
- .map(({neighborAddress}) => new Author(this.graph, neighborAddress));
+ .map(({neighbor}) => new Author(this.graph, neighbor));
}
body(): string {
@@ -130,7 +130,7 @@ class Commentable extends Post<
edgeType: CONTAINS_EDGE_TYPE,
nodeType: COMMENT_NODE_TYPE,
})
- .map(({neighborAddress}) => new Comment(this.graph, neighborAddress));
+ .map(({neighbor}) => new Comment(this.graph, neighbor));
}
}
diff --git a/src/plugins/github/parser.test.js b/src/plugins/github/parser.test.js
index b93a071..35cbe67 100644
--- a/src/plugins/github/parser.test.js
+++ b/src/plugins/github/parser.test.js
@@ -38,14 +38,16 @@ describe("GithubParser", () => {
.filter((n) => n.address.type === "COMMENT");
expect(comments).not.toHaveLength(0);
comments.forEach((c) => {
- const authorEdges = graph
- .outEdges(c.address)
- .filter((e) => e.address.type === AUTHORS_EDGE_TYPE);
- expect(authorEdges.length).toBe(1);
- const containerEdges = graph
- .inEdges(c.address)
- .filter((e) => e.address.type === CONTAINS_EDGE_TYPE);
- expect(containerEdges.length).toBe(1);
+ const authorNeighbors = graph.neighborhood(c.address, {
+ edgeType: AUTHORS_EDGE_TYPE,
+ direction: "OUT",
+ });
+ expect(authorNeighbors.length).toBe(1);
+ const containerNeighbors = graph.neighborhood(c.address, {
+ direction: "IN",
+ edgeType: CONTAINS_EDGE_TYPE,
+ });
+ expect(containerNeighbors.length).toBe(1);
});
});
@@ -57,11 +59,11 @@ describe("GithubParser", () => {
);
expect(issuesAndPRs).not.toHaveLength(0);
issuesAndPRs.forEach((x) => {
- const outEdges = graph.outEdges(x.address);
- const authorEdges = outEdges.filter(
- (e) => e.address.type === AUTHORS_EDGE_TYPE
- );
- expect(authorEdges.length).toBe(1);
+ const authorNeighbors = graph.neighborhood(x.address, {
+ edgeType: AUTHORS_EDGE_TYPE,
+ direction: "OUT",
+ });
+ expect(authorNeighbors.length).toBe(1);
});
});
});