Dependency-inject `LocalStore` (#522)
Summary: This commit modifies components that directly depend on the browser-specific local store implementation to instead have their dependencies injected. Test Plan: Tests pass, but are likely not sufficient. Manual testing indicates that the local storage still works, for both reads and writes, on a fresh profile or with existing data, for both the repository owner/name and the weight configuration. wchargin-branch: di-localstore
This commit is contained in:
parent
1fa039ba6c
commit
801b4ec700
|
@ -3,7 +3,8 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {StyleSheet, css} from "aphrodite/no-important";
|
import {StyleSheet, css} from "aphrodite/no-important";
|
||||||
|
|
||||||
import LocalStore from "./LocalStore";
|
import type {LocalStore} from "../localStore";
|
||||||
|
import BrowserLocalStore from "../browserLocalStore";
|
||||||
import {StaticPluginAdapter as GithubAdapter} from "../../plugins/github/pluginAdapter";
|
import {StaticPluginAdapter as GithubAdapter} from "../../plugins/github/pluginAdapter";
|
||||||
import {StaticPluginAdapter as GitAdapter} from "../../plugins/git/pluginAdapter";
|
import {StaticPluginAdapter as GitAdapter} from "../../plugins/git/pluginAdapter";
|
||||||
import {Graph} from "../../core/graph";
|
import {Graph} from "../../core/graph";
|
||||||
|
@ -16,7 +17,22 @@ import type {PagerankNodeDecomposition} from "../../core/attribution/pagerankNod
|
||||||
|
|
||||||
import * as NullUtil from "../../util/null";
|
import * as NullUtil from "../../util/null";
|
||||||
|
|
||||||
type Props = {||};
|
const REPO_OWNER_KEY = "repoOwner";
|
||||||
|
const REPO_NAME_KEY = "repoName";
|
||||||
|
const MAX_ENTRIES_PER_LIST = 100;
|
||||||
|
|
||||||
|
export default class AppPage extends React.Component<{||}> {
|
||||||
|
static _LOCAL_STORE = new BrowserLocalStore({
|
||||||
|
version: "1",
|
||||||
|
keyPrefix: "cred-explorer",
|
||||||
|
});
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return <App localStore={AppPage._LOCAL_STORE} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = {|+localStore: LocalStore|};
|
||||||
type State = {
|
type State = {
|
||||||
repoOwner: string,
|
repoOwner: string,
|
||||||
repoName: string,
|
repoName: string,
|
||||||
|
@ -32,11 +48,7 @@ type State = {
|
||||||
edgeEvaluator: ?EdgeEvaluator,
|
edgeEvaluator: ?EdgeEvaluator,
|
||||||
};
|
};
|
||||||
|
|
||||||
const REPO_OWNER_KEY = "repoOwner";
|
export class App extends React.Component<Props, State> {
|
||||||
const REPO_NAME_KEY = "repoName";
|
|
||||||
const MAX_ENTRIES_PER_LIST = 100;
|
|
||||||
|
|
||||||
export default class App extends React.Component<Props, State> {
|
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -48,13 +60,15 @@ export default class App extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
const {localStore} = this.props;
|
||||||
this.setState((state) => ({
|
this.setState((state) => ({
|
||||||
repoOwner: LocalStore.get(REPO_OWNER_KEY, state.repoOwner),
|
repoOwner: localStore.get(REPO_OWNER_KEY, state.repoOwner),
|
||||||
repoName: LocalStore.get(REPO_NAME_KEY, state.repoName),
|
repoName: localStore.get(REPO_NAME_KEY, state.repoName),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const {localStore} = this.props;
|
||||||
const {edgeEvaluator} = this.state;
|
const {edgeEvaluator} = this.state;
|
||||||
const {graphWithMetadata, pnd} = this.state.data;
|
const {graphWithMetadata, pnd} = this.state.data;
|
||||||
return (
|
return (
|
||||||
|
@ -70,7 +84,7 @@ export default class App extends React.Component<Props, State> {
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const value = e.target.value;
|
const value = e.target.value;
|
||||||
this.setState({repoOwner: value}, () => {
|
this.setState({repoOwner: value}, () => {
|
||||||
LocalStore.set(REPO_OWNER_KEY, this.state.repoOwner);
|
localStore.set(REPO_OWNER_KEY, this.state.repoOwner);
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -83,7 +97,7 @@ export default class App extends React.Component<Props, State> {
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const value = e.target.value;
|
const value = e.target.value;
|
||||||
this.setState({repoName: value}, () => {
|
this.setState({repoName: value}, () => {
|
||||||
LocalStore.set(REPO_NAME_KEY, this.state.repoName);
|
localStore.set(REPO_NAME_KEY, this.state.repoName);
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -122,7 +136,10 @@ export default class App extends React.Component<Props, State> {
|
||||||
) : (
|
) : (
|
||||||
<p>Graph not loaded.</p>
|
<p>Graph not loaded.</p>
|
||||||
)}
|
)}
|
||||||
<WeightConfig onChange={(ee) => this.setState({edgeEvaluator: ee})} />
|
<WeightConfig
|
||||||
|
localStore={localStore}
|
||||||
|
onChange={(ee) => this.setState({edgeEvaluator: ee})}
|
||||||
|
/>
|
||||||
<PagerankTable
|
<PagerankTable
|
||||||
adapters={NullUtil.map(graphWithMetadata, (x) => x.adapters)}
|
adapters={NullUtil.map(graphWithMetadata, (x) => x.adapters)}
|
||||||
pnd={pnd}
|
pnd={pnd}
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {shallow} from "enzyme";
|
import {shallow} from "enzyme";
|
||||||
|
|
||||||
|
import BrowserLocalStore from "../browserLocalStore";
|
||||||
import {pagerank} from "../../core/attribution/pagerank";
|
import {pagerank} from "../../core/attribution/pagerank";
|
||||||
import App from "./App";
|
import {App} from "./App";
|
||||||
|
|
||||||
import {Graph, NodeAddress, EdgeAddress} from "../../core/graph";
|
import {Graph, NodeAddress, EdgeAddress} from "../../core/graph";
|
||||||
|
|
||||||
|
@ -95,17 +96,27 @@ function example() {
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("app/credExplorer/App", () => {
|
describe("app/credExplorer/App", () => {
|
||||||
|
function makeLocalStore() {
|
||||||
|
// TODO(@wchargin): This should be an in-memory implementation of
|
||||||
|
// LocalStore, not the browser version. This only works because the
|
||||||
|
// store is not actually needed for the shallow render to complete
|
||||||
|
// successfully.
|
||||||
|
return new BrowserLocalStore({
|
||||||
|
version: "1",
|
||||||
|
keyPrefix: "cred-explorer",
|
||||||
|
});
|
||||||
|
}
|
||||||
it("renders with clean state", () => {
|
it("renders with clean state", () => {
|
||||||
shallow(<App />);
|
shallow(<App localStore={makeLocalStore()} />);
|
||||||
});
|
});
|
||||||
it("renders with graph and adapters set", () => {
|
it("renders with graph and adapters set", () => {
|
||||||
const app = shallow(<App />);
|
const app = shallow(<App localStore={makeLocalStore()} />);
|
||||||
const {graph, adapters} = example();
|
const {graph, adapters} = example();
|
||||||
const data = {graph, adapters, pagerankResult: null};
|
const data = {graph, adapters, pagerankResult: null};
|
||||||
app.setState({data});
|
app.setState({data});
|
||||||
});
|
});
|
||||||
it("renders with graph and adapters and pagerankResult", () => {
|
it("renders with graph and adapters and pagerankResult", () => {
|
||||||
const app = shallow(<App />);
|
const app = shallow(<App localStore={makeLocalStore()} />);
|
||||||
const {graph, adapters, pagerankResult} = example();
|
const {graph, adapters, pagerankResult} = example();
|
||||||
const data = {graph, adapters, pagerankResult};
|
const data = {graph, adapters, pagerankResult};
|
||||||
app.setState({data});
|
app.setState({data});
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
// @flow
|
|
||||||
|
|
||||||
import LocalStore from "../browserLocalStore";
|
|
||||||
|
|
||||||
export default new LocalStore({version: "1", keyPrefix: "cred-explorer"});
|
|
|
@ -9,9 +9,9 @@ import {
|
||||||
NodeAddress,
|
NodeAddress,
|
||||||
} from "../../core/graph";
|
} from "../../core/graph";
|
||||||
|
|
||||||
|
import type {LocalStore} from "../localStore";
|
||||||
import {type EdgeEvaluator} from "../../core/attribution/pagerank";
|
import {type EdgeEvaluator} from "../../core/attribution/pagerank";
|
||||||
import {byEdgeType, byNodeType} from "./edgeWeights";
|
import {byEdgeType, byNodeType} from "./edgeWeights";
|
||||||
import LocalStore from "./LocalStore";
|
|
||||||
import * as MapUtil from "../../util/map";
|
import * as MapUtil from "../../util/map";
|
||||||
import * as NullUtil from "../../util/null";
|
import * as NullUtil from "../../util/null";
|
||||||
|
|
||||||
|
@ -19,9 +19,10 @@ import type {StaticPluginAdapter} from "../pluginAdapter";
|
||||||
import {StaticPluginAdapter as GithubAdapter} from "../../plugins/github/pluginAdapter";
|
import {StaticPluginAdapter as GithubAdapter} from "../../plugins/github/pluginAdapter";
|
||||||
import {StaticPluginAdapter as GitAdapter} from "../../plugins/git/pluginAdapter";
|
import {StaticPluginAdapter as GitAdapter} from "../../plugins/git/pluginAdapter";
|
||||||
|
|
||||||
type Props = {
|
type Props = {|
|
||||||
onChange: (EdgeEvaluator) => void,
|
+localStore: LocalStore,
|
||||||
};
|
+onChange: (EdgeEvaluator) => void,
|
||||||
|
|};
|
||||||
|
|
||||||
type EdgeWeights = Map<EdgeAddressT, UserEdgeWeight>;
|
type EdgeWeights = Map<EdgeAddressT, UserEdgeWeight>;
|
||||||
type UserEdgeWeight = {|+logWeight: number, +directionality: number|};
|
type UserEdgeWeight = {|+logWeight: number, +directionality: number|};
|
||||||
|
@ -71,15 +72,16 @@ export class WeightConfig extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
const {localStore} = this.props;
|
||||||
this.setState(
|
this.setState(
|
||||||
(state) => {
|
(state) => {
|
||||||
return {
|
return {
|
||||||
edgeWeights: NullUtil.orElse(
|
edgeWeights: NullUtil.orElse(
|
||||||
NullUtil.map(LocalStore.get(EDGE_WEIGHTS_KEY), MapUtil.fromObject),
|
NullUtil.map(localStore.get(EDGE_WEIGHTS_KEY), MapUtil.fromObject),
|
||||||
state.edgeWeights
|
state.edgeWeights
|
||||||
),
|
),
|
||||||
nodeWeights: NullUtil.orElse(
|
nodeWeights: NullUtil.orElse(
|
||||||
NullUtil.map(LocalStore.get(NODE_WEIGHTS_KEY), MapUtil.fromObject),
|
NullUtil.map(localStore.get(NODE_WEIGHTS_KEY), MapUtil.fromObject),
|
||||||
state.nodeWeights
|
state.nodeWeights
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
@ -126,9 +128,10 @@ export class WeightConfig extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fire() {
|
fire() {
|
||||||
|
const {localStore} = this.props;
|
||||||
const {edgeWeights, nodeWeights} = this.state;
|
const {edgeWeights, nodeWeights} = this.state;
|
||||||
LocalStore.set(EDGE_WEIGHTS_KEY, MapUtil.toObject(edgeWeights));
|
localStore.set(EDGE_WEIGHTS_KEY, MapUtil.toObject(edgeWeights));
|
||||||
LocalStore.set(NODE_WEIGHTS_KEY, MapUtil.toObject(nodeWeights));
|
localStore.set(NODE_WEIGHTS_KEY, MapUtil.toObject(nodeWeights));
|
||||||
const edgePrefixes = Array.from(edgeWeights.entries()).map(
|
const edgePrefixes = Array.from(edgeWeights.entries()).map(
|
||||||
([prefix, {logWeight, directionality}]) => ({
|
([prefix, {logWeight, directionality}]) => ({
|
||||||
prefix,
|
prefix,
|
||||||
|
|
Loading…
Reference in New Issue