diff --git a/src/analysis/pluginDeclaration.js b/src/analysis/pluginDeclaration.js new file mode 100644 index 0000000..1e3e3b3 --- /dev/null +++ b/src/analysis/pluginDeclaration.js @@ -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, + +edgeTypes: $ReadOnlyArray, +|}; diff --git a/src/app/adapters/adapterSet.js b/src/app/adapters/adapterSet.js index 97bc00c..4c98f91 100644 --- a/src/app/adapters/adapterSet.js +++ b/src/app/adapters/adapterSet.js @@ -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); }); } diff --git a/src/app/adapters/adapterSet.test.js b/src/app/adapters/adapterSet.test.js index 7e7f055..af7c2a7 100644 --- a/src/app/adapters/adapterSet.test.js +++ b/src/app/adapters/adapterSet.test.js @@ -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); }); }); }); diff --git a/src/app/adapters/demoAdapters.js b/src/app/adapters/demoAdapters.js index d138955..f3b183e 100644 --- a/src/app/adapters/demoAdapters.js +++ b/src/app/adapters/demoAdapters.js @@ -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 { if (this.loadingMock) { diff --git a/src/app/adapters/fallbackAdapter.js b/src/app/adapters/fallbackAdapter.js index 37c184c..2a5ca46 100644 --- a/src/app/adapters/fallbackAdapter.js +++ b/src/app/adapters/fallbackAdapter.js @@ -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) { diff --git a/src/app/adapters/pluginAdapter.js b/src/app/adapters/pluginAdapter.js index 1bf601f..5c08f82 100644 --- a/src/app/adapters/pluginAdapter.js +++ b/src/app/adapters/pluginAdapter.js @@ -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; } diff --git a/src/app/credExplorer/pagerankTable/Table.js b/src/app/credExplorer/pagerankTable/Table.js index 853d866..c929726 100644 --- a/src/app/credExplorer/pagerankTable/Table.js +++ b/src/app/credExplorer/pagerankTable/Table.js @@ -95,17 +95,17 @@ export class PagerankTable extends React.PureComponent< function optionGroup(adapter: DynamicPluginAdapter) { const header = ( ); const entries = adapter .static() - .nodeTypes() - .map((type) => ( + .declaration() + .nodeTypes.map((type) => ( @@ -122,10 +122,11 @@ export class PagerankTable extends React.PureComponent< }} > - {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)} diff --git a/src/app/credExplorer/weights/PluginWeightConfig.js b/src/app/credExplorer/weights/PluginWeightConfig.js index 0f64bd1..652b8a5 100644 --- a/src/app/credExplorer/weights/PluginWeightConfig.js +++ b/src/app/credExplorer/weights/PluginWeightConfig.js @@ -66,14 +66,14 @@ export class PluginWeightConfig extends React.Component { _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 { this._validateWeightedTypesWithAdapter(); return (
-

{this.props.adapter.name()}

+

{this.props.adapter.declaration().name}

Node weights

{this._renderNodeWeightControls()}

Edge weights

diff --git a/src/app/credExplorer/weights/PluginWeightConfig.test.js b/src/app/credExplorer/weights/PluginWeightConfig.test.js index 5255dfa..5ca8587 100644 --- a/src/app/credExplorer/weights/PluginWeightConfig.test.js +++ b/src/app/credExplorer/weights/PluginWeightConfig.test.js @@ -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])), }; diff --git a/src/app/credExplorer/weights/WeightConfig.js b/src/app/credExplorer/weights/WeightConfig.js index e8d1dbc..378af51 100644 --- a/src/app/credExplorer/weights/WeightConfig.js +++ b/src/app/credExplorer/weights/WeightConfig.js @@ -39,7 +39,7 @@ export class WeightConfig extends React.Component { _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 { 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 { } return ( { 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); diff --git a/src/app/credExplorer/weights/weights.js b/src/app/credExplorer/weights/weights.js index c689f44..fa69129 100644 --- a/src/app/credExplorer/weights/weights.js +++ b/src/app/credExplorer/weights/weights.js @@ -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)]) ), }; } diff --git a/src/app/credExplorer/weights/weights.test.js b/src/app/credExplorer/weights/weights.test.js index b4bed7d..c4ff17e 100644 --- a/src/app/credExplorer/weights/weights.test.js +++ b/src/app/credExplorer/weights/weights.test.js @@ -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); diff --git a/src/plugins/git/declaration.js b/src/plugins/git/declaration.js new file mode 100644 index 0000000..f9be376 --- /dev/null +++ b/src/plugins/git/declaration.js @@ -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, +}); diff --git a/src/plugins/git/pluginAdapter.js b/src/plugins/git/pluginAdapter.js index 0ee46de..0983e2a 100644 --- a/src/plugins/git/pluginAdapter.js +++ b/src/plugins/git/pluginAdapter.js @@ -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 { const baseUrl = `/api/v1/data/data/${repoId.owner}/${repoId.name}/git/`; diff --git a/src/plugins/github/declaration.js b/src/plugins/github/declaration.js new file mode 100644 index 0000000..6207a5c --- /dev/null +++ b/src/plugins/github/declaration.js @@ -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, +}); diff --git a/src/plugins/github/pluginAdapter.js b/src/plugins/github/pluginAdapter.js index 353b1d8..aea5599 100644 --- a/src/plugins/github/pluginAdapter.js +++ b/src/plugins/github/pluginAdapter.js @@ -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 { const url = assets.resolve(