Implement the GitHub plugin adapter (#461)

Summary:
This enables grabbing the GitHub relational view from disk and
converting it to a graph on the client.

Paired with @decentralion.

Test Plan:
For testing, information about the GitHub graph is printed when you
click the “Load data” button in the UI. Do so.

wchargin-branch: github-plugin-adapter
This commit is contained in:
William Chargin 2018-06-29 18:22:15 -07:00 committed by GitHub
parent 95c206b346
commit 4767dce749
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 80 additions and 1 deletions

View File

@ -4,6 +4,7 @@ import React from "react";
import {StyleSheet, css} from "aphrodite/no-important";
import LocalStore from "./LocalStore";
import {createPluginAdapter as createGithubAdapter} from "../../plugins/github/pluginAdapter";
type Props = {};
type State = {
@ -81,7 +82,12 @@ export default class App extends React.Component<Props, State> {
console.error(`Invalid repository name: ${JSON.stringify(repoName)}`);
return;
}
console.log(`Would load data for: ${repoOwner}/${repoName}.`);
createGithubAdapter(repoOwner, repoName).then((githubAdapter) => {
const graph = githubAdapter.graph();
const nodeCount = Array.from(graph.nodes()).length;
const edgeCount = Array.from(graph.edges()).length;
console.log(`Loaded graph: ${nodeCount} nodes, ${edgeCount} edges.`);
});
}
}

View File

@ -0,0 +1,13 @@
// @flow
import type {Graph, NodeAddressT} from "../core/graph";
export interface Renderer {
nodeDescription(NodeAddressT): string;
}
export interface PluginAdapter {
graph(): Graph;
renderer(): Renderer;
nodePrefix(): NodeAddressT;
}

View File

@ -0,0 +1,60 @@
// @flow
import type {
PluginAdapter as IPluginAdapter,
Renderer as IRenderer,
} from "../../app/pluginAdapter";
import {type Graph, NodeAddress} from "../../core/graph";
import {createGraph} from "./createGraph";
import * as N from "./nodes";
import {RelationalView} from "./relationalView";
import {description} from "./render";
export async function createPluginAdapter(
repoOwner: string,
repoName: string
): Promise<IPluginAdapter> {
const url = `/api/v1/data/data/${repoOwner}/${repoName}/github/view.json`;
const response = await fetch(url);
if (!response.ok) {
return Promise.reject(response);
}
const json = await response.json();
const view = RelationalView.fromJSON(json);
const graph = createGraph(view);
return new PluginAdapter(view, graph);
}
class PluginAdapter implements IPluginAdapter {
+_view: RelationalView;
+_graph: Graph;
constructor(view: RelationalView, graph: Graph) {
this._view = view;
this._graph = graph;
}
graph() {
return this._graph;
}
renderer() {
return new Renderer(this._view);
}
nodePrefix() {
return N._Prefix.base;
}
}
class Renderer implements IRenderer {
+_view: RelationalView;
constructor(view) {
this._view = view;
}
nodeDescription(node) {
// This cast is unsound, and might throw at runtime, but won't have
// silent failures or cause problems down the road.
const address = N.fromRaw((node: any));
const entity = this._view.entity(address);
if (entity == null) {
throw new Error(`unknown entity: ${NodeAddress.toString(node)}`);
}
return description(entity);
}
}