-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(react): improve handling of routes nested under path="/" #14821
Conversation
We noticed this in Sentry's `/issues/:groupId/` route, which uses SDK v8.43.0. The full route tree is complex, but the relevent parts are: ```js <Route path="/" element={<div>root</div>}> <Route path="/issues/:groupId/" element={<div>issues group</div>}> <Route index element={<div>index</div>} /> </Route> </Route> ``` If you navigate to e.g. `/issues/123`, the recorded transaction name is `//issues/:groupId/` (notice the double slash). This is messy but doesn't have too much of a consequence. The worse issue is when you navigate to e.g. `/issues/123/` (note the trailing slash), the transaction name is `/issues/123/` - it is not parameterized. This breaks transaction grouping. On the `master` and `develop` branch of the SDK, the transaction name is recorded as `/`. This causes the transactions to be grouped incorrectly with the root, as well as other routes with this structure (nested under a route with path `/`). This commit fixes both of these issues and passes both the new tests (which reproduce the above issues) and all existing tests, but I still don't trust the change. First, `newPath` now always has a leading slash, and I don't know how that interacts with the other path concatenations inside `getNormalizedName`. It feels like dealing with path concatenation should be handled in a more systematic way. Second, I revert a change from 3473447 where ```js if (basename + branch.pathname === location.pathname) { ``` became ``` if (location.pathname.endsWith(basename + branch.pathname)) { ``` This is the change that difference in results between SDK versions. This will always match when `basename` is empty, `branch.pathname` is `/`, and `location.pathname` ends with `/` - in other words, if you have a parent route with `path="/"`, every request ending in a slash will match it instead of the more specific child route. This seems likely to be a wide regression in transaction names. But, no tests failed from this change, which makes me worried that there's some necessary behaviour that isn't covered.
const newPath = path[0] === '/' || pathBuilder[pathBuilder.length - 1] === '/' ? path : `/${path}`; | ||
pathBuilder += newPath; | ||
pathBuilder = trimSlash(pathBuilder) + prefixWithSlash(newPath); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Originally I thought to change these lines to
const newPath = prefixWithSlash(path);
pathBuilder = trimSlash(pathBuilder) + newPath;
but newPath
is used in a different path concatenation further down the method and, even though no tests fail, I didn't want collateral damage. (Tests probably should have failed though - I think the path concatenation in this method could be better handled, and is under-tested.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can make a refactor/cleanup once we get the tests in a better state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the fix @mjq! I think it's safe to merge, and I agree that we need to improve tests to cover as many route declaration patterns as possible.
|
||
// If the path matches the current location, return the path | ||
if (location.pathname.endsWith(basename + branch.pathname)) { | ||
if (trimSlash(location.pathname) === trimSlash(basename + branch.pathname)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd expect this to break descendant routes. But in the final implementation, descendant routes don't end up here. So, I believe it's safe to revert.
const newPath = path[0] === '/' || pathBuilder[pathBuilder.length - 1] === '/' ? path : `/${path}`; | ||
pathBuilder += newPath; | ||
pathBuilder = trimSlash(pathBuilder) + prefixWithSlash(newPath); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can make a refactor/cleanup once we get the tests in a better state.
Thanks @onurtemizkan! Going to merge in that case 👍 |
We noticed this in Sentry's `/issues/:groupId/` route, which uses SDK v8.43.0. The full route tree is complex, but the relevant parts are: ```js <Route path="/" element={<div>root</div>}> <Route path="/issues/:groupId/" element={<div>issues group</div>}> <Route index element={<div>index</div>} /> </Route> </Route> ``` If you navigate to e.g. `/issues/123` (no trailing slash), the recorded transaction name is `//issues/:groupId/` (notice the double slash). This looks messy but doesn't have too much of a consequence. The worse issue is when you navigate to e.g. `/issues/123/` (with trailing slash), the transaction name is `/issues/123/` - it is not parameterized. This breaks transaction grouping. On the `master` and `develop` branch of the SDK, the transaction name is recorded as `/`. This causes the transactions to be grouped incorrectly with the root, as well as any other routes nested under a route with `path="/"`. (Thanks @JoshFerge for noticing the bad data in Sentry! 🙌) --- Note that this commit effectively reverts a change from #14304 where ```js if (basename + branch.pathname === location.pathname) { ``` became ```js if (location.pathname.endsWith(basename + branch.pathname)) { ``` This is the change that caused the difference in results between SDK versions. This will always match when `basename` is empty, `branch.pathname` is `/`, and `location.pathname` ends with `/` - in other words, if you have a parent route with `path="/"`, every request ending in a slash will match it instead of the more specific child route. (Depending on how often this kind of `Route` nesting happens in the wild, this could be a wide regression in transaction names.) But, no tests failed from the removal of `endsWith`. @onurtemizkan, who wrote the change in question: > I'd expect this to break descendant routes. But in the final > implementation, descendant routes don't end up here. So, I > believe it's safe to revert.
We noticed this in Sentry's `/issues/:groupId/` route, which uses SDK v8.43.0. The full route tree is complex, but the relevant parts are: ```js <Route path="/" element={<div>root</div>}> <Route path="/issues/:groupId/" element={<div>issues group</div>}> <Route index element={<div>index</div>} /> </Route> </Route> ``` If you navigate to e.g. `/issues/123` (no trailing slash), the recorded transaction name is `//issues/:groupId/` (notice the double slash). This looks messy but doesn't have too much of a consequence. The worse issue is when you navigate to e.g. `/issues/123/` (with trailing slash), the transaction name is `/issues/123/` - it is not parameterized. This breaks transaction grouping. On the `master` and `develop` branch of the SDK, the transaction name is recorded as `/`. This causes the transactions to be grouped incorrectly with the root, as well as any other routes nested under a route with `path="/"`. (Thanks @JoshFerge for noticing the bad data in Sentry! 🙌) --- Note that this commit effectively reverts a change from #14304 where ```js if (basename + branch.pathname === location.pathname) { ``` became ```js if (location.pathname.endsWith(basename + branch.pathname)) { ``` This is the change that caused the difference in results between SDK versions. This will always match when `basename` is empty, `branch.pathname` is `/`, and `location.pathname` ends with `/` - in other words, if you have a parent route with `path="/"`, every request ending in a slash will match it instead of the more specific child route. (Depending on how often this kind of `Route` nesting happens in the wild, this could be a wide regression in transaction names.) But, no tests failed from the removal of `endsWith`. @onurtemizkan, who wrote the change in question: > I'd expect this to break descendant routes. But in the final > implementation, descendant routes don't end up here. So, I > believe it's safe to revert.
We noticed this in Sentry's
/issues/:groupId/
route, which uses SDK v8.43.0. The full route tree is complex, but the relevant parts are:If you navigate to e.g.
/issues/123
(no trailing slash), the recorded transaction name is//issues/:groupId/
(notice the double slash). This looks messy but doesn't have too much of a consequence.The worse issue is when you navigate to e.g.
/issues/123/
(with trailing slash), the transaction name is/issues/123/
- it is not parameterized. This breaks transaction grouping. On themaster
anddevelop
branch of the SDK, the transaction name is recorded as/
. This causes the transactions to be grouped incorrectly with the root, as well as any other routes nested under a route withpath="/"
.(Thanks @JoshFerge for noticing the bad data in Sentry! 🙌)
This commit fixes both of these issues and passes both the new tests (which reproduce the above issues) and all existing tests, but I don't trust my changes.
I effectively revert a change from #14304 where
became
This is the change that caused the difference in results between SDK versions. This will always match when
basename
is empty,branch.pathname
is/
, andlocation.pathname
ends with/
- in other words, if you have a parent route withpath="/"
, every request ending in a slash will match it instead of the more specific child route. (Depending on how often this kind ofRoute
nesting happens in the wild, this could be a wide regression in transaction names.) But, no tests failed from the removal ofendsWith
, which makes me worried that there's some necessary behaviour that isn't covered and would be broken by this fix. @onurtemizkan can you give some context into that change? Thanks!