Factor out plugin declarations (#947)

The plugin adapters are specific to `app/` and have logic for fetching
data from the backend, producing React nodes for descriptions, et
cetera.

However, they also have information that is generic to the plugin
itself: its name, its node/edge prefixes, and its types.

This method factors out the generic info into a `PluginDeclaration`,
which is a type (rather than an interface). Then, the plugin adapter has
a `declaration` method which returns the declaration.

Current users of the plugin adapters get additional mechanical
complexity because they need to call `.declaration().property` rather
than `.property()`, but this is not too significant.

The main benefit is that #704 is unblocked, as the cli `analyze` command
will be able to get necessary information from the declaration. As an
added benefit, the organization of plugin code is somewhat improved.

Test plan: `yarn test` sufficies, but I also ran `yarn start` and
checked the UI behavior to be safe.
This commit is contained in:
Dandelion Mané 2018-10-30 00:13:11 +00:00 committed by GitHub
parent 5f2cc56172
commit cb30023a02
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 300 additions and 240 deletions

View File

@ -0,0 +1,12 @@
// @flow
import {type NodeAddressT, type EdgeAddressT} from "../core/graph";
import type {EdgeType, NodeType} from "./types";
export type PluginDeclaration = {|
+name: string,
+nodePrefix: NodeAddressT,
+edgePrefix: EdgeAddressT,
+nodeTypes: $ReadOnlyArray<NodeType>,
+edgeTypes: $ReadOnlyArray<EdgeType>,
|};

View File

@ -25,13 +25,13 @@ export class StaticAdapterSet {
this._typeEdgeTrie = new EdgeTrie();
const usedPluginNames = new Set();
this._adapters.forEach((a) => {
const name = a.name();
const name = a.declaration().name;
if (usedPluginNames.has(name)) {
throw new Error(`Multiple plugins with name "${name}"`);
}
usedPluginNames.add(name);
this._adapterNodeTrie.add(a.nodePrefix(), a);
this._adapterEdgeTrie.add(a.edgePrefix(), a);
this._adapterNodeTrie.add(a.declaration().nodePrefix, a);
this._adapterEdgeTrie.add(a.declaration().edgePrefix, a);
});
this.nodeTypes().forEach((t) => this._typeNodeTrie.add(t.prefix, t));
this.edgeTypes().forEach((t) => this._typeEdgeTrie.add(t.prefix, t));
@ -42,11 +42,11 @@ export class StaticAdapterSet {
}
nodeTypes(): NodeType[] {
return [].concat(...this._adapters.map((x) => x.nodeTypes()));
return [].concat(...this._adapters.map((x) => x.declaration().nodeTypes));
}
edgeTypes(): EdgeType[] {
return [].concat(...this._adapters.map((x) => x.edgeTypes()));
return [].concat(...this._adapters.map((x) => x.declaration().edgeTypes));
}
adapterMatchingNode(x: NodeAddressT): StaticPluginAdapter {
@ -87,8 +87,8 @@ export class DynamicAdapterSet {
this._adapterNodeTrie = new NodeTrie();
this._adapterEdgeTrie = new EdgeTrie();
this._adapters.forEach((a) => {
this._adapterNodeTrie.add(a.static().nodePrefix(), a);
this._adapterEdgeTrie.add(a.static().edgePrefix(), a);
this._adapterNodeTrie.add(a.static().declaration().nodePrefix, a);
this._adapterEdgeTrie.add(a.static().declaration().edgePrefix, a);
});
}

View File

@ -27,26 +27,26 @@ describe("app/adapters/adapterSet", () => {
});
it("always includes the fallback plugin", () => {
const {sas} = example();
expect(sas.adapters()[0].name()).toBe(FALLBACK_NAME);
expect(sas.adapters()[0].declaration().name).toBe(FALLBACK_NAME);
});
it("includes the manually provided plugin adapters", () => {
const {x, sas} = example();
expect(sas.adapters()[1].name()).toBe(x.name());
expect(sas.adapters()[1].declaration().name).toBe(x.declaration().name);
});
it("aggregates NodeTypes across plugins", () => {
const {sas} = example();
const nodeTypes = sas.nodeTypes();
const expectedNumNodeTypes =
new FactorioStaticAdapter().nodeTypes().length +
new FallbackStaticAdapter().nodeTypes().length;
new FactorioStaticAdapter().declaration().nodeTypes.length +
new FallbackStaticAdapter().declaration().nodeTypes.length;
expect(nodeTypes).toHaveLength(expectedNumNodeTypes);
});
it("aggregates EdgeTypes across plugins", () => {
const {sas} = example();
const edgeTypes = sas.edgeTypes();
const expectedNumEdgeTypes =
new FactorioStaticAdapter().edgeTypes().length +
new FallbackStaticAdapter().edgeTypes().length;
new FactorioStaticAdapter().declaration().edgeTypes.length +
new FallbackStaticAdapter().declaration().edgeTypes.length;
expect(edgeTypes).toHaveLength(expectedNumEdgeTypes);
});
it("finds adapter matching a node", () => {
@ -54,24 +54,24 @@ describe("app/adapters/adapterSet", () => {
const matching = sas.adapterMatchingNode(
NodeAddress.fromParts(["factorio", "inserter"])
);
expect(matching.name()).toBe(x.name());
expect(matching.declaration().name).toBe(x.declaration().name);
});
it("finds adapter matching an edge", () => {
const {x, sas} = example();
const matching = sas.adapterMatchingEdge(
EdgeAddress.fromParts(["factorio", "assembles"])
);
expect(matching.name()).toBe(x.name());
expect(matching.declaration().name).toBe(x.declaration().name);
});
it("finds fallback adapter for unregistered node", () => {
const {sas} = example();
const adapter = sas.adapterMatchingNode(NodeAddress.fromParts(["weird"]));
expect(adapter.name()).toBe(FALLBACK_NAME);
expect(adapter.declaration().name).toBe(FALLBACK_NAME);
});
it("finds fallback adapter for unregistered edge", () => {
const {sas} = example();
const adapter = sas.adapterMatchingEdge(EdgeAddress.fromParts(["weird"]));
expect(adapter.name()).toBe(FALLBACK_NAME);
expect(adapter.declaration().name).toBe(FALLBACK_NAME);
});
it("finds type matching a node", () => {
const {sas} = example();
@ -133,8 +133,8 @@ describe("app/adapters/adapterSet", () => {
});
it("allows accessing the dynamic adapters", async () => {
const {sas, das} = await example();
expect(das.adapters().map((a) => a.static().name())).toEqual(
sas.adapters().map((a) => a.name())
expect(das.adapters().map((a) => a.static().declaration().name)).toEqual(
sas.adapters().map((a) => a.declaration().name)
);
});
it("allows retrieval of the aggregated graph", async () => {
@ -147,24 +147,24 @@ describe("app/adapters/adapterSet", () => {
const matching = das.adapterMatchingNode(
NodeAddress.fromParts(["factorio", "inserter"])
);
expect(matching.static().name()).toBe(x.name());
expect(matching.static().declaration().name).toBe(x.declaration().name);
});
it("finds adapter matching an edge", async () => {
const {x, das} = await example();
const matching = das.adapterMatchingEdge(
EdgeAddress.fromParts(["factorio", "assembles"])
);
expect(matching.static().name()).toBe(x.name());
expect(matching.static().declaration().name).toBe(x.declaration().name);
});
it("finds fallback adapter for unregistered node", async () => {
const {das} = await example();
const adapter = das.adapterMatchingNode(NodeAddress.fromParts(["weird"]));
expect(adapter.static().name()).toBe(FALLBACK_NAME);
expect(adapter.static().declaration().name).toBe(FALLBACK_NAME);
});
it("finds fallback adapter for unregistered edge", async () => {
const {das} = await example();
const adapter = das.adapterMatchingEdge(EdgeAddress.fromParts(["weird"]));
expect(adapter.static().name()).toBe(FALLBACK_NAME);
expect(adapter.static().declaration().name).toBe(FALLBACK_NAME);
});
});
});

View File

@ -9,6 +9,7 @@ import {
} from "../../core/graph";
import type {StaticPluginAdapter, DynamicPluginAdapter} from "./pluginAdapter";
import type {EdgeType, NodeType} from "../../analysis/types";
import type {PluginDeclaration} from "../../analysis/pluginDeclaration";
import {StaticAdapterSet} from "./adapterSet";
import {makeRepoId, type RepoId} from "../../core/repoId";
@ -43,22 +44,18 @@ export const transportsEdgeType: EdgeType = Object.freeze({
prefix: EdgeAddress.fromParts(["factorio", "transports"]),
});
export const declaration: PluginDeclaration = Object.freeze({
name: "Factorio demo adapter",
nodePrefix: NodeAddress.fromParts(["factorio"]),
nodeTypes: [inserterNodeType, machineNodeType],
edgePrefix: EdgeAddress.fromParts(["factorio"]),
edgeTypes: [assemblesEdgeType, transportsEdgeType],
});
export class FactorioStaticAdapter implements StaticPluginAdapter {
loadingMock: Function;
name() {
return "Factorio demo adapter";
}
nodePrefix() {
return NodeAddress.fromParts(["factorio"]);
}
nodeTypes() {
return [inserterNodeType, machineNodeType];
}
edgePrefix() {
return EdgeAddress.fromParts(["factorio"]);
}
edgeTypes() {
return [assemblesEdgeType, transportsEdgeType];
declaration() {
return declaration;
}
async load(assets: Assets, repoId: RepoId): Promise<DynamicPluginAdapter> {
if (this.loadingMock) {

View File

@ -28,25 +28,17 @@ export const fallbackEdgeType = Object.freeze({
prefix: EdgeAddress.empty,
});
export const fallbackDeclaration = Object.freeze({
name: FALLBACK_NAME,
nodePrefix: NodeAddress.empty,
edgePrefix: EdgeAddress.empty,
nodeTypes: [fallbackNodeType],
edgeTypes: [fallbackEdgeType],
});
export class FallbackStaticAdapter implements StaticPluginAdapter {
name() {
return FALLBACK_NAME;
}
nodePrefix() {
return NodeAddress.empty;
}
edgePrefix() {
return EdgeAddress.empty;
}
nodeTypes() {
return [fallbackNodeType];
}
edgeTypes() {
return [fallbackEdgeType];
declaration() {
return fallbackDeclaration;
}
load(_unused_assets: Assets, _unused_repoId: RepoId) {

View File

@ -1,17 +1,13 @@
// @flow
import {type Node as ReactNode} from "react";
import {Graph, type NodeAddressT, type EdgeAddressT} from "../../core/graph";
import {Graph, type NodeAddressT} from "../../core/graph";
import type {Assets} from "../assets";
import type {RepoId} from "../../core/repoId";
import type {EdgeType, NodeType} from "../../analysis/types";
import type {PluginDeclaration} from "../../analysis/pluginDeclaration";
export interface StaticPluginAdapter {
name(): string;
nodePrefix(): NodeAddressT;
edgePrefix(): EdgeAddressT;
nodeTypes(): NodeType[];
edgeTypes(): EdgeType[];
declaration(): PluginDeclaration;
load(assets: Assets, repoId: RepoId): Promise<DynamicPluginAdapter>;
}

View File

@ -95,17 +95,17 @@ export class PagerankTable extends React.PureComponent<
function optionGroup(adapter: DynamicPluginAdapter) {
const header = (
<option
key={adapter.static().nodePrefix()}
value={adapter.static().nodePrefix()}
key={adapter.static().declaration().nodePrefix}
value={adapter.static().declaration().nodePrefix}
style={{fontWeight: "bold"}}
>
{adapter.static().name()}
{adapter.static().declaration().name}
</option>
);
const entries = adapter
.static()
.nodeTypes()
.map((type) => (
.declaration()
.nodeTypes.map((type) => (
<option key={type.prefix} value={type.prefix}>
{"\u2003" + type.name}
</option>
@ -122,10 +122,11 @@ export class PagerankTable extends React.PureComponent<
}}
>
<option value={NodeAddress.empty}>Show all</option>
{sortBy(adapters.adapters(), (a: DynamicPluginAdapter) =>
a.static().name()
{sortBy(
adapters.adapters(),
(a: DynamicPluginAdapter) => a.static().declaration().name
)
.filter((a) => a.static().name() !== FALLBACK_NAME)
.filter((a) => a.static().declaration().name !== FALLBACK_NAME)
.map(optionGroup)}
</select>
</label>

View File

@ -66,14 +66,14 @@ export class PluginWeightConfig extends React.Component<Props> {
_validateWeightedTypesWithAdapter() {
const expectedNodePrefixes = new Set(
this.props.adapter.nodeTypes().map((x) => x.prefix)
this.props.adapter.declaration().nodeTypes.map((x) => x.prefix)
);
const actualNodePrefixes = new Set(this.props.weightedTypes.nodes.keys());
if (!deepEqual(expectedNodePrefixes, actualNodePrefixes)) {
throw new Error("weightedTypes has wrong node prefixes for adapter");
}
const expectedEdgePrefixes = new Set(
this.props.adapter.edgeTypes().map((x) => x.prefix)
this.props.adapter.declaration().edgeTypes.map((x) => x.prefix)
);
const actualEdgePrefixes = new Set(this.props.weightedTypes.edges.keys());
if (!deepEqual(expectedEdgePrefixes, actualEdgePrefixes)) {
@ -85,7 +85,7 @@ export class PluginWeightConfig extends React.Component<Props> {
this._validateWeightedTypesWithAdapter();
return (
<div>
<h3>{this.props.adapter.name()}</h3>
<h3>{this.props.adapter.declaration().name}</h3>
<h4 style={{marginBottom: "0.3em"}}>Node weights</h4>
{this._renderNodeWeightControls()}
<h4 style={{marginBottom: "0.3em"}}>Edge weights</h4>

View File

@ -41,7 +41,7 @@ describe("app/credExplorer/weights/PluginWeightConfig", () => {
it("renders a NodeTypeConfig for each node type", () => {
const {el, adapter} = example();
const ntc = el.find(NodeTypeConfig);
const nodeTypes = adapter.nodeTypes();
const nodeTypes = adapter.declaration().nodeTypes;
for (let i = 0; i < nodeTypes.length; i++) {
const weightedType = defaultWeightedNodeType(nodeTypes[i]);
expect(ntc.at(i).props().weightedType).toEqual(weightedType);
@ -50,7 +50,7 @@ describe("app/credExplorer/weights/PluginWeightConfig", () => {
it("renders a EdgeTypeConfig for each edge type", () => {
const {el, adapter} = example();
const ntc = el.find(EdgeTypeConfig);
const edgeTypes = adapter.edgeTypes();
const edgeTypes = adapter.declaration().edgeTypes;
for (let i = 0; i < edgeTypes.length; i++) {
const weightedType = defaultWeightedEdgeType(edgeTypes[i]);
expect(ntc.at(i).props().weightedType).toEqual(weightedType);
@ -60,13 +60,17 @@ describe("app/credExplorer/weights/PluginWeightConfig", () => {
const {el, adapter, onChange} = example();
const ntc = el.find(NodeTypeConfig).at(0);
const nodes = adapter.nodeTypes().map(defaultWeightedNodeType);
const nodes = adapter
.declaration()
.nodeTypes.map(defaultWeightedNodeType);
const newWeightedType = {...nodes[0], weight: 707};
const newNodes = [newWeightedType, ...nodes.slice(1)];
const expected = {
nodes: new Map(newNodes.map((x) => [x.type.prefix, x])),
edges: new Map(
adapter.edgeTypes().map((x) => [x.prefix, defaultWeightedEdgeType(x)])
adapter
.declaration()
.edgeTypes.map((x) => [x.prefix, defaultWeightedEdgeType(x)])
),
};
ntc.props().onChange(newWeightedType);
@ -76,12 +80,16 @@ describe("app/credExplorer/weights/PluginWeightConfig", () => {
it("EdgeTypeConfig onChange wired properly", () => {
const {el, adapter, onChange} = example();
const ntc = el.find(EdgeTypeConfig).at(0);
const edges = adapter.edgeTypes().map(defaultWeightedEdgeType);
const edges = adapter
.declaration()
.edgeTypes.map(defaultWeightedEdgeType);
const newWeightedType = {...edges[0], weight: 707};
const newEdges = [newWeightedType, ...edges.slice(1)];
const expected = {
nodes: new Map(
adapter.nodeTypes().map((x) => [x.prefix, defaultWeightedNodeType(x)])
adapter
.declaration()
.nodeTypes.map((x) => [x.prefix, defaultWeightedNodeType(x)])
),
edges: new Map(newEdges.map((x) => [x.type.prefix, x])),
};

View File

@ -39,7 +39,7 @@ export class WeightConfig extends React.Component<Props> {
_renderPluginWeightConfigs() {
return this.props.adapters
.adapters()
.filter((x) => x.name() !== FALLBACK_NAME)
.filter((x) => x.declaration().name !== FALLBACK_NAME)
.map((adapter) => {
const onChange = (weightedTypes) => {
const newWeightedTypes = {
@ -58,13 +58,13 @@ export class WeightConfig extends React.Component<Props> {
nodes: new Map(),
edges: new Map(),
};
for (const {prefix} of adapter.nodeTypes()) {
for (const {prefix} of adapter.declaration().nodeTypes) {
pluginScopedWeightedTypes.nodes.set(
prefix,
NullUtil.get(this.props.weightedTypes.nodes.get(prefix))
);
}
for (const {prefix} of adapter.edgeTypes()) {
for (const {prefix} of adapter.declaration().edgeTypes) {
pluginScopedWeightedTypes.edges.set(
prefix,
NullUtil.get(this.props.weightedTypes.edges.get(prefix))
@ -72,7 +72,7 @@ export class WeightConfig extends React.Component<Props> {
}
return (
<PluginWeightConfig
key={adapter.name()}
key={adapter.declaration().name}
adapter={adapter}
onChange={onChange}
weightedTypes={pluginScopedWeightedTypes}

View File

@ -38,11 +38,12 @@ describe("app/credExplorer/weights/WeightConfig", () => {
const pwcs = el.find(PluginWeightConfig);
expect(pwcs).toHaveLength(adapters.adapters().length - 1);
for (const adapter of adapters.adapters()) {
if (adapter.name() === FALLBACK_NAME) {
if (adapter.declaration().name === FALLBACK_NAME) {
continue;
}
const pwc = pwcs.findWhere(
(x) => x.props().adapter.name() === adapter.name()
(x) =>
x.props().adapter.declaration().name === adapter.declaration().name
);
expect(pwc).toHaveLength(1);
}
@ -52,7 +53,9 @@ describe("app/credExplorer/weights/WeightConfig", () => {
const pwc = el
.find(PluginWeightConfig)
.findWhere(
(x) => x.props().adapter.name() === new FactorioStaticAdapter().name()
(x) =>
x.props().adapter.declaration().name ===
new FactorioStaticAdapter().declaration().name
);
expect(pwc).toHaveLength(1);
const expectedTypes = defaultWeightsForAdapter(
@ -84,7 +87,9 @@ describe("app/credExplorer/weights/WeightConfig", () => {
const factorioConfig = el
.find(PluginWeightConfig)
.findWhere(
(x) => x.props().adapter.name() === new FactorioStaticAdapter().name()
(x) =>
x.props().adapter.declaration().name ===
new FactorioStaticAdapter().declaration().name
);
factorioConfig.props().onChange(newFactorioWeights);
expect(onChange).toHaveBeenCalledTimes(1);

View File

@ -40,10 +40,14 @@ export function defaultWeightsForAdapter(
): WeightedTypes {
return {
nodes: new Map(
adapter.nodeTypes().map((x) => [x.prefix, defaultWeightedNodeType(x)])
adapter
.declaration()
.nodeTypes.map((x) => [x.prefix, defaultWeightedNodeType(x)])
),
edges: new Map(
adapter.edgeTypes().map((x) => [x.prefix, defaultWeightedEdgeType(x)])
adapter
.declaration()
.edgeTypes.map((x) => [x.prefix, defaultWeightedEdgeType(x)])
),
};
}

View File

@ -35,10 +35,14 @@ describe("app/credExplorer/weights/weights", () => {
const adapter = new FactorioStaticAdapter();
const expected = {
nodes: new Map(
adapter.nodeTypes().map((x) => [x.prefix, defaultWeightedNodeType(x)])
adapter
.declaration()
.nodeTypes.map((x) => [x.prefix, defaultWeightedNodeType(x)])
),
edges: new Map(
adapter.edgeTypes().map((x) => [x.prefix, defaultWeightedEdgeType(x)])
adapter
.declaration()
.edgeTypes.map((x) => [x.prefix, defaultWeightedEdgeType(x)])
),
};
expect(defaultWeightsForAdapter(adapter)).toEqual(expected);

View File

@ -0,0 +1,31 @@
// @flow
import type {PluginDeclaration} from "../../analysis/pluginDeclaration";
import * as N from "./nodes";
import * as E from "./edges";
const commitNodeType = Object.freeze({
name: "Commit",
pluralName: "Commits",
prefix: N.Prefix.commit,
defaultWeight: 2,
});
const hasParentEdgeType = Object.freeze({
forwardName: "has parent",
backwardName: "is parent of",
prefix: E.Prefix.hasParent,
defaultForwardWeight: 1,
defaultBackwardWeight: 1,
});
const nodeTypes = Object.freeze([commitNodeType]);
const edgeTypes = Object.freeze([hasParentEdgeType]);
export const declaration: PluginDeclaration = Object.freeze({
name: "Git",
nodePrefix: N.Prefix.base,
edgePrefix: E.Prefix.base,
nodeTypes,
edgeTypes,
});

View File

@ -5,12 +5,13 @@ import type {
} from "../../app/adapters/pluginAdapter";
import {Graph} from "../../core/graph";
import * as N from "./nodes";
import * as E from "./edges";
import {description} from "./render";
import type {Assets} from "../../app/assets";
import type {RepoId} from "../../core/repoId";
import type {Repository} from "./types";
import type {GitGateway} from "./gitGateway";
import type {PluginDeclaration} from "../../analysis/pluginDeclaration";
import {declaration} from "./declaration";
export class StaticPluginAdapter implements IStaticPluginAdapter {
_gitGateway: GitGateway;
@ -18,35 +19,8 @@ export class StaticPluginAdapter implements IStaticPluginAdapter {
constructor(gg: GitGateway): void {
this._gitGateway = gg;
}
name() {
return "Git";
}
nodePrefix() {
return N.Prefix.base;
}
edgePrefix() {
return E.Prefix.base;
}
nodeTypes() {
return [
{
name: "Commit",
pluralName: "Commits",
prefix: N.Prefix.commit,
defaultWeight: 2,
},
];
}
edgeTypes() {
return [
{
forwardName: "has parent",
backwardName: "is parent of",
prefix: E.Prefix.hasParent,
defaultForwardWeight: 1,
defaultBackwardWeight: 1,
},
];
declaration(): PluginDeclaration {
return declaration;
}
async load(assets: Assets, repoId: RepoId): Promise<IDynamicPluginAdapter> {
const baseUrl = `/api/v1/data/data/${repoId.owner}/${repoId.name}/git/`;

View File

@ -0,0 +1,151 @@
// @flow
import type {PluginDeclaration} from "../../analysis/pluginDeclaration";
import * as N from "./nodes";
import * as E from "./edges";
const repoNodeType = Object.freeze({
name: "Repository",
pluralName: "Repositories",
prefix: N.Prefix.repo,
defaultWeight: 4,
});
const issueNodeType = Object.freeze({
name: "Issue",
pluralName: "Issues",
prefix: N.Prefix.issue,
defaultWeight: 2,
});
const pullNodeType = Object.freeze({
name: "Pull request",
pluralName: "Pull requests",
prefix: N.Prefix.pull,
defaultWeight: 4,
});
const reviewNodeType = Object.freeze({
name: "Pull request review",
pluralName: "Pull request reviews",
prefix: N.Prefix.review,
defaultWeight: 1,
});
const commentNodeType = Object.freeze({
name: "Comment",
pluralName: "Comments",
prefix: N.Prefix.comment,
defaultWeight: 1,
});
const userNodeType = Object.freeze({
name: "User",
pluralName: "Users",
prefix: N.Prefix.user,
defaultWeight: 1,
});
const botNodeType = Object.freeze({
name: "Bot",
pluralName: "Bots",
prefix: N.Prefix.bot,
defaultWeight: 0.25,
});
const nodeTypes = Object.freeze([
repoNodeType,
issueNodeType,
pullNodeType,
reviewNodeType,
commentNodeType,
userNodeType,
botNodeType,
]);
const authorsEdgeType = Object.freeze({
forwardName: "authors",
backwardName: "is authored by",
defaultForwardWeight: 1 / 2,
defaultBackwardWeight: 1,
prefix: E.Prefix.authors,
});
const hasParentEdgeType = Object.freeze({
forwardName: "has parent",
backwardName: "has child",
defaultForwardWeight: 1,
defaultBackwardWeight: 1 / 4,
prefix: E.Prefix.hasParent,
});
const mergedAsEdgeType = Object.freeze({
forwardName: "merges",
backwardName: "is merged by",
defaultForwardWeight: 1 / 2,
defaultBackwardWeight: 1,
prefix: E.Prefix.mergedAs,
});
const referencesEdgeType = Object.freeze({
forwardName: "references",
backwardName: "is referenced by",
defaultForwardWeight: 1,
defaultBackwardWeight: 1 / 16,
prefix: E.Prefix.references,
});
const mentionsAuthorEdgeType = Object.freeze({
forwardName: "mentions author of",
backwardName: "has author mentioned by",
defaultForwardWeight: 1,
// TODO(#811): Probably change this to 0
defaultBackwardWeight: 1 / 32,
prefix: E.Prefix.mentionsAuthor,
});
const reactsHeartEdgeType = Object.freeze({
forwardName: "reacted ❤️ to",
backwardName: "got ❤️ from",
defaultForwardWeight: 2,
// TODO(#811): Probably change this to 0
defaultBackwardWeight: 1 / 32,
prefix: E.Prefix.reactsHeart,
});
const reactsThumbsUpEdgeType = Object.freeze({
forwardName: "reacted 👍 to",
backwardName: "got 👍 from",
defaultForwardWeight: 1,
// TODO(#811): Probably change this to 0
defaultBackwardWeight: 1 / 32,
prefix: E.Prefix.reactsThumbsUp,
});
const reactsHoorayEdgeType = Object.freeze({
forwardName: "reacted 🎉 to",
backwardName: "got 🎉 from",
defaultForwardWeight: 4,
// TODO(#811): Probably change this to 0
defaultBackwardWeight: 1 / 32,
prefix: E.Prefix.reactsHooray,
});
const edgeTypes = Object.freeze([
authorsEdgeType,
hasParentEdgeType,
mergedAsEdgeType,
referencesEdgeType,
mentionsAuthorEdgeType,
reactsThumbsUpEdgeType,
reactsHeartEdgeType,
reactsHoorayEdgeType,
]);
export const declaration: PluginDeclaration = Object.freeze({
name: "GitHub",
nodePrefix: N.Prefix.base,
edgePrefix: E.Prefix.base,
nodeTypes: nodeTypes,
edgeTypes: edgeTypes,
});

View File

@ -8,131 +8,16 @@ import type {
import {type Graph, NodeAddress} from "../../core/graph";
import {createGraph} from "./createGraph";
import * as N from "./nodes";
import * as E from "./edges";
import {RelationalView} from "./relationalView";
import {description} from "./render";
import type {Assets} from "../../app/assets";
import type {RepoId} from "../../core/repoId";
import type {PluginDeclaration} from "../../analysis/pluginDeclaration";
import {declaration} from "./declaration";
export class StaticPluginAdapter implements IStaticPluginAdapter {
name() {
return "GitHub";
}
nodePrefix() {
return N.Prefix.base;
}
edgePrefix() {
return E.Prefix.base;
}
nodeTypes() {
return [
{
name: "Repository",
pluralName: "Repositories",
prefix: N.Prefix.repo,
defaultWeight: 4,
},
{
name: "Issue",
pluralName: "Issues",
prefix: N.Prefix.issue,
defaultWeight: 2,
},
{
name: "Pull request",
pluralName: "Pull requests",
prefix: N.Prefix.pull,
defaultWeight: 4,
},
{
name: "Pull request review",
pluralName: "Pull request reviews",
prefix: N.Prefix.review,
defaultWeight: 1,
},
{
name: "Comment",
pluralName: "Comments",
prefix: N.Prefix.comment,
defaultWeight: 1,
},
{
name: "User",
pluralName: "Users",
prefix: N.Prefix.user,
defaultWeight: 1,
},
{
name: "Bot",
pluralName: "Bots",
prefix: N.Prefix.bot,
defaultWeight: 0.25,
},
];
}
edgeTypes() {
return [
{
forwardName: "authors",
backwardName: "is authored by",
defaultForwardWeight: 1 / 2,
defaultBackwardWeight: 1,
prefix: E.Prefix.authors,
},
{
forwardName: "has parent",
backwardName: "has child",
defaultForwardWeight: 1,
defaultBackwardWeight: 1 / 4,
prefix: E.Prefix.hasParent,
},
{
forwardName: "merges",
backwardName: "is merged by",
defaultForwardWeight: 1 / 2,
defaultBackwardWeight: 1,
prefix: E.Prefix.mergedAs,
},
{
forwardName: "references",
backwardName: "is referenced by",
defaultForwardWeight: 1,
defaultBackwardWeight: 1 / 16,
prefix: E.Prefix.references,
},
{
forwardName: "mentions author of",
backwardName: "has author mentioned by",
defaultForwardWeight: 1,
// TODO(#811): Probably change this to 0
defaultBackwardWeight: 1 / 32,
prefix: E.Prefix.mentionsAuthor,
},
{
forwardName: "reacted ❤️ to",
backwardName: "got ❤️ from",
defaultForwardWeight: 2,
// TODO(#811): Probably change this to 0
defaultBackwardWeight: 1 / 32,
prefix: E.Prefix.reactsHeart,
},
{
forwardName: "reacted 👍 to",
backwardName: "got 👍 from",
defaultForwardWeight: 1,
// TODO(#811): Probably change this to 0
defaultBackwardWeight: 1 / 32,
prefix: E.Prefix.reactsThumbsUp,
},
{
forwardName: "reacted 🎉 to",
backwardName: "got 🎉 from",
defaultForwardWeight: 4,
// TODO(#811): Probably change this to 0
defaultBackwardWeight: 1 / 32,
prefix: E.Prefix.reactsHooray,
},
];
declaration(): PluginDeclaration {
return declaration;
}
async load(assets: Assets, repoId: RepoId): Promise<IDynamicPluginAdapater> {
const url = assets.resolve(