diff --git a/.github/workflows/gitflow-sync-develop.yml b/.github/workflows/gitflow-sync-develop.yml index b56feb2b0a79..374e83aeda65 100644 --- a/.github/workflows/gitflow-sync-develop.yml +++ b/.github/workflows/gitflow-sync-develop.yml @@ -38,13 +38,8 @@ jobs: # This token is scoped to Daniel Griesser github_token: ${{ secrets.REPO_SCOPED_TOKEN }} - # https://github.com/marketplace/actions/enable-pull-request-automerge - name: Enable automerge for PR - if: steps.open-pr.outputs.pr_number != '' - uses: peter-evans/enable-pull-request-automerge@v2 - with: - pull-request-number: ${{ steps.open-pr.outputs.pr_number }} - merge-method: merge + run: gh pr merge --merge --auto "1" # https://github.com/marketplace/actions/auto-approve - name: Auto approve PR diff --git a/packages/angular/src/tracing.ts b/packages/angular/src/tracing.ts index 0d51b2dbfcbc..c200fbc567cb 100644 --- a/packages/angular/src/tracing.ts +++ b/packages/angular/src/tracing.ts @@ -282,14 +282,24 @@ export function TraceMethodDecorator(): MethodDecorator { * child route with its parent to produce the complete parameterized URL of the activated route. * This happens recursively until the last child (i.e. the end of the URL) is reached. * - * @param route the ActivatedRouteSnapshot of which its path and its child's path is concantenated + * @param route the ActivatedRouteSnapshot of which its path and its child's path is concatenated * - * @returns the concatenated parameterzited route string + * @returns the concatenated parameterized route string */ export function getParameterizedRouteFromSnapshot(route?: ActivatedRouteSnapshot | null): string { - const path = route && route.firstChild && route.firstChild.routeConfig && route.firstChild.routeConfig.path; - if (!path) { - return '/'; + const parts: string[] = []; + + let currentRoute = route && route.firstChild; + while (currentRoute) { + const path = currentRoute && currentRoute.routeConfig && currentRoute.routeConfig.path; + if (path === null || path === undefined) { + break; + } + + parts.push(path); + currentRoute = currentRoute.firstChild; } - return `/${path}${getParameterizedRouteFromSnapshot(route && route.firstChild)}`; + + const fullPath = parts.filter(part => part).join('/'); + return fullPath ? `/${fullPath}/` : '/'; } diff --git a/packages/angular/test/tracing.test.ts b/packages/angular/test/tracing.test.ts index 18bc1324f1eb..0afef2771add 100644 --- a/packages/angular/test/tracing.test.ts +++ b/packages/angular/test/tracing.test.ts @@ -55,7 +55,14 @@ describe('Angular Tracing', () => { describe('getParameterizedRouteFromSnapshot', () => { it.each([ - ['returns `/` empty object if the route no children', {}, '/'], + ['returns `/` if the route has no children', {}, '/'], + [ + 'returns `/` if the route has an empty child', + { + firstChild: { routeConfig: { path: '' } }, + }, + '/', + ], [ 'returns the route of a snapshot without children', { @@ -76,6 +83,21 @@ describe('Angular Tracing', () => { }, '/orgs/:orgId/projects/:projId/overview/', ], + [ + 'returns the route of a snapshot without children but with empty paths', + { + firstChild: { + routeConfig: { path: 'users' }, + firstChild: { + routeConfig: { path: '' }, + firstChild: { + routeConfig: { path: ':id' }, + }, + }, + }, + }, + '/users/:id/', + ], ])('%s', (_, routeSnapshot, expectedParams) => { expect(getParameterizedRouteFromSnapshot(routeSnapshot as unknown as ActivatedRouteSnapshot)).toEqual( expectedParams, @@ -345,6 +367,7 @@ describe('Angular Tracing', () => { public ngOnInit() { origNgOnInitMock(); } + public ngAfterViewInit() { origNgAfterViewInitMock(); } @@ -398,6 +421,7 @@ describe('Angular Tracing', () => { public ngOnInit() { origNgOnInitMock(); } + @TraceMethodDecorator() public ngAfterViewInit() { origNgAfterViewInitMock(); diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 3a8cbb774078..4365d928a928 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -79,5 +79,6 @@ }, "volta": { "extends": "../../package.json" - } + }, + "sideEffects": false } diff --git a/packages/replay/README.md b/packages/replay/README.md index 6dbab272780a..80d568cff52e 100644 --- a/packages/replay/README.md +++ b/packages/replay/README.md @@ -60,6 +60,9 @@ If you do not want to start Replay immediately (e.g. if you want to lazy-load it you can also use `addIntegration` to load it later: ```js +import * as Sentry from "@sentry/react"; +import { BrowserClient } from "@sentry/browser"; + Sentry.init({ // Do not load it initially integrations: [] @@ -67,7 +70,10 @@ Sentry.init({ // Sometime later const { Replay } = await import('@sentry/browser'); -getCurrentHub().getClient().addIntegration(new Replay()); +const client = Sentry.getCurrentHub().getClient(); + +// Client can be undefined +client?.addIntegration(new Replay()); ``` ### Identifying Users @@ -76,6 +82,7 @@ If you have only followed the above instructions to setup session replays, you w ```javascript import * as Sentry from "@sentry/browser"; + Sentry.setUser({ email: "jane.doe@example.com" }); ``` @@ -84,17 +91,22 @@ Sentry.setUser({ email: "jane.doe@example.com" }); Replay recording only starts when it is included in the `integrations` array when calling `Sentry.init` or calling `addIntegration` from the a Sentry client instance. To stop recording you can call the `stop()`. ```js +import * as Sentry from "@sentry/react"; +import { BrowserClient } from "@sentry/browser"; + const replay = new Replay(); + Sentry.init({ integrations: [replay] }); -const client = getClient(); +const client = Sentry.getCurrentHub().getClient(); // Add replay integration, will start recoring -client.addIntegration(replay); +client?.addIntegration(replay); -replay.stop(); // Stop recording +// Stop recording +replay.stop(); ``` ## Loading Replay as a CDN Bundle