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:
Dandelion Mané 2018-05-24 12:03:47 -07:00 committed by GitHub
parent e20b794edc
commit 418d046691
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 93 additions and 139 deletions

View File

@ -13,7 +13,7 @@ type Props = {};
type State = {
repoOwner: string,
repoName: string,
graph: ?Graph<mixed, mixed>,
graph: ?Graph,
pagerankResult: ?PagerankResult,
};

View File

@ -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)

View File

@ -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,
|};

View File

@ -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}));
});

View File

@ -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) => {

View File

@ -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();
}
});
});
});

View File

@ -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;
}

View File

@ -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

View File

@ -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,
};

View File

@ -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,
};

View File

@ -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) => {

View File

@ -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() + "!";
},
});

View File

@ -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> {

View File

@ -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() {

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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));
}

View File

@ -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();

View File

@ -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 = {

View File

@ -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;
}

View File

@ -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));