From e6bad115a0fd6f6c1451c83aa410e56d602c910f Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Mon, 17 Feb 2020 11:57:13 +0800 Subject: [PATCH 01/11] Refactor + reload page on InvalidToken/AuthRequired --- src/botPage/bot/tools.js | 41 +++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/src/botPage/bot/tools.js b/src/botPage/bot/tools.js index 49f15f96b4..397e459682 100644 --- a/src/botPage/bot/tools.js +++ b/src/botPage/bot/tools.js @@ -99,12 +99,25 @@ const getBackoffDelay = (error, delayIndex) => { return linearIncrease * 1000; }; -export const shouldThrowError = (e, types = [], delayIndex = 0) => - e && - (!types - .concat(['CallError', 'WrongResponse', 'GetProposalFailure', 'RateLimit', 'DisconnectError']) - .includes(e.name) || - (e.name !== 'DisconnectError' && delayIndex > maxRetries)); +export const shouldThrowError = (error, types = [], delayIndex = 0) => { + if (!error) { + return false; + } + + const defaultErrors = ['CallError', 'WrongResponse', 'GetProposalFailure', 'RateLimit', 'DisconnectError']; + + const errors = types.concat(defaultErrors); + + if (errors.includes(error.name)) { + // If error is unrecoverable, throw error. + return true; + } else if (error.name !== 'DisconnectError' && delayIndex > maxRetries) { + // If exceeded maxRetries, throw error. + return true; + } + + return false; +}; export const recoverFromError = (f, r, types, delayIndex) => new Promise((resolve, reject) => { @@ -125,14 +138,24 @@ export const recoverFromError = (f, r, types, delayIndex) => }); }); -export const doUntilDone = (f, types) => { +export const doUntilDone = (func, errorTypes) => { let delayIndex = 0; + const criticalErrors = ['InvalidToken', 'AuthorizationRequired']; return new Promise((resolve, reject) => { + delayIndex++; + const repeat = () => { - recoverFromError(f, (errorCode, makeDelay) => makeDelay().then(repeat), types, delayIndex++) + const makeDelayFunc = (errorCode, makeDelay) => makeDelay().then(repeat); + recoverFromError(func, makeDelayFunc, errorTypes, delayIndex) .then(resolve) - .catch(reject); + .catch(e => { + reject(e); + + if (criticalErrors.includes(e.name)) { + window.location.reload(); + } + }); }; repeat(); }); From 7cae2ccb5cf347166b322fea3e2ad3570dbc66b3 Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Mon, 17 Feb 2020 11:57:43 +0800 Subject: [PATCH 02/11] Remove empty line --- src/botPage/bot/tools.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/botPage/bot/tools.js b/src/botPage/bot/tools.js index 397e459682..c494d41af0 100644 --- a/src/botPage/bot/tools.js +++ b/src/botPage/bot/tools.js @@ -105,7 +105,6 @@ export const shouldThrowError = (error, types = [], delayIndex = 0) => { } const defaultErrors = ['CallError', 'WrongResponse', 'GetProposalFailure', 'RateLimit', 'DisconnectError']; - const errors = types.concat(defaultErrors); if (errors.includes(error.name)) { From 7db8f67fb58555c6710360816d5a7cddb7bc08f9 Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Mon, 17 Feb 2020 13:14:49 +0800 Subject: [PATCH 03/11] Throw TrackJSError, supply meta data --- src/botPage/bot/TradeEngine/Proposal.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/botPage/bot/TradeEngine/Proposal.js b/src/botPage/bot/TradeEngine/Proposal.js index 8fced7acd7..18d450e68d 100644 --- a/src/botPage/bot/TradeEngine/Proposal.js +++ b/src/botPage/bot/TradeEngine/Proposal.js @@ -1,6 +1,7 @@ import { translate } from '../../../common/i18n'; import { tradeOptionToProposal, doUntilDone } from '../tools'; import { proposalsReady, clearProposals } from './state/actions'; +import { TrackJSError } from '../../view/logger'; export default Engine => class Proposal extends Engine { @@ -30,7 +31,11 @@ export default Engine => }); if (!toBuy) { - throw Error(translate('Selected proposal does not exist')); + throw new TrackJSError( + 'CustomInvalidProposal', + translate('Selected proposal does not exist'), + Array.from(this.data.get('proposals')).map(proposal => proposal[1]) + ); } return { From 18e5abc0dbad0030758adfbef1a1e7251617005f Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Mon, 17 Feb 2020 13:27:03 +0800 Subject: [PATCH 04/11] Relay correct error to TrackJS --- src/botPage/bot/TradeEngine/Proposal.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/botPage/bot/TradeEngine/Proposal.js b/src/botPage/bot/TradeEngine/Proposal.js index 18d450e68d..c508ef9408 100644 --- a/src/botPage/bot/TradeEngine/Proposal.js +++ b/src/botPage/bot/TradeEngine/Proposal.js @@ -23,7 +23,8 @@ export default Engine => this.data.get('proposals').forEach(proposal => { if (proposal.contractType === contractType) { if (proposal.error) { - throw Error(proposal.error.error.error.message); + const { error } = proposal.error; + throw new TrackJSError(error.error.code, error.error.message, error); } else { toBuy = proposal; } From 8b799cb320e4a4178070e22e12952f0e31664f2c Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Mon, 17 Feb 2020 13:34:39 +0800 Subject: [PATCH 05/11] Remove isRequired prop on PropTypes --- src/botPage/view/LogTable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/botPage/view/LogTable.js b/src/botPage/view/LogTable.js index a2a06d4acf..b8f413afb1 100644 --- a/src/botPage/view/LogTable.js +++ b/src/botPage/view/LogTable.js @@ -24,7 +24,7 @@ export default class LogTable extends Component { type : PropTypes.string, timestamp: PropTypes.string, message : PropTypes.string, - }).isRequired, + }), }; constructor() { super(); From 08bd3a12d6de2d71411c8fff92e841a64a5f13ed Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Mon, 17 Feb 2020 14:59:57 +0800 Subject: [PATCH 06/11] Promise all subscriptions to be executed --- src/botPage/bot/TradeEngine/Proposal.js | 55 +++++++++++++------------ 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/src/botPage/bot/TradeEngine/Proposal.js b/src/botPage/bot/TradeEngine/Proposal.js index c508ef9408..b76ed969e2 100644 --- a/src/botPage/bot/TradeEngine/Proposal.js +++ b/src/botPage/bot/TradeEngine/Proposal.js @@ -52,34 +52,37 @@ export default Engine => this.store.dispatch(clearProposals()); } requestProposals() { - this.proposalTemplates.map(proposal => - doUntilDone(() => - this.api - .subscribeToPriceForContractProposal(proposal) - // eslint-disable-next-line consistent-return - .catch(e => { - if (e && e.name === 'RateLimit') { - return Promise.reject(e); - } - - const errorCode = e.error && e.error.error && e.error.error.code; - - if (errorCode === 'ContractBuyValidationError') { - const { uuid } = e.error.echo_req.passthrough; - - if (!this.data.hasIn(['forgetProposals', uuid])) { - this.data = this.data.setIn(['proposals', uuid], { - ...proposal, - contractType: proposal.contract_type, - error : e, - }); + Promise.all( + this.proposalTemplates.map(proposal => + doUntilDone(() => + this.api + .subscribeToPriceForContractProposal(proposal) + // eslint-disable-next-line consistent-return + .catch(e => { + if (e && e.name === 'RateLimit') { + return Promise.reject(e); } - } else { - this.$scope.observer.emit('Error', e); - } - }) + + const errorCode = e.error && e.error.error && e.error.error.code; + + if (errorCode === 'ContractBuyValidationError') { + const { uuid } = e.error.echo_req.passthrough; + + if (!this.data.hasIn(['forgetProposals', uuid])) { + // Add to proposals map with error. Will later be shown to user, see selectProposal. + this.data = this.data.setIn(['proposals', uuid], { + ...proposal, + contractType: proposal.contract_type, + error : e, + }); + } + } else { + this.$scope.observer.emit('Error', e); + } + }) + ) ) - ); + ).catch(e => this.$scope.observer.emit('Error', e)); } observeProposals() { this.listen('proposal', r => { From 1bbaaff8f1ef08197ec3e2253dc14a11313be358 Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Mon, 17 Feb 2020 17:02:13 +0800 Subject: [PATCH 07/11] Minor refactor --- src/botPage/bot/TradeEngine/Proposal.js | 45 ++++++++++++------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/src/botPage/bot/TradeEngine/Proposal.js b/src/botPage/bot/TradeEngine/Proposal.js index b76ed969e2..159a339c9f 100644 --- a/src/botPage/bot/TradeEngine/Proposal.js +++ b/src/botPage/bot/TradeEngine/Proposal.js @@ -55,31 +55,30 @@ export default Engine => Promise.all( this.proposalTemplates.map(proposal => doUntilDone(() => - this.api - .subscribeToPriceForContractProposal(proposal) - // eslint-disable-next-line consistent-return - .catch(e => { - if (e && e.name === 'RateLimit') { - return Promise.reject(e); + this.api.subscribeToPriceForContractProposal(proposal).catch(e => { + if (e && e.name === 'RateLimit') { + return e; + } + + const errorCode = e.error && e.error.error && e.error.error.code; + + if (errorCode === 'ContractBuyValidationError') { + const { uuid } = e.error.echo_req.passthrough; + + if (!this.data.hasIn(['forgetProposals', uuid])) { + // Add to proposals map with error. Will later be shown to user, see selectProposal. + this.data = this.data.setIn(['proposals', uuid], { + ...proposal, + ...proposal.passthrough, + error: e, + }); } - const errorCode = e.error && e.error.error && e.error.error.code; - - if (errorCode === 'ContractBuyValidationError') { - const { uuid } = e.error.echo_req.passthrough; - - if (!this.data.hasIn(['forgetProposals', uuid])) { - // Add to proposals map with error. Will later be shown to user, see selectProposal. - this.data = this.data.setIn(['proposals', uuid], { - ...proposal, - contractType: proposal.contract_type, - error : e, - }); - } - } else { - this.$scope.observer.emit('Error', e); - } - }) + return null; + } + + return e; + }) ) ) ).catch(e => this.$scope.observer.emit('Error', e)); From 10912f443d08f2ea12f43caf00f1168cacdcbbb1 Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Mon, 17 Feb 2020 17:02:41 +0800 Subject: [PATCH 08/11] Remove ContractBuyValidationError from unrecoverable errors --- src/botPage/bot/Interpreter.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/botPage/bot/Interpreter.js b/src/botPage/bot/Interpreter.js index 73eaac960d..1c7a1bd01d 100644 --- a/src/botPage/bot/Interpreter.js +++ b/src/botPage/bot/Interpreter.js @@ -8,7 +8,6 @@ const unrecoverableErrors = [ 'CustomLimitsReached', 'OfferingsValidationError', 'InvalidCurrency', - 'ContractBuyValidationError', 'NotDefaultCurrency', 'PleaseAuthenticate', 'FinancialAssessmentRequired', From 1b3a462aab0f4f22f2e132624086158814f5aad7 Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Tue, 18 Feb 2020 15:58:36 +0800 Subject: [PATCH 09/11] Revert return value --- src/botPage/bot/TradeEngine/Proposal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/botPage/bot/TradeEngine/Proposal.js b/src/botPage/bot/TradeEngine/Proposal.js index 159a339c9f..e421dc7114 100644 --- a/src/botPage/bot/TradeEngine/Proposal.js +++ b/src/botPage/bot/TradeEngine/Proposal.js @@ -57,7 +57,7 @@ export default Engine => doUntilDone(() => this.api.subscribeToPriceForContractProposal(proposal).catch(e => { if (e && e.name === 'RateLimit') { - return e; + return Promise.reject(e); } const errorCode = e.error && e.error.error && e.error.error.code; From 0bde3cdc123cbf43113ba3d98715e16db9f5c93d Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Tue, 18 Feb 2020 18:03:35 +0800 Subject: [PATCH 10/11] Throw rather than returning values --- src/botPage/bot/TradeEngine/Proposal.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/botPage/bot/TradeEngine/Proposal.js b/src/botPage/bot/TradeEngine/Proposal.js index e421dc7114..e9203b3379 100644 --- a/src/botPage/bot/TradeEngine/Proposal.js +++ b/src/botPage/bot/TradeEngine/Proposal.js @@ -57,7 +57,7 @@ export default Engine => doUntilDone(() => this.api.subscribeToPriceForContractProposal(proposal).catch(e => { if (e && e.name === 'RateLimit') { - return Promise.reject(e); + throw e; } const errorCode = e.error && e.error.error && e.error.error.code; @@ -77,7 +77,7 @@ export default Engine => return null; } - return e; + throw e; }) ) ) From c33eb942d692864d16c2f537596cbc6ed4066806 Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Tue, 18 Feb 2020 18:04:04 +0800 Subject: [PATCH 11/11] Add auth errors --- src/botPage/bot/tools.js | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/botPage/bot/tools.js b/src/botPage/bot/tools.js index c494d41af0..9008c5987c 100644 --- a/src/botPage/bot/tools.js +++ b/src/botPage/bot/tools.js @@ -105,9 +105,14 @@ export const shouldThrowError = (error, types = [], delayIndex = 0) => { } const defaultErrors = ['CallError', 'WrongResponse', 'GetProposalFailure', 'RateLimit', 'DisconnectError']; + const authErrors = ['InvalidToken', 'AuthorizationRequired']; const errors = types.concat(defaultErrors); - if (errors.includes(error.name)) { + if (authErrors.includes(error.name)) { + // If auth error, reload page. + window.location.reload(); + return true; + } else if (!errors.includes(error.name)) { // If error is unrecoverable, throw error. return true; } else if (error.name !== 'DisconnectError' && delayIndex > maxRetries) { @@ -137,24 +142,14 @@ export const recoverFromError = (f, r, types, delayIndex) => }); }); -export const doUntilDone = (func, errorTypes) => { +export const doUntilDone = (f, types) => { let delayIndex = 0; - const criticalErrors = ['InvalidToken', 'AuthorizationRequired']; return new Promise((resolve, reject) => { - delayIndex++; - const repeat = () => { - const makeDelayFunc = (errorCode, makeDelay) => makeDelay().then(repeat); - recoverFromError(func, makeDelayFunc, errorTypes, delayIndex) + recoverFromError(f, (errorCode, makeDelay) => makeDelay().then(repeat), types, delayIndex++) .then(resolve) - .catch(e => { - reject(e); - - if (criticalErrors.includes(e.name)) { - window.location.reload(); - } - }); + .catch(reject); }; repeat(); });