From d31433db4e7bc87536509795d0def17763c66144 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Thu, 7 Jul 2022 12:21:28 -0400 Subject: [PATCH] ref(react): Add transaction source for react router v6 --- packages/react/src/reactrouterv6.tsx | 42 +++++++++++++--------- packages/react/test/reactrouterv6.test.tsx | 15 ++++++-- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/packages/react/src/reactrouterv6.tsx b/packages/react/src/reactrouterv6.tsx index 255e06ae22c0..8f2c2825a2b3 100644 --- a/packages/react/src/reactrouterv6.tsx +++ b/packages/react/src/reactrouterv6.tsx @@ -1,7 +1,7 @@ // Inspired from Donnie McNeal's solution: // https://gist.github.com/wontondon/e8c4bdf2888875e4c755712e99279536 -import { Transaction, TransactionContext } from '@sentry/types'; +import { Transaction, TransactionContext, TransactionSource } from '@sentry/types'; import { getGlobalObject, logger } from '@sentry/utils'; import hoistNonReactStatics from 'hoist-non-react-statics'; import React from 'react'; @@ -48,14 +48,6 @@ const SENTRY_TAGS = { 'routing.instrumentation': 'react-router-v6', }; -function getInitPathName(): string | undefined { - if (global && global.location) { - return global.location.pathname; - } - - return undefined; -} - export function reactRouterV6Instrumentation( useEffect: UseEffect, useLocation: UseLocation, @@ -68,12 +60,15 @@ export function reactRouterV6Instrumentation( startTransactionOnPageLoad = true, startTransactionOnLocationChange = true, ): void => { - const initPathName = getInitPathName(); + const initPathName = global && global.location && global.location.pathname; if (startTransactionOnPageLoad && initPathName) { activeTransaction = customStartTransaction({ name: initPathName, op: 'pageload', tags: SENTRY_TAGS, + metadata: { + source: 'url', + }, }); } @@ -88,9 +83,13 @@ export function reactRouterV6Instrumentation( }; } -const getTransactionName = (routes: RouteObject[], location: Location, matchRoutes: MatchRoutes): string => { +function getNormalizedName( + routes: RouteObject[], + location: Location, + matchRoutes: MatchRoutes, +): [string, TransactionSource] { if (!routes || routes.length === 0 || !matchRoutes) { - return location.pathname; + return [location.pathname, 'url']; } const branches = matchRoutes(routes, location); @@ -99,13 +98,16 @@ const getTransactionName = (routes: RouteObject[], location: Location, matchRout // eslint-disable-next-line @typescript-eslint/prefer-for-of for (let x = 0; x < branches.length; x++) { if (branches[x].route && branches[x].route.path && branches[x].pathname === location.pathname) { - return branches[x].route.path || location.pathname; + const path = branches[x].route.path; + if (path) { + return [path, 'route']; + } } } } - return location.pathname; -}; + return [location.pathname, 'url']; +} export function withSentryReactRouterV6Routing

, R extends React.FC

>(Routes: R): R { if ( @@ -136,7 +138,9 @@ export function withSentryReactRouterV6Routing

, R isBaseLocation = true; if (activeTransaction) { - activeTransaction.setName(getTransactionName(routes, location, _matchRoutes)); + const [name, source] = getNormalizedName(routes, location, _matchRoutes); + activeTransaction.setName(name); + activeTransaction.setMetadata({ source }); } // eslint-disable-next-line react-hooks/exhaustive-deps @@ -156,10 +160,14 @@ export function withSentryReactRouterV6Routing

, R activeTransaction.finish(); } + const [name, source] = getNormalizedName(routes, location, _matchRoutes); activeTransaction = _customStartTransaction({ - name: getTransactionName(routes, location, _matchRoutes), + name, op: 'navigation', tags: SENTRY_TAGS, + metadata: { + source, + }, }); } }, [props.children, location, navigationType, isBaseLocation]); diff --git a/packages/react/test/reactrouterv6.test.tsx b/packages/react/test/reactrouterv6.test.tsx index 5e30d96a83a9..2a5acb921ae8 100644 --- a/packages/react/test/reactrouterv6.test.tsx +++ b/packages/react/test/reactrouterv6.test.tsx @@ -19,7 +19,7 @@ describe('React Router v6', () => { function createInstrumentation(_opts?: { startTransactionOnPageLoad?: boolean; startTransactionOnLocationChange?: boolean; - }): [jest.Mock, { mockSetName: jest.Mock; mockFinish: jest.Mock }] { + }): [jest.Mock, { mockSetName: jest.Mock; mockFinish: jest.Mock; mockSetMetadata: jest.Mock }] { const options = { matchPath: _opts ? matchPath : undefined, startTransactionOnLocationChange: true, @@ -28,7 +28,10 @@ describe('React Router v6', () => { }; const mockFinish = jest.fn(); const mockSetName = jest.fn(); - const mockStartTransaction = jest.fn().mockReturnValue({ setName: mockSetName, finish: mockFinish }); + const mockSetMetadata = jest.fn(); + const mockStartTransaction = jest + .fn() + .mockReturnValue({ setName: mockSetName, finish: mockFinish, setMetadata: mockSetMetadata }); reactRouterV6Instrumentation( React.useEffect, @@ -37,7 +40,7 @@ describe('React Router v6', () => { createRoutesFromChildren, matchRoutes, )(mockStartTransaction, options.startTransactionOnPageLoad, options.startTransactionOnLocationChange); - return [mockStartTransaction, { mockSetName, mockFinish }]; + return [mockStartTransaction, { mockSetName, mockFinish, mockSetMetadata }]; } it('starts a pageload transaction', () => { @@ -57,6 +60,7 @@ describe('React Router v6', () => { name: '/', op: 'pageload', tags: { 'routing.instrumentation': 'react-router-v6' }, + metadata: { source: 'url' }, }); }); @@ -93,6 +97,7 @@ describe('React Router v6', () => { name: '/', op: 'pageload', tags: { 'routing.instrumentation': 'react-router-v6' }, + metadata: { source: 'url' }, }); }); @@ -114,6 +119,7 @@ describe('React Router v6', () => { name: '/about', op: 'navigation', tags: { 'routing.instrumentation': 'react-router-v6' }, + metadata: { source: 'route' }, }); }); @@ -137,6 +143,7 @@ describe('React Router v6', () => { name: '/about/us', op: 'navigation', tags: { 'routing.instrumentation': 'react-router-v6' }, + metadata: { source: 'route' }, }); }); @@ -160,6 +167,7 @@ describe('React Router v6', () => { name: '/about/:page', op: 'navigation', tags: { 'routing.instrumentation': 'react-router-v6' }, + metadata: { source: 'route' }, }); }); @@ -185,6 +193,7 @@ describe('React Router v6', () => { name: '/stores/:storeId/products/:productId', op: 'navigation', tags: { 'routing.instrumentation': 'react-router-v6' }, + metadata: { source: 'route' }, }); }); });