Add plugins and versions to backend tab

This commit is contained in:
Anthony Laibe 2018-08-10 11:31:10 +01:00 committed by Pascal Precht
parent 950f285d2e
commit 83d6130259
No known key found for this signature in database
GPG Key ID: 0EE28D8D6FD85D7D
12 changed files with 175 additions and 91 deletions

View File

@ -104,11 +104,18 @@ export const contractProfile = {
failure: (error) => action(CONTRACT_PROFILE[FAILURE], {error})
};
export const MESSAGE_VERSION = createRequestTypes('MESSAGE_VERSION');
export const messageVersion = {
request: () => action(MESSAGE_VERSION[REQUEST]),
success: (messageVersion) => action(MESSAGE_VERSION[SUCCESS], {messageVersion}),
failure: (error) => action(MESSAGE_VERSION[FAILURE], {error})
export const VERSIONS = createRequestTypes('VERSIONS');
export const versions = {
request: () => action(VERSIONS[REQUEST]),
success: (versions) => action(VERSIONS[SUCCESS], {versions}),
failure: (error) => action(VERSIONS[FAILURE], {error})
};
export const PLUGINS = createRequestTypes('PLUGINS');
export const plugins = {
request: () => action(PLUGINS[REQUEST]),
success: (plugins) => action(PLUGINS[SUCCESS], {plugins}),
failure: (error) => action(PLUGINS[FAILURE], {error})
};
export const MESSAGE_SEND = createRequestTypes('MESSAGE_SEND');

View File

@ -68,8 +68,12 @@ export function fetchContract(payload) {
return get(`/contract/${payload.contractName}`);
}
export function communicationVersion() {
return get(`/communication/version`);
export function fetchVersions() {
return get('/versions');
}
export function fetchPlugins() {
return get('/plugins');
}
export function sendMessage(payload) {

View File

@ -10,9 +10,18 @@ import AccountsContainer from '../containers/AccountsContainer';
import AccountContainer from '../containers/AccountContainer';
import BlocksContainer from '../containers/BlocksContainer';
import BlockContainer from '../containers/BlockContainer';
import CommunicationContainer from '../containers/CommunicationContainer';
import TransactionsContainer from '../containers/TransactionsContainer';
import TransactionContainer from '../containers/TransactionContainer';
import CommunicationContainer from '../containers/CommunicationContainer';
import ENSContainer from '../containers/ENSContainer';
const groupItems = [
{to: "/embark/explorer/accounts", icon: "users", value: "Accounts"},
{to: "/embark/explorer/blocks", icon: "book-open", value: "Blocks"},
{to: "/embark/explorer/transactions", icon: "activity", value: "Transactions"},
{to: "/embark/explorer/communication", icon: "phone-call", value: "Communication"},
{to: "/embark/explorer/ens", icon: "disc", value: "ENS"}
];
const className = "d-flex align-items-center";
@ -22,38 +31,16 @@ const ExplorerLayout = () => (
<Page.Title className="my-5">Explorer</Page.Title>
<div>
<List.Group transparent={true}>
{groupItems.map((groupItem) => (
<List.GroupItem
className={className}
to="/embark/explorer/accounts"
icon="users"
to={groupItem.to}
icon={groupItem.icon}
RootComponent={withRouter(NavLink)}
>
Accounts
</List.GroupItem>
<List.GroupItem
className={className}
to="/embark/explorer/blocks"
icon="book-open"
RootComponent={withRouter(NavLink)}
>
Blocks
</List.GroupItem>
<List.GroupItem
className={className}
to="/embark/explorer/transactions"
icon="activity"
RootComponent={withRouter(NavLink)}
>
Transactions
</List.GroupItem>
<List.GroupItem
className={className}
to="/embark/explorer/communication"
icon="phone-call"
RootComponent={withRouter(NavLink)}
>
Communication
{groupItem.value}
</List.GroupItem>
))}
</List.Group>
</div>
</Grid.Col>
@ -63,9 +50,10 @@ const ExplorerLayout = () => (
<Route exact path="/embark/explorer/accounts/:address" component={AccountContainer} />
<Route exact path="/embark/explorer/blocks" component={BlocksContainer} />
<Route exact path="/embark/explorer/blocks/:blockNumber" component={BlockContainer} />
<Route exact path="/embark/explorer/communication" component={CommunicationContainer} />
<Route exact path="/embark/explorer/transactions" component={TransactionsContainer} />
<Route exact path="/embark/explorer/transactions/:hash" component={TransactionContainer} />
<Route exact path="/embark/explorer/communication" component={CommunicationContainer} />
<Route exact path="/embark/explorer/ens" component={ENSContainer} />
</Switch>
</Grid.Col>
</Grid.Row>

View File

@ -6,13 +6,22 @@ import React, {Component} from 'react';
import history from '../history';
import Layout from '../components/Layout';
import routes from '../routes';
import {contracts as contractsAction, initBlockHeader, processes as processesAction} from '../actions';
import {
initBlockHeader,
contracts as contractsAction,
processes as processesAction,
versions as versionsAction,
plugins as pluginsAction
} from '../actions';
class AppContainer extends Component {
componentDidMount() {
this.props.initBlockHeader();
this.props.fetchProcesses();
this.props.fetchContracts();
this.props.fetchVersions();
this.props.fetchPlugins();
}
render() {
@ -29,7 +38,9 @@ class AppContainer extends Component {
AppContainer.propTypes = {
initBlockHeader: PropTypes.func,
fetchContracts: PropTypes.func,
fetchProcesses: PropTypes.func
fetchProcesses: PropTypes.func,
fetchPlugins: PropTypes.func,
fetchVersions: PropTypes.func
};
export default connect(
@ -37,6 +48,8 @@ export default connect(
{
initBlockHeader,
fetchProcesses: processesAction.request,
fetchContracts: contractsAction.request
fetchContracts: contractsAction.request,
fetchVersions: versionsAction.request,
fetchPlugins: pluginsAction.request
},
)(AppContainer);

View File

@ -2,16 +2,11 @@ import PropTypes from "prop-types";
import React, {Component} from 'react';
import connect from "react-redux/es/connect/connect";
import {Alert, Loader, Page} from 'tabler-react';
import {messageSend, messageListen, messageVersion} from "../actions";
import {messageSend, messageListen} from "../actions";
import Communication from "../components/Communication";
import Loading from "../components/Loading";
import {getMessageVersion, getMessages, getMessageChannels} from "../reducers/selectors";
import {getMessages, getMessageChannels} from "../reducers/selectors";
class CommunicationContainer extends Component {
componentDidMount() {
this.props.communicationVersion();
}
sendMessage(topic, message) {
this.props.messageSend({topic, message});
}
@ -31,9 +26,6 @@ class CommunicationContainer extends Component {
isEnabledMessage = <Alert type="warning">The node uses an unsupported version of Whisper</Alert>;
}
if (!this.props.messages) {
return <Loading/>;
}
return (
<Page.Content title="Communication explorer">
{isEnabledMessage}
@ -49,8 +41,7 @@ class CommunicationContainer extends Component {
CommunicationContainer.propTypes = {
messageSend: PropTypes.func,
messageListen: PropTypes.func,
communicationVersion: PropTypes.func,
messageVersion: PropTypes.number,
isWhisperEnabled: PropTypes.bool,
messages: PropTypes.object,
messageChannels: PropTypes.array
};
@ -59,7 +50,7 @@ function mapStateToProps(state) {
return {
messages: getMessages(state),
messageChannels: getMessageChannels(state),
messageVersion: getMessageVersion(state)
isWhisperEnabled: isWhisperEnabled(state)
};
}
@ -68,7 +59,6 @@ export default connect(
{
messageSend: messageSend.request,
messageListen: messageListen.request,
communicationVersion: messageVersion.request
}
)(CommunicationContainer);

View File

@ -0,0 +1,67 @@
import PropTypes from "prop-types";
import React, {Component} from 'react';
import connect from "react-redux/es/connect/connect";
import {Alert, Loader, Page} from 'tabler-react';
import {messageSend, messageListen, messageVersion} from "../actions";
import Communication from "../components/Communication";
import Loading from "../components/Loading";
import {getMessages, getMessageChannels} from "../reducers/selectors";
class CommunicationContainer extends Component {
sendMessage(topic, message) {
this.props.messageSend({topic, message});
}
listenToChannel(channel) {
this.props.messageListen(channel);
}
render() {
let isEnabledMessage = '';
if (this.props.messageVersion === undefined || this.props.messageVersion === null) {
isEnabledMessage =
<Alert bsStyle="secondary "><Loader/> Checking Whisper support, please wait</Alert>;
} else if (!this.props.messageVersion) {
isEnabledMessage = <Alert type="warning">The node you are using does not support Whisper</Alert>;
} else if (this.props.messageVersion === -1) {
isEnabledMessage = <Alert type="warning">The node uses an unsupported version of Whisper</Alert>;
}
if (!this.props.messages) {
return <Loading/>;
}
return (
<Page.Content title="Communication explorer">
{isEnabledMessage}
<Communication listenToMessages={(channel) => this.listenToChannel(channel)}
sendMessage={(channel, message) => this.sendMessage(channel, message)}
channels={this.props.messages}
subscriptions={this.props.messageChannels}/>
</Page.Content>
);
}
}
CommunicationContainer.propTypes = {
messageSend: PropTypes.func,
messageListen: PropTypes.func,
messageVersion: PropTypes.number,
messages: PropTypes.object,
messageChannels: PropTypes.array
};
function mapStateToProps(state) {
return {
messages: getMessages(state),
messageChannels: getMessageChannels(state)
};
}
export default connect(
mapStateToProps,
{
messageSend: messageSend.request,
messageListen: messageListen.request
}
)(CommunicationContainer);

View File

@ -15,8 +15,9 @@ const entitiesDefaultState = {
commands: [],
messages: [],
messageChannels: [],
messageVersion: null,
fiddle: null
fiddle: null,
versions: [],
plugins: []
};
const sorter = {
@ -61,10 +62,7 @@ function entities(state = entitiesDefaultState, action) {
for (let name of Object.keys(state)) {
let filter = filtrer[name] || (() => true);
let sort = sorter[name] || (() => true);
if (action[name] && !Array.isArray(action[name])) {
return {...state, [name]: action[name]};
}
if (action[name] && (!Array.isArray(action[name]) || action[name].length > 1)) {
if (action[name] && action[name].length > 1) {
return {...state, [name]: [...action[name], ...state[name]].filter(filter).sort(sort)};
}
if (action[name] && action[name].length === 1) {

View File

@ -4,8 +4,7 @@ import {eventChannel} from 'redux-saga';
import {all, call, fork, put, takeEvery, take} from 'redux-saga/effects';
const {account, accounts, block, blocks, transaction, transactions, processes, commands, processLogs,
contracts, contract, contractProfile, messageSend, messageVersion, messageListen, contractLogs,
fiddle} = actions;
contracts, contract, contractProfile, messageSend, versions, plugins, messageListen, fiddle} = actions;
function *doRequest(entity, apiFn, payload) {
const {response, error} = yield call(apiFn, payload);
@ -16,6 +15,8 @@ function *doRequest(entity, apiFn, payload) {
}
}
export const fetchPlugins = doRequest.bind(null, plugins, api.fetchPlugins);
export const fetchVersions = doRequest.bind(null, versions, api.fetchVersions);
export const fetchAccount = doRequest.bind(null, account, api.fetchAccount);
export const fetchBlock = doRequest.bind(null, block, api.fetchBlock);
export const fetchTransaction = doRequest.bind(null, transaction, api.fetchTransaction);
@ -30,6 +31,7 @@ export const fetchContracts = doRequest.bind(null, contracts, api.fetchContracts
export const fetchContract = doRequest.bind(null, contract, api.fetchContract);
export const fetchContractProfile = doRequest.bind(null, contractProfile, api.fetchContractProfile);
export const fetchFiddle = doRequest.bind(null, fiddle, api.fetchFiddle);
export const sendMessage = doRequest.bind(null, messageSend, api.sendMessage);
export function *watchFetchTransaction() {
yield takeEvery(actions.TRANSACTION[actions.REQUEST], fetchTransaction);
@ -83,6 +85,18 @@ export function *watchFetchContractProfile() {
yield takeEvery(actions.CONTRACT_PROFILE[actions.REQUEST], fetchContractProfile);
}
export function *watchFetchVersions() {
yield takeEvery(actions.VERSIONS[actions.REQUEST], fetchVersions);
}
export function *watchFetchPlugins() {
yield takeEvery(actions.PLUGINS[actions.REQUEST], fetchPlugins);
}
export function *watchSendMessage() {
yield takeEvery(actions.MESSAGE_SEND[actions.REQUEST], sendMessage);
}
function createChannel(socket) {
return eventChannel(emit => {
socket.onmessage = ((message) => {
@ -134,8 +148,6 @@ export function *watchListenToContractLogs() {
yield takeEvery(actions.WATCH_NEW_CONTRACT_LOGS, listenToContractLogs);
}
export const sendMessage = doRequest.bind(null, messageSend, api.sendMessage);
export function *watchSendMessage() {
yield takeEvery(actions.MESSAGE_SEND[actions.REQUEST], sendMessage);
}
@ -153,16 +165,11 @@ export function *watchListenToMessages() {
yield takeEvery(actions.MESSAGE_LISTEN[actions.REQUEST], listenToMessages);
}
export const fetchCommunicationVersion = doRequest.bind(null, messageVersion, api.communicationVersion);
export function *watchCommunicationVersion() {
yield takeEvery(actions.MESSAGE_VERSION[actions.REQUEST], fetchCommunicationVersion);
}
export function *watchFetchFiddle() {
yield takeEvery(actions.FIDDLE[actions.REQUEST], fetchFiddle);
}
export default function *root() {
yield all([
fork(watchInitBlockHeader),
@ -176,7 +183,8 @@ export default function *root() {
fork(watchFetchBlock),
fork(watchFetchTransactions),
fork(watchPostCommand),
fork(watchCommunicationVersion),
fork(watchFetchVersions),
fork(watchFetchPlugins),
fork(watchFetchBlocks),
fork(watchFetchContracts),
fork(watchListenToMessages),
@ -187,4 +195,3 @@ export default function *root() {
fork(watchFetchFiddle)
]);
}

View File

@ -15,21 +15,19 @@ var Plugins = function(options) {
};
Plugins.prototype.loadPlugins = function() {
var pluginConfig;
for (var pluginName in this.pluginList) {
pluginConfig = this.pluginList[pluginName];
for (let pluginName in this.pluginList) {
let pluginConfig = this.pluginList[pluginName];
this.loadPlugin(pluginName, pluginConfig);
}
};
Plugins.prototype.listPlugins = function() {
const list = [];
this.plugins.forEach(plugin => {
return this.plugins.reduce((list, plugin) => {
if (plugin.loaded) {
list.push(plugin.name);
}
});
return list;
}, []);
};
// for services that act as a plugin but have core functionality

View File

@ -11,6 +11,7 @@ class LibraryManager {
this.determineVersions();
this.registerCommands();
this.registerAPICommands();
this.listenToCommandsToGetVersions();
this.listenToCommandsToGetLibrary();
}
@ -51,6 +52,18 @@ class LibraryManager {
});
}
registerAPICommands() {
const self = this;
self.embark.registerAPICall(
'get',
'/embark-api/versions',
(req, res) => {
const versions = Object.keys(self.versions).map((name) => ({value: self.versions[name], name}));
res.send(versions);
}
);
}
listenToCommandsToGetVersions() {
const self = this;
for (let libName in this.versions) {

View File

@ -85,6 +85,13 @@ class Server {
if (self.plugins) {
let apiCalls = self.plugins.getPluginsProperty("apiCalls", "apiCalls");
app.get('/embark-api/plugins', function(req, res) {
res.send(JSON.stringify(self.plugins.plugins.map((plugin) => {
return {name: plugin.name};
})));
});
for (let apiCall of apiCalls) {
console.dir("adding " + apiCall.method + " " + apiCall.endpoint);
app[apiCall.method].apply(app, [apiCall.endpoint, apiCall.cb]);

View File

@ -199,14 +199,6 @@ class Whisper {
ws.send(JSON.stringify(result));
});
});
self.embark.registerAPICall(
'get',
'/embark-api/communication/version',
(req, res) => {
res.send(self.isOldWeb3 ? -1 : self.version || 0);
}
);
});
}
}