Adding method to capture one-time Sentry exceptions (#11553)

feature/default_network_editable
ryanml 3 years ago committed by GitHub
parent a96af9e5fc
commit 8cb1557f1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 11
      ui/components/app/transaction-icon/transaction-icon.js
  2. 12
      ui/ducks/app/app.js
  3. 9
      ui/hooks/useTransactionDisplayData.js
  4. 3
      ui/hooks/useTransactionDisplayData.test.js
  5. 1
      ui/store/actionConstants.js
  6. 14
      ui/store/actions.js

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import { useDispatch } from 'react-redux';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { captureException } from '@sentry/browser';
import Approve from '../../ui/icon/approve-icon.component'; import Approve from '../../ui/icon/approve-icon.component';
import Interaction from '../../ui/icon/interaction-icon.component'; import Interaction from '../../ui/icon/interaction-icon.component';
import Receive from '../../ui/icon/receive-icon.component'; import Receive from '../../ui/icon/receive-icon.component';
@ -12,6 +12,7 @@ import {
TRANSACTION_GROUP_STATUSES, TRANSACTION_GROUP_STATUSES,
TRANSACTION_STATUSES, TRANSACTION_STATUSES,
} from '../../../../shared/constants/transaction'; } from '../../../../shared/constants/transaction';
import { captureSingleException } from '../../../store/actions';
const ICON_MAP = { const ICON_MAP = {
[TRANSACTION_GROUP_CATEGORIES.APPROVAL]: Approve, [TRANSACTION_GROUP_CATEGORIES.APPROVAL]: Approve,
@ -38,17 +39,17 @@ const COLOR_MAP = {
}; };
export default function TransactionIcon({ status, category }) { export default function TransactionIcon({ status, category }) {
const color = COLOR_MAP[status] || OK_COLOR; const dispatch = useDispatch();
const color = COLOR_MAP[status] || OK_COLOR;
const Icon = ICON_MAP[category]; const Icon = ICON_MAP[category];
if (!Icon) { if (!Icon) {
captureException( dispatch(
Error( captureSingleException(
`The category prop passed to TransactionIcon is not supported. The prop is: ${category}`, `The category prop passed to TransactionIcon is not supported. The prop is: ${category}`,
), ),
); );
return <div className="transaction-icon__grey-circle" />; return <div className="transaction-icon__grey-circle" />;
} }

@ -50,6 +50,9 @@ export default function reduceApp(state = {}, action) {
openMetaMaskTabs: {}, openMetaMaskTabs: {},
currentWindowTab: {}, currentWindowTab: {},
showWhatsNewPopup: true, showWhatsNewPopup: true,
singleExceptions: {
testKey: null,
},
...state, ...state,
}; };
@ -346,6 +349,15 @@ export default function reduceApp(state = {}, action) {
showWhatsNewPopup: false, showWhatsNewPopup: false,
}; };
case actionConstants.CAPTURE_SINGLE_EXCEPTION:
return {
...appState,
singleExceptions: {
...appState.singleExceptions,
[action.value]: null,
},
};
default: default:
return appState; return appState;
} }

@ -1,5 +1,4 @@
import { useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { captureException } from '@sentry/browser';
import { getKnownMethodData } from '../selectors/selectors'; import { getKnownMethodData } from '../selectors/selectors';
import { import {
getStatusKey, getStatusKey,
@ -23,6 +22,7 @@ import {
TRANSACTION_GROUP_CATEGORIES, TRANSACTION_GROUP_CATEGORIES,
TRANSACTION_STATUSES, TRANSACTION_STATUSES,
} from '../../shared/constants/transaction'; } from '../../shared/constants/transaction';
import { captureSingleException } from '../store/actions';
import { useI18nContext } from './useI18nContext'; import { useI18nContext } from './useI18nContext';
import { useTokenFiatAmount } from './useTokenFiatAmount'; import { useTokenFiatAmount } from './useTokenFiatAmount';
import { useUserPreferencedCurrency } from './useUserPreferencedCurrency'; import { useUserPreferencedCurrency } from './useUserPreferencedCurrency';
@ -58,6 +58,7 @@ import { useCurrentAsset } from './useCurrentAsset';
export function useTransactionDisplayData(transactionGroup) { export function useTransactionDisplayData(transactionGroup) {
// To determine which primary currency to display for swaps transactions we need to be aware // To determine which primary currency to display for swaps transactions we need to be aware
// of which asset, if any, we are viewing at present // of which asset, if any, we are viewing at present
const dispatch = useDispatch();
const currentAsset = useCurrentAsset(); const currentAsset = useCurrentAsset();
const knownTokens = useSelector(getTokens); const knownTokens = useSelector(getTokens);
const t = useI18nContext(); const t = useI18nContext();
@ -222,8 +223,8 @@ export function useTransactionDisplayData(transactionGroup) {
title = t('send'); title = t('send');
subtitle = t('toAddress', [shortenAddress(recipientAddress)]); subtitle = t('toAddress', [shortenAddress(recipientAddress)]);
} else { } else {
captureException( dispatch(
Error( captureSingleException(
`useTransactionDisplayData does not recognize transaction type. Type received is: ${type}`, `useTransactionDisplayData does not recognize transaction type. Type received is: ${type}`,
), ),
); );

@ -132,6 +132,8 @@ const renderHookWithRouter = (cb, tokenAddress) => {
}; };
describe('useTransactionDisplayData', () => { describe('useTransactionDisplayData', () => {
const dispatch = sinon.spy();
beforeAll(() => { beforeAll(() => {
useSelector = sinon.stub(reactRedux, 'useSelector'); useSelector = sinon.stub(reactRedux, 'useSelector');
useTokenFiatAmount = sinon.stub( useTokenFiatAmount = sinon.stub(
@ -169,6 +171,7 @@ describe('useTransactionDisplayData', () => {
} }
return null; return null;
}); });
sinon.stub(reactRedux, 'useDispatch').returns(dispatch);
}); });
afterAll(() => { afterAll(() => {

@ -30,6 +30,7 @@ export const LOCK_METAMASK = 'LOCK_METAMASK';
// error handling // error handling
export const DISPLAY_WARNING = 'DISPLAY_WARNING'; export const DISPLAY_WARNING = 'DISPLAY_WARNING';
export const HIDE_WARNING = 'HIDE_WARNING'; export const HIDE_WARNING = 'HIDE_WARNING';
export const CAPTURE_SINGLE_EXCEPTION = 'CAPTURE_SINGLE_EXCEPTION';
// accounts screen // accounts screen
export const SHOW_ACCOUNT_DETAIL = 'SHOW_ACCOUNT_DETAIL'; export const SHOW_ACCOUNT_DETAIL = 'SHOW_ACCOUNT_DETAIL';
export const SHOW_ACCOUNTS_PAGE = 'SHOW_ACCOUNTS_PAGE'; export const SHOW_ACCOUNTS_PAGE = 'SHOW_ACCOUNTS_PAGE';

@ -1,5 +1,6 @@
import pify from 'pify'; import pify from 'pify';
import log from 'loglevel'; import log from 'loglevel';
import { captureException } from '@sentry/browser';
import { capitalize, isEqual } from 'lodash'; import { capitalize, isEqual } from 'lodash';
import getBuyEthUrl from '../../app/scripts/lib/buy-eth-url'; import getBuyEthUrl from '../../app/scripts/lib/buy-eth-url';
import { import {
@ -2715,6 +2716,19 @@ export function setLedgerLivePreference(value) {
}; };
} }
export function captureSingleException(error) {
return async (dispatch, getState) => {
const { singleExceptions } = getState().appState;
if (!(error in singleExceptions)) {
dispatch({
type: actionConstants.CAPTURE_SINGLE_EXCEPTION,
value: error,
});
captureException(Error(error));
}
};
}
// Wrappers around promisifedBackground // Wrappers around promisifedBackground
/** /**
* The "actions" below are not actions nor action creators. They cannot use * The "actions" below are not actions nor action creators. They cannot use

Loading…
Cancel
Save