mirror of
https://github.com/status-im/sourcecred.git
synced 2025-02-17 06:56:36 +00:00
Remove Node/Edge polymorphism on Graph (#289)
As previously implemented, the Graph was polymorphic in its NodePayload and EdgePayload. This was a lot of bookkeeping for very little apparent benefit. In general, graphs may be constructed with foreign plugins, so it's hard to do anything with this information. In my experience, having the Graph polymorphism has never caught a bug, and has led to lots of boilerplate code, especially typing `Graph<any, any>`. I view the fact that in #286 we added a new core `NodeReference` concept, which always types its Graph as `<any, any>`, as strongly suggestive that this was not going to provide any lasting value. In this commit, I've removed the Graph polymorphism. Note how in many cases where we were typing the graph, it provided no value, as evidenced by the fact that the imported Node and Edge types were used no-where else in the file other than in the Graph declaration. Test plan: Removing extra typing information is very unlikely to cause regressions. `yarn flow` and `yarn lint` both pass.
This commit is contained in:
parent
e20b794edc
commit
418d046691
@ -13,7 +13,7 @@ type Props = {};
|
||||
type State = {
|
||||
repoOwner: string,
|
||||
repoName: string,
|
||||
graph: ?Graph<mixed, mixed>,
|
||||
graph: ?Graph,
|
||||
pagerankResult: ?PagerankResult,
|
||||
};
|
||||
|
||||
|
@ -29,7 +29,7 @@ type OrderedSparseMarkovChain = {|
|
||||
+chain: SparseMarkovChain,
|
||||
|};
|
||||
|
||||
export default function basicPagerank(graph: Graph<any, any>): PagerankResult {
|
||||
export default function basicPagerank(graph: Graph): PagerankResult {
|
||||
const {nodeOrder, chain} = graphToOrderedSparseMarkovChain(graph);
|
||||
const pi = findStationaryDistribution(chain, {verbose: true});
|
||||
return distributionToPagerankResult(nodeOrder, pi);
|
||||
@ -41,9 +41,7 @@ function edgeWeight(
|
||||
return {toWeight: 1, froWeight: 1};
|
||||
}
|
||||
|
||||
function graphToAddressMapMarkovChain(
|
||||
graph: Graph<any, any>
|
||||
): AddressMapMarkovChain {
|
||||
function graphToAddressMapMarkovChain(graph: Graph): AddressMapMarkovChain {
|
||||
const result = new AddressMap();
|
||||
const unnormalizedTotalOutWeights = new AddressMap();
|
||||
|
||||
@ -109,7 +107,7 @@ function addressMapMarkovChainToOrderedSparseMarkovChain(
|
||||
}
|
||||
|
||||
export function graphToOrderedSparseMarkovChain(
|
||||
graph: Graph<any, any>
|
||||
graph: Graph
|
||||
): OrderedSparseMarkovChain {
|
||||
return addressMapMarkovChainToOrderedSparseMarkovChain(
|
||||
graphToAddressMapMarkovChain(graph)
|
||||
|
@ -15,7 +15,7 @@ import type {PagerankResult} from "./basicPagerank";
|
||||
|
||||
type Props = {
|
||||
pagerankResult: ?PagerankResult,
|
||||
graph: ?Graph<any, any>,
|
||||
graph: ?Graph,
|
||||
};
|
||||
|
||||
type State = {
|
||||
@ -65,7 +65,7 @@ export class PagerankTable extends React.Component<Props, State> {
|
||||
if (this.props.graph == null || this.props.pagerankResult == null) {
|
||||
throw new Error("Impossible.");
|
||||
}
|
||||
const graph: Graph<any, any> = this.props.graph;
|
||||
const graph: Graph = this.props.graph;
|
||||
const typesByPlugin: {[pluginName: string]: Set<string>} = {};
|
||||
graph.nodes().forEach((node) => {
|
||||
if (!typesByPlugin[node.address.pluginName]) {
|
||||
@ -154,7 +154,7 @@ export class PagerankTable extends React.Component<Props, State> {
|
||||
type RTState = {expanded: boolean};
|
||||
type RTProps = {|
|
||||
+address: Address,
|
||||
+graph: Graph<any, any>,
|
||||
+graph: Graph,
|
||||
+pagerankResult: PagerankResult,
|
||||
+depth: number,
|
||||
|};
|
||||
|
@ -87,7 +87,7 @@ function pluginGraph(
|
||||
}
|
||||
}
|
||||
|
||||
function display<NP, EP>(promise: Promise<Graph<NP, EP>>) {
|
||||
function display(promise: Promise<Graph>) {
|
||||
promise.then((graph) => {
|
||||
console.log(stringify(graph, {space: 4}));
|
||||
});
|
||||
|
@ -21,33 +21,33 @@ export type Edge<+T> = {|
|
||||
+payload: T,
|
||||
|};
|
||||
|
||||
type IndexedEdge<+T> = {|
|
||||
type IndexedEdge = {|
|
||||
+address: Address,
|
||||
+srcIndex: Integer,
|
||||
+dstIndex: Integer,
|
||||
+payload: T,
|
||||
+payload: any,
|
||||
|};
|
||||
|
||||
const COMPAT_TYPE = "sourcecred/sourcecred/Graph";
|
||||
const COMPAT_VERSION = "0.2.0";
|
||||
|
||||
type NodesSortedByStringifiedAddress<NP> = {|
|
||||
type NodesSortedByStringifiedAddress = {|
|
||||
+address: Address,
|
||||
+payload?: NP,
|
||||
+payload?: any,
|
||||
|}[];
|
||||
export type GraphJSON<NP, EP> = {|
|
||||
+nodes: NodesSortedByStringifiedAddress<NP>,
|
||||
+edges: AddressMapJSON<IndexedEdge<EP>>,
|
||||
export type GraphJSON = {|
|
||||
+nodes: NodesSortedByStringifiedAddress,
|
||||
+edges: AddressMapJSON<IndexedEdge>,
|
||||
|};
|
||||
|
||||
type MaybeNode<+NP> = {|+address: Address, +node: Node<NP> | void|};
|
||||
type MaybeNode = {|+address: Address, +node: Node<any> | void|};
|
||||
|
||||
export class Graph<NP, EP> {
|
||||
export class Graph {
|
||||
// Invariant: sizes of `_nodeIndices`, `_nodes`, `_outEdges`, and
|
||||
// `_inEdges` are all equal.
|
||||
_nodeIndices: AddressMap<{|+address: Address, +index: Integer|}>;
|
||||
_nodes: MaybeNode<NP>[];
|
||||
_edges: AddressMap<IndexedEdge<EP>>;
|
||||
_nodes: MaybeNode[];
|
||||
_edges: AddressMap<IndexedEdge>;
|
||||
|
||||
// If `idx` is the index of a node `v`, then `_outEdges[idx]` is the
|
||||
// list of `e.address` for all edges `e` whose source is `v`.
|
||||
@ -63,11 +63,11 @@ export class Graph<NP, EP> {
|
||||
this._inEdges = [];
|
||||
}
|
||||
|
||||
copy(): Graph<$Supertype<NP>, $Supertype<EP>> {
|
||||
copy(): Graph {
|
||||
return Graph.mergeConservative(new Graph(), this);
|
||||
}
|
||||
|
||||
equals(that: Graph<NP, EP>): boolean {
|
||||
equals(that: Graph): boolean {
|
||||
const theseNodes = this.nodes();
|
||||
const thoseNodes = that.nodes();
|
||||
if (theseNodes.length !== thoseNodes.length) {
|
||||
@ -93,13 +93,13 @@ export class Graph<NP, EP> {
|
||||
return true;
|
||||
}
|
||||
|
||||
toJSON(): Compatible<GraphJSON<NP, EP>> {
|
||||
toJSON(): Compatible<GraphJSON> {
|
||||
const partialNodes: {|
|
||||
key: string,
|
||||
oldIndex: Integer,
|
||||
data: {|
|
||||
+address: Address,
|
||||
+payload?: NP,
|
||||
+payload?: any,
|
||||
|},
|
||||
|}[] = this._nodes
|
||||
.map((maybeNode, oldIndex) => {
|
||||
@ -162,8 +162,8 @@ export class Graph<NP, EP> {
|
||||
);
|
||||
}
|
||||
|
||||
static fromJSON<NP, EP>(json: Compatible<GraphJSON<NP, EP>>): Graph<NP, EP> {
|
||||
const compatJson: GraphJSON<NP, EP> = fromCompat(
|
||||
static fromJSON(json: Compatible<GraphJSON>): Graph {
|
||||
const compatJson: GraphJSON = fromCompat(
|
||||
{
|
||||
type: COMPAT_TYPE,
|
||||
version: COMPAT_VERSION,
|
||||
@ -173,7 +173,7 @@ export class Graph<NP, EP> {
|
||||
const result = new Graph();
|
||||
compatJson.nodes.forEach((partialNode) => {
|
||||
if ("payload" in partialNode) {
|
||||
const node: Node<NP> = (partialNode: any);
|
||||
const node: Node<any> = (partialNode: any);
|
||||
result.addNode(node);
|
||||
} else {
|
||||
result._addNodeAddress(partialNode.address);
|
||||
@ -201,7 +201,7 @@ export class Graph<NP, EP> {
|
||||
}
|
||||
}
|
||||
|
||||
addNode(node: Node<NP>): this {
|
||||
addNode(node: Node<any>): this {
|
||||
if (node == null) {
|
||||
throw new Error(`node is ${String(node)}`);
|
||||
}
|
||||
@ -230,7 +230,7 @@ export class Graph<NP, EP> {
|
||||
return this;
|
||||
}
|
||||
|
||||
addEdge(edge: Edge<EP>): this {
|
||||
addEdge(edge: Edge<any>): this {
|
||||
if (edge == null) {
|
||||
throw new Error(`edge is ${String(edge)}`);
|
||||
}
|
||||
@ -245,7 +245,7 @@ export class Graph<NP, EP> {
|
||||
return this._addIndexedEdge(indexedEdge);
|
||||
}
|
||||
|
||||
_addIndexedEdge(indexedEdge: IndexedEdge<EP>): this {
|
||||
_addIndexedEdge(indexedEdge: IndexedEdge): this {
|
||||
const existingIndexedEdge = this._edges.get(indexedEdge.address);
|
||||
if (existingIndexedEdge !== undefined) {
|
||||
if (deepEqual(existingIndexedEdge, indexedEdge)) {
|
||||
@ -283,18 +283,18 @@ export class Graph<NP, EP> {
|
||||
return this;
|
||||
}
|
||||
|
||||
node(address: Address): Node<NP> {
|
||||
node(address: Address): Node<any> {
|
||||
const indexDatum = this._nodeIndices.get(address);
|
||||
if (indexDatum == null) {
|
||||
// We've never heard of this node.
|
||||
return (undefined: any);
|
||||
} else {
|
||||
const node: Node<NP> | void = this._nodes[indexDatum.index].node;
|
||||
return ((node: any): Node<NP>);
|
||||
const node: Node<any> | void = this._nodes[indexDatum.index].node;
|
||||
return ((node: any): Node<any>);
|
||||
}
|
||||
}
|
||||
|
||||
edge(address: Address): Edge<EP> {
|
||||
edge(address: Address): Edge<any> {
|
||||
const indexedEdge = this._edges.get(address);
|
||||
if (!indexedEdge) {
|
||||
// Lie.
|
||||
@ -327,7 +327,7 @@ export class Graph<NP, EP> {
|
||||
+edgeType?: string,
|
||||
+direction?: "IN" | "OUT" | "ANY",
|
||||
|}
|
||||
): {|+edge: Edge<EP>, +neighbor: Address|}[] {
|
||||
): {|+edge: Edge<any>, +neighbor: Address|}[] {
|
||||
if (nodeAddress == null) {
|
||||
throw new Error(`address is ${String(nodeAddress)}`);
|
||||
}
|
||||
@ -338,7 +338,7 @@ export class Graph<NP, EP> {
|
||||
}
|
||||
const nodeIndex = indexDatum.index;
|
||||
|
||||
let result: {|+edge: Edge<EP>, +neighbor: Address|}[] = [];
|
||||
let result: {|+edge: Edge<any>, +neighbor: Address|}[] = [];
|
||||
const direction = (options != null && options.direction) || "ANY";
|
||||
|
||||
if (direction === "ANY" || direction === "IN") {
|
||||
@ -380,7 +380,7 @@ export class Graph<NP, EP> {
|
||||
*
|
||||
* If filter is provided, it will return only nodes with the requested type.
|
||||
*/
|
||||
nodes(filter?: {type?: string}): Node<NP>[] {
|
||||
nodes(filter?: {type?: string}): Node<any>[] {
|
||||
/*:: declare function nonNulls<T>(x: (T | void)[]): T[]; */
|
||||
let nodes = this._nodes.map((x) => x.node).filter((x) => Boolean(x));
|
||||
/*:: nodes = nonNulls(nodes); */
|
||||
@ -396,7 +396,7 @@ export class Graph<NP, EP> {
|
||||
*
|
||||
* If filter is provided, it will return only edges with the requested type.
|
||||
*/
|
||||
edges(filter?: {type?: string}): Edge<EP>[] {
|
||||
edges(filter?: {type?: string}): Edge<any>[] {
|
||||
let edges = this._edges.getAll().map((indexedEdge) => ({
|
||||
address: indexedEdge.address,
|
||||
src: this._nodes[indexedEdge.srcIndex].address,
|
||||
@ -418,13 +418,13 @@ export class Graph<NP, EP> {
|
||||
*
|
||||
* The existing graph objects are not modified.
|
||||
*/
|
||||
static merge<NP, EP, N1: NP, E1: EP, N2: NP, E2: EP>(
|
||||
g1: Graph<N1, E1>,
|
||||
g2: Graph<N2, E2>,
|
||||
nodeResolver: (Node<N1>, Node<N2>) => Node<NP>,
|
||||
edgeResolver: (Edge<E1>, Edge<E2>) => Edge<EP>
|
||||
): Graph<NP, EP> {
|
||||
const result: Graph<NP, EP> = new Graph();
|
||||
static merge(
|
||||
g1: Graph,
|
||||
g2: Graph,
|
||||
nodeResolver: (Node<any>, Node<any>) => Node<any>,
|
||||
edgeResolver: (Edge<any>, Edge<any>) => Edge<any>
|
||||
): Graph {
|
||||
const result: Graph = new Graph();
|
||||
g1.nodes().forEach((node) => {
|
||||
if (g2.node(node.address) !== undefined) {
|
||||
const resolved = nodeResolver(node, g2.node(node.address));
|
||||
@ -460,10 +460,7 @@ export class Graph<NP, EP> {
|
||||
* for edges). If this assumption does not hold, this function will
|
||||
* raise an error.
|
||||
*/
|
||||
static mergeConservative<NP, EP, N1: NP, E1: EP, N2: NP, E2: EP>(
|
||||
g1: Graph<N1, E1>,
|
||||
g2: Graph<N2, E2>
|
||||
): Graph<NP, EP> {
|
||||
static mergeConservative(g1: Graph, g2: Graph): Graph {
|
||||
function conservativeResolver<T: Addressable>(
|
||||
kinds: string /* used for an error message on mismatch */,
|
||||
a: T,
|
||||
@ -477,7 +474,7 @@ export class Graph<NP, EP> {
|
||||
);
|
||||
}
|
||||
}
|
||||
const result: Graph<NP, EP> = Graph.merge(
|
||||
const result: Graph = Graph.merge(
|
||||
g1,
|
||||
g2,
|
||||
(u, v) => conservativeResolver("nodes", u, v),
|
||||
@ -493,9 +490,7 @@ export class Graph<NP, EP> {
|
||||
*
|
||||
* but uses a mutable accumulator for improved performance.
|
||||
*/
|
||||
static mergeManyConservative<NP, EP>(
|
||||
graphs: $ReadOnlyArray<Graph<$Subtype<NP>, $Subtype<EP>>>
|
||||
): Graph<NP, EP> {
|
||||
static mergeManyConservative(graphs: $ReadOnlyArray<Graph>): Graph {
|
||||
const result = new Graph();
|
||||
graphs.forEach((graph) => {
|
||||
graph.nodes().forEach((node) => {
|
||||
|
@ -388,7 +388,7 @@ describe("graph", () => {
|
||||
describe("neighborhood detection", () => {
|
||||
describe("type filtering", () => {
|
||||
class ExampleGraph {
|
||||
graph: Graph<{}, {}>;
|
||||
graph: Graph;
|
||||
root: Address;
|
||||
idIncrement: number;
|
||||
inEdges: {[string]: Edge<{}>};
|
||||
@ -891,9 +891,7 @@ describe("graph", () => {
|
||||
* node `u`, create a graph with just that node, its neighbors,
|
||||
* and its incident edges (in both directions).
|
||||
*/
|
||||
function neighborhoodDecomposition<NP, EP>(
|
||||
originalGraph: Graph<NP, EP>
|
||||
): Graph<NP, EP>[] {
|
||||
function neighborhoodDecomposition(originalGraph: Graph): Graph[] {
|
||||
return originalGraph.nodes().map((node) => {
|
||||
const miniGraph = new Graph();
|
||||
miniGraph.addNode(node);
|
||||
@ -925,9 +923,7 @@ describe("graph", () => {
|
||||
* create a graph with just that edge and its two endpoints, and
|
||||
* for each isolated node createa graph with just that node.
|
||||
*/
|
||||
function edgeDecomposition<NP, EP>(
|
||||
originalGraph: Graph<NP, EP>
|
||||
): Graph<NP, EP>[] {
|
||||
function edgeDecomposition(originalGraph: Graph): Graph[] {
|
||||
const edgeGraphs = originalGraph.edges().map((edge) => {
|
||||
const miniGraph = new Graph();
|
||||
miniGraph.addNode(originalGraph.node(edge.src));
|
||||
@ -1001,17 +997,16 @@ describe("graph", () => {
|
||||
payload: null,
|
||||
}),
|
||||
};
|
||||
const g1: Graph<string, number> = new Graph()
|
||||
const g1: Graph = new Graph()
|
||||
.addNode(data.a())
|
||||
.addNode(data.b())
|
||||
.addEdge(data.u());
|
||||
const g2: Graph<boolean, null> = new Graph()
|
||||
const g2: Graph = new Graph()
|
||||
.addNode(data.c())
|
||||
.addNode(data.d())
|
||||
.addEdge(data.v());
|
||||
type ResultGraph = Graph<string | boolean, number | null>;
|
||||
const result: ResultGraph = Graph.mergeConservative(g1, g2);
|
||||
const expected: ResultGraph = new Graph()
|
||||
const result = Graph.mergeConservative(g1, g2);
|
||||
const expected = new Graph()
|
||||
.addNode(data.a())
|
||||
.addNode(data.b())
|
||||
.addEdge(data.u())
|
||||
@ -1022,7 +1017,7 @@ describe("graph", () => {
|
||||
});
|
||||
|
||||
it("conservatively rejects a graph with conflicting nodes", () => {
|
||||
const makeGraph: (nodePayload: string) => Graph<*, *> = (nodePayload) =>
|
||||
const makeGraph: (nodePayload: string) => Graph = (nodePayload) =>
|
||||
new Graph().addNode({
|
||||
address: demoData.makeAddress("conflicting-node", "EXPERIMENT"),
|
||||
payload: nodePayload,
|
||||
@ -1037,7 +1032,7 @@ describe("graph", () => {
|
||||
it("conservatively rejects a graph with conflicting edges", () => {
|
||||
const srcAddress = demoData.makeAddress("src", "EXPERIMENT");
|
||||
const dstAddress = demoData.makeAddress("dst", "EXPERIMENT");
|
||||
const makeGraph: (edgePayload: string) => Graph<*, *> = (edgePayload) =>
|
||||
const makeGraph: (edgePayload: string) => Graph = (edgePayload) =>
|
||||
new Graph()
|
||||
.addNode({address: srcAddress, payload: {}})
|
||||
.addNode({address: dstAddress, payload: {}})
|
||||
@ -1149,7 +1144,6 @@ describe("graph", () => {
|
||||
address: demoData.makeAddress("hello", "EXPERIMENT"),
|
||||
payload: 17,
|
||||
};
|
||||
// This will be a Graph<string | number, *>.
|
||||
new Graph().addNode(stringNode).addNode(numberNode);
|
||||
});
|
||||
});
|
||||
@ -1176,7 +1170,6 @@ describe("graph", () => {
|
||||
dst: dst.address,
|
||||
payload: 18,
|
||||
};
|
||||
// This will be a Graph<{}, string | number>.
|
||||
new Graph()
|
||||
.addNode(src)
|
||||
.addNode(dst)
|
||||
@ -1205,12 +1198,6 @@ describe("graph", () => {
|
||||
expect(g1.equals(g2)).toBe(true);
|
||||
expect(g1.equals(demoData.advancedMealGraph())).toBe(true);
|
||||
});
|
||||
|
||||
function _unused_itAllowsUpcastingPayloadTypes(
|
||||
g: Graph<{x: string, y: number}, boolean>
|
||||
): Graph<{x: string}, ?boolean> {
|
||||
return g.copy();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -4,10 +4,10 @@ import type {Address} from "./address";
|
||||
import type {Edge, Graph, Node} from "./graph";
|
||||
|
||||
export class NodeReference<+T> {
|
||||
_graph: Graph<any, any>;
|
||||
_graph: Graph;
|
||||
_address: Address;
|
||||
|
||||
constructor(g: Graph<any, any>, a: Address) {
|
||||
constructor(g: Graph, a: Address) {
|
||||
this._graph = g;
|
||||
this._address = a;
|
||||
}
|
||||
@ -25,7 +25,7 @@ export class NodeReference<+T> {
|
||||
}));
|
||||
}
|
||||
|
||||
graph(): Graph<any, any> {
|
||||
graph(): Graph {
|
||||
return this._graph;
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ export type EdgePayload = IncludesEdgePayload;
|
||||
const NON_SLUG_CHARACTER: RegExp = /[^a-z]/g;
|
||||
|
||||
export function artifactAddress(
|
||||
graph: Graph<NodePayload, EdgePayload>,
|
||||
graph: Graph,
|
||||
repoOwner: string,
|
||||
repoName: string,
|
||||
artifactName: string
|
||||
|
@ -6,14 +6,7 @@ import {StyleSheet, css} from "aphrodite/no-important";
|
||||
import "./pluginAdapter";
|
||||
|
||||
import type {Graph, Node} from "../../../core/graph";
|
||||
import type {
|
||||
NodePayload as GithubNodePayload,
|
||||
EdgePayload as GithubEdgePayload,
|
||||
} from "../../github/types";
|
||||
import type {
|
||||
NodePayload as ArtifactNodePayload,
|
||||
EdgePayload as ArtifactEdgePayload,
|
||||
} from "../artifactPlugin";
|
||||
import type {NodePayload as ArtifactNodePayload} from "../artifactPlugin";
|
||||
import type {Settings} from "./SettingsConfig";
|
||||
import {ArtifactGraphEditor} from "./ArtifactGraphEditor";
|
||||
import {ContributionList} from "./ContributionList";
|
||||
@ -24,8 +17,8 @@ import standardAdapterSet from "./standardAdapterSet";
|
||||
type Props = {};
|
||||
type State = {
|
||||
artifacts: Node<ArtifactNodePayload>[],
|
||||
githubGraph: ?Graph<GithubNodePayload, GithubEdgePayload>,
|
||||
artifactGraph: ?Graph<ArtifactNodePayload, ArtifactEdgePayload>,
|
||||
githubGraph: ?Graph,
|
||||
artifactGraph: ?Graph,
|
||||
settings: Settings,
|
||||
};
|
||||
|
||||
|
@ -4,16 +4,16 @@ import React from "react";
|
||||
|
||||
import type {Node} from "../../../core/graph";
|
||||
import type {Settings} from "./SettingsConfig";
|
||||
import type {NodePayload, EdgePayload} from "../artifactPlugin";
|
||||
import type {NodePayload} from "../artifactPlugin";
|
||||
import {Graph} from "../../../core/graph";
|
||||
import {artifactAddress} from "../artifactPlugin";
|
||||
|
||||
type Props = {
|
||||
settings: Settings,
|
||||
onChange: (Graph<NodePayload, EdgePayload>) => void,
|
||||
onChange: (Graph) => void,
|
||||
};
|
||||
type State = {
|
||||
graph: Graph<NodePayload, EdgePayload>,
|
||||
graph: Graph,
|
||||
artifactInProgressName: string,
|
||||
};
|
||||
|
||||
|
@ -7,7 +7,7 @@ import {AdapterSet} from "./adapterSet";
|
||||
import {Graph} from "../../../core/graph";
|
||||
|
||||
type Props = {
|
||||
graph: ?Graph<any, any>,
|
||||
graph: ?Graph,
|
||||
adapters: AdapterSet,
|
||||
};
|
||||
type State = {
|
||||
@ -39,7 +39,7 @@ export class ContributionList extends React.Component<Props, State> {
|
||||
if (this.props.graph == null) {
|
||||
return null;
|
||||
}
|
||||
const graph: Graph<any, any> = this.props.graph;
|
||||
const graph: Graph = this.props.graph;
|
||||
const typesByPlugin: {[pluginName: string]: Set<string>} = {};
|
||||
graph.nodes().forEach((node) => {
|
||||
const adapter = this.props.adapters.getAdapter(node);
|
||||
@ -88,7 +88,7 @@ export class ContributionList extends React.Component<Props, State> {
|
||||
if (this.props.graph == null) {
|
||||
return <div>(no graph)</div>;
|
||||
} else {
|
||||
const graph: Graph<any, any> = this.props.graph;
|
||||
const graph: Graph = this.props.graph;
|
||||
const {typeFilter} = this.state;
|
||||
const shouldDisplay: (node: Node<any>) => boolean = typeFilter
|
||||
? (node) => {
|
||||
|
@ -19,8 +19,6 @@ function createTestData(): * {
|
||||
type PayloadA = number;
|
||||
type PayloadB = boolean;
|
||||
type PayloadC = string;
|
||||
type NodePayload = PayloadA | PayloadB | PayloadC;
|
||||
type EdgePayload = null;
|
||||
|
||||
const PLUGIN_A = "sourcecred/example-plugin-a";
|
||||
const PLUGIN_B = "sourcecred/example-plugin-b";
|
||||
@ -71,7 +69,7 @@ function createTestData(): * {
|
||||
dst: nodeA3().address,
|
||||
});
|
||||
|
||||
const graph: () => Graph<NodePayload, EdgePayload> = () =>
|
||||
const graph: () => Graph = () =>
|
||||
new Graph()
|
||||
.addNode(nodeA1())
|
||||
.addNode(nodeA2())
|
||||
@ -84,7 +82,7 @@ function createTestData(): * {
|
||||
const adapterA: () => PluginAdapter<PayloadA> = () => ({
|
||||
pluginName: PLUGIN_A,
|
||||
renderer: class RendererA extends React.Component<{
|
||||
graph: Graph<any, any>,
|
||||
graph: Graph,
|
||||
node: Node<PayloadA>,
|
||||
}> {
|
||||
render() {
|
||||
@ -100,7 +98,7 @@ function createTestData(): * {
|
||||
);
|
||||
}
|
||||
},
|
||||
extractTitle(graph: Graph<NodePayload, EdgePayload>, node: Node<PayloadA>) {
|
||||
extractTitle(graph: Graph, node: Node<PayloadA>) {
|
||||
return `the number ${String(node.payload)}`;
|
||||
},
|
||||
});
|
||||
@ -108,7 +106,7 @@ function createTestData(): * {
|
||||
const adapterB: () => PluginAdapter<PayloadB> = () => ({
|
||||
pluginName: PLUGIN_B,
|
||||
renderer: class RendererB extends React.Component<{
|
||||
graph: Graph<any, any>,
|
||||
graph: Graph,
|
||||
node: Node<PayloadB>,
|
||||
}> {
|
||||
render() {
|
||||
@ -120,7 +118,7 @@ function createTestData(): * {
|
||||
);
|
||||
}
|
||||
},
|
||||
extractTitle(graph: Graph<NodePayload, EdgePayload>, node: Node<PayloadB>) {
|
||||
extractTitle(graph: Graph, node: Node<PayloadB>) {
|
||||
return String(node.payload).toUpperCase() + "!";
|
||||
},
|
||||
});
|
||||
|
@ -5,15 +5,11 @@ import React from "react";
|
||||
import type {Graph} from "../../../core/graph";
|
||||
import type {Settings} from "./SettingsConfig";
|
||||
import fetchGithubRepo from "../../github/fetchGithubRepo";
|
||||
import type {
|
||||
NodePayload as GithubNodePayload,
|
||||
EdgePayload as GithubEdgePayload,
|
||||
} from "../../github/types";
|
||||
import {parse} from "../../github/parser";
|
||||
|
||||
type Props = {
|
||||
settings: Settings,
|
||||
onCreateGraph: (graph: Graph<GithubNodePayload, GithubEdgePayload>) => void,
|
||||
onCreateGraph: (graph: Graph) => void,
|
||||
};
|
||||
|
||||
export class GithubGraphFetcher extends React.Component<Props> {
|
||||
|
@ -23,7 +23,7 @@ const adapter: PluginAdapter<NodePayload> = {
|
||||
pluginName: PLUGIN_NAME,
|
||||
|
||||
renderer: class GithubNodeRenderer extends React.Component<{
|
||||
graph: Graph<any, any>,
|
||||
graph: Graph,
|
||||
node: Node<NodePayload>,
|
||||
}> {
|
||||
render() {
|
||||
|
@ -5,8 +5,6 @@ import type {ComponentType} from "react";
|
||||
|
||||
export interface PluginAdapter<-NodePayload> {
|
||||
pluginName: string;
|
||||
renderer: $Subtype<
|
||||
ComponentType<{graph: Graph<any, any>, node: Node<NodePayload>}>
|
||||
>;
|
||||
extractTitle(graph: Graph<any, any>, node: Node<NodePayload>): string;
|
||||
renderer: $Subtype<ComponentType<{graph: Graph, node: Node<NodePayload>}>>;
|
||||
extractTitle(graph: Graph, node: Node<NodePayload>): string;
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
import cloneAndLoadRepository from "./cloneAndLoadRepository";
|
||||
import {createGraph} from "./createGraph";
|
||||
import type {NodePayload, EdgePayload} from "./types";
|
||||
import type {Graph} from "../../core/graph";
|
||||
|
||||
/**
|
||||
@ -18,7 +17,7 @@ import type {Graph} from "../../core/graph";
|
||||
export default function fetchGitGraph(
|
||||
repoOwner: string,
|
||||
repoName: string
|
||||
): Graph<NodePayload, EdgePayload> {
|
||||
): Graph {
|
||||
const repo = cloneAndLoadRepository(repoOwner, repoName);
|
||||
return createGraph(repo);
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import type {
|
||||
BecomesEdgePayload,
|
||||
BlobNodePayload,
|
||||
Commit,
|
||||
EdgePayload,
|
||||
HasContentsEdgePayload,
|
||||
Hash,
|
||||
IncludesEdgePayload,
|
||||
@ -37,7 +36,7 @@ import {
|
||||
import {_makeAddress} from "./address";
|
||||
|
||||
class GitGraphCreator {
|
||||
createGraph(repository: Repository): Graph<NodePayload, EdgePayload> {
|
||||
createGraph(repository: Repository): Graph {
|
||||
const treeAndNameToSubmoduleUrls = this.treeAndNameToSubmoduleUrls(
|
||||
repository
|
||||
);
|
||||
@ -198,7 +197,7 @@ class GitGraphCreator {
|
||||
return result;
|
||||
}
|
||||
|
||||
becomesEdges(repository: Repository): Graph<NodePayload, EdgePayload> {
|
||||
becomesEdges(repository: Repository): Graph {
|
||||
const result = new Graph();
|
||||
for (const {
|
||||
childCommit,
|
||||
@ -300,8 +299,6 @@ export function* findBecomesEdges(
|
||||
}
|
||||
}
|
||||
|
||||
export function createGraph(
|
||||
repository: Repository
|
||||
): Graph<NodePayload, EdgePayload> {
|
||||
export function createGraph(repository: Repository): Graph {
|
||||
return new GitGraphCreator().createGraph(repository);
|
||||
}
|
||||
|
@ -60,8 +60,8 @@ function assertAddressType(address: Address, t: NodeType) {
|
||||
}
|
||||
|
||||
export class GraphPorcelain {
|
||||
graph: Graph<any, any>;
|
||||
constructor(graph: Graph<any, any>) {
|
||||
graph: Graph;
|
||||
constructor(graph: Graph) {
|
||||
this.graph = graph;
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
* docstring of the default export for more details.
|
||||
*/
|
||||
|
||||
import type {NodePayload, EdgePayload} from "./types";
|
||||
import type {Graph} from "../../core/graph";
|
||||
import fetchGithubRepo from "./fetchGithubRepo";
|
||||
import {parse} from "./parser";
|
||||
@ -19,13 +18,13 @@ import {parse} from "./parser";
|
||||
* @param {String} token
|
||||
* authentication token to be used for the GitHub API; generate a
|
||||
* token at: https://github.com/settings/tokens
|
||||
* @return {Promise<Graph<NodePayload, EdgePayload>}
|
||||
* @return {Promise<Graph>}
|
||||
* a promise that resolves to a GitHub contribution graph
|
||||
*/
|
||||
export default function fetchGithubGraph(
|
||||
repoOwner: string,
|
||||
repoName: string,
|
||||
token: string
|
||||
): Promise<Graph<NodePayload, EdgePayload>> {
|
||||
): Promise<Graph> {
|
||||
return fetchGithubRepo(repoOwner, repoName, token).then((x) => parse(x));
|
||||
}
|
||||
|
@ -6,8 +6,6 @@ import type {Node, Edge} from "../../core/graph";
|
||||
import type {
|
||||
NodeType,
|
||||
EdgeType,
|
||||
NodePayload,
|
||||
EdgePayload,
|
||||
PullRequestReviewNodePayload,
|
||||
RepositoryNodePayload,
|
||||
AuthorNodePayload,
|
||||
@ -37,9 +35,7 @@ import {Graph, edgeID} from "../../core/graph";
|
||||
import {findReferences} from "./findReferences";
|
||||
import {commitAddress} from "../git/address";
|
||||
|
||||
export function parse(
|
||||
githubResponseJSON: GithubResponseJSON
|
||||
): Graph<NodePayload, EdgePayload> {
|
||||
export function parse(githubResponseJSON: GithubResponseJSON): Graph {
|
||||
const parser = new GithubParser();
|
||||
parser.addData(githubResponseJSON);
|
||||
parser.addReferenceEdges();
|
||||
@ -47,7 +43,7 @@ export function parse(
|
||||
}
|
||||
|
||||
class GithubParser {
|
||||
graph: Graph<NodePayload, EdgePayload>;
|
||||
graph: Graph;
|
||||
|
||||
constructor() {
|
||||
this.graph = new Graph();
|
||||
|
@ -1,7 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import {AUTHORS_EDGE_TYPE, CONTAINS_EDGE_TYPE} from "./types";
|
||||
import type {NodePayload, EdgePayload} from "./types";
|
||||
import {parse} from "./parser";
|
||||
import type {GithubResponseJSON, PullRequestJSON, IssueJSON} from "./graphql";
|
||||
import {Graph} from "../../core/graph";
|
||||
@ -92,7 +91,7 @@ describe("GithubParser", () => {
|
||||
function parseExample({
|
||||
issues: issueNums = [],
|
||||
prs: prNums = [],
|
||||
}: ExampleInput): Graph<NodePayload, EdgePayload> {
|
||||
}: ExampleInput): Graph {
|
||||
const issues = issueNums.map(getIssue);
|
||||
const pullRequests = prNums.map(getPR);
|
||||
const exampleData: GithubResponseJSON = {
|
||||
|
@ -22,7 +22,6 @@ import type {
|
||||
AuthorNodePayload,
|
||||
AuthorSubtype,
|
||||
CommentNodePayload,
|
||||
EdgePayload,
|
||||
IssueNodePayload,
|
||||
MergedAsEdgePayload,
|
||||
NodePayload,
|
||||
@ -99,9 +98,9 @@ function asGithubReference(
|
||||
}
|
||||
|
||||
export class GraphPorcelain {
|
||||
graph: Graph<NodePayload, EdgePayload>;
|
||||
graph: Graph;
|
||||
|
||||
constructor(graph: Graph<NodePayload, EdgePayload>) {
|
||||
constructor(graph: Graph) {
|
||||
this.graph = graph;
|
||||
}
|
||||
|
||||
|
@ -21,14 +21,14 @@ export type EdgePayload = GitEdgePayload | GithubEdgePayload;
|
||||
* the GitHub username of the owner of the repository to be cloned
|
||||
* @param {String} repoName
|
||||
* the name of the repository to be cloned
|
||||
* @return {Promise<Graph<NodePayload, EdgePayload>>}
|
||||
* @return {Promise<Graph>}
|
||||
* a Promise containing the combined contribution graph
|
||||
*/
|
||||
export function loadCombinedGraph(
|
||||
repoOwner: string,
|
||||
repoName: string,
|
||||
token: string
|
||||
): Promise<Graph<NodePayload, EdgePayload>> {
|
||||
): Promise<Graph> {
|
||||
const githubGraphPromise = fetchGithubGraph(repoOwner, repoName, token);
|
||||
const gitGraph = cloneGitGraph(repoOwner, repoName);
|
||||
return githubGraphPromise.then((x) => Graph.mergeConservative(gitGraph, x));
|
||||
|
Loading…
x
Reference in New Issue
Block a user