Adding new reducer and selector

This commit is contained in:
Anthony Laibe 2018-08-07 15:09:55 +01:00 committed by Pascal Precht
parent bb844caa48
commit 1bd5174f61
No known key found for this signature in database
GPG Key ID: 0EE28D8D6FD85D7D
36 changed files with 534 additions and 706 deletions

View File

@ -1,4 +1,3 @@
// Accounts
export const REQUEST = 'REQUEST';
export const SUCCESS = 'SUCCESS';
export const FAILURE = 'FAILURE';
@ -24,7 +23,7 @@ export const accounts = {
export const ACCOUNT = createRequestTypes('ACCOUNT');
export const account = {
request: (address) => action(ACCOUNT[REQUEST], {address}),
success: (account) => action(ACCOUNT[SUCCESS], {account}),
success: (account) => action(ACCOUNT[SUCCESS], {accounts: [account]}),
failure: (error) => action(ACCOUNT[FAILURE], {error})
};
@ -38,7 +37,7 @@ export const blocks = {
export const BLOCK = createRequestTypes('BLOCK');
export const block = {
request: (blockNumber) => action(BLOCK[REQUEST], {blockNumber}),
success: (block) => action(BLOCK[SUCCESS], {block}),
success: (block) => action(BLOCK[SUCCESS], {blocks: [block]}),
failure: (error) => action(BLOCK[FAILURE], {error})
};
@ -52,7 +51,7 @@ export const transactions = {
export const TRANSACTION = createRequestTypes('TRANSACTION');
export const transaction = {
request: (hash) => action(TRANSACTION[REQUEST], {hash}),
success: (transaction) => action(TRANSACTION[SUCCESS], {transaction}),
success: (transaction) => action(TRANSACTION[SUCCESS], {transactions: [transaction]}),
failure: (error) => action(TRANSACTION[FAILURE], {error})
};
@ -66,37 +65,41 @@ export const processes = {
export const COMMANDS = createRequestTypes('COMMANDS');
export const commands = {
post: (command) => action(COMMANDS[REQUEST], {command}),
success: (result) => action(COMMANDS[SUCCESS], {result}),
success: (command) => action(COMMANDS[SUCCESS], {commands: [command]}),
failure: (error) => action(COMMANDS[FAILURE], {error})
};
// Process logs
export const FETCH_PROCESS_LOGS = 'FETCH_PROCESS_LOGS';
export const RECEIVE_PROCESS_LOGS = 'RECEIVE_PROCESS_LOGS';
export const WATCH_NEW_PROCESS_LOGS = 'WATCH_NEW_PROCESS_LOGS';
export const RECEIVE_NEW_PROCESS_LOG = 'RECEIVE_NEW_PROCESS_LOG';
export const RECEIVE_PROCESS_LOGS_ERROR = 'RECEIVE_PROCESS_LOGS_ERROR';
// BlockHeader
export const INIT_BLOCK_HEADER = 'INIT_BLOCK_HEADER';
// Contracts
export const FETCH_CONTRACTS = 'FETCH_CONTRACTS';
export const RECEIVE_CONTRACTS = 'RECEIVE_CONTRACTS';
export const RECEIVE_CONTRACTS_ERROR = 'RECEIVE_CONTRACTS_ERROR';
// Contract
export const FETCH_CONTRACT = 'FETCH_CONTRACT';
export const RECEIVE_CONTRACT = 'RECEIVE_CONTRACT';
export const RECEIVE_CONTRACT_ERROR = 'RECEIVE_CONTRACT_ERROR';
// Contract Profile
export const FETCH_CONTRACT_PROFILE = 'FETCH_CONTRACT_PROFILE';
export const RECEIVE_CONTRACT_PROFILE = 'RECEIVE_CONTRACT_PROFILE';
export const RECEIVE_CONTRACT_PROFILE_ERROR = 'RECEIVE_CONTRACT_PROFILE_ERROR';
export const PROCESS_LOGS = createRequestTypes('PROCESS_LOGS');
export const processLogs = {
request: (processName) => action(PROCESS_LOGS[REQUEST], {processName}),
success: (processLogs) => action(PROCESS_LOGS[SUCCESS], {processLogs}),
failure: (error) => action(PROCESS_LOGS[FAILURE], {error})
};
export function fetchProcessLogs(processName) {
return {
type: FETCH_PROCESS_LOGS,
processName
};
}
export const CONTRACTS = createRequestTypes('CONTRACTS');
export const contracts = {
request: () => action(CONTRACTS[REQUEST]),
success: (contracts) => action(CONTRACTS[SUCCESS], {contracts}),
failure: (error) => action(CONTRACTS[FAILURE], {error})
};
export const CONTRACT = createRequestTypes('CONTRACT');
export const contract = {
request: (contractName) => action(CONTRACT[REQUEST], {contractName}),
success: (contract) => action(CONTRACT[SUCCESS], {contracts: [contract]}),
failure: (error) => action(CONTRACT[FAILURE], {error})
};
export const CONTRACT_PROFILE = createRequestTypes('CONTRACT_PROFILE');
export const contractProfile = {
request: (contractName) => action(CONTRACT_PROFILE[REQUEST], {contractName}),
success: (contractProfile) => action(CONTRACT_PROFILE[SUCCESS], {contractProfiles: [contractProfile]}),
failure: (error) => action(CONTRACT_PROFILE[FAILURE], {error})
};
// Web Socket
export const WATCH_NEW_PROCESS_LOGS = 'WATCH_NEW_PROCESS_LOGS';
export const INIT_BLOCK_HEADER = 'INIT_BLOCK_HEADER';
export function listenToProcessLogs(processName) {
return {
@ -105,84 +108,8 @@ export function listenToProcessLogs(processName) {
};
}
export function receiveProcessLogs(processName, logs) {
return {
type: RECEIVE_PROCESS_LOGS,
processName,
logs
};
}
export function receiveProcessLogsError(error) {
return {
type: RECEIVE_PROCESS_LOGS_ERROR,
error
};
}
export function initBlockHeader(){
return {
type: INIT_BLOCK_HEADER
};
}
export function fetchContractProfile(contractName) {
return {
type: FETCH_CONTRACT_PROFILE,
contractName
};
}
export function receiveContractProfile(contractProfile) {
return {
type: RECEIVE_CONTRACT_PROFILE,
contractProfile
};
}
export function receiveContractProfileError() {
return {
type: RECEIVE_CONTRACT_PROFILE_ERROR
};
}
export function fetchContract(contractName) {
return {
type: FETCH_CONTRACT,
contractName
};
}
export function receiveContract(contract) {
return {
type: RECEIVE_CONTRACT,
contract
};
}
export function receiveContractError() {
return {
type: RECEIVE_CONTRACT_ERROR
};
}
export function fetchContracts() {
return {
type: FETCH_CONTRACTS
};
}
export function receiveContracts(contracts) {
return {
type: RECEIVE_CONTRACTS,
contracts
};
}
export function receiveContractsError() {
return {
type: RECEIVE_CONTRACTS_ERROR
};
}

View File

@ -53,8 +53,20 @@ export function fetchProcesses() {
return get('/processes');
}
export function fetchProcessLogs(processName) {
return axios.get(`${constants.httpEndpoint}/process-logs/${processName}`);
export function fetchProcessLogs(payload) {
return get(`/process-logs/${payload.processName}`);
}
export function fetchContracts() {
return get('/contracts');
}
export function fetchContract(payload) {
return get(`/contract/${payload.contractName}`);
}
export function fetchContractProfile(payload) {
return get(`/profiler/${payload.contractName}`);
}
export function webSocketProcess(processName) {
@ -64,15 +76,3 @@ export function webSocketProcess(processName) {
export function webSocketBlockHeader() {
return new WebSocket(`${constants.wsEndpoint}/blockchain/blockHeader`);
}
export function fetchContract(contractName) {
return axios.get(`${constants.httpEndpoint}/contract/${contractName}`);
}
export function fetchContracts() {
return axios.get(`${constants.httpEndpoint}/contracts`);
}
export function fetchContractProfile(contractName) {
return axios.get(`${constants.httpEndpoint}/profiler/${contractName}`);
}

View File

@ -36,8 +36,7 @@ class Console extends Component {
</Card.Header>
<Card.Body>
<div>
{this.props.commandResults &&
this.props.commandResults.map((result) => <CommandResult key={result} result={result} />)}
{this.props.commands.map((command, index) => <CommandResult key={index} result={command.result} />)}
</div>
<Form onSubmit={(event) => this.handleSubmit(event)}>
<Form.Input value={this.state.value}
@ -55,7 +54,7 @@ class Console extends Component {
Console.propTypes = {
postCommand: PropTypes.func,
commandResults: PropTypes.arrayOf(PropTypes.string)
commands: PropTypes.arrayOf(PropTypes.object)
};
export default Console;

View File

@ -1,3 +1,4 @@
import PropTypes from "prop-types";
import React from 'react';
import {
Page,
@ -33,5 +34,9 @@ const Contract = ({contract}) => (
</Page.Content>
);
Contract.propTypes = {
contract: PropTypes.object
};
export default Contract;

View File

@ -1,3 +1,4 @@
import PropTypes from "prop-types";
import React from 'react';
import {NavLink, Route, Switch, withRouter} from 'react-router-dom';
import {
@ -59,4 +60,8 @@ const ContractLayout = (props) => (
</Grid.Row>
);
ContractLayout.propTypes = {
match: PropTypes.object
};
export default withRouter(ContractLayout);

View File

@ -1,3 +1,4 @@
import PropTypes from "prop-types";
import React from 'react';
import {
Page,
@ -6,8 +7,8 @@ import {
Table
} from "tabler-react";
const ContractProfile = ({contract}) => (
<Page.Content title={contract.name}>
const ContractProfile = ({contractProfile}) => (
<Page.Content title={contractProfile.name}>
<Grid.Row>
<Grid.Col>
<Card>
@ -23,7 +24,7 @@ const ContractProfile = ({contract}) => (
{content: "Gas Estimates"}
]}
bodyItems={
contract.methods.map((method) => {
contractProfile.methods.map((method) => {
return ([
{content: method.name},
{content: (method.payable === true).toString()},
@ -41,5 +42,9 @@ const ContractProfile = ({contract}) => (
</Page.Content>
);
ContractProfile.propTypes = {
contractProfile: PropTypes.object
};
export default ContractProfile;

View File

@ -1,3 +1,4 @@
import PropTypes from "prop-types";
import React from 'react';
import {
Page,
@ -25,7 +26,7 @@ const Contracts = ({contracts}) => (
return ([
{content: <Link to={`contracts/${contract.name}`}>{contract.name}</Link>},
{content: contract.address},
{content: contract.deploy}
{content: contract.deploy.toString()}
]);
})
}
@ -36,5 +37,9 @@ const Contracts = ({contracts}) => (
</Page.Content>
);
Contracts.propTypes = {
contracts: PropTypes.arrayOf(PropTypes.object)
};
export default Contracts;

View File

@ -0,0 +1,30 @@
import PropTypes from 'prop-types';
import React from "react";
import Loading from '../components/Loading';
import Error from '../components/Error';
const DataWrapper = ({error, loading, shouldRender, render, ...rest}) => {
if (error) {
return <Error error={error} />;
}
if (loading) {
return <Loading />;
}
if (shouldRender) {
return render(rest);
}
return <React.Fragment />;
};
DataWrapper.propTypes = {
error: PropTypes.string,
loading: PropTypes.bool,
render: PropTypes.func,
shouldRender: PropTypes.bool
};
export default DataWrapper;

View File

@ -1,30 +1,26 @@
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {Page} from "tabler-react";
import Loading from "./Loading";
class Process extends Component {
render() {
const logs = this.props.logs;
const {processLogs, process}= this.props;
return (
<Page.Content className="text-capitalize" title={this.props.processName}>
<p className="text-capitalize">State: {this.props.state}</p>
{!logs &&
<Loading/>}
{logs &&
<Page.Content className="text-capitalize" title={process.name}>
<p className="text-capitalize">State: {process.state}</p>
<div className="logs">
{
logs.map((item, i) => <p key={i} className={item.logLevel}>{item.msg_clear || item.msg}</p>)
processLogs.map((item, i) => <p key={i} className={item.logLevel}>{item.msg_clear || item.msg}</p>)
}
</div>}
</Page.Content>);
</div>
</Page.Content>
);
}
}
Process.propTypes = {
processName: PropTypes.string.isRequired,
state: PropTypes.string.isRequired,
logs: PropTypes.array
process: PropTypes.object,
processLogs: PropTypes.arrayOf(PropTypes.object)
};
export default Process;

View File

@ -11,15 +11,15 @@ function stampClasses(state){
});
}
const Process = ({name, state}) => (
const Process = ({process}) => (
<Grid.Col sm={6} lg={3}>
<Card className="p-3">
<div className="d-flex align-items-center">
<span className={stampClasses(state)}>
<span className={stampClasses(process.state)}>
<i className="fe fa-cube"></i>
</span>
<div>
<h4 className="text-capitalize m-0"><Link to={`/embark/processes/${name}`}>{name}</Link></h4>
<h4 className="text-capitalize m-0"><Link to={`/embark/processes/${process.name}`}>{process.name}</Link></h4>
</div>
</div>
</Card>
@ -27,18 +27,17 @@ const Process = ({name, state}) => (
);
Process.propTypes = {
name: PropTypes.string,
state: PropTypes.string
process: PropTypes.object
};
const Processes = ({processes}) => (
<Grid.Row cards>
{Object.keys(processes).map((name) => <Process key={name} name={name} state={processes[name].state} />)}
{processes.map((process) => <Process key={process.name} process={process} />)}
</Grid.Row>
);
Processes.propTypes = {
processes: PropTypes.object
processes: PropTypes.arrayOf(PropTypes.object)
};
export default Processes;

View File

@ -1,40 +1,38 @@
import PropTypes from "prop-types";
import React, {Component} from 'react';
import connect from "react-redux/es/connect/connect";
import {NavLink, Route, Switch, withRouter} from 'react-router-dom';
import {NavLink, Route, Switch, withRouter, Redirect} from 'react-router-dom';
import {
Page,
Grid,
List
} from "tabler-react";
import ProcessesContainer from '../containers/ProcessesContainer';
import ProcessContainer from '../containers/ProcessContainer';
import {getProcesses} from "../reducers/selectors";
import Loading from "./Loading";
const routePrefix = '/embark/processes';
class ProcessesLayout extends Component {
render() {
if (!this.props.processes || !this.props.processes.data) {
if (this.props.processes.length === 0) {
return <Loading />;
}
const processNames = Object.keys(this.props.processes.data) || [];
return (<Grid.Row>
<Grid.Col md={3}>
<Page.Title className="my-5">Processes</Page.Title>
<div>
<List.Group transparent={true}>
{processNames.map((processName, index) => {
{this.props.processes.map((process, index) => {
return (<List.GroupItem
className="d-flex align-items-center text-capitalize"
to={`${routePrefix}/${processName}`}
key={'process-' + processName}
to={`${routePrefix}/${process.name}`}
key={'process-' + process.name}
active={index === 0 && this.props.match.isExact === true}
RootComponent={withRouter(NavLink)}
>
{processName}
{process.name}
</List.GroupItem>);
})}
@ -43,10 +41,8 @@ class ProcessesLayout extends Component {
</Grid.Col>
<Grid.Col md={9}>
<Switch>
<Route exact path={`${routePrefix}/`} component={ProcessesContainer} />
{processNames.map((processName, index) => {
return (<Route key={'procesRoute-' + index} exact path={`${routePrefix}/${processName}`} component={ProcessesContainer}/>);
})}
<Route exact path={`${routePrefix}/:processName`} component={() => <ProcessContainer />} />
<Redirect exact from={`${routePrefix}/`} to={`${routePrefix}/${this.props.processes[0].name}`} />
</Switch>
</Grid.Col>
</Grid.Row>);
@ -54,12 +50,12 @@ class ProcessesLayout extends Component {
}
ProcessesLayout.propTypes = {
processes: PropTypes.object,
processes: PropTypes.arrayOf(PropTypes.object),
match: PropTypes.object
};
function mapStateToProps(state) {
return {processes: state.processes};
return {processes: getProcesses(state)};
}
export default connect(

View File

@ -5,9 +5,9 @@ import {withRouter} from 'react-router-dom';
import {account as accountAction} from '../actions';
import Account from '../components/Account';
import NoMatch from "../components/NoMatch";
import DataWrapper from "../components/DataWrapper";
import Transactions from '../components/Transactions';
import Error from '../components/Error';
import {getAccount, getTransactionsByAccount} from "../reducers/selectors";
class AccountContainer extends Component {
componentDidMount() {
@ -15,37 +15,30 @@ class AccountContainer extends Component {
}
render() {
const {account, error} = this.props;
if (error) {
return <Error error={error} />;
}
if (!account) {
return <NoMatch />;
}
return (
<React.Fragment>
<Account account={account} />
<Transactions transactions={account.transactions || []} />
</React.Fragment>
<DataWrapper shouldRender={this.props.account !== undefined } {...this.props} render={({account, transactions}) => (
<React.Fragment>
<Account account={account} />
<Transactions transactions={transactions || []} />
</React.Fragment>
)} />
);
}
}
function mapStateToProps(state, props) {
if(state.accounts.error) {
return {error: state.accounts.error};
}
if(state.accounts.data) {
return {account: state.accounts.data.find(account => account.address === props.match.params.address)};
}
return {};
return {
account: getAccount(state, props.match.params.address),
transactions: getTransactionsByAccount(state, props.match.params.address),
error: state.errorMessage,
loading: state.loading
};
}
AccountContainer.propTypes = {
match: PropTypes.object,
account: PropTypes.object,
transactions: PropTypes.arrayOf(PropTypes.object),
fetchAccount: PropTypes.func,
error: PropTypes.string
};

View File

@ -4,8 +4,8 @@ import PropTypes from 'prop-types';
import {accounts as accountsAction} from '../actions';
import Accounts from '../components/Accounts';
import Loading from '../components/Loading';
import Error from '../components/Error';
import DataWrapper from "../components/DataWrapper";
import {getAccounts} from "../reducers/selectors";
class AccountsContainer extends Component {
componentDidMount() {
@ -13,28 +13,23 @@ class AccountsContainer extends Component {
}
render() {
const {accounts} = this.props;
if (accounts.error) {
return <Error error={accounts.error} />;
}
if (!accounts.data) {
return <Loading />;
}
return (
<Accounts accounts={accounts.data} />
<DataWrapper shouldRender={this.props.accounts.length > 0} {...this.props} render={({accounts}) => (
<Accounts accounts={accounts} />
)} />
);
}
}
function mapStateToProps(state) {
return {accounts: state.accounts};
return {accounts: getAccounts(state), error: state.errorMessage, loading: state.loading};
}
AccountsContainer.propTypes = {
accounts: PropTypes.object,
fetchAccounts: PropTypes.func
accounts: PropTypes.arrayOf(PropTypes.object),
fetchAccounts: PropTypes.func,
error: PropTypes.string,
loading: PropTypes.bool
};
export default connect(

View File

@ -5,9 +5,9 @@ import {withRouter} from 'react-router-dom';
import {block as blockAction} from '../actions';
import Block from '../components/Block';
import Error from "../components/Error";
import NoMatch from "../components/NoMatch";
import DataWrapper from "../components/DataWrapper";
import Transactions from '../components/Transactions';
import {getBlock, getTransactionsByBlock} from "../reducers/selectors";
class BlockContainer extends Component {
componentDidMount() {
@ -15,36 +15,30 @@ class BlockContainer extends Component {
}
render() {
const {block, error} = this.props;
if (error) {
return <Error error={error} />;
}
if (!block) {
return <NoMatch />;
}
return (
<React.Fragment>
<Block block={block} />
<Transactions transactions={block.transactions} />
</React.Fragment>
<DataWrapper shouldRender={this.props.block !== undefined } {...this.props} render={({block, transactions}) => (
<React.Fragment>
<Block block={block} />
<Transactions transactions={transactions || []} />
</React.Fragment>
)} />
);
}
}
function mapStateToProps(state, props) {
if(state.blocks.error) {
return {error: state.blocks.error};
}
if(state.blocks.data) {
return {block: state.blocks.data.find(block => block.number.toString() === props.match.params.blockNumber)};
}
return {};
return {
block: getBlock(state, props.match.params.blockNumber),
transactions: getTransactionsByBlock(state, props.match.params.blockNumber),
error: state.errorMessage,
loading: state.loading
};
}
BlockContainer.propTypes = {
match: PropTypes.object,
block: PropTypes.object,
transactions: PropTypes.arrayOf(PropTypes.object),
fetchBlock: PropTypes.func,
error: PropTypes.string
};

View File

@ -4,9 +4,9 @@ import PropTypes from 'prop-types';
import {blocks as blocksAction} from '../actions';
import Blocks from '../components/Blocks';
import Loading from '../components/Loading';
import LoadMore from '../components/LoadMore';
import Error from '../components/Error';
import DataWrapper from "../components/DataWrapper";
import LoadMore from "../components/LoadMore";
import {getBlocks} from "../reducers/selectors";
class BlocksContainer extends Component {
componentDidMount() {
@ -18,23 +18,19 @@ class BlocksContainer extends Component {
}
loadMoreFrom() {
let blocks = this.props.blocks.data;
let blocks = this.props.blocks;
if (blocks.length === 0) {
return 0;
}
return blocks[blocks.length - 1].number - 1;
}
render() {
const {blocks} = this.props;
if (blocks.error) {
return <Error error={blocks.error} />;
}
if (!blocks.data) {
return <Loading />;
}
return (
<React.Fragment>
<Blocks blocks={blocks.data}/>
<DataWrapper shouldRender={this.props.blocks.length > 0} {...this.props} render={({blocks}) => (
<Blocks blocks={blocks} />
)} />
{(this.loadMoreFrom() >= 0) ? <LoadMore loadMore={() => this.loadMore()} /> : <React.Fragment />}
</React.Fragment>
);
@ -42,12 +38,14 @@ class BlocksContainer extends Component {
}
function mapStateToProps(state) {
return {blocks: state.blocks};
return {blocks: getBlocks(state), error: state.errorMessage, loading: state.loading};
}
BlocksContainer.propTypes = {
blocks: PropTypes.object,
fetchBlocks: PropTypes.func
blocks: PropTypes.arrayOf(PropTypes.object),
fetchBlocks: PropTypes.func,
error: PropTypes.string,
loading: PropTypes.bool
};
export default connect(

View File

@ -1,48 +1,45 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { compose } from 'redux';
import { fetchContract } from '../actions';
import React, {Component} from 'react';
import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import {withRouter} from 'react-router-dom';
import {contract as contractAction} from '../actions';
import Contract from '../components/Contract';
import { withRouter } from 'react-router'
import DataWrapper from "../components/DataWrapper";
import {getContract} from "../reducers/selectors";
class ContractContainer extends Component {
componentWillMount() {
componentDidMount() {
this.props.fetchContract(this.props.match.params.contractName);
}
render() {
const {contract} = this.props;
if (!contract.data) {
return (
<h1>
<i>Loading contract...</i>
</h1>
);
}
if (contract.error) {
return (
<h1>
<i>Error API...</i>
</h1>
);
}
return (
<Contract contract={contract.data} />
<DataWrapper shouldRender={this.props.contract !== undefined } {...this.props} render={({contract}) => (
<Contract contract={contract} />
)} />
);
}
}
function mapStateToProps(state, props) {
return {
contract: getContract(state, props.match.params.contractName),
error: state.errorMessage,
loading: state.loading
};
}
ContractContainer.propTypes = {
match: PropTypes.object,
contract: PropTypes.object,
fetchContract: PropTypes.func,
error: PropTypes.string
};
function mapStateToProps(state) {
return { contract: state.contract }
};
export default compose(
connect(
mapStateToProps,
{fetchContract}
),
withRouter
)(ContractContainer);
export default withRouter(connect(
mapStateToProps,
{
fetchContract: contractAction.request
}
)(ContractContainer));

View File

@ -1,48 +1,45 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { compose } from 'redux';
import { fetchContractProfile } from '../actions';
import React, {Component} from 'react';
import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import {withRouter} from 'react-router-dom';
import {contractProfile as contractProfileAction} from '../actions';
import ContractProfile from '../components/ContractProfile';
import { withRouter } from 'react-router';
import DataWrapper from "../components/DataWrapper";
import {getContractProfile} from "../reducers/selectors";
class ContractProfileContainer extends Component {
componentWillMount() {
componentDidMount() {
this.props.fetchContractProfile(this.props.match.params.contractName);
}
render() {
const { contractProfile } = this.props;
if (!contractProfile.data) {
return (
<h1>
<i>Loading contract profile...</i>
</h1>
);
}
if (contractProfile.data.error) {
return (
<h1>
<i>Error API...</i>
</h1>
);
}
return (
<ContractProfile contract={contractProfile.data} />
<DataWrapper shouldRender={this.props.contractProfile !== undefined } {...this.props} render={({contractProfile}) => (
<ContractProfile contractProfile={contractProfile} />
)} />
);
}
}
function mapStateToProps(state) {
return { contractProfile: state.contractProfile };
function mapStateToProps(state, props) {
return {
contractProfile: getContractProfile(state, props.match.params.contractName),
error: state.errorMessage,
loading: state.loading
};
}
export default compose(
connect(
mapStateToProps,
{ fetchContractProfile }
),
withRouter
)(ContractProfileContainer);
ContractProfileContainer.propTypes = {
match: PropTypes.object,
contractProfile: PropTypes.object,
fetchContractProfile: PropTypes.func,
error: PropTypes.string
};
export default withRouter(connect(
mapStateToProps,
{
fetchContractProfile: contractProfileAction.request
}
)(ContractProfileContainer));

View File

@ -1,45 +1,38 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchContracts } from '../actions';
import React, {Component} from 'react';
import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import {contracts as contractsAction} from '../actions';
import Contracts from '../components/Contracts';
import DataWrapper from "../components/DataWrapper";
import {getContracts} from "../reducers/selectors";
class ContractsContainer extends Component {
componentWillMount() {
componentDidMount() {
this.props.fetchContracts();
}
render() {
const { contracts } = this.props;
if (!contracts.data) {
return (
<h1>
<i>Loading contracts...</i>
</h1>
);
}
if (contracts.error) {
return (
<h1>
<i>Error API...</i>
</h1>
);
}
return (
<Contracts contracts={contracts.data} />
<DataWrapper shouldRender={this.props.contracts.length > 0} {...this.props} render={({contracts}) => (
<Contracts contracts={contracts} />
)} />
);
}
}
function mapStateToProps(state) {
return { contracts: state.contracts };
return {contracts: getContracts(state), error: state.errorMessage, loading: state.loading};
}
ContractsContainer.propTypes = {
contracts: PropTypes.object,
fetchContracts: PropTypes.func
};
export default connect(
mapStateToProps,
{
fetchContracts
fetchContracts: contractsAction.request
},
)(ContractsContainer);

View File

@ -2,31 +2,40 @@ import PropTypes from "prop-types";
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Page} from "tabler-react";
import {commands as commandsAction} from "../actions";
import {commands as commandsAction} from "../actions";
import DataWrapper from "../components/DataWrapper";
import Processes from '../components/Processes';
import Console from '../components/Console';
import {getProcesses, getCommands} from "../reducers/selectors";
class HomeContainer extends Component {
render() {
return (
<React.Fragment>
<Page.Title className="my-5">Dashboard</Page.Title>
{this.props.processes.data && <Processes processes={this.props.processes.data} />}
<Console postCommand={this.props.postCommand} commandResults={this.props.commandResults} />
<DataWrapper shouldRender={this.props.processes.length > 0 } {...this.props} render={({processes}) => (
<Processes processes={processes} />
)} />
<Console postCommand={this.props.postCommand} commands={this.props.commands} />
</React.Fragment>
);
}
}
HomeContainer.propTypes = {
processes: PropTypes.object,
processes: PropTypes.arrayOf(PropTypes.object),
postCommand: PropTypes.func,
commandResults: PropTypes.arrayOf(PropTypes.string)
commands: PropTypes.arrayOf(PropTypes.object)
};
function mapStateToProps(state) {
return {processes: state.processes, commandResults: state.commands.results};
return {
processes: getProcesses(state),
commands: getCommands(state),
error: state.errorMessage,
loading: state.loading
};
}
export default connect(

View File

@ -0,0 +1,55 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import {withRouter} from "react-router-dom";
import {processLogs as processLogsAction, listenToProcessLogs} from '../actions';
import DataWrapper from "../components/DataWrapper";
import Process from "../components/Process";
import {getProcess, getProcessLogsByProcess} from "../reducers/selectors";
class ProcessContainer extends Component {
componentDidMount() {
if (this.props.process.state === 'running' && this.props.processLogs.length === 0) {
this.props.fetchProcessLogs(this.props.match.params.processName);
this.props.listenToProcessLogs(this.props.match.params.processName);
}
}
render() {
return (
<DataWrapper shouldRender={this.props.process !== undefined } {...this.props} render={({process, processLogs}) => (
<div className="processes-container">
<Process process={process}
processLogs={processLogs}/>
</div>
)} />
);
}
}
ProcessContainer.propTypes = {
fetchProcessLogs: PropTypes.func,
listenToProcessLogs: PropTypes.func,
process: PropTypes.object,
processLogs: PropTypes.arrayOf(PropTypes.object),
error: PropTypes.string,
loading: PropTypes.bool,
match: PropTypes.object
};
function mapStateToProps(state, props) {
return {
process: getProcess(state, props.match.params.processName),
processLogs: getProcessLogsByProcess(state, props.match.params.processName),
error: state.errorMessage,
loading: state.loading
};
}
export default withRouter(connect(
mapStateToProps,
{
fetchProcessLogs: processLogsAction.request,
listenToProcessLogs
}
)(ProcessContainer));

View File

@ -1,58 +0,0 @@
import React, {Component} from 'react';
import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import {fetchProcessLogs, listenToProcessLogs} from '../actions';
import Process from "../components/Process";
class ProcessesContainer extends Component {
componentDidMount() {
// Get correct process name
const pathParts = this.props.match.path.split('/');
this.processName = pathParts[pathParts.length - 1];
// If we are not in a specific process page (eg: processes/ root), get first process
if (Object.keys(this.props.processes.data).indexOf(this.processName) < 0) {
this.processName = Object.keys(this.props.processes.data)[0];
}
// Fetch logs for the process
this.props.fetchProcessLogs(this.processName);
// Only start watching if we are not already watching
if (!this.props.processes.data[this.processName].isListening) {
this.props.listenToProcessLogs(this.processName);
}
}
render() {
if (!this.processName) {
return '';
}
return (
<div className="processes-container">
<Process processName={this.processName}
state={this.props.processes.data[this.processName].state}
logs={this.props.processes.data[this.processName].logs}/>
</div>
);
}
}
ProcessesContainer.propTypes = {
match: PropTypes.object,
processes: PropTypes.object,
fetchProcessLogs: PropTypes.func,
listenToProcessLogs: PropTypes.func
};
function mapStateToProps(state) {
return {processes: state.processes};
}
export default connect(
mapStateToProps,
{
fetchProcessLogs,
listenToProcessLogs
}
)(ProcessesContainer);

View File

@ -4,9 +4,9 @@ import PropTypes from 'prop-types';
import {withRouter} from 'react-router-dom';
import {transaction as transactionAction} from '../actions';
import Error from "../components/Error";
import NoMatch from "../components/NoMatch";
import Transaction from '../components/Transaction';
import DataWrapper from "../components/DataWrapper";
import {getTransaction} from "../reducers/selectors";
class TransactionContainer extends Component {
componentDidMount() {
@ -14,35 +14,26 @@ class TransactionContainer extends Component {
}
render() {
const {transaction, error} = this.props;
if (error) {
return <Error error={error} />;
}
if (!transaction) {
return <NoMatch />;
}
return (
<React.Fragment>
<DataWrapper shouldRender={this.props.transaction !== undefined } {...this.props} render={({transaction}) => (
<Transaction transaction={transaction} />
</React.Fragment>
)} />
);
}
}
function mapStateToProps(state, props) {
if(state.transactions.error) {
return {error: state.transactions.error};
}
if(state.transactions.data) {
return {transaction: state.transactions.data.find(transaction => transaction.hash === props.match.params.hash)};
}
return {};
return {
transaction: getTransaction(state, props.match.params.hash),
error: state.errorMessage,
loading: state.loading
};
}
TransactionContainer.propTypes = {
match: PropTypes.object,
transaction: PropTypes.object,
transactions: PropTypes.arrayOf(PropTypes.object),
fetchTransaction: PropTypes.func,
error: PropTypes.string
};

View File

@ -3,10 +3,10 @@ import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import {transactions as transactionsAction} from '../actions';
import LoadMore from "../components/LoadMore";
import Transactions from '../components/Transactions';
import Loading from '../components/Loading';
import LoadMore from '../components/LoadMore';
import Error from '../components/Error';
import DataWrapper from "../components/DataWrapper";
import {getTransactions} from "../reducers/selectors";
class TransactionsContainer extends Component {
componentDidMount() {
@ -18,23 +18,19 @@ class TransactionsContainer extends Component {
}
loadMoreFrom() {
let transactions = this.props.transactions.data;
let transactions = this.props.transactions;
if (transactions.length === 0) {
return 0;
}
return transactions[transactions.length - 1].blockNumber - 1;
}
render() {
const {transactions} = this.props;
if (transactions.error) {
return <Error error={transactions.error} />;
}
if (!transactions.data) {
return <Loading />;
}
return (
<React.Fragment>
<Transactions transactions={transactions.data}/>
<DataWrapper shouldRender={this.props.transactions.length > 0} {...this.props} render={({transactions}) => (
<Transactions transactions={transactions} />
)} />
{(this.loadMoreFrom() > 0) ? <LoadMore loadMore={() => this.loadMore()} /> : <React.Fragment />}
</React.Fragment>
);
@ -42,12 +38,14 @@ class TransactionsContainer extends Component {
}
function mapStateToProps(state) {
return {transactions: state.transactions};
return {transactions: getTransactions(state), error: state.errorMessage, loading: state.loading};
}
TransactionsContainer.propTypes = {
transactions: PropTypes.object,
fetchTransactions: PropTypes.func
fetchTransactions: PropTypes.func,
error: PropTypes.string,
loading: PropTypes.bool
};
export default connect(

View File

@ -1,26 +0,0 @@
import * as actions from "../actions";
function filterAccount(account, index, self) {
return index === self.findIndex((a) => a.address === account.address);
}
export default function accounts(state = {}, action) {
switch (action.type) {
case actions.ACCOUNTS[actions.SUCCESS]:
return {
...state, error: null, data: [...action.accounts.data, ...state.data || []]
.filter(filterAccount)
};
case actions.ACCOUNTS[actions.FAILURE]:
return Object.assign({}, state, {error: action.error});
case actions.ACCOUNT[actions.SUCCESS]:
return {
...state, error: null, data: [action.account.data, ...state.data || []]
.filter(filterAccount)
};
case actions.ACCOUNT[actions.FAILURE]:
return Object.assign({}, state, {error: action.error});
default:
return state;
}
}

View File

@ -1,32 +0,0 @@
import * as actions from "../actions";
function sortBlock(a, b) {
return b.number - a.number;
}
function filterBlock(block, index, self) {
return index === self.findIndex((t) => t.number === block.number);
}
export default function blocks(state = {}, action) {
switch (action.type) {
case actions.BLOCKS[actions.SUCCESS]:
return {
...state, error: null, data: [...action.blocks.data, ...state.data || []]
.filter(filterBlock)
.sort(sortBlock)
};
case actions.BLOCKS[actions.FAILURE]:
return Object.assign({}, state, {error: action.error});
case actions.BLOCK[actions.SUCCESS]:
return {
...state, error: null, data: [action.block.data, ...state.data || []]
.filter(filterBlock)
.sort(sortBlock)
};
case actions.BLOCK[actions.FAILURE]:
return Object.assign({}, state, {error: action.error});
default:
return state;
}
}

View File

@ -1,14 +0,0 @@
import * as actions from "../actions";
export default function commands(state = {}, action) {
switch (action.type) {
case actions.COMMANDS[actions.SUCCESS]:
return {
...state, error: null, results: [...state.results || [], action.result.data.result]
};
case actions.COMMANDS[actions.FAILURE]:
return Object.assign({}, state, {error: action.error});
default:
return state;
}
}

View File

@ -1,12 +0,0 @@
import {RECEIVE_CONTRACT_PROFILE, RECEIVE_CONTRACT_PROFILE_ERROR} from "../actions";
export default function contractProfile(state = {}, action) {
switch (action.type) {
case RECEIVE_CONTRACT_PROFILE:
return Object.assign({}, state, {data: action.contractProfile.data});
case RECEIVE_CONTRACT_PROFILE_ERROR:
return Object.assign({}, state, {error: true});
default:
return state;
}
}

View File

@ -1,12 +0,0 @@
import {RECEIVE_CONTRACT, RECEIVE_CONTRACT_ERROR} from "../actions";
export default function contract(state = {}, action) {
switch (action.type) {
case RECEIVE_CONTRACT:
return Object.assign({}, state, {data: action.contract.data});
case RECEIVE_CONTRACT_ERROR:
return Object.assign({}, state, {error: true});
default:
return state;
}
}

View File

@ -1,12 +0,0 @@
import {RECEIVE_CONTRACTS, RECEIVE_CONTRACTS_ERROR} from "../actions";
export default function contracts(state = {}, action) {
switch (action.type) {
case RECEIVE_CONTRACTS:
return Object.assign({}, state, {data: action.contracts.data});
case RECEIVE_CONTRACTS_ERROR:
return Object.assign({}, state, {error: true});
default:
return state;
}
}

View File

@ -1,22 +1,89 @@
import {combineReducers} from 'redux';
import processesReducer from './processesReducer';
import accountsReducer from './accountsReducer';
import blocksReducer from './blocksReducer';
import transactionsReducer from './transactionsReducer';
import commandsReducer from './commandsReducer';
import contractsReducer from './contractsReducer';
import contractReducer from './contractReducer';
import contractProfileReducer from './contractProfileReducer';
import {REQUEST} from "../actions";
const BN_FACTOR = 10000;
const entitiesDefaultState = {
accounts: [],
blocks: [],
transactions: [],
processes: [],
processLogs: [],
contracts: [],
contractProfiles: [],
commands: []
};
const sorter = {
blocks: function(a, b) {
return b.number - a.number;
},
transactions: function(a, b) {
return ((BN_FACTOR * b.blockNumber) + b.transactionIndex) - ((BN_FACTOR * a.blockNumber) + a.transactionIndex);
},
processLogs: function(a, b) {
return a.timestamp - b.timestamp;
}
};
const filtrer = {
processes: function(process, index, self) {
return index === self.findIndex((t) => t.name === process.name);
},
contracts: function(contract, index, self) {
return index === self.findIndex((t) => t.name === contract.name);
},
accounts: function(account, index, self) {
return index === self.findIndex((t) => t.address === account.address);
},
blocks: function(block, index, self) {
return index === self.findIndex((t) => t.number === block.number);
},
transactions: function(tx, index, self) {
return index === self.findIndex((t) => (
t.blockNumber === tx.blockNumber && t.transactionIndex === tx.transactionIndex
));
}
};
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] && action[name].length > 1) {
return {...state, [name]: [...action[name], ...state[name]].filter(filter).sort(sort)};
}
if (action[name] && action[name].length === 1) {
let entity = action[name][0];
let nested = Object.keys(state).reduce((acc, entityName) => {
if (entity[entityName] && entity[entityName].length > 0) {
let entityFilter = filtrer[entityName] || (() => true);
let entitySort = sorter[entityName] || (() => true);
acc[entityName] = [...entity[entityName], ...state[entityName]].filter(entityFilter).sort(entitySort);
}
return acc;
}, {});
return {
...state, ...nested, [name]: [...action[name], ...state[name]].filter(filter).sort(sort)
};
}
}
return state;
}
function errorMessage(state = null, action) {
return action.error || state;
}
function loading(_state = false, action) {
return action.type.endsWith(REQUEST);
}
const rootReducer = combineReducers({
accounts: accountsReducer,
processes: processesReducer,
contracts: contractsReducer,
contract: contractReducer,
contractProfile: contractProfileReducer,
blocks: blocksReducer,
transactions: transactionsReducer,
commands: commandsReducer
entities,
loading,
errorMessage
});
export default rootReducer;

View File

@ -1,51 +0,0 @@
import * as actions from "../actions";
export default function processes(state = {}, action) {
switch (action.type) {
case actions.PROCESSES[actions.SUCCESS]:
return Object.assign({}, state, {data: action.processes.data});
case actions.PROCESSES[actions.FAILURE]:
return Object.assign({}, state, {error: action.error});
case actions.RECEIVE_PROCESS_LOGS:
return {
...state,
data: {
...state.data,
[action.processName]: {
...state.data[action.processName],
logs: action.logs.data
}
}
};
case actions.RECEIVE_NEW_PROCESS_LOG: {
const logs = state.data[action.processName].logs || [];
logs.push(action.log);
return {
...state,
data: {
...state.data,
[action.processName]: {
...state.data[action.processName],
logs: logs
}
}
};
}
case actions.WATCH_NEW_PROCESS_LOGS: {
return {
...state,
data: {
...state.data,
[action.processName]: {
...state.data[action.processName],
isListening: true
}
}
};
}
case actions.RECEIVE_PROCESS_LOGS_ERROR:
return Object.assign({}, state, {error: action.error});
default:
return state;
}
}

View File

@ -0,0 +1,59 @@
export function getAccounts(state) {
return state.entities.accounts;
}
export function getAccount(state, address) {
return state.entities.accounts.find((account) => account.address === address);
}
export function getTransactions(state) {
return state.entities.transactions;
}
export function getTransaction(state, hash) {
return state.entities.transactions.find((transaction) => transaction.hash === hash);
}
export function getTransactionsByAccount(state, address) {
return state.entities.transactions.filter((transaction) => transaction.from === address);
}
export function getTransactionsByBlock(state, blockNumber) {
return state.entities.transactions.filter((transaction) => transaction.blockNumber.toString() === blockNumber);
}
export function getBlocks(state) {
return state.entities.blocks;
}
export function getBlock(state, number) {
return state.entities.blocks.find((block) => block.number.toString() === number);
}
export function getCommands(state) {
return state.entities.commands;
}
export function getProcesses(state) {
return state.entities.processes;
}
export function getProcess(state, name) {
return state.entities.processes.find((process) => process.name === name);
}
export function getProcessLogsByProcess(state, processName) {
return state.entities.processLogs.filter((processLog => processLog.name === processName));
}
export function getContracts(state) {
return state.entities.contracts;
}
export function getContract(state, contractName) {
return state.entities.contracts.find((contract => contract.name === contractName));
}
export function getContractProfile(state, contractName) {
return state.entities.contractProfiles.find((contractProfile => contractProfile.name === contractName));
}

View File

@ -1,36 +0,0 @@
import * as actions from "../actions";
const BN_FACTOR = 10000;
function sortTransaction(a, b) {
return ((BN_FACTOR * b.blockNumber) + b.transactionIndex) - ((BN_FACTOR * a.blockNumber) + a.transactionIndex);
}
function filterTransaction(tx, index, self) {
return index === self.findIndex((t) => (
t.blockNumber === tx.blockNumber && t.transactionIndex === tx.transactionIndex
));
}
export default function transactions(state = {}, action) {
switch (action.type) {
case actions.TRANSACTIONS[actions.SUCCESS]:
return {
...state, error: null, data: [...action.transactions.data, ...state.data || []]
.filter(filterTransaction)
.sort(sortTransaction)
};
case actions.TRANSACTIONS[actions.FAILURE]:
return Object.assign({}, state, {error: action.error});
case actions.TRANSACTION[actions.SUCCESS]:
return {
...state, error: null, data: [action.transaction.data, ...state.data || []]
.filter(filterTransaction)
.sort(sortTransaction)
};
case actions.TRANSACTION[actions.FAILURE]:
return Object.assign({}, state, {error: action.error});
default:
return state;
}
}

View File

@ -3,12 +3,13 @@ import * as api from '../api';
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} = actions;
const {account, accounts, block, blocks, transaction, transactions, processes, commands, processLogs,
contracts, contract, contractProfile} = actions;
function *doRequest(entity, apiFn, payload) {
const {response, error} = yield call(apiFn, payload);
if(response) {
yield put(entity.success(response));
yield put(entity.success(response.data, payload));
} else if (error) {
yield put(entity.failure(error));
}
@ -22,6 +23,10 @@ export const fetchBlocks = doRequest.bind(null, blocks, api.fetchBlocks);
export const fetchTransactions = doRequest.bind(null, transactions, api.fetchTransactions);
export const fetchProcesses = doRequest.bind(null, processes, api.fetchProcesses);
export const postCommand = doRequest.bind(null, commands, api.postCommand);
export const fetchProcessLogs = doRequest.bind(null, processLogs, api.fetchProcessLogs);
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 function *watchFetchTransaction() {
yield takeEvery(actions.TRANSACTION[actions.REQUEST], fetchTransaction);
@ -55,17 +60,20 @@ export function *watchPostCommand() {
yield takeEvery(actions.COMMANDS[actions.REQUEST], postCommand);
}
export function *fetchProcessLogs(action) {
try {
const logs = yield call(api.fetchProcessLogs, action.processName);
yield put(actions.receiveProcessLogs(action.processName, logs));
} catch (e) {
yield put(actions.receiveProcessLogsError(e));
}
export function *watchFetchProcessLogs() {
yield takeEvery(actions.PROCESS_LOGS[actions.REQUEST], fetchProcessLogs);
}
export function *watchFetchProcessLogs() {
yield takeEvery(actions.FETCH_PROCESS_LOGS, fetchProcessLogs);
export function *watchFetchContract() {
yield takeEvery(actions.CONTRACT[actions.REQUEST], fetchContract);
}
export function *watchFetchContracts() {
yield takeEvery(actions.CONTRACTS[actions.REQUEST], fetchContracts);
}
export function *watchFetchContractProfile() {
yield takeEvery(actions.CONTRACT_PROFILE[actions.REQUEST], fetchContractProfile);
}
function createChannel(socket) {
@ -97,8 +105,8 @@ export function *listenToProcessLogs(action) {
const socket = api.webSocketProcess(action.processName);
const channel = yield call(createChannel, socket);
while (true) {
const log = yield take(channel);
yield put({type: actions.RECEIVE_NEW_PROCESS_LOG, processName: action.processName, log});
const processLog = yield take(channel);
yield put(processLogs.success([processLog]));
}
}
@ -106,45 +114,6 @@ export function *watchListenToProcessLogs() {
yield takeEvery(actions.WATCH_NEW_PROCESS_LOGS, listenToProcessLogs);
}
export function *fetchContract(action) {
try {
const contract = yield call(api.fetchContract, action.contractName);
yield put(actions.receiveContract(contract));
} catch (e) {
yield put(actions.receiveContractError());
}
}
export function *watchFetchContract() {
yield takeEvery(actions.FETCH_CONTRACT, fetchContract);
}
export function *fetchContracts() {
try {
const contracts = yield call(api.fetchContracts);
yield put(actions.receiveContracts(contracts));
} catch (e) {
yield put(actions.receiveContractsError());
}
}
export function *watchFetchContracts() {
yield takeEvery(actions.FETCH_CONTRACTS, fetchContracts);
}
export function *fetchContractProfile(action) {
try {
const profile = yield call(api.fetchContractProfile, action.contractName);
yield put(actions.receiveContractProfile(profile));
} catch (e) {
yield put(actions.receiveContractError());
}
}
export function *watchFetchContractProfile() {
yield takeEvery(actions.FETCH_CONTRACT_PROFILE, fetchContractProfile);
}
export default function *root() {
yield all([
fork(watchInitBlockHeader),

View File

@ -73,8 +73,8 @@ class ProcessLauncher {
'ws',
apiRoute,
(ws, _req) => {
self.events.on('process-log-' + self.name, function(logLevel, msg) {
ws.send(JSON.stringify({msg, msg_clear: msg.stripColors, logLevel}), () => {});
self.events.on('process-log-' + self.name, function(logLevel, msg, name, timestamp) {
ws.send(JSON.stringify({msg, msg_clear: msg.stripColors, logLevel, name, timestamp}), () => {});
});
}
);
@ -89,8 +89,9 @@ class ProcessLauncher {
// Translates logs from the child process to the logger
_handleLog(msg) {
this.events.emit('process-log-' + this.name, msg.type, msg.message);
this.logs.push({msg: msg.message, msg_clear: msg.message.stripColors, logLevel: msg.logLevel});
const timestamp = new Date().getTime();
this.events.emit('process-log-' + this.name, msg.type, msg.message, this.name, timestamp);
this.logs.push({msg: msg.message, msg_clear: msg.message.stripColors, logLevel: msg.logLevel, name: this.name, timestamp});
if (this.silent && msg.type !== 'error') {
return;
}

View File

@ -17,13 +17,11 @@ class ProcessManager {
'get',
'/embark-api/processes',
(req, res) => {
let parsedProcesses = {};
Object.keys(self.processes).forEach(processName => {
parsedProcesses[processName] = {
state: self.processes[processName].state
};
});
res.send(parsedProcesses);
const formatter = (acc, processName) => {
acc.push({state: self.processes[processName].state, name: processName});
return acc;
};
res.send(Object.keys(self.processes).reduce(formatter, []));
}
);
}