From 5fd43bf62de8e18dce3be42f4aa7a60eb91a7ec7 Mon Sep 17 00:00:00 2001 From: Robin van Boven <497556+Beanow@users.noreply.github.com> Date: Tue, 4 Feb 2020 21:08:51 +0100 Subject: [PATCH] Backend: implement PluginLoaders.contractPluginGraphs (#1619) Note: this doesn't include a WeightedGraph.overrideWeights step. Because overriding weights isn't related to plugins, this will be handled as a separate feature, later in the load pipeline. --- src/backend/pluginLoaders.js | 24 ++++++++++ src/backend/pluginLoaders.test.js | 73 +++++++++++++++++++++++++++++++ src/plugins/identity/loader.js | 8 ++++ 3 files changed, 105 insertions(+) diff --git a/src/backend/pluginLoaders.js b/src/backend/pluginLoaders.js index b9fe1a7..c1ba8cb 100644 --- a/src/backend/pluginLoaders.js +++ b/src/backend/pluginLoaders.js @@ -3,6 +3,7 @@ import {TaskReporter} from "../util/taskReporter"; import {type Project} from "../core/project"; import {type WeightedGraph as WeightedGraphT} from "../core/weightedGraph"; +import * as WeightedGraph from "../core/weightedGraph"; import {type PluginDeclaration} from "../analysis/pluginDeclaration"; import {type CacheProvider} from "./cache"; import {type GithubToken} from "../plugins/github/token"; @@ -123,3 +124,26 @@ export async function createPluginGraphs( cachedProject: {cache, project}, }; } + +/** + * Takes PluginGraphs and merges it into a WeightedGraph with identities contracted. + */ +export async function contractPluginGraphs( + {identity}: PluginLoaders, + {graphs, cachedProject}: PluginGraphs +): Promise { + const {project} = cachedProject; + const mergedGraph = WeightedGraph.merge(graphs); + + // Don't contract when there's no identities. This will prevent unnecessary copying. + if (!project.identities.length) { + return mergedGraph; + } + + const discourseServer = project.discourseServer || {serverUrl: null}; + const identitySpec = { + identities: project.identities, + discourseServerUrl: discourseServer.serverUrl, + }; + return identity.contractIdentities(mergedGraph, identitySpec); +} diff --git a/src/backend/pluginLoaders.test.js b/src/backend/pluginLoaders.test.js index b0d6a9f..459ebb6 100644 --- a/src/backend/pluginLoaders.test.js +++ b/src/backend/pluginLoaders.test.js @@ -10,6 +10,7 @@ import * as PluginLoaders from "./pluginLoaders"; const githubSentinel = graphNode("github-sentinel"); const discourseSentinel = graphNode("discourse-sentinel"); +const identitySentinel = graphNode("identity-sentinel"); const mockGithubGraph = () => { const wg = WeightedGraph.empty(); @@ -23,6 +24,12 @@ const mockDiscourseGraph = () => { return wg; }; +const mockContractedGraph = () => { + const wg = WeightedGraph.empty(); + wg.graph.addNode(identitySentinel); + return wg; +}; + const mockCacheProvider = () => ({ database: jest.fn(), }); @@ -44,6 +51,7 @@ const mockPluginLoaders = () => ({ }, identity: { declaration: jest.fn().mockReturnValue(fakeIdentityDec), + contractIdentities: jest.fn().mockReturnValue(mockContractedGraph()), }, }); @@ -269,4 +277,69 @@ describe("src/backend/pluginLoaders", () => { ); }); }); + + describe("contractPluginGraphs", () => { + it("should only merge graphs when no identities are defined", async () => { + // Given + const loaders = mockPluginLoaders(); + const cache = mockCacheProvider(); + const project = createProject({ + id: "has-github-and-discourse", + discourseServer: {serverUrl: "http://foo.bar"}, + repoIds: [exampleRepoId], + }); + const pluginGraphs = ({ + graphs: [mockGithubGraph(), mockDiscourseGraph()], + cachedProject: {project, cache}, + }: any); + + // When + const graph = await PluginLoaders.contractPluginGraphs( + loaders, + pluginGraphs + ); + + // Then + const expectedGraph = WeightedGraph.merge([ + mockGithubGraph(), + mockDiscourseGraph(), + ]); + expect(graph).toEqual(expectedGraph); + }); + + it("should contract identities when they are defined", async () => { + // Given + const loaders = mockPluginLoaders(); + const cache = mockCacheProvider(); + const project = createProject({ + id: "has-github-and-discourse-and-identity", + identities: [{username: "foo", aliases: ["github/foo"]}], + discourseServer: {serverUrl: "http://foo.bar"}, + repoIds: [exampleRepoId], + }); + const pluginGraphs = ({ + graphs: [mockGithubGraph(), mockDiscourseGraph()], + cachedProject: {project, cache}, + }: any); + + // When + const graph = await PluginLoaders.contractPluginGraphs( + loaders, + pluginGraphs + ); + + // Then + const {identity} = loaders; + const expectedGraph = WeightedGraph.merge([ + mockGithubGraph(), + mockDiscourseGraph(), + ]); + expect(graph).toEqual(mockContractedGraph()); + expect(identity.contractIdentities).toBeCalledTimes(1); + expect(identity.contractIdentities).toBeCalledWith(expectedGraph, { + identities: project.identities, + discourseServerUrl: (project.discourseServer: any).serverUrl, + }); + }); + }); }); diff --git a/src/plugins/identity/loader.js b/src/plugins/identity/loader.js index 9360176..b9d93ce 100644 --- a/src/plugins/identity/loader.js +++ b/src/plugins/identity/loader.js @@ -1,12 +1,20 @@ // @flow import {type PluginDeclaration} from "../../analysis/pluginDeclaration"; +import {type WeightedGraph} from "../../core/weightedGraph"; +import {type IdentitySpec} from "./identity"; +import {contractIdentities} from "./contractIdentities"; import {declaration} from "./declaration"; export interface Loader { declaration(): PluginDeclaration; + contractIdentities( + weightedGraph: WeightedGraph, + identitySpec: IdentitySpec + ): WeightedGraph; } export default ({ declaration: () => declaration, + contractIdentities, }: Loader);