credExplorer gets adapters from one point (#786)

Currently, the `credExplorer` uses the `defaultStaticAdapters`, but it
imports these adapters in multiple places. If we decide to make the
adapters configurable (e.g. when we start supporting more plugins) this
will be a problem.

This change modifies the cred explorer so that the adapters always come
from a prop declaration on the app. Then the adapters are passed into
the `state` module's functional entry points, rather than letting
`state` depend on the default adapters directly.

This change is motivated by the fact that my WeightConfig cleanup can be
done more cleanly if the adapters are present as a prop on the App.

Test plan: Unit tests are updated. Also, `git grep
defaultStaticAdapters` reaveals that the adapters are only consumed
once.
This commit is contained in:
Dandelion Mané 2018-09-05 16:10:11 -07:00 committed by GitHub
parent f7383bbc90
commit d77c76082d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 88 additions and 30 deletions

View File

@ -9,7 +9,6 @@ import CheckedLocalStore from "../checkedLocalStore";
import BrowserLocalStore from "../browserLocalStore"; import BrowserLocalStore from "../browserLocalStore";
import {type EdgeEvaluator} from "../../core/attribution/pagerank"; import {type EdgeEvaluator} from "../../core/attribution/pagerank";
import {defaultStaticAdapters} from "../adapters/defaultPlugins";
import {PagerankTable} from "./pagerankTable/Table"; import {PagerankTable} from "./pagerankTable/Table";
import {WeightConfig} from "./WeightConfig"; import {WeightConfig} from "./WeightConfig";
import RepositorySelect from "./RepositorySelect"; import RepositorySelect from "./RepositorySelect";
@ -20,6 +19,8 @@ import {
type StateTransitionMachineInterface, type StateTransitionMachineInterface,
uninitializedState, uninitializedState,
} from "./state"; } from "./state";
import {StaticAdapterSet} from "../adapters/adapterSet";
import {defaultStaticAdapters} from "../adapters/defaultPlugins";
export default class AppPage extends React.Component<{|+assets: Assets|}> { export default class AppPage extends React.Component<{|+assets: Assets|}> {
static _LOCAL_STORE = new CheckedLocalStore( static _LOCAL_STORE = new CheckedLocalStore(
@ -31,11 +32,21 @@ export default class AppPage extends React.Component<{|+assets: Assets|}> {
render() { render() {
const App = createApp(createStateTransitionMachine); const App = createApp(createStateTransitionMachine);
return <App assets={this.props.assets} localStore={AppPage._LOCAL_STORE} />; return (
<App
assets={this.props.assets}
adapters={defaultStaticAdapters()}
localStore={AppPage._LOCAL_STORE}
/>
);
} }
} }
type Props = {|+assets: Assets, +localStore: LocalStore|}; type Props = {|
+assets: Assets,
+localStore: LocalStore,
+adapters: StaticAdapterSet,
|};
type State = {| type State = {|
appState: AppState, appState: AppState,
edgeEvaluator: ?EdgeEvaluator, edgeEvaluator: ?EdgeEvaluator,
@ -110,6 +121,7 @@ export function createApp(
onClick={() => onClick={() =>
this.stateTransitionMachine.loadGraphAndRunPagerank( this.stateTransitionMachine.loadGraphAndRunPagerank(
this.props.assets, this.props.assets,
this.props.adapters,
NullUtil.get(this.state.edgeEvaluator), NullUtil.get(this.state.edgeEvaluator),
GithubPrefix.user GithubPrefix.user
) )
@ -119,7 +131,7 @@ export function createApp(
</button> </button>
<WeightConfig <WeightConfig
onChange={(edgeEvaluator) => this.setState({edgeEvaluator})} onChange={(edgeEvaluator) => this.setState({edgeEvaluator})}
adapters={defaultStaticAdapters()} adapters={this.props.adapters}
/> />
<LoadingIndicator appState={this.state.appState} /> <LoadingIndicator appState={this.state.appState} />
{pagerankTable} {pagerankTable}

View File

@ -38,7 +38,11 @@ describe("app/credExplorer/App", () => {
} }
const App = createApp(createMockSTM); const App = createApp(createMockSTM);
const el = shallow( const el = shallow(
<App assets={new Assets("/foo/")} localStore={localStore} /> <App
assets={new Assets("/foo/")}
adapters={new StaticAdapterSet([])}
localStore={localStore}
/>
); );
if (setState == null || getState == null) { if (setState == null || getState == null) {
throw new Error("Initialization problems"); throw new Error("Initialization problems");
@ -156,6 +160,7 @@ describe("app/credExplorer/App", () => {
expect(loadGraphAndRunPagerank).toBeCalledTimes(1); expect(loadGraphAndRunPagerank).toBeCalledTimes(1);
expect(loadGraphAndRunPagerank).toBeCalledWith( expect(loadGraphAndRunPagerank).toBeCalledWith(
el.instance().props.assets, el.instance().props.assets,
el.instance().props.adapters,
edgeEvaluator, edgeEvaluator,
GithubPrefix.user GithubPrefix.user
); );

View File

@ -12,9 +12,7 @@ import {
pagerank, pagerank,
} from "../../core/attribution/pagerank"; } from "../../core/attribution/pagerank";
import {DynamicAdapterSet} from "../adapters/adapterSet"; import {StaticAdapterSet, DynamicAdapterSet} from "../adapters/adapterSet";
import {defaultStaticAdapters} from "../adapters/defaultPlugins";
/* /*
This models the UI states of the credExplorer/App as a state machine. This models the UI states of the credExplorer/App as a state machine.
@ -72,10 +70,11 @@ export function uninitializedState(): AppState {
// Exported for testing purposes. // Exported for testing purposes.
export interface StateTransitionMachineInterface { export interface StateTransitionMachineInterface {
+setRepo: (Repo) => void; +setRepo: (Repo) => void;
+loadGraph: (Assets) => Promise<boolean>; +loadGraph: (Assets, StaticAdapterSet) => Promise<boolean>;
+runPagerank: (EdgeEvaluator, NodeAddressT) => Promise<void>; +runPagerank: (EdgeEvaluator, NodeAddressT) => Promise<void>;
+loadGraphAndRunPagerank: ( +loadGraphAndRunPagerank: (
Assets, Assets,
StaticAdapterSet,
EdgeEvaluator, EdgeEvaluator,
NodeAddressT NodeAddressT
) => Promise<void>; ) => Promise<void>;
@ -89,6 +88,7 @@ export class StateTransitionMachine implements StateTransitionMachineInterface {
setState: (AppState) => void; setState: (AppState) => void;
loadGraphWithAdapters: ( loadGraphWithAdapters: (
assets: Assets, assets: Assets,
adapters: StaticAdapterSet,
repo: Repo repo: Repo
) => Promise<GraphWithAdapters>; ) => Promise<GraphWithAdapters>;
pagerank: ( pagerank: (
@ -102,6 +102,7 @@ export class StateTransitionMachine implements StateTransitionMachineInterface {
setState: (AppState) => void, setState: (AppState) => void,
loadGraphWithAdapters: ( loadGraphWithAdapters: (
assets: Assets, assets: Assets,
adapters: StaticAdapterSet,
repo: Repo repo: Repo
) => Promise<GraphWithAdapters>, ) => Promise<GraphWithAdapters>,
pagerank: ( pagerank: (
@ -126,7 +127,10 @@ export class StateTransitionMachine implements StateTransitionMachineInterface {
} }
/** Loads the graph, reports whether it was successful */ /** Loads the graph, reports whether it was successful */
async loadGraph(assets: Assets): Promise<boolean> { async loadGraph(
assets: Assets,
adapters: StaticAdapterSet
): Promise<boolean> {
const state = this.getState(); const state = this.getState();
if (state.type !== "READY_TO_LOAD_GRAPH") { if (state.type !== "READY_TO_LOAD_GRAPH") {
throw new Error("Tried to loadGraph in incorrect state"); throw new Error("Tried to loadGraph in incorrect state");
@ -137,7 +141,11 @@ export class StateTransitionMachine implements StateTransitionMachineInterface {
let newState: ?AppState; let newState: ?AppState;
let success = true; let success = true;
try { try {
const graphWithAdapters = await this.loadGraphWithAdapters(assets, repo); const graphWithAdapters = await this.loadGraphWithAdapters(
assets,
adapters,
repo
);
newState = { newState = {
type: "READY_TO_RUN_PAGERANK", type: "READY_TO_RUN_PAGERANK",
graphWithAdapters, graphWithAdapters,
@ -206,6 +214,7 @@ export class StateTransitionMachine implements StateTransitionMachineInterface {
async loadGraphAndRunPagerank( async loadGraphAndRunPagerank(
assets: Assets, assets: Assets,
adapters: StaticAdapterSet,
edgeEvaluator: EdgeEvaluator, edgeEvaluator: EdgeEvaluator,
totalScoreNodePrefix: NodeAddressT totalScoreNodePrefix: NodeAddressT
) { ) {
@ -216,7 +225,7 @@ export class StateTransitionMachine implements StateTransitionMachineInterface {
} }
switch (type) { switch (type) {
case "READY_TO_LOAD_GRAPH": case "READY_TO_LOAD_GRAPH":
const loadedGraph = await this.loadGraph(assets); const loadedGraph = await this.loadGraph(assets, adapters);
if (loadedGraph) { if (loadedGraph) {
await this.runPagerank(edgeEvaluator, totalScoreNodePrefix); await this.runPagerank(edgeEvaluator, totalScoreNodePrefix);
} }
@ -237,8 +246,9 @@ export type GraphWithAdapters = {|
|}; |};
export async function loadGraphWithAdapters( export async function loadGraphWithAdapters(
assets: Assets, assets: Assets,
adapters: StaticAdapterSet,
repo: Repo repo: Repo
): Promise<GraphWithAdapters> { ): Promise<GraphWithAdapters> {
const adapters = await defaultStaticAdapters().load(assets, repo); const dynamicAdapters = await adapters.load(assets, repo);
return {graph: adapters.graph(), adapters}; return {graph: dynamicAdapters.graph(), adapters: dynamicAdapters};
} }

View File

@ -26,6 +26,7 @@ describe("app/credExplorer/state", () => {
}; };
const loadGraphMock: ( const loadGraphMock: (
assets: Assets, assets: Assets,
adapters: StaticAdapterSet,
repo: Repo repo: Repo
) => Promise<GraphWithAdapters> = jest.fn(); ) => Promise<GraphWithAdapters> = jest.fn();
const pagerankMock: ( const pagerankMock: (
@ -136,25 +137,28 @@ describe("app/credExplorer/state", () => {
]; ];
for (const b of badStates) { for (const b of badStates) {
const {stm} = example(b); const {stm} = example(b);
await expect(stm.loadGraph(new Assets("/my/gateway/"))).rejects.toThrow( await expect(
"incorrect state" stm.loadGraph(new Assets("/my/gateway/"), new StaticAdapterSet([]))
); ).rejects.toThrow("incorrect state");
} }
}); });
it("passes along the adapters and repo", () => { it("passes along the adapters and repo", () => {
const {stm, loadGraphMock} = example(readyToLoadGraph()); const {stm, loadGraphMock} = example(readyToLoadGraph());
expect(loadGraphMock).toHaveBeenCalledTimes(0); expect(loadGraphMock).toHaveBeenCalledTimes(0);
const assets = new Assets("/my/gateway/"); const assets = new Assets("/my/gateway/");
stm.loadGraph(assets); const adapters = new StaticAdapterSet([]);
stm.loadGraph(assets, adapters);
expect(loadGraphMock).toHaveBeenCalledTimes(1); expect(loadGraphMock).toHaveBeenCalledTimes(1);
expect(loadGraphMock.mock.calls[0]).toHaveLength(2); expect(loadGraphMock).toHaveBeenCalledWith(
expect(loadGraphMock.mock.calls[0][0]).toBe(assets); assets,
expect(loadGraphMock.mock.calls[0][1]).toEqual(makeRepo("foo", "bar")); adapters,
makeRepo("foo", "bar")
);
}); });
it("immediately sets loading status", () => { it("immediately sets loading status", () => {
const {getState, stm} = example(readyToLoadGraph()); const {getState, stm} = example(readyToLoadGraph());
expect(loading(getState())).toBe("NOT_LOADING"); expect(loading(getState())).toBe("NOT_LOADING");
stm.loadGraph(new Assets("/my/gateway/")); stm.loadGraph(new Assets("/my/gateway/"), new StaticAdapterSet([]));
expect(loading(getState())).toBe("LOADING"); expect(loading(getState())).toBe("LOADING");
expect(getState().type).toBe("READY_TO_LOAD_GRAPH"); expect(getState().type).toBe("READY_TO_LOAD_GRAPH");
}); });
@ -162,7 +166,10 @@ describe("app/credExplorer/state", () => {
const {getState, stm, loadGraphMock} = example(readyToLoadGraph()); const {getState, stm, loadGraphMock} = example(readyToLoadGraph());
const gwa = graphWithAdapters(); const gwa = graphWithAdapters();
loadGraphMock.mockResolvedValue(gwa); loadGraphMock.mockResolvedValue(gwa);
const succeeded = await stm.loadGraph(new Assets("/my/gateway/")); const succeeded = await stm.loadGraph(
new Assets("/my/gateway/"),
new StaticAdapterSet([])
);
expect(succeeded).toBe(true); expect(succeeded).toBe(true);
const state = getState(); const state = getState();
expect(loading(state)).toBe("NOT_LOADING"); expect(loading(state)).toBe("NOT_LOADING");
@ -182,7 +189,10 @@ describe("app/credExplorer/state", () => {
resolve(graphWithAdapters()); resolve(graphWithAdapters());
}) })
); );
const succeeded = await stm.loadGraph(new Assets("/my/gateway/")); const succeeded = await stm.loadGraph(
new Assets("/my/gateway/"),
new StaticAdapterSet([])
);
expect(succeeded).toBe(false); expect(succeeded).toBe(false);
const state = getState(); const state = getState();
expect(loading(state)).toBe("NOT_LOADING"); expect(loading(state)).toBe("NOT_LOADING");
@ -195,7 +205,10 @@ describe("app/credExplorer/state", () => {
// $ExpectFlowError // $ExpectFlowError
console.error = jest.fn(); console.error = jest.fn();
loadGraphMock.mockRejectedValue(error); loadGraphMock.mockRejectedValue(error);
const succeeded = await stm.loadGraph(new Assets("/my/gateway/")); const succeeded = await stm.loadGraph(
new Assets("/my/gateway/"),
new StaticAdapterSet([])
);
expect(succeeded).toBe(false); expect(succeeded).toBe(false);
const state = getState(); const state = getState();
expect(loading(state)).toBe("FAILED"); expect(loading(state)).toBe("FAILED");
@ -282,6 +295,7 @@ describe("app/credExplorer/state", () => {
await expect( await expect(
stm.loadGraphAndRunPagerank( stm.loadGraphAndRunPagerank(
new Assets("gateway"), new Assets("gateway"),
new StaticAdapterSet([]),
edgeEvaluator(), edgeEvaluator(),
NodeAddress.empty NodeAddress.empty
) )
@ -293,11 +307,12 @@ describe("app/credExplorer/state", () => {
(stm: any).runPagerank = jest.fn(); (stm: any).runPagerank = jest.fn();
stm.loadGraph.mockResolvedValue(true); stm.loadGraph.mockResolvedValue(true);
const assets = new Assets("/gateway/"); const assets = new Assets("/gateway/");
const adapters = new StaticAdapterSet([]);
const prefix = NodeAddress.fromParts(["bar"]); const prefix = NodeAddress.fromParts(["bar"]);
const ee = edgeEvaluator(); const ee = edgeEvaluator();
await stm.loadGraphAndRunPagerank(assets, ee, prefix); await stm.loadGraphAndRunPagerank(assets, adapters, ee, prefix);
expect(stm.loadGraph).toHaveBeenCalledTimes(1); expect(stm.loadGraph).toHaveBeenCalledTimes(1);
expect(stm.loadGraph).toHaveBeenCalledWith(assets); expect(stm.loadGraph).toHaveBeenCalledWith(assets, adapters);
expect(stm.runPagerank).toHaveBeenCalledTimes(1); expect(stm.runPagerank).toHaveBeenCalledTimes(1);
expect(stm.runPagerank).toHaveBeenCalledWith(ee, prefix); expect(stm.runPagerank).toHaveBeenCalledWith(ee, prefix);
}); });
@ -307,8 +322,14 @@ describe("app/credExplorer/state", () => {
(stm: any).runPagerank = jest.fn(); (stm: any).runPagerank = jest.fn();
stm.loadGraph.mockResolvedValue(false); stm.loadGraph.mockResolvedValue(false);
const assets = new Assets("/gateway/"); const assets = new Assets("/gateway/");
const adapters = new StaticAdapterSet([]);
const prefix = NodeAddress.fromParts(["bar"]); const prefix = NodeAddress.fromParts(["bar"]);
await stm.loadGraphAndRunPagerank(assets, edgeEvaluator(), prefix); await stm.loadGraphAndRunPagerank(
assets,
adapters,
edgeEvaluator(),
prefix
);
expect(stm.loadGraph).toHaveBeenCalledTimes(1); expect(stm.loadGraph).toHaveBeenCalledTimes(1);
expect(stm.runPagerank).toHaveBeenCalledTimes(0); expect(stm.runPagerank).toHaveBeenCalledTimes(0);
}); });
@ -318,7 +339,12 @@ describe("app/credExplorer/state", () => {
(stm: any).runPagerank = jest.fn(); (stm: any).runPagerank = jest.fn();
const prefix = NodeAddress.fromParts(["bar"]); const prefix = NodeAddress.fromParts(["bar"]);
const ee = edgeEvaluator(); const ee = edgeEvaluator();
await stm.loadGraphAndRunPagerank(new Assets("/gateway/"), ee, prefix); await stm.loadGraphAndRunPagerank(
new Assets("/gateway/"),
new StaticAdapterSet([]),
ee,
prefix
);
expect(stm.loadGraph).toHaveBeenCalledTimes(0); expect(stm.loadGraph).toHaveBeenCalledTimes(0);
expect(stm.runPagerank).toHaveBeenCalledTimes(1); expect(stm.runPagerank).toHaveBeenCalledTimes(1);
expect(stm.runPagerank).toHaveBeenCalledWith(ee, prefix); expect(stm.runPagerank).toHaveBeenCalledWith(ee, prefix);
@ -329,7 +355,12 @@ describe("app/credExplorer/state", () => {
(stm: any).runPagerank = jest.fn(); (stm: any).runPagerank = jest.fn();
const prefix = NodeAddress.fromParts(["bar"]); const prefix = NodeAddress.fromParts(["bar"]);
const ee = edgeEvaluator(); const ee = edgeEvaluator();
await stm.loadGraphAndRunPagerank(new Assets("/gateway/"), ee, prefix); await stm.loadGraphAndRunPagerank(
new Assets("/gateway/"),
new StaticAdapterSet([]),
ee,
prefix
);
expect(stm.loadGraph).toHaveBeenCalledTimes(0); expect(stm.loadGraph).toHaveBeenCalledTimes(0);
expect(stm.runPagerank).toHaveBeenCalledTimes(1); expect(stm.runPagerank).toHaveBeenCalledTimes(1);
expect(stm.runPagerank).toHaveBeenCalledWith(ee, prefix); expect(stm.runPagerank).toHaveBeenCalledWith(ee, prefix);