Skip to content
This repository has been archived by the owner on Apr 15, 2019. It is now read-only.

Commit

Permalink
Merge pull request #889 from LiskHQ/843-websocket
Browse files Browse the repository at this point in the history
Use Websocket to get new transactions - Closes #843
  • Loading branch information
gina contrino authored Nov 3, 2017
2 parents 2be50a5 + 0bdcfb3 commit 0a7345d
Show file tree
Hide file tree
Showing 13 changed files with 260 additions and 351 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"redux": "3.7.2",
"redux-logger": "=3.0.6",
"redux-thunk": "=2.2.0",
"socket.io-client": "=2.0.3",
"webpack-merge": "=4.1.0"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion src/constants/actions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const actionTypes = {
metronomeBeat: 'METRONOME_BEAT',
newBlockCreated: 'NEW_BLOCK_CREATED',
accountUpdated: 'ACCOUNT_UPDATED',
accountLoggedOut: 'ACCOUNT_LOGGED_OUT',
accountLoggedIn: 'ACCOUNT_LOGGED_IN',
Expand Down
5 changes: 0 additions & 5 deletions src/constants/api.js

This file was deleted.

50 changes: 36 additions & 14 deletions src/store/middlewares/account.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getAccount, transactions } from '../../utils/api/account';
import { getAccount, transactions as getTransactions } from '../../utils/api/account';
import { accountUpdated, accountLoggedIn } from '../../actions/account';
import { transactionsUpdated } from '../../actions/transactions';
import { activePeerUpdate } from '../../actions/peers';
Expand All @@ -7,32 +7,27 @@ import actionTypes from '../../constants/actions';
import { fetchAndUpdateForgedBlocks } from '../../actions/forging';
import { getDelegate } from '../../utils/api/delegate';
import transactionTypes from '../../constants/transactionTypes';
import { SYNC_ACTIVE_INTERVAL, SYNC_INACTIVE_INTERVAL } from '../../constants/api';

const updateTransactions = (store, peers, account) => {
const maxBlockSize = 25;
transactions(peers.data, account.address, maxBlockSize)
getTransactions(peers.data, account.address, maxBlockSize)
.then(response => store.dispatch(transactionsUpdated({
confirmed: response.transactions,
count: parseInt(response.count, 10),
})));
};

const hasRecentTransactions = state => (
state.transactions.confirmed.filter(tx => tx.confirmations < 1000).length !== 0 ||
state.transactions.pending.length !== 0
const hasRecentTransactions = txs => (
txs.confirmed.filter(tx => tx.confirmations < 1000).length !== 0 ||
txs.pending.length !== 0
);

const updateAccountData = (store, action) => { // eslint-disable-line
const state = store.getState();
const { peers, account } = state;
const updateAccountData = (store, action) => {
const { peers, account } = store.getState();

getAccount(peers.data, account.address).then((result) => {
if (action.data.interval === SYNC_ACTIVE_INTERVAL && hasRecentTransactions(state)) {
updateTransactions(store, peers, account);
}
if (result.balance !== account.balance) {
if (action.data.interval === SYNC_INACTIVE_INTERVAL) {
if (!action.data.windowIsFocused) {
updateTransactions(store, peers, account);
}
if (account.isDelegate) {
Expand Down Expand Up @@ -95,12 +90,39 @@ const passphraseUsed = (store, action) => {
}
};

const checkTransactionsAndUpdateAccount = (store, action) => {
const state = store.getState();
const { peers, account, transactions } = state;

if (action.data.windowIsFocused && hasRecentTransactions(transactions)) {
updateTransactions(store, peers, account);
}

const tx = action.data.block.transactions;
const accountAddress = state.account.address;
const blockContainsRelevantTransaction = tx.filter((transaction) => {
const sender = transaction ? transaction.senderId : null;
const recipient = transaction ? transaction.recipientId : null;
return accountAddress === recipient || accountAddress === sender;
}).length > 0;

if (blockContainsRelevantTransaction) {
updateAccountData(store, action);
}
};

const accountMiddleware = store => next => (action) => {
next(action);
switch (action.type) {
case actionTypes.metronomeBeat:
// update on login because the 'save account' button
// depends on a rerendering of the page
// TODO: fix the 'save account' path problem, so we can remove this
case actionTypes.accountLoggedIn:
updateAccountData(store, action);
break;
case actionTypes.newBlockCreated:
checkTransactionsAndUpdateAccount(store, action);
break;
case actionTypes.transactionsUpdated:
delegateRegistration(store, action);
votePlaced(store, action);
Expand Down
71 changes: 33 additions & 38 deletions src/store/middlewares/account.test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { expect } from 'chai';
import { spy, stub } from 'sinon';

import { SYNC_ACTIVE_INTERVAL, SYNC_INACTIVE_INTERVAL } from '../../constants/api';
import { accountUpdated } from '../../actions/account';
import { activePeerUpdate } from '../../actions/peers';
import * as votingActions from '../../actions/voting';
import * as forgingActions from '../../actions/forging';
import * as accountApi from '../../utils/api/account';
import actionTypes from '../../constants/actions';
import * as delegateApi from '../../utils/api/delegate';
Expand All @@ -19,6 +18,8 @@ describe('Account middleware', () => {
let stubTransactions;
const passphrase = 'right cat soul renew under climb middle maid powder churn cram coconut';

const transactions = { transactions: [{ senderId: 'sample_address', receiverId: 'some_address' }] };

const transactionsUpdatedAction = {
type: actionTypes.transactionsUpdated,
data: {
Expand All @@ -29,17 +30,19 @@ describe('Account middleware', () => {
},
};

const activeBeatAction = {
type: actionTypes.metronomeBeat,
const newBlockCreated = {
type: actionTypes.newBlockCreated,
data: {
interval: SYNC_ACTIVE_INTERVAL,
windowIsFocused: true,
block: transactions,
},
};

const inactiveBeatAction = {
type: actionTypes.metronomeBeat,
const inactiveNewBlockCreated = {
type: actionTypes.newBlockCreated,
data: {
interval: SYNC_INACTIVE_INTERVAL,
windowIsFocused: false,
block: transactions,
},
};

Expand Down Expand Up @@ -74,85 +77,77 @@ describe('Account middleware', () => {
});

it('should pass the action to next middleware', () => {
const expectedAction = {
type: 'TEST_ACTION',
};

middleware(store)(next)(expectedAction);
expect(next).to.have.been.calledWith(expectedAction);
middleware(store)(next)(newBlockCreated);
expect(next).to.have.been.calledWith(newBlockCreated);
});

it(`should call account API methods on ${actionTypes.metronomeBeat} action when online`, () => {
it(`should call account API methods on ${actionTypes.newBlockCreated} action when online`, () => {
// does this matter?
stubGetAccount.resolves({ balance: 0 });

middleware(store)(next)(activeBeatAction);
middleware(store)(next)(newBlockCreated);

expect(stubGetAccount).to.have.been.calledWith();
expect(store.dispatch).to.have.been.calledWith(activePeerUpdate({ online: true }));
});

it(`should call account API methods on ${actionTypes.metronomeBeat} action when offline`, () => {
it(`should call account API methods on ${actionTypes.newBlockCreated} action when offline`, () => {
const errorCode = 'EUNAVAILABLE';
stubGetAccount.rejects({ error: { code: errorCode } });

middleware(store)(next)(activeBeatAction);
middleware(store)(next)(newBlockCreated);

expect(store.dispatch).to.have.been.calledWith(activePeerUpdate(
{ online: false, code: errorCode }));
});

it(`should call transactions API methods on ${actionTypes.metronomeBeat} action if account.balance changes`, () => {
it(`should call transactions API methods on ${actionTypes.newBlockCreated} action if account.balance changes`, () => {
stubGetAccount.resolves({ balance: 10e8 });

middleware(store)(next)(activeBeatAction);
middleware(store)(next)(newBlockCreated);

expect(stubGetAccount).to.have.been.calledWith();
// TODO why next expect doesn't work despite it being called according to test coverage?
// expect(stubTransactions).to.have.been.calledWith();
expect(stubTransactions).to.have.been.calledWith();
});

it(`should call transactions API methods on ${actionTypes.metronomeBeat} action if account.balance changes and action.data.interval is SYNC_INACTIVE_INTERVAL`, () => {
it(`should call transactions API methods on ${actionTypes.newBlockCreated} action if account.balance changes and the window is in blur`, () => {
stubGetAccount.resolves({ balance: 10e8 });

middleware(store)(next)(inactiveBeatAction);
middleware(store)(next)(inactiveNewBlockCreated);

expect(stubGetAccount).to.have.been.calledWith();
// TODO why next expect doesn't work despite it being called according to test coverage?
// expect(stubTransactions).to.have.been.calledWith();
expect(stubTransactions).to.have.been.calledWith();
});

it(`should call transactions API methods on ${actionTypes.metronomeBeat} action if action.data.interval is SYNC_ACTIVE_INTERVAL and there are recent transactions`, () => {
it(`should call transactions API methods on ${actionTypes.newBlockCreated} action if the window is in focus and there are recent transactions`, () => {
stubGetAccount.resolves({ balance: 0 });

middleware(store)(next)(activeBeatAction);
middleware(store)(next)(newBlockCreated);

expect(stubGetAccount).to.have.been.calledWith();
// TODO why next expect doesn't work despite it being called according to test coverage?
// expect(stubTransactions).to.have.been.calledWith();
expect(stubTransactions).to.have.been.calledWith();
});

it(`should fetch delegate info on ${actionTypes.metronomeBeat} action if account.balance changes and account.isDelegate`, () => {
it(`should fetch delegate info on ${actionTypes.newBlockCreated} action if account.balance changes and account.isDelegate`, () => {
const delegateApiMock = stub(delegateApi, 'getDelegate').returnsPromise().resolves({ success: true, delegate: {} });
stubGetAccount.resolves({ balance: 10e8 });
state.account.isDelegate = true;
store.getState = () => (state);

middleware(store)(next)(activeBeatAction);
middleware(store)(next)(newBlockCreated);
expect(store.dispatch).to.have.been.calledWith();

delegateApiMock.restore();
});

it(`should call fetchAndUpdateForgedBlocks(...) on ${actionTypes.metronomeBeat} action if account.balance changes and account.isDelegate`, () => {
it(`should call fetchAndUpdateForgedBlocks(...) on ${actionTypes.newBlockCreated} action if account.balance changes and account.isDelegate`, () => {
state.account.isDelegate = true;
store.getState = () => (state);
stubGetAccount.resolves({ balance: 10e8 });
// const fetchAndUpdateForgedBlocksSpy = spy(forgingActions, 'fetchAndUpdateForgedBlocks');

middleware(store)(next)({ type: actionTypes.metronomeBeat });
const fetchAndUpdateForgedBlocksSpy = spy(forgingActions, 'fetchAndUpdateForgedBlocks');

// TODO why next expect doesn't work despite it being called according to test coverage?
// expect(fetchAndUpdateForgedBlocksSpy).to.have.been.calledWith();
middleware(store)(next)(newBlockCreated);
expect(fetchAndUpdateForgedBlocksSpy).to.have.been.calledWith();
});

it(`should fetch delegate info on ${actionTypes.transactionsUpdated} action if action.data.confirmed contains delegateRegistration transactions`, () => {
Expand Down
4 changes: 2 additions & 2 deletions src/store/middlewares/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import thunk from 'redux-thunk';
import metronomeMiddleware from './metronome';
import accountMiddleware from './account';
import loginMiddleware from './login';
import transactionsMiddleware from './transactions';
Expand All @@ -8,12 +7,13 @@ import offlineMiddleware from './offline';
import notificationMiddleware from './notification';
import votingMiddleware from './voting';
import savedAccountsMiddleware from './savedAccounts';
import socketMiddleware from './socket';

export default [
thunk,
transactionsMiddleware,
loginMiddleware,
metronomeMiddleware,
socketMiddleware,
accountMiddleware,
loadingBarMiddleware,
offlineMiddleware,
Expand Down
20 changes: 0 additions & 20 deletions src/store/middlewares/metronome.js

This file was deleted.

45 changes: 0 additions & 45 deletions src/store/middlewares/metronome.test.js

This file was deleted.

Loading

0 comments on commit 0a7345d

Please sign in to comment.