Skip to content

Commit

Permalink
chore: cherry pick tags for custom instrumentation (#11826)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: Cal Leung <[email protected]>
Co-authored-by: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com><!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**
cherry pick tags
[PR](#11623) to the
7.33.0

## **Related issues**

Fixes:

## **Manual testing steps**

1. Go to this page...
2.
3.

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

- [ ] I’ve followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile
Coding
Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [ ] I've completed the PR template to the best of my ability
- [ ] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
  • Loading branch information
tommasini authored Oct 16, 2024
1 parent 0556903 commit 97d4640
Show file tree
Hide file tree
Showing 8 changed files with 469 additions and 32 deletions.
13 changes: 12 additions & 1 deletion app/selectors/nftController.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createSelector } from 'reselect';
import { NftState } from '@metamask/assets-controllers';
import { Nft, NftState } from '@metamask/assets-controllers';
import { RootState } from '../reducers';

const selectNftControllerState = (state: RootState) =>
Expand All @@ -14,3 +14,14 @@ export const selectAllNfts = createSelector(
selectNftControllerState,
(nftControllerState: NftState) => nftControllerState.allNfts,
);

export const selectAllNftsFlat = createSelector(
selectAllNfts,
(nftsByChainByAccount) => {
const nftsByChainArray = Object.values(nftsByChainByAccount);
return nftsByChainArray.reduce((acc, nftsByChain) => {
const nftsArrays = Object.values(nftsByChain);
return acc.concat(...nftsArrays);
}, [] as Nft[]);
},
);
20 changes: 20 additions & 0 deletions app/selectors/tokensController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,23 @@ export const selectDetectedTokens = createSelector(
selectTokensControllerState,
(tokensControllerState: TokensState) => tokensControllerState?.detectedTokens,
);

const selectAllTokens = createSelector(
selectTokensControllerState,
(tokensControllerState: TokensState) => tokensControllerState?.allTokens,
);

export const selectAllTokensFlat = createSelector(
selectAllTokens,
(tokensByAccountByChain) => {
if (Object.values(tokensByAccountByChain).length === 0) {
return [];
}
const tokensByAccountArray = Object.values(tokensByAccountByChain);

return tokensByAccountArray.reduce((acc, tokensByAccount) => {
const tokensArray = Object.values(tokensByAccount);
return acc.concat(...tokensArray);
}, [] as Token[]);
},
);
2 changes: 2 additions & 0 deletions app/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import thunk from 'redux-thunk';

import persistConfig from './persistConfig';
import { AppStateEventProcessor } from '../core/AppStateEventListener';
import { getTraceTags } from '../util/sentry/tags';

// TODO: Improve type safety by using real Action types instead of `any`
// TODO: Replace "any" with type
Expand Down Expand Up @@ -119,6 +120,7 @@ const createStoreAndPersistor = async (appStartTime: number) => {
{
name: TraceName.EngineInitialization,
op: TraceOperation.EngineInitialization,
tags: getTraceTags(store.getState?.()),
},
() => {
EngineService.initalizeEngine(store);
Expand Down
229 changes: 229 additions & 0 deletions app/util/sentry/tags/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
import { RootState } from '../../../reducers';
import { getTraceTags } from './';
import initialRootState, {
backgroundState,
} from '../../../util/test/initial-root-state';
import { userInitialState } from '../../../reducers/user';
import { createMockAccountsControllerState } from '../../../util/test/accountsControllerTestUtils';

describe('Tags Utils', () => {
beforeEach(() => {
jest.resetAllMocks();
});

describe('getTraceTags', () => {
it('includes if unlocked', () => {
const state = {
...initialRootState,
user: { ...userInitialState, userLoggedIn: true },
};

const tags = getTraceTags(state);

expect(tags?.['wallet.unlocked']).toStrictEqual(true);
});

it('includes if not unlocked', () => {
const state = {
...initialRootState,
user: { ...userInitialState, userLoggedIn: false },
};

const tags = getTraceTags(state);

expect(tags?.['wallet.unlocked']).toStrictEqual(false);
});

it('includes pending approval type', () => {
const state = {
...initialRootState,
engine: {
backgroundState: {
...backgroundState,
ApprovalController: {
...backgroundState.ApprovalController,
pendingApprovals: {
1: {
type: 'eth_sendTransaction',
},
},
},
},
},
} as unknown as RootState;

const tags = getTraceTags(state);

expect(tags?.['wallet.pending_approval']).toStrictEqual(
'eth_sendTransaction',
);
});

it('includes first pending approval type if multiple', () => {
const state = {
...initialRootState,
engine: {
backgroundState: {
...backgroundState,

ApprovalController: {
...backgroundState.ApprovalController,
pendingApprovals: {
1: {
type: 'eth_sendTransaction',
},
2: {
type: 'personal_sign',
},
},
},
},
},
} as unknown as RootState;

const tags = getTraceTags(state);

expect(tags?.['wallet.pending_approval']).toStrictEqual(
'eth_sendTransaction',
);
});

it('includes account count', () => {
const state = {
...initialRootState,
engine: {
backgroundState: {
...backgroundState,
AccountsController: createMockAccountsControllerState([
'0x1234',
'0x4321',
]),
},
},
} as unknown as RootState;

const tags = getTraceTags(state);

expect(tags?.['wallet.account_count']).toStrictEqual(2);
});

it('includes nft count', () => {
const state = {
...initialRootState,
engine: {
backgroundState: {
...backgroundState,
NftController: {
...backgroundState.NftController,
allNfts: {
'0x1234': {
'0x1': [
{
tokenId: '1',
},
{
tokenId: '2',
},
],
'0x2': [
{
tokenId: '3',
},
{
tokenId: '4',
},
],
},
'0x4321': {
'0x3': [
{
tokenId: '5',
},
],
},
},
},
},
},
} as unknown as RootState;

const tags = getTraceTags(state);

expect(tags?.['wallet.nft_count']).toStrictEqual(5);
});

it('includes notification count', () => {
const state = {
...initialRootState,
engine: {
backgroundState: {
...backgroundState,
NotificationServicesController: {
metamaskNotificationsList: [{}, {}, {}],
},
},
},
} as unknown as RootState;

const tags = getTraceTags(state);

expect(tags?.['wallet.notification_count']).toStrictEqual(3);
});

it('includes token count', () => {
const state = {
...initialRootState,
engine: {
backgroundState: {
...backgroundState,
TokensController: {
allTokens: {
'0x1': {
'0x1234': [{}, {}],
'0x4321': [{}],
},
'0x2': {
'0x5678': [{}],
},
},
},
},
},
} as unknown as RootState;

const tags = getTraceTags(state);

expect(tags?.['wallet.token_count']).toStrictEqual(4);
});

it('includes transaction count', () => {
const state = {
...initialRootState,
engine: {
backgroundState: {
...backgroundState,
TransactionController: {
transactions: [
{
id: 1,
chainId: '0x1',
},
{
id: 2,
chainId: '0x1',
},
{
id: 3,
chainId: '0x2',
},
],
},
},
},
} as unknown as RootState;
const tags = getTraceTags(state);

expect(tags?.['wallet.transaction_count']).toStrictEqual(3);
});
});
});
30 changes: 30 additions & 0 deletions app/util/sentry/tags/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { RootState } from '../../../reducers';
import { selectAllNftsFlat } from '../../../selectors/nftController';
import { selectInternalAccounts } from '../../../selectors/accountsController';
import { selectAllTokensFlat } from '../../../selectors/tokensController';
import { getNotificationsList } from '../../../selectors/notifications';
import { selectTransactions } from '../../../selectors/transactionController';
import { selectPendingApprovals } from '../../../selectors/approvalController';

export function getTraceTags(state: RootState) {
if (!Object.keys(state?.engine?.backgroundState).length) return;
const unlocked = state.user.userLoggedIn;
const accountCount = selectInternalAccounts(state).length;
const nftCount = selectAllNftsFlat(state).length;
const notificationCount = getNotificationsList(state).length;
const tokenCount = selectAllTokensFlat(state).length;
const transactionCount = selectTransactions(state).length;
const pendingApprovals = Object.values(selectPendingApprovals(state));

const firstApprovalType = pendingApprovals?.[0]?.type;

return {
'wallet.account_count': accountCount,
'wallet.nft_count': nftCount,
'wallet.notification_count': notificationCount,
'wallet.pending_approval': firstApprovalType,
'wallet.token_count': tokenCount,
'wallet.transaction_count': transactionCount,
'wallet.unlocked': unlocked,
};
}
Loading

0 comments on commit 97d4640

Please sign in to comment.