From 54a889e13100e10c6b164b9acdb835f15f4ebe74 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Tue, 16 Jul 2024 14:21:47 +0200 Subject: [PATCH 01/21] ci: Fix nextjs-15 canary tests (#12918) --- .../test-applications/nextjs-15/package.json | 2 +- .../nextjs-15/tests/nested-rsc-error.test.ts | 11 +++++++++++ .../nextjs-15/tests/streaming-rsc-error.test.ts | 11 +++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15/package.json b/dev-packages/e2e-tests/test-applications/nextjs-15/package.json index 4c3f56b0aa0c..265ee010b8d5 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-15/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-15/package.json @@ -8,7 +8,7 @@ "test:prod": "TEST_ENV=production __NEXT_EXPERIMENTAL_INSTRUMENTATION=1 playwright test", "test:dev": "TEST_ENV=development __NEXT_EXPERIMENTAL_INSTRUMENTATION=1 playwright test", "test:build": "pnpm install && npx playwright install && pnpm build", - "test:build-canary": "pnpm install && pnpm add next@rc && pnpm add react@beta && pnpm add react-dom@beta && npx playwright install && pnpm build", + "test:build-canary": "pnpm install && pnpm add next@canary && pnpm add react@beta && pnpm add react-dom@beta && npx playwright install && pnpm build", "test:build-latest": "pnpm install && pnpm add next@rc && pnpm add react@beta && pnpm add react-dom@beta && npx playwright install && pnpm build", "test:assert": "pnpm test:prod && pnpm test:dev" }, diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15/tests/nested-rsc-error.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-15/tests/nested-rsc-error.test.ts index 223da5b245e9..863e5de111a2 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-15/tests/nested-rsc-error.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-15/tests/nested-rsc-error.test.ts @@ -1,9 +1,20 @@ import { expect, test } from '@playwright/test'; import { waitForError, waitForTransaction } from '@sentry-internal/test-utils'; +const packageJson = require('../package.json'); + test('Should capture errors from nested server components when `Sentry.captureRequestError` is added to the `onRequestError` hook', async ({ page, }) => { + const [, minor, patch, canary] = packageJson.dependencies.next.split('.'); + + test.skip( + minor === '0' && + patch.startsWith('0-') && + ((patch.includes('canary') && Number(canary) < 63) || patch.includes('rc')), + 'Next.js version does not expose these errors', + ); + const errorEventPromise = waitForError('nextjs-15', errorEvent => { return !!errorEvent?.exception?.values?.some(value => value.value === 'I am technically uncatchable'); }); diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15/tests/streaming-rsc-error.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-15/tests/streaming-rsc-error.test.ts index b50e9688861e..0a20e97be74a 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-15/tests/streaming-rsc-error.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-15/tests/streaming-rsc-error.test.ts @@ -1,9 +1,20 @@ import { expect, test } from '@playwright/test'; import { waitForError, waitForTransaction } from '@sentry-internal/test-utils'; +const packageJson = require('../package.json'); + test('Should capture errors for crashing streaming promises in server components when `Sentry.captureRequestError` is added to the `onRequestError` hook', async ({ page, }) => { + const [, minor, patch, canary] = packageJson.dependencies.next.split('.'); + + test.skip( + minor === '0' && + patch.startsWith('0-') && + ((patch.includes('canary') && Number(canary) < 63) || patch.includes('rc')), + 'Next.js version does not expose these errors', + ); + const errorEventPromise = waitForError('nextjs-15', errorEvent => { return !!errorEvent?.exception?.values?.some(value => value.value === 'I am a data streaming error'); }); From 52eb466c4ed0a80edd0436fb78c4574051f88b33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jul 2024 14:02:40 +0000 Subject: [PATCH 02/21] feat(deps): bump @opentelemetry/instrumentation-aws-sdk from 0.42.0 to 0.43.0 (#12921) --- packages/aws-serverless/package.json | 2 +- yarn.lock | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/aws-serverless/package.json b/packages/aws-serverless/package.json index f94fbb596982..e43e9f77570a 100644 --- a/packages/aws-serverless/package.json +++ b/packages/aws-serverless/package.json @@ -65,7 +65,7 @@ }, "dependencies": { "@opentelemetry/instrumentation-aws-lambda": "0.42.0", - "@opentelemetry/instrumentation-aws-sdk": "0.42.0", + "@opentelemetry/instrumentation-aws-sdk": "0.43.0", "@sentry/core": "8.17.0", "@sentry/node": "8.17.0", "@sentry/types": "8.17.0", diff --git a/yarn.lock b/yarn.lock index 7cb6273253bc..9e2ff07d8c53 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6968,14 +6968,14 @@ dependencies: "@opentelemetry/semantic-conventions" "1.24.1" -"@opentelemetry/core@1.25.0", "@opentelemetry/core@^1.1.0", "@opentelemetry/core@^1.8.0": +"@opentelemetry/core@1.25.0": version "1.25.0" resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.25.0.tgz#ad034f5c2669f589bd703bfbbaa38b51f8504053" integrity sha512-n0B3s8rrqGrasTgNkXLKXzN0fXo+6IYP7M5b7AMsrZM33f/y6DS6kJ0Btd7SespASWq8bgL3taLo0oe0vB52IQ== dependencies: "@opentelemetry/semantic-conventions" "1.25.0" -"@opentelemetry/core@1.25.1", "@opentelemetry/core@^1.23.0", "@opentelemetry/core@^1.25.1": +"@opentelemetry/core@1.25.1", "@opentelemetry/core@^1.1.0", "@opentelemetry/core@^1.23.0", "@opentelemetry/core@^1.25.1", "@opentelemetry/core@^1.8.0": version "1.25.1" resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.25.1.tgz#ff667d939d128adfc7c793edae2f6bca177f829d" integrity sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ== @@ -7002,10 +7002,10 @@ "@opentelemetry/semantic-conventions" "^1.22.0" "@types/aws-lambda" "8.10.122" -"@opentelemetry/instrumentation-aws-sdk@0.42.0": - version "0.42.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.42.0.tgz#5e0884282814163c328c1b3ec68fe257108a2a04" - integrity sha512-6b4LQAeBSKU5RhKEP9rH+wMcKswlllIT9J65uREmnWQQJo5zogD6cWa2sJ814o9K25/aDi+zheVHDFDuA7iVCQ== +"@opentelemetry/instrumentation-aws-sdk@0.43.0": + version "0.43.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.43.0.tgz#b579d66e624cc1545f29d2858c180204e515e110" + integrity sha512-klfA48MT0uZY/mGw3cYdQeCXTyMhtY4FzHcZy9R7DdTcuCExgbxWrUlOSiqIJ5kBgsCZfBMEeA6UQKDBwa6X7Q== dependencies: "@opentelemetry/core" "^1.8.0" "@opentelemetry/instrumentation" "^0.52.0" @@ -7305,12 +7305,12 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.24.1.tgz#d4bcebda1cb5146d47a2a53daaa7922f8e084dfb" integrity sha512-VkliWlS4/+GHLLW7J/rVBA00uXus1SWvwFvcUDxDwmFxYfg/2VI6ekwdXS28cjI8Qz2ky2BzG8OUHo+WeYIWqw== -"@opentelemetry/semantic-conventions@1.25.0", "@opentelemetry/semantic-conventions@^1.17.0", "@opentelemetry/semantic-conventions@^1.22.0", "@opentelemetry/semantic-conventions@^1.23.0": +"@opentelemetry/semantic-conventions@1.25.0": version "1.25.0" resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.0.tgz#390eb4d42a29c66bdc30066af9035645e9bb7270" integrity sha512-M+kkXKRAIAiAP6qYyesfrC5TOmDpDVtsxuGfPcqd9B/iBrac+E14jYwrgm0yZBUIbIP2OnqC3j+UgkXLm1vxUQ== -"@opentelemetry/semantic-conventions@1.25.1", "@opentelemetry/semantic-conventions@^1.25.1": +"@opentelemetry/semantic-conventions@1.25.1", "@opentelemetry/semantic-conventions@^1.17.0", "@opentelemetry/semantic-conventions@^1.22.0", "@opentelemetry/semantic-conventions@^1.23.0", "@opentelemetry/semantic-conventions@^1.25.1": version "1.25.1" resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz#0deecb386197c5e9c2c28f2f89f51fb8ae9f145e" integrity sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ== From 8b2473153cf50dc769da7f88e9d8affde166d4f6 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Tue, 16 Jul 2024 17:58:58 +0200 Subject: [PATCH 03/21] feat(node): Add `request` parameter to `httpIntegration` ignore callbacks (#12930) Add a second parameter (`request`) to the `httpIntegration`'s `ignoreIncomingRequests` and `ignoreOutgoingRequests` callbacks. This allows users to not only filter on the URL as previously but on the entire request objects. --- .../server-ignoreIncomingRequests.js | 14 +- .../server-ignoreOutgoingRequests.js | 21 ++- .../suites/tracing/httpIntegration/test.ts | 145 ++++++++++++------ packages/node/src/integrations/http.ts | 16 +- 4 files changed, 144 insertions(+), 52 deletions(-) diff --git a/dev-packages/node-integration-tests/suites/tracing/httpIntegration/server-ignoreIncomingRequests.js b/dev-packages/node-integration-tests/suites/tracing/httpIntegration/server-ignoreIncomingRequests.js index f1e5d9870fcf..bafd8b8f2bc4 100644 --- a/dev-packages/node-integration-tests/suites/tracing/httpIntegration/server-ignoreIncomingRequests.js +++ b/dev-packages/node-integration-tests/suites/tracing/httpIntegration/server-ignoreIncomingRequests.js @@ -9,8 +9,14 @@ Sentry.init({ integrations: [ Sentry.httpIntegration({ - ignoreIncomingRequests: url => { - return url.includes('/liveness'); + ignoreIncomingRequests: (url, request) => { + if (url.includes('/liveness')) { + return true; + } + if (request.method === 'POST' && request.url.includes('readiness')) { + return true; + } + return false; }, }), ], @@ -33,6 +39,10 @@ app.get('/liveness', (_req, res) => { res.send({ response: 'liveness' }); }); +app.post('/readiness', (_req, res) => { + res.send({ response: 'readiness' }); +}); + Sentry.setupExpressErrorHandler(app); startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/tracing/httpIntegration/server-ignoreOutgoingRequests.js b/dev-packages/node-integration-tests/suites/tracing/httpIntegration/server-ignoreOutgoingRequests.js index ce520c999259..9d7d2ed069d1 100644 --- a/dev-packages/node-integration-tests/suites/tracing/httpIntegration/server-ignoreOutgoingRequests.js +++ b/dev-packages/node-integration-tests/suites/tracing/httpIntegration/server-ignoreOutgoingRequests.js @@ -10,8 +10,14 @@ Sentry.init({ integrations: [ Sentry.httpIntegration({ - ignoreOutgoingRequests: url => { - return url.includes('example.com'); + ignoreOutgoingRequests: (url, request) => { + if (url.includes('example.com')) { + return true; + } + if (request.method === 'POST' && request.path === '/path') { + return true; + } + return false; }, }), ], @@ -37,6 +43,17 @@ app.get('/test', (_req, response) => { .end(); }); +app.post('/testPath', (_req, response) => { + http + .request('http://example.com/path', res => { + res.on('data', () => {}); + res.on('end', () => { + response.send({ response: 'done' }); + }); + }) + .end(); +}); + Sentry.setupExpressErrorHandler(app); startExpressServerAndSendPortToRunner(app); diff --git a/dev-packages/node-integration-tests/suites/tracing/httpIntegration/test.ts b/dev-packages/node-integration-tests/suites/tracing/httpIntegration/test.ts index aebe0dd676ba..972ba30eab43 100644 --- a/dev-packages/node-integration-tests/suites/tracing/httpIntegration/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/httpIntegration/test.ts @@ -76,58 +76,117 @@ describe('httpIntegration', () => { .makeRequest('get', '/test'); }); - test("doesn't create a root span for incoming requests ignored via `ignoreIncomingRequests`", done => { - const runner = createRunner(__dirname, 'server-ignoreIncomingRequests.js') - .expect({ - transaction: { - contexts: { - trace: { - span_id: expect.any(String), - trace_id: expect.any(String), - data: { - url: expect.stringMatching(/\/test$/), - 'http.response.status_code': 200, + describe("doesn't create a root span for incoming requests ignored via `ignoreIncomingRequests`", () => { + test('via the url param', done => { + const runner = createRunner(__dirname, 'server-ignoreIncomingRequests.js') + .expect({ + transaction: { + contexts: { + trace: { + span_id: expect.any(String), + trace_id: expect.any(String), + data: { + url: expect.stringMatching(/\/test$/), + 'http.response.status_code': 200, + }, + op: 'http.server', + status: 'ok', }, - op: 'http.server', - status: 'ok', }, + transaction: 'GET /test', }, - transaction: 'GET /test', - }, - }) - .start(done); + }) + .start(done); + + runner.makeRequest('get', '/liveness'); // should be ignored + runner.makeRequest('get', '/test'); + }); + + test('via the request param', done => { + const runner = createRunner(__dirname, 'server-ignoreIncomingRequests.js') + .expect({ + transaction: { + contexts: { + trace: { + span_id: expect.any(String), + trace_id: expect.any(String), + data: { + url: expect.stringMatching(/\/test$/), + 'http.response.status_code': 200, + }, + op: 'http.server', + status: 'ok', + }, + }, + transaction: 'GET /test', + }, + }) + .start(done); - runner.makeRequest('get', '/liveness'); // should be ignored - runner.makeRequest('get', '/test'); + runner.makeRequest('post', '/readiness'); // should be ignored + runner.makeRequest('get', '/test'); + }); }); - test("doesn't create child spans for outgoing requests ignored via `ignoreOutgoingRequests`", done => { - const runner = createRunner(__dirname, 'server-ignoreOutgoingRequests.js') - .expect({ - transaction: { - contexts: { - trace: { - span_id: expect.any(String), - trace_id: expect.any(String), - data: { - url: expect.stringMatching(/\/test$/), - 'http.response.status_code': 200, + describe("doesn't create child spans for outgoing requests ignored via `ignoreOutgoingRequests`", () => { + test('via the url param', done => { + const runner = createRunner(__dirname, 'server-ignoreOutgoingRequests.js') + .expect({ + transaction: { + contexts: { + trace: { + span_id: expect.any(String), + trace_id: expect.any(String), + data: { + url: expect.stringMatching(/\/test$/), + 'http.response.status_code': 200, + }, + op: 'http.server', + status: 'ok', }, - op: 'http.server', - status: 'ok', }, + transaction: 'GET /test', + spans: [ + expect.objectContaining({ op: 'middleware.express', description: 'query' }), + expect.objectContaining({ op: 'middleware.express', description: 'expressInit' }), + expect.objectContaining({ op: 'middleware.express', description: 'corsMiddleware' }), + expect.objectContaining({ op: 'request_handler.express', description: '/test' }), + ], }, - transaction: 'GET /test', - spans: [ - expect.objectContaining({ op: 'middleware.express', description: 'query' }), - expect.objectContaining({ op: 'middleware.express', description: 'expressInit' }), - expect.objectContaining({ op: 'middleware.express', description: 'corsMiddleware' }), - expect.objectContaining({ op: 'request_handler.express', description: '/test' }), - ], - }, - }) - .start(done); + }) + .start(done); + + runner.makeRequest('get', '/test'); + }); + + test('via the request param', done => { + const runner = createRunner(__dirname, 'server-ignoreOutgoingRequests.js') + .expect({ + transaction: { + contexts: { + trace: { + span_id: expect.any(String), + trace_id: expect.any(String), + data: { + url: expect.stringMatching(/\/testPath$/), + 'http.response.status_code': 200, + }, + op: 'http.server', + status: 'ok', + }, + }, + transaction: 'POST /testPath', + spans: [ + expect.objectContaining({ op: 'middleware.express', description: 'query' }), + expect.objectContaining({ op: 'middleware.express', description: 'expressInit' }), + expect.objectContaining({ op: 'middleware.express', description: 'corsMiddleware' }), + expect.objectContaining({ op: 'request_handler.express', description: '/testPath' }), + ], + }, + }) + .start(done); - runner.makeRequest('get', '/test'); + runner.makeRequest('post', '/testPath'); + }); }); }); diff --git a/packages/node/src/integrations/http.ts b/packages/node/src/integrations/http.ts index 632b6023e7a3..615506605c9b 100644 --- a/packages/node/src/integrations/http.ts +++ b/packages/node/src/integrations/http.ts @@ -1,4 +1,4 @@ -import type { ClientRequest, ServerResponse } from 'node:http'; +import type { ClientRequest, IncomingMessage, RequestOptions, ServerResponse } from 'node:http'; import type { Span } from '@opentelemetry/api'; import { HttpInstrumentation } from '@opentelemetry/instrumentation-http'; import { addOpenTelemetryInstrumentation } from '@sentry/opentelemetry'; @@ -37,8 +37,11 @@ interface HttpOptions { * * The `url` param contains the entire URL, including query string (if any), protocol, host, etc. of the outgoing request. * For example: `'https://someService.com/users/details?id=123'` + * + * The `request` param contains the original {@type RequestOptions} object used to make the outgoing request. + * You can use it to filter on additional properties like method, headers, etc. */ - ignoreOutgoingRequests?: (url: string) => boolean; + ignoreOutgoingRequests?: (url: string, request: RequestOptions) => boolean; /** * Do not capture spans or breadcrumbs for incoming HTTP requests to URLs where the given callback returns `true`. @@ -46,8 +49,11 @@ interface HttpOptions { * * The `urlPath` param consists of the URL path and query string (if any) of the incoming request. * For example: `'/users/details?id=123'` + * + * The `request` param contains the original {@type IncomingMessage} object of the incoming request. + * You can use it to filter on additional properties like method, headers, etc. */ - ignoreIncomingRequests?: (urlPath: string) => boolean; + ignoreIncomingRequests?: (urlPath: string, request: IncomingMessage) => boolean; /** * Additional instrumentation options that are passed to the underlying HttpInstrumentation. @@ -101,7 +107,7 @@ export const instrumentHttp = Object.assign( } const _ignoreOutgoingRequests = _httpOptions.ignoreOutgoingRequests; - if (_ignoreOutgoingRequests && _ignoreOutgoingRequests(url)) { + if (_ignoreOutgoingRequests && _ignoreOutgoingRequests(url, request)) { return true; } @@ -120,7 +126,7 @@ export const instrumentHttp = Object.assign( } const _ignoreIncomingRequests = _httpOptions.ignoreIncomingRequests; - if (urlPath && _ignoreIncomingRequests && _ignoreIncomingRequests(urlPath)) { + if (urlPath && _ignoreIncomingRequests && _ignoreIncomingRequests(urlPath, request)) { return true; } From 29c90c532e15ffce0810319679192eaef2b83b72 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jul 2024 17:44:12 +0000 Subject: [PATCH 04/21] feat(deps): bump @prisma/instrumentation from 5.16.1 to 5.17.0 (#12937) --- packages/node/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/node/package.json b/packages/node/package.json index 6a607c5c09fe..68dec88cf54c 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -87,7 +87,7 @@ "@opentelemetry/resources": "^1.25.1", "@opentelemetry/sdk-trace-base": "^1.25.1", "@opentelemetry/semantic-conventions": "^1.25.1", - "@prisma/instrumentation": "5.16.1", + "@prisma/instrumentation": "5.17.0", "@sentry/core": "8.18.0", "@sentry/opentelemetry": "8.18.0", "@sentry/types": "8.18.0", diff --git a/yarn.lock b/yarn.lock index 9e2ff07d8c53..ee9e5ff6b791 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7465,10 +7465,10 @@ resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.9.1.tgz#d92bd2f7f006e0316cb4fda9d73f235965cf2c64" integrity sha512-caSOnG4kxcSkhqC/2ShV7rEoWwd3XrftokxJqOCMVvia4NYV/TPtJlS9C2os3Igxw/Qyxumj9GBQzcStzECvtQ== -"@prisma/instrumentation@5.16.1": - version "5.16.1" - resolved "https://registry.yarnpkg.com/@prisma/instrumentation/-/instrumentation-5.16.1.tgz#93f996f9c95874156badbb5edbb97994667f7c3f" - integrity sha512-4m5gRFWnQb8s/yTyGbMZkL7A5uJgqOWcWJxapwcAD0T0kh5sGPEVSQl/zTQvE9aduXhFAxOtC3gO+R8Hb5xO1Q== +"@prisma/instrumentation@5.17.0": + version "5.17.0" + resolved "https://registry.yarnpkg.com/@prisma/instrumentation/-/instrumentation-5.17.0.tgz#f741ff517f54b1a896fb8605e0d702f29855c6cb" + integrity sha512-c1Sle4ji8aasMcYfBBHFM56We4ljfenVtRmS8aY06BllS7SoU6SmJBwG7vil+GHiR0Yrh+t9iBwt4AY0Jr4KNQ== dependencies: "@opentelemetry/api" "^1.8" "@opentelemetry/instrumentation" "^0.49 || ^0.50 || ^0.51 || ^0.52.0" From c7e7cc91a086f95d103b0b375b028392c2923a6c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jul 2024 19:12:35 +0000 Subject: [PATCH 05/21] feat(deps): bump @sentry/cli from 2.32.1 to 2.32.2 (#12923) --- packages/remix/package.json | 2 +- yarn.lock | 92 ++++++++++++++++++------------------- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/packages/remix/package.json b/packages/remix/package.json index 92a6d04b3436..7fb23dbce78d 100644 --- a/packages/remix/package.json +++ b/packages/remix/package.json @@ -54,7 +54,7 @@ "dependencies": { "@opentelemetry/instrumentation-http": "0.52.1", "@remix-run/router": "1.x", - "@sentry/cli": "^2.32.1", + "@sentry/cli": "^2.32.2", "@sentry/core": "8.18.0", "@sentry/node": "8.18.0", "@sentry/opentelemetry": "8.18.0", diff --git a/yarn.lock b/yarn.lock index ee9e5ff6b791..f573adce7753 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8172,45 +8172,45 @@ magic-string "0.30.8" unplugin "1.0.1" -"@sentry/cli-darwin@2.32.1": - version "2.32.1" - resolved "https://registry.yarnpkg.com/@sentry/cli-darwin/-/cli-darwin-2.32.1.tgz#9cb3b8cfb7068d40979514dee72e2bb3ad2c6d0a" - integrity sha512-z/lEwANTYPCzbWTZ2+eeeNYxRLllC8knd0h+vtAKlhmGw/fyc/N39cznIFyFu+dLJ6tTdjOWOeikHtKuS/7onw== - -"@sentry/cli-linux-arm64@2.32.1": - version "2.32.1" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.32.1.tgz#785a5d5d3d2919c581bf5b4efc638c3695d8c3bf" - integrity sha512-hsGqHYuecUl1Yhq4MhiRejfh1gNlmhyNPcQEoO/DDRBnGnJyEAdiDpKXJcc2e/lT9k40B55Ob2CP1SeY040T2w== - -"@sentry/cli-linux-arm@2.32.1": - version "2.32.1" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm/-/cli-linux-arm-2.32.1.tgz#7f9e8292850311bab263e7b84800eb407ff37998" - integrity sha512-m0lHkn+o4YKBq8KptGZvpT64FAwSl9mYvHZO9/ChnEGIJ/WyJwiN1X1r9JHVaW4iT5lD0Y5FAyq3JLkk0m0XHg== - -"@sentry/cli-linux-i686@2.32.1": - version "2.32.1" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-i686/-/cli-linux-i686-2.32.1.tgz#8e85fa58dee042e6a4642e960d226788f8e7288b" - integrity sha512-SuMLN1/ceFd3Q/B0DVyh5igjetTAF423txiABAHASenEev0lG0vZkRDXFclfgDtDUKRPmOXW7VDMirM3yZWQHQ== - -"@sentry/cli-linux-x64@2.32.1": - version "2.32.1" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-x64/-/cli-linux-x64-2.32.1.tgz#b68ed9c4ba163b6730d386dbeca828114f1c979b" - integrity sha512-x4FGd6xgvFddz8V/dh6jii4wy9qjWyvYLBTz8Fhi9rIP+b8wQ3oxwHIdzntareetZP7C1ggx+hZheiYocNYVwA== - -"@sentry/cli-win32-i686@2.32.1": - version "2.32.1" - resolved "https://registry.yarnpkg.com/@sentry/cli-win32-i686/-/cli-win32-i686-2.32.1.tgz#e2532893f87f5d180f6e56f49904d4ac141c8788" - integrity sha512-i6aZma9mFzR+hqMY5VliQZEX6ypP/zUjPK0VtIMYWs5cC6PsQLRmuoeJmy3Z7d4nlh0CdK5NPC813Ej6RY6/vg== - -"@sentry/cli-win32-x64@2.32.1": - version "2.32.1" - resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.32.1.tgz#6b60607cbba243f3708779443cd3f16e09d4289c" - integrity sha512-B58w/lRHLb4MUSjJNfMMw2cQykfimDCMLMmeK+1EiT2RmSeNQliwhhBxYcKk82a8kszH6zg3wT2vCea7LyPUyA== - -"@sentry/cli@^2.22.3", "@sentry/cli@^2.32.1": - version "2.32.1" - resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.32.1.tgz#80932d3d58e6d3b52e2bd705673e08deeb9cb5b0" - integrity sha512-MWkbkzZfnlE7s2pPbg4VozRSAeMlIObfZlTIou9ye6XnPt6ZmmxCLOuOgSKMv4sXg6aeqKNzMNiadThxCWyvPg== +"@sentry/cli-darwin@2.32.2": + version "2.32.2" + resolved "https://registry.yarnpkg.com/@sentry/cli-darwin/-/cli-darwin-2.32.2.tgz#ae0147b73c2d98f8a64d1e9a1431c1afc0d6ec33" + integrity sha512-GDtePIavx3FKSRowdPdtIssahn46MfFFYNN+s7a9MjlhFwJtvC9A1bSDw7ksEtDaQolepUwmLPHaVe19y0T/zw== + +"@sentry/cli-linux-arm64@2.32.2": + version "2.32.2" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.32.2.tgz#a2f47447e1ad4053a7cff4f176e3b7c66b8ad24e" + integrity sha512-VECLVC1rLyvXk6rTVUfmfs4vhANjMgm4BVKGlA3rydmf2PJw2/NfipH3KeyijdE2vEoyLri+/6HH883pP0iniQ== + +"@sentry/cli-linux-arm@2.32.2": + version "2.32.2" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm/-/cli-linux-arm-2.32.2.tgz#59fa70957d4fd8e06fdb2f2a59cfaf7165431859" + integrity sha512-u9s08wr8bDDqsAl6pk9iGGlOHtU+T8btU6voNKy71QzeIBpV9c8VVk/OnmP9aswp/ea4NY416yjnzcTvCrFKAw== + +"@sentry/cli-linux-i686@2.32.2": + version "2.32.2" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-i686/-/cli-linux-i686-2.32.2.tgz#479c972781ffce2ca448f084a4d7787106dfaef1" + integrity sha512-XhofQz32OqLrQK1DEOsryhT7d29Df6VkccvxueGoIt2gpXEXtgRczsUwZjZqquDdkNCt+HPj9eUGcj8pY8JkmQ== + +"@sentry/cli-linux-x64@2.32.2": + version "2.32.2" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-x64/-/cli-linux-x64-2.32.2.tgz#fa4ede3ac428a92e305f15f23515a9f977f1d1e9" + integrity sha512-anyng4Qqt7zX4ZY4IzDH1RJWAVZNBe6sUHcuciNy7giCU3B4/XnxAHlwYmBSN5txpaumsWdstPgRKEUJG6AOSA== + +"@sentry/cli-win32-i686@2.32.2": + version "2.32.2" + resolved "https://registry.yarnpkg.com/@sentry/cli-win32-i686/-/cli-win32-i686-2.32.2.tgz#393176a54fecd5226571d980b84425ef73cf0b1a" + integrity sha512-/auqx7QXG7F556fNK7vaB26pX7Far1CQMfI65iV4u/VWg6gV2WfvJWXB4iowhjqkYv56sZ+zOymLkEVF0R8wtg== + +"@sentry/cli-win32-x64@2.32.2": + version "2.32.2" + resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.32.2.tgz#145b56132c04c62bb09bac802602295cd60e43e1" + integrity sha512-w7hW2sEWVYQquqdILBSFhcVW+HdoyLqVPPkLPAXRSLTwBnuni9nQEIdXr0h/7db+K3cm7PvWndp5ixVyswLHZA== + +"@sentry/cli@^2.22.3", "@sentry/cli@^2.32.2": + version "2.32.2" + resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.32.2.tgz#ed1ee74a498a3cf1e3344309f600184ab270b52e" + integrity sha512-m/6Z3FWu+rTd8jepVlJPKQhvbT8vCjt0N7BSWZiEUVW/8mhwAYJiwO0b+Ch/u4IqbBg1dp3805q5TFPl4AdrNw== dependencies: https-proxy-agent "^5.0.0" node-fetch "^2.6.7" @@ -8218,13 +8218,13 @@ proxy-from-env "^1.1.0" which "^2.0.2" optionalDependencies: - "@sentry/cli-darwin" "2.32.1" - "@sentry/cli-linux-arm" "2.32.1" - "@sentry/cli-linux-arm64" "2.32.1" - "@sentry/cli-linux-i686" "2.32.1" - "@sentry/cli-linux-x64" "2.32.1" - "@sentry/cli-win32-i686" "2.32.1" - "@sentry/cli-win32-x64" "2.32.1" + "@sentry/cli-darwin" "2.32.2" + "@sentry/cli-linux-arm" "2.32.2" + "@sentry/cli-linux-arm64" "2.32.2" + "@sentry/cli-linux-i686" "2.32.2" + "@sentry/cli-linux-x64" "2.32.2" + "@sentry/cli-win32-i686" "2.32.2" + "@sentry/cli-win32-x64" "2.32.2" "@sentry/vite-plugin@2.19.0": version "2.19.0" From 7adbec4d50f1c8ec3008c6abecbb22071467c2c7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jul 2024 19:29:24 +0000 Subject: [PATCH 06/21] feat(deps): bump @opentelemetry/instrumentation-aws-lambda from 0.42.0 to 0.43.0 (#12922) --- packages/aws-serverless/package.json | 2 +- yarn.lock | 30 +++++----------------------- 2 files changed, 6 insertions(+), 26 deletions(-) diff --git a/packages/aws-serverless/package.json b/packages/aws-serverless/package.json index d15b48f3e86a..9d89e4f2a327 100644 --- a/packages/aws-serverless/package.json +++ b/packages/aws-serverless/package.json @@ -64,7 +64,7 @@ "access": "public" }, "dependencies": { - "@opentelemetry/instrumentation-aws-lambda": "0.42.0", + "@opentelemetry/instrumentation-aws-lambda": "0.43.0", "@opentelemetry/instrumentation-aws-sdk": "0.43.0", "@sentry/core": "8.18.0", "@sentry/node": "8.18.0", diff --git a/yarn.lock b/yarn.lock index f573adce7753..e703f7baaec2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6961,13 +6961,6 @@ dependencies: "@opentelemetry/semantic-conventions" "1.23.0" -"@opentelemetry/core@1.24.1": - version "1.24.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.24.1.tgz#35ab9d2ac9ca938e0ffbdfa40c49c169ac8ba80d" - integrity sha512-wMSGfsdmibI88K9wB498zXY04yThPexo8jvwNNlm542HZB7XrrMRBbAyKJqG8qDRJwIBdBrPMi4V9ZPW/sqrcg== - dependencies: - "@opentelemetry/semantic-conventions" "1.24.1" - "@opentelemetry/core@1.25.0": version "1.25.0" resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.25.0.tgz#ad034f5c2669f589bd703bfbbaa38b51f8504053" @@ -6991,10 +6984,10 @@ "@opentelemetry/context-base" "^0.12.0" semver "^7.1.3" -"@opentelemetry/instrumentation-aws-lambda@0.42.0": - version "0.42.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.42.0.tgz#b832660078cbcd65293405c9bcbf9d335f021f4d" - integrity sha512-GhV3s62W8gWXDuCdPkWj60W3giHGadHoGBPGW5Wud2fUK9lY6FiYxv6AmCokzugTaiRfB2RjsaJWd9xTtYttVA== +"@opentelemetry/instrumentation-aws-lambda@0.43.0": + version "0.43.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.43.0.tgz#6a96d582627873bb34d197655b7b69792f0f8da3" + integrity sha512-pSxcWlsE/pCWQRIw92sV2C+LmKXelYkjkA7C5s39iPUi4pZ2lA1nIiw+1R/y2pDEhUHcaKkNyljQr3cx9ZpVlQ== dependencies: "@opentelemetry/instrumentation" "^0.52.0" "@opentelemetry/propagator-aws-xray" "^1.3.1" @@ -7227,7 +7220,7 @@ "@opentelemetry/core" "1.25.0" "@opentelemetry/semantic-conventions" "1.25.0" -"@opentelemetry/resources@1.25.1", "@opentelemetry/resources@^1.23.0", "@opentelemetry/resources@^1.25.1": +"@opentelemetry/resources@1.25.1", "@opentelemetry/resources@^1.23.0", "@opentelemetry/resources@^1.25.1", "@opentelemetry/resources@^1.8.0": version "1.25.1" resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.25.1.tgz#bb9a674af25a1a6c30840b755bc69da2796fefbb" integrity sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ== @@ -7243,14 +7236,6 @@ "@opentelemetry/api" "^0.12.0" "@opentelemetry/core" "^0.12.0" -"@opentelemetry/resources@^1.8.0": - version "1.24.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.24.1.tgz#5e2cb84814824f3b1e1017e6caeeee8402e0ad6e" - integrity sha512-cyv0MwAaPF7O86x5hk3NNgenMObeejZFLJJDVuSeSMIsknlsj3oOZzRv3qSzlwYomXsICfBeFFlxwHQte5mGXQ== - dependencies: - "@opentelemetry/core" "1.24.1" - "@opentelemetry/semantic-conventions" "1.24.1" - "@opentelemetry/sdk-logs@0.50.0": version "0.50.0" resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-logs/-/sdk-logs-0.50.0.tgz#6636492cf626a9666f61d91025e25243d1a43bfc" @@ -7300,11 +7285,6 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.23.0.tgz#627f2721b960fe586b7f72a07912cb7699f06eef" integrity sha512-MiqFvfOzfR31t8cc74CTP1OZfz7MbqpAnLCra8NqQoaHJX6ncIRTdYOQYBDQ2uFISDq0WY8Y9dDTWvsgzzBYRg== -"@opentelemetry/semantic-conventions@1.24.1": - version "1.24.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.24.1.tgz#d4bcebda1cb5146d47a2a53daaa7922f8e084dfb" - integrity sha512-VkliWlS4/+GHLLW7J/rVBA00uXus1SWvwFvcUDxDwmFxYfg/2VI6ekwdXS28cjI8Qz2ky2BzG8OUHo+WeYIWqw== - "@opentelemetry/semantic-conventions@1.25.0": version "1.25.0" resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.0.tgz#390eb4d42a29c66bdc30066af9035645e9bb7270" From a8bce24908b4546936b416bae175b124c391a720 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Wed, 17 Jul 2024 08:51:20 +0200 Subject: [PATCH 07/21] docs: Add docs about resolving gitflow merge conflicts (#12936) This adds docs about how to resolve gitflow merge conflicts. --------- Co-authored-by: Andrei <168741329+andreiborza@users.noreply.github.com> --- docs/gitflow.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/gitflow.md b/docs/gitflow.md index 8926c614bcfc..8bc853e8dbf0 100644 --- a/docs/gitflow.md +++ b/docs/gitflow.md @@ -18,3 +18,16 @@ We use [Gitflow](https://docs.github.com/en/get-started/quickstart/github-flow) While a release is pending, we may merge anything into develop, **except for changes to package.json files**. If we change the package.json files on develop, the gitflow PR master -> develop will have merge conflicts, because during the release the package.json files are updated on master. + +## What to do if there is a merge conflict? + +Although gitflow should help us to avoid merge conflicts, as mentioned above in "Important Caveats" it can still happen +that you get a merge conflict when trying to merge master into develop after a successful release. + +If this happen, you can resolve this as follows: + +- Close the automated PR that was created by the gitflow automation +- Create a new branch on top of `master` (e.g. `manual-develop-sync`) +- Merge `develop` into this branch, with a merge commit (and fix any merge conflicts that come up) +- Now create a PR against `develop` from your branch (e.g. `manual-develop-sync`) +- Merge this PR with a merge commit From 36d1d432476c8a1dd483a1b50b655b1b886012cc Mon Sep 17 00:00:00 2001 From: Andrei <168741329+andreiborza@users.noreply.github.com> Date: Wed, 17 Jul 2024 10:24:18 +0200 Subject: [PATCH 08/21] meta(changelog): Add missing contributor attribution (#12946) Unfortunately, we missed adding a contributor to the changelog https://github.com/getsentry/sentry-javascript/pull/12932/files#r1679943685 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 392564b0c24e..336529b9ff23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,7 +38,7 @@ instead. If you want to disable performance monitoring, remove the `tracesSample - fix(tracing): Ensure you can pass `null` as `parentSpan` in `startSpan*` (#12928) - ref(core): Small bundle size improvement (#12830) -Work in this release was contributed by @GitSquared and @mcous. Thank you for your contributions! +Work in this release was contributed by @GitSquared, @ziyadkhalil and @mcous. Thank you for your contributions! ## 8.17.0 From e710f3bcdbe8b77fd24a4bb7e487fa8456286a68 Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Wed, 17 Jul 2024 14:34:48 +0200 Subject: [PATCH 09/21] fix(browser): Fix bug causing unintentional dropping of transactions (#12933) --- packages/browser-utils/src/metrics/browserMetrics.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/browser-utils/src/metrics/browserMetrics.ts b/packages/browser-utils/src/metrics/browserMetrics.ts index cb48c2e8b675..02c044322bd3 100644 --- a/packages/browser-utils/src/metrics/browserMetrics.ts +++ b/packages/browser-utils/src/metrics/browserMetrics.ts @@ -287,7 +287,13 @@ export function addPerformanceEntries(span: Span): void { // eslint-disable-next-line @typescript-eslint/no-explicit-any performanceEntries.slice(_performanceCursor).forEach((entry: Record) => { const startTime = msToSec(entry.startTime); - const duration = msToSec(entry.duration); + const duration = msToSec( + // Inexplicibly, Chrome sometimes emits a negative duration. We need to work around this. + // There is a SO post attempting to explain this, but it leaves one with open questions: https://stackoverflow.com/questions/23191918/peformance-getentries-and-negative-duration-display + // The way we clamp the value is probably not accurate, since we have observed this happen for things that may take a while to load, like for example the replay worker. + // TODO: Investigate why this happens and how to properly mitigate. For now, this is a workaround to prevent transactions being dropped due to negative duration spans. + Math.max(0, entry.duration), + ); if (op === 'navigation' && transactionStartTime && timeOrigin + startTime < transactionStartTime) { return; From 5ceb1f4a0c76e2be195b0bd489655796ce381f66 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Wed, 17 Jul 2024 11:16:21 -0400 Subject: [PATCH 10/21] chore: Update changed package paths (#12954) I noticed these while working on the cloudflare sdk. - update browser path to use new replay packages We were previously referencing `packages/replay`, which does not exist anymore. This updates us to use the correct paths. - add vercel-edge to nextjs The nextjs sdk depends on vercel-edge, so we should be running tests for it when that package changes. - remove browser from deno The deno sdk no longer relies on the browser sdk, this updates the paths accordingly - add bun section We can be smarter about CI time and only run bun tests when relevant package paths change. --- .github/workflows/build.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 91e40645e0fc..cbf2fe9030da 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -105,7 +105,8 @@ jobs: - *shared - 'packages/browser/**' - 'packages/browser-utils/**' - - 'packages/replay/**' + - 'packages/replay-internal/**' + - 'packages/replay-worker/**' - 'packages/replay-canvas/**' - 'packages/feedback/**' - 'packages/wasm/**' @@ -131,6 +132,7 @@ jobs: - *node - 'packages/nextjs/**' - 'packages/react/**' + - 'packages/vercel-edge/**' remix: - *shared - *browser @@ -147,8 +149,10 @@ jobs: - 'dev-packages/e2e-tests/test-applications/node-profiling/**' deno: - *shared - - *browser - 'packages/deno/**' + bun: + - *shared + - 'packages/bun/**' any_code: - '!**/*.md' @@ -166,6 +170,7 @@ jobs: changed_profiling_node: ${{ steps.changed.outputs.profiling_node }} changed_profiling_node_bindings: ${{ steps.changed.outputs.profiling_node_bindings }} changed_deno: ${{ steps.changed.outputs.deno }} + changed_bun: ${{ steps.changed.outputs.bun }} changed_browser: ${{ steps.changed.outputs.browser }} changed_browser_integration: ${{ steps.changed.outputs.browser_integration }} changed_any_code: ${{ steps.changed.outputs.any_code }} @@ -451,6 +456,7 @@ jobs: job_bun_unit_tests: name: Bun Unit Tests needs: [job_get_metadata, job_build] + if: needs.job_get_metadata.outputs.changed_bun == 'true' || github.event_name != 'pull_request' timeout-minutes: 10 runs-on: ubuntu-20.04 strategy: From 80d76c9a475684d88abd0d04b48f0987ef52f960 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Wed, 17 Jul 2024 13:30:34 -0400 Subject: [PATCH 11/21] test(vercel-edge): Switch to using vitest (#12958) Before: `Time: 2.621 s` After: `Duration 638ms (transform 343ms, setup 0ms, collect 1.61s, tests 22ms, environment 0ms, prepare 278ms)` ref https://github.com/getsentry/sentry-javascript/issues/11084 Also removes `edge-runtime/jest-environment` which we weren't using anyway, removes a lot of unnecessary stuff in our `yarn.lock` (-151 lines!) --- packages/vercel-edge/jest.config.js | 7 - packages/vercel-edge/package.json | 7 +- packages/vercel-edge/test/async.test.ts | 233 +++++++++--------- packages/vercel-edge/test/sdk.test.ts | 4 +- .../vercel-edge/test/transports/index.test.ts | 22 +- .../vercel-edge/test/wintercg-fetch.test.ts | 12 +- packages/vercel-edge/tsconfig.test.json | 4 +- packages/vercel-edge/vite.config.ts | 5 + yarn.lock | 151 +----------- 9 files changed, 164 insertions(+), 281 deletions(-) delete mode 100644 packages/vercel-edge/jest.config.js create mode 100644 packages/vercel-edge/vite.config.ts diff --git a/packages/vercel-edge/jest.config.js b/packages/vercel-edge/jest.config.js deleted file mode 100644 index dfc8f746b929..000000000000 --- a/packages/vercel-edge/jest.config.js +++ /dev/null @@ -1,7 +0,0 @@ -const baseConfig = require('../../jest/jest.config.js'); - -module.exports = { - ...baseConfig, - // TODO: Fix tests to work with the Edge environment - // testEnvironment: '@edge-runtime/jest-environment', -}; diff --git a/packages/vercel-edge/package.json b/packages/vercel-edge/package.json index 34c4a5a0e828..f9dbdc7789e9 100644 --- a/packages/vercel-edge/package.json +++ b/packages/vercel-edge/package.json @@ -44,8 +44,7 @@ "@sentry/utils": "8.18.0" }, "devDependencies": { - "@edge-runtime/jest-environment": "2.2.3", - "@edge-runtime/types": "2.2.3" + "@edge-runtime/types": "3.0.1" }, "scripts": { "build": "run-p build:transpile build:types", @@ -63,8 +62,8 @@ "clean": "rimraf build coverage sentry-vercel-edge-*.tgz", "fix": "eslint . --format stylish --fix", "lint": "eslint . --format stylish", - "test": "jest", - "test:watch": "jest --watch", + "test": "vitest run", + "test:watch": "vitest --watch", "yalc:publish": "yalc publish --push --sig" }, "volta": { diff --git a/packages/vercel-edge/test/async.test.ts b/packages/vercel-edge/test/async.test.ts index 37b357ab4910..a4423e0ca434 100644 --- a/packages/vercel-edge/test/async.test.ts +++ b/packages/vercel-edge/test/async.test.ts @@ -1,6 +1,7 @@ import { Scope, getCurrentScope, getGlobalScope, getIsolationScope, withIsolationScope, withScope } from '@sentry/core'; import { GLOBAL_OBJ } from '@sentry/utils'; import { AsyncLocalStorage } from 'async_hooks'; +import { beforeEach, describe, expect, it } from 'vitest'; import { setAsyncLocalStorageAsyncContextStrategy } from '../src/async'; describe('withScope()', () => { @@ -13,67 +14,73 @@ describe('withScope()', () => { setAsyncLocalStorageAsyncContextStrategy(); }); - it('will make the passed scope the active scope within the callback', done => { - withScope(scope => { - expect(getCurrentScope()).toBe(scope); - done(); - }); - }); - - it('will pass a scope that is different from the current active isolation scope', done => { - withScope(scope => { - expect(getIsolationScope()).not.toBe(scope); - done(); - }); - }); - - it('will always make the inner most passed scope the current scope when nesting calls', done => { - withIsolationScope(_scope1 => { - withIsolationScope(scope2 => { - expect(getIsolationScope()).toBe(scope2); + it('will make the passed scope the active scope within the callback', () => + new Promise(done => { + withScope(scope => { + expect(getCurrentScope()).toBe(scope); done(); }); - }); - }); + })); - it('forks the scope when not passing any scope', done => { - const initialScope = getCurrentScope(); - initialScope.setTag('aa', 'aa'); - - withScope(scope => { - expect(getCurrentScope()).toBe(scope); - scope.setTag('bb', 'bb'); - expect(scope).not.toBe(initialScope); - expect(scope.getScopeData().tags).toEqual({ aa: 'aa', bb: 'bb' }); - done(); - }); - }); - - it('forks the scope when passing undefined', done => { - const initialScope = getCurrentScope(); - initialScope.setTag('aa', 'aa'); - - withScope(undefined, scope => { - expect(getCurrentScope()).toBe(scope); - scope.setTag('bb', 'bb'); - expect(scope).not.toBe(initialScope); - expect(scope.getScopeData().tags).toEqual({ aa: 'aa', bb: 'bb' }); - done(); - }); - }); + it('will pass a scope that is different from the current active isolation scope', () => + new Promise(done => { + withScope(scope => { + expect(getIsolationScope()).not.toBe(scope); + done(); + }); + })); + + it('will always make the inner most passed scope the current scope when nesting calls', () => + new Promise(done => { + withIsolationScope(_scope1 => { + withIsolationScope(scope2 => { + expect(getIsolationScope()).toBe(scope2); + done(); + }); + }); + })); + + it('forks the scope when not passing any scope', () => + new Promise(done => { + const initialScope = getCurrentScope(); + initialScope.setTag('aa', 'aa'); + + withScope(scope => { + expect(getCurrentScope()).toBe(scope); + scope.setTag('bb', 'bb'); + expect(scope).not.toBe(initialScope); + expect(scope.getScopeData().tags).toEqual({ aa: 'aa', bb: 'bb' }); + done(); + }); + })); + + it('forks the scope when passing undefined', () => + new Promise(done => { + const initialScope = getCurrentScope(); + initialScope.setTag('aa', 'aa'); + + withScope(undefined, scope => { + expect(getCurrentScope()).toBe(scope); + scope.setTag('bb', 'bb'); + expect(scope).not.toBe(initialScope); + expect(scope.getScopeData().tags).toEqual({ aa: 'aa', bb: 'bb' }); + done(); + }); + })); - it('sets the passed in scope as active scope', done => { - const initialScope = getCurrentScope(); - initialScope.setTag('aa', 'aa'); + it('sets the passed in scope as active scope', () => + new Promise(done => { + const initialScope = getCurrentScope(); + initialScope.setTag('aa', 'aa'); - const customScope = new Scope(); + const customScope = new Scope(); - withScope(customScope, scope => { - expect(getCurrentScope()).toBe(customScope); - expect(scope).toBe(customScope); - done(); - }); - }); + withScope(customScope, scope => { + expect(getCurrentScope()).toBe(customScope); + expect(scope).toBe(customScope); + done(); + }); + })); }); describe('withIsolationScope()', () => { @@ -86,65 +93,71 @@ describe('withIsolationScope()', () => { setAsyncLocalStorageAsyncContextStrategy(); }); - it('will make the passed isolation scope the active isolation scope within the callback', done => { - withIsolationScope(scope => { - expect(getIsolationScope()).toBe(scope); - done(); - }); - }); - - it('will pass an isolation scope that is different from the current active scope', done => { - withIsolationScope(scope => { - expect(getCurrentScope()).not.toBe(scope); - done(); - }); - }); - - it('will always make the inner most passed scope the current scope when nesting calls', done => { - withIsolationScope(_scope1 => { - withIsolationScope(scope2 => { - expect(getIsolationScope()).toBe(scope2); + it('will make the passed isolation scope the active isolation scope within the callback', () => + new Promise(done => { + withIsolationScope(scope => { + expect(getIsolationScope()).toBe(scope); done(); }); - }); - }); + })); - it('forks the isolation scope when not passing any isolation scope', done => { - const initialScope = getIsolationScope(); - initialScope.setTag('aa', 'aa'); - - withIsolationScope(scope => { - expect(getIsolationScope()).toBe(scope); - scope.setTag('bb', 'bb'); - expect(scope).not.toBe(initialScope); - expect(scope.getScopeData().tags).toEqual({ aa: 'aa', bb: 'bb' }); - done(); - }); - }); - - it('forks the isolation scope when passing undefined', done => { - const initialScope = getIsolationScope(); - initialScope.setTag('aa', 'aa'); - - withIsolationScope(undefined, scope => { - expect(getIsolationScope()).toBe(scope); - scope.setTag('bb', 'bb'); - expect(scope).not.toBe(initialScope); - expect(scope.getScopeData().tags).toEqual({ aa: 'aa', bb: 'bb' }); - done(); - }); - }); + it('will pass an isolation scope that is different from the current active scope', () => + new Promise(done => { + withIsolationScope(scope => { + expect(getCurrentScope()).not.toBe(scope); + done(); + }); + })); + + it('will always make the inner most passed scope the current scope when nesting calls', () => + new Promise(done => { + withIsolationScope(_scope1 => { + withIsolationScope(scope2 => { + expect(getIsolationScope()).toBe(scope2); + done(); + }); + }); + })); + + it('forks the isolation scope when not passing any isolation scope', () => + new Promise(done => { + const initialScope = getIsolationScope(); + initialScope.setTag('aa', 'aa'); + + withIsolationScope(scope => { + expect(getIsolationScope()).toBe(scope); + scope.setTag('bb', 'bb'); + expect(scope).not.toBe(initialScope); + expect(scope.getScopeData().tags).toEqual({ aa: 'aa', bb: 'bb' }); + done(); + }); + })); + + it('forks the isolation scope when passing undefined', () => + new Promise(done => { + const initialScope = getIsolationScope(); + initialScope.setTag('aa', 'aa'); + + withIsolationScope(undefined, scope => { + expect(getIsolationScope()).toBe(scope); + scope.setTag('bb', 'bb'); + expect(scope).not.toBe(initialScope); + expect(scope.getScopeData().tags).toEqual({ aa: 'aa', bb: 'bb' }); + done(); + }); + })); - it('sets the passed in isolation scope as active isolation scope', done => { - const initialScope = getIsolationScope(); - initialScope.setTag('aa', 'aa'); + it('sets the passed in isolation scope as active isolation scope', () => + new Promise(done => { + const initialScope = getIsolationScope(); + initialScope.setTag('aa', 'aa'); - const customScope = new Scope(); + const customScope = new Scope(); - withIsolationScope(customScope, scope => { - expect(getIsolationScope()).toBe(customScope); - expect(scope).toBe(customScope); - done(); - }); - }); + withIsolationScope(customScope, scope => { + expect(getIsolationScope()).toBe(customScope); + expect(scope).toBe(customScope); + done(); + }); + })); }); diff --git a/packages/vercel-edge/test/sdk.test.ts b/packages/vercel-edge/test/sdk.test.ts index f693e2777d99..b1367716c73a 100644 --- a/packages/vercel-edge/test/sdk.test.ts +++ b/packages/vercel-edge/test/sdk.test.ts @@ -1,9 +1,11 @@ +import { describe, expect, it, vi } from 'vitest'; + import * as SentryCore from '@sentry/core'; import { init } from '../src/sdk'; describe('init', () => { it('initializes and returns client', () => { - const initSpy = jest.spyOn(SentryCore, 'initAndBind'); + const initSpy = vi.spyOn(SentryCore, 'initAndBind'); expect(init({})).not.toBeUndefined(); expect(initSpy).toHaveBeenCalledTimes(1); diff --git a/packages/vercel-edge/test/transports/index.test.ts b/packages/vercel-edge/test/transports/index.test.ts index 252522171780..c2ead21998be 100644 --- a/packages/vercel-edge/test/transports/index.test.ts +++ b/packages/vercel-edge/test/transports/index.test.ts @@ -1,3 +1,5 @@ +import { afterAll, describe, expect, it, vi } from 'vitest'; + import type { EventEnvelope, EventItem } from '@sentry/types'; import { createEnvelope, serializeEnvelope } from '@sentry/utils'; @@ -23,7 +25,7 @@ class Headers { } } -const mockFetch = jest.fn(); +const mockFetch = vi.fn(); const oldFetch = global.fetch; global.fetch = mockFetch; @@ -57,7 +59,7 @@ describe('Edge Transport', () => { it('sets rate limit headers', async () => { const headers = { - get: jest.fn(), + get: vi.fn(), }; mockFetch.mockImplementationOnce(() => @@ -110,8 +112,8 @@ describe('IsolatedPromiseBuffer', () => { it('should not call tasks until drained', async () => { const ipb = new IsolatedPromiseBuffer(); - const task1 = jest.fn(() => Promise.resolve({})); - const task2 = jest.fn(() => Promise.resolve({})); + const task1 = vi.fn(() => Promise.resolve({})); + const task2 = vi.fn(() => Promise.resolve({})); await ipb.add(task1); await ipb.add(task2); @@ -128,10 +130,10 @@ describe('IsolatedPromiseBuffer', () => { it('should not allow adding more items than the specified limit', async () => { const ipb = new IsolatedPromiseBuffer(3); - const task1 = jest.fn(() => Promise.resolve({})); - const task2 = jest.fn(() => Promise.resolve({})); - const task3 = jest.fn(() => Promise.resolve({})); - const task4 = jest.fn(() => Promise.resolve({})); + const task1 = vi.fn(() => Promise.resolve({})); + const task2 = vi.fn(() => Promise.resolve({})); + const task3 = vi.fn(() => Promise.resolve({})); + const task4 = vi.fn(() => Promise.resolve({})); await ipb.add(task1); await ipb.add(task2); @@ -143,8 +145,8 @@ describe('IsolatedPromiseBuffer', () => { it('should not throw when one of the tasks throws when drained', async () => { const ipb = new IsolatedPromiseBuffer(); - const task1 = jest.fn(() => Promise.resolve({})); - const task2 = jest.fn(() => Promise.reject(new Error())); + const task1 = vi.fn(() => Promise.resolve({})); + const task2 = vi.fn(() => Promise.reject(new Error())); await ipb.add(task1); await ipb.add(task2); diff --git a/packages/vercel-edge/test/wintercg-fetch.test.ts b/packages/vercel-edge/test/wintercg-fetch.test.ts index ae830ed9c1e8..34328734cd83 100644 --- a/packages/vercel-edge/test/wintercg-fetch.test.ts +++ b/packages/vercel-edge/test/wintercg-fetch.test.ts @@ -1,3 +1,5 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + import * as sentryCore from '@sentry/core'; import type { HandlerDataFetch, Integration } from '@sentry/types'; import * as sentryUtils from '@sentry/utils'; @@ -12,15 +14,15 @@ class FakeClient extends VercelEdgeClient { } } -const addFetchInstrumentationHandlerSpy = jest.spyOn(sentryUtils, 'addFetchInstrumentationHandler'); -const instrumentFetchRequestSpy = jest.spyOn(sentryCore, 'instrumentFetchRequest'); -const addBreadcrumbSpy = jest.spyOn(sentryCore, 'addBreadcrumb'); +const addFetchInstrumentationHandlerSpy = vi.spyOn(sentryUtils, 'addFetchInstrumentationHandler'); +const instrumentFetchRequestSpy = vi.spyOn(sentryCore, 'instrumentFetchRequest'); +const addBreadcrumbSpy = vi.spyOn(sentryCore, 'addBreadcrumb'); describe('WinterCGFetch instrumentation', () => { let client: FakeClient; beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); client = new FakeClient({ dsn: 'https://public@dsn.ingest.sentry.io/1337', @@ -35,7 +37,7 @@ describe('WinterCGFetch instrumentation', () => { stackParser: createStackParser(), }); - jest.spyOn(sentryCore, 'getClient').mockImplementation(() => client); + vi.spyOn(sentryCore, 'getClient').mockImplementation(() => client); }); it('should call `instrumentFetchRequest` for outgoing fetch requests', () => { diff --git a/packages/vercel-edge/tsconfig.test.json b/packages/vercel-edge/tsconfig.test.json index 87f6afa06b86..ca7dbeb3be94 100644 --- a/packages/vercel-edge/tsconfig.test.json +++ b/packages/vercel-edge/tsconfig.test.json @@ -1,11 +1,11 @@ { "extends": "./tsconfig.json", - "include": ["test/**/*"], + "include": ["test/**/*", "vite.config.ts"], "compilerOptions": { // should include all types from `./tsconfig.json` plus types for all test frameworks used - "types": ["node", "jest"] + "types": ["node"] // other package-specific, test-specific options } diff --git a/packages/vercel-edge/vite.config.ts b/packages/vercel-edge/vite.config.ts new file mode 100644 index 000000000000..0582a58f479a --- /dev/null +++ b/packages/vercel-edge/vite.config.ts @@ -0,0 +1,5 @@ +import baseConfig from '../../vite/vite.config'; + +export default { + ...baseConfig, +}; diff --git a/yarn.lock b/yarn.lock index e703f7baaec2..e1afaa9a9f1f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4008,40 +4008,17 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== -"@edge-runtime/jest-environment@2.2.3": - version "2.2.3" - resolved "https://registry.yarnpkg.com/@edge-runtime/jest-environment/-/jest-environment-2.2.3.tgz#2fef094d769f45b5018b33bdc58e664b35bbc312" - integrity sha512-5DEv8nzuMFGoVbNYbOz7/mileYSbq1/oIvisyTeSfyjId7Pc5Qh2t3BH7ixLa62aVz7oCQlALM4cYGbZQZw1YQ== - dependencies: - "@edge-runtime/vm" "3.0.3" - "@jest/environment" "29.5.0" - "@jest/fake-timers" "29.5.0" - jest-mock "29.5.0" - jest-util "29.5.0" - -"@edge-runtime/primitives@3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@edge-runtime/primitives/-/primitives-3.0.3.tgz#adc6a4bd34c44faf81c954cf8e5816c7d408a1ea" - integrity sha512-YnfMWMRQABAH8IsnFMJWMW+SyB4ZeYBPnR7V0aqdnew7Pq60cbH5DyFjS/FhiLwvHQk9wBREmXD7PP0HooEQ1A== - -"@edge-runtime/primitives@4.0.1": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@edge-runtime/primitives/-/primitives-4.0.1.tgz#12efffac1caa8a29ae8f86a3f87f20cc0ae07131" - integrity sha512-hxWUzx1SeyOed/Ea9Z6y6tyFKSj8gQWIdLilybTR2ene1IthLZE01A1SLGoch1szUdhFlUwpWDaYBYQw00lj2g== - -"@edge-runtime/types@2.2.3": - version "2.2.3" - resolved "https://registry.yarnpkg.com/@edge-runtime/types/-/types-2.2.3.tgz#cb57b7215bcf406324ec591346b7b51c75a54bdf" - integrity sha512-zL0ENQWwdocECEQXVopGTfnqI0tJ8wzDOCoQymoc8MLRz+Zw2V1W0ex9vczniTUzB+H/P7ubMgx3GFzLp3NPBg== - dependencies: - "@edge-runtime/primitives" "4.0.1" +"@edge-runtime/primitives@5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@edge-runtime/primitives/-/primitives-5.0.1.tgz#29e2de862d8e02d1e85fd8aa1c73afe0317e6740" + integrity sha512-qjqqCa9v3IE7Fo9OnmkIWg9l0WUu3uOfUYomuOVxaaHqlIvNI75E5IB0XNNDypz249ObRSmjRj8jLjkBUmFYYw== -"@edge-runtime/vm@3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@edge-runtime/vm/-/vm-3.0.3.tgz#92f1930d1eb8d0ccf6a3c165561cc22b2d9ddff8" - integrity sha512-SPfI1JeIRNs/4EEE2Oc0X6gG3RqjD1TnKu2lwmwFXq0435xgZGKhc3UiKkYAdoMn2dNFD73nlabMKHBRoMRpxg== +"@edge-runtime/types@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@edge-runtime/types/-/types-3.0.1.tgz#907bd98ec551deba6fd26b72189fde651779191b" + integrity sha512-yZSWlGMQXXtpE4m1WYRpjA8V9+uU3uKHJzx9lngSYDZYYABuYxb2bICBwE1NQD0WS/u/PreP/83GcndezMFmnQ== dependencies: - "@edge-runtime/primitives" "3.0.3" + "@edge-runtime/primitives" "5.0.1" "@ember-data/rfc395-data@^0.0.4": version "0.0.4" @@ -5664,16 +5641,6 @@ slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.5.0.tgz#9152d56317c1fdb1af389c46640ba74ef0bb4c65" - integrity sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ== - dependencies: - "@jest/fake-timers" "^29.5.0" - "@jest/types" "^29.5.0" - "@types/node" "*" - jest-mock "^29.5.0" - "@jest/environment@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74" @@ -5684,18 +5651,6 @@ "@types/node" "*" jest-mock "^27.5.1" -"@jest/fake-timers@29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.5.0.tgz#d4d09ec3286b3d90c60bdcd66ed28d35f1b4dc2c" - integrity sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg== - dependencies: - "@jest/types" "^29.5.0" - "@sinonjs/fake-timers" "^10.0.2" - "@types/node" "*" - jest-message-util "^29.5.0" - jest-mock "^29.5.0" - jest-util "^29.5.0" - "@jest/fake-timers@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" @@ -5708,18 +5663,6 @@ jest-mock "^27.5.1" jest-util "^27.5.1" -"@jest/fake-timers@^29.5.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" - integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== - dependencies: - "@jest/types" "^29.6.3" - "@sinonjs/fake-timers" "^10.0.2" - "@types/node" "*" - jest-message-util "^29.7.0" - jest-mock "^29.7.0" - jest-util "^29.7.0" - "@jest/globals@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b" @@ -5835,18 +5778,6 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@jest/types@^29.5.0", "@jest/types@^29.6.3": - version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" - integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== - dependencies: - "@jest/schemas" "^29.6.3" - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^17.0.8" - chalk "^4.0.0" - "@josephg/resolvable@^1.0.0": version "1.0.1" resolved "https://registry.yarnpkg.com/@josephg/resolvable/-/resolvable-1.0.1.tgz#69bc4db754d79e1a2f17a650d3466e038d94a5eb" @@ -10123,13 +10054,6 @@ dependencies: "@types/yargs-parser" "*" -"@types/yargs@^17.0.8": - version "17.0.24" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.24.tgz#b3ef8d50ad4aa6aecf6ddc97c580a00f5aa11902" - integrity sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw== - dependencies: - "@types/yargs-parser" "*" - "@types/yauzl@^2.9.1": version "2.10.0" resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.0.tgz#b3248295276cf8c6f153ebe6a9aba0c988cb2599" @@ -22452,30 +22376,6 @@ jest-message-util@^27.5.1: slash "^3.0.0" stack-utils "^2.0.3" -jest-message-util@^29.5.0, jest-message-util@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" - integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.6.3" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - micromatch "^4.0.4" - pretty-format "^29.7.0" - slash "^3.0.0" - stack-utils "^2.0.3" - -jest-mock@29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.5.0.tgz#26e2172bcc71d8b0195081ff1f146ac7e1518aed" - integrity sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw== - dependencies: - "@jest/types" "^29.5.0" - "@types/node" "*" - jest-util "^29.5.0" - jest-mock@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" @@ -22484,15 +22384,6 @@ jest-mock@^27.5.1: "@jest/types" "^27.5.1" "@types/node" "*" -jest-mock@^29.5.0, jest-mock@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" - integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== - dependencies: - "@jest/types" "^29.6.3" - "@types/node" "*" - jest-util "^29.7.0" - jest-pnp-resolver@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" @@ -22619,18 +22510,6 @@ jest-snapshot@^27.5.1: pretty-format "^27.5.1" semver "^7.3.2" -jest-util@29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f" - integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ== - dependencies: - "@jest/types" "^29.5.0" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - jest-util@^27.0.0, jest-util@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9" @@ -22643,18 +22522,6 @@ jest-util@^27.0.0, jest-util@^27.5.1: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-util@^29.5.0, jest-util@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" - integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== - dependencies: - "@jest/types" "^29.6.3" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - jest-validate@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067" From b4272be98af0ede8ed9cbdaae2a120e0c458e492 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Wed, 17 Jul 2024 15:29:12 -0400 Subject: [PATCH 12/21] test(replay): Switch to using vitest for replay worker (#12961) Before: `Time: 0.303 s` After: `Duration 271ms (transform 28ms, setup 0ms, collect 26ms, tests 3ms, environment 0ms, prepare 68ms)` --- packages/replay-worker/jest.config.js | 1 - packages/replay-worker/package.json | 4 ++-- packages/replay-worker/test/unit/Compressor.test.ts | 2 ++ packages/replay-worker/tsconfig.test.json | 4 ++-- packages/replay-worker/vite.config.ts | 5 +++++ 5 files changed, 11 insertions(+), 5 deletions(-) delete mode 100644 packages/replay-worker/jest.config.js create mode 100644 packages/replay-worker/vite.config.ts diff --git a/packages/replay-worker/jest.config.js b/packages/replay-worker/jest.config.js deleted file mode 100644 index 24f49ab59a4c..000000000000 --- a/packages/replay-worker/jest.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../jest/jest.config.js'); diff --git a/packages/replay-worker/package.json b/packages/replay-worker/package.json index 07e967a3bc33..597e44329f33 100644 --- a/packages/replay-worker/package.json +++ b/packages/replay-worker/package.json @@ -32,8 +32,8 @@ "clean": "rimraf build", "fix": "eslint . --format stylish --fix", "lint": "eslint . --format stylish", - "test": "jest", - "test:watch": "jest --watch" + "test": "vitest run", + "test:watch": "vitest --watch" }, "repository": { "type": "git", diff --git a/packages/replay-worker/test/unit/Compressor.test.ts b/packages/replay-worker/test/unit/Compressor.test.ts index 9ac4a5bb5a6c..dcce14edce70 100644 --- a/packages/replay-worker/test/unit/Compressor.test.ts +++ b/packages/replay-worker/test/unit/Compressor.test.ts @@ -1,3 +1,5 @@ +import { describe, expect, it } from 'vitest'; + import { decompressSync, strFromU8 } from 'fflate'; import { Compressor } from '../../src/Compressor'; diff --git a/packages/replay-worker/tsconfig.test.json b/packages/replay-worker/tsconfig.test.json index 6a49bbfc2a41..2910a21e50fc 100644 --- a/packages/replay-worker/tsconfig.test.json +++ b/packages/replay-worker/tsconfig.test.json @@ -1,7 +1,7 @@ { "extends": "./tsconfig.json", - "include": ["test/**/*.ts"], + "include": ["test/**/*.ts", "vite.config.ts"], "compilerOptions": { - "types": ["node", "jest"] + "types": ["node"] } } diff --git a/packages/replay-worker/vite.config.ts b/packages/replay-worker/vite.config.ts new file mode 100644 index 000000000000..0582a58f479a --- /dev/null +++ b/packages/replay-worker/vite.config.ts @@ -0,0 +1,5 @@ +import baseConfig from '../../vite/vite.config'; + +export default { + ...baseConfig, +}; From d70182e2a5250a4a1f7eef99dab82825d58d8bd5 Mon Sep 17 00:00:00 2001 From: Abhijeet Prasad Date: Wed, 17 Jul 2024 16:13:45 -0400 Subject: [PATCH 13/21] test(replay-canvas): Switch to using vitest (#12964) Before: `Time: 2.026 s` After: `Duration 960ms (transform 291ms, setup 0ms, collect 446ms, tests 5ms, environment 283ms, prepare 65ms)` Also: - removes unused `@babel/core` dev dep - removes unused eslintrc logic --- packages/replay-canvas/.eslintrc.js | 14 -------------- packages/replay-canvas/jest.config.js | 6 ------ packages/replay-canvas/package.json | 5 ++--- packages/replay-canvas/test/canvas.test.ts | 10 ++++++++-- packages/replay-canvas/tsconfig.test.json | 4 ++-- packages/replay-canvas/vite.config.ts | 5 +++++ 6 files changed, 17 insertions(+), 27 deletions(-) delete mode 100644 packages/replay-canvas/jest.config.js create mode 100644 packages/replay-canvas/vite.config.ts diff --git a/packages/replay-canvas/.eslintrc.js b/packages/replay-canvas/.eslintrc.js index 216184b75e47..0c83f7d0ff9d 100644 --- a/packages/replay-canvas/.eslintrc.js +++ b/packages/replay-canvas/.eslintrc.js @@ -5,18 +5,4 @@ module.exports = { extends: ['../../.eslintrc.js'], - overrides: [ - { - files: ['src/**/*.ts'], - }, - { - files: ['jest.setup.ts', 'jest.config.ts'], - parserOptions: { - project: ['tsconfig.test.json'], - }, - rules: { - 'no-console': 'off', - }, - }, - ], }; diff --git a/packages/replay-canvas/jest.config.js b/packages/replay-canvas/jest.config.js deleted file mode 100644 index cd02790794a7..000000000000 --- a/packages/replay-canvas/jest.config.js +++ /dev/null @@ -1,6 +0,0 @@ -const baseConfig = require('../../jest/jest.config.js'); - -module.exports = { - ...baseConfig, - testEnvironment: 'jsdom', -}; diff --git a/packages/replay-canvas/package.json b/packages/replay-canvas/package.json index cc0c3d589847..014342ac2482 100644 --- a/packages/replay-canvas/package.json +++ b/packages/replay-canvas/package.json @@ -47,8 +47,8 @@ "clean": "rimraf build sentry-replay-*.tgz", "fix": "eslint . --format stylish --fix", "lint": "eslint . --format stylish", - "test": "jest", - "test:watch": "jest --watch", + "test": "vitest run", + "test:watch": "vitest --watch", "yalc:publish": "yalc publish --push --sig" }, "publishConfig": { @@ -65,7 +65,6 @@ }, "homepage": "https://docs.sentry.io/platforms/javascript/session-replay/", "devDependencies": { - "@babel/core": "^7.17.5", "@sentry-internal/rrweb": "2.25.0" }, "dependencies": { diff --git a/packages/replay-canvas/test/canvas.test.ts b/packages/replay-canvas/test/canvas.test.ts index a7de3b6a28a3..6f75321a5ab5 100644 --- a/packages/replay-canvas/test/canvas.test.ts +++ b/packages/replay-canvas/test/canvas.test.ts @@ -1,10 +1,16 @@ +/** + * @vitest-environment jsdom + */ + +import { beforeEach, expect, it, vi } from 'vitest'; + import { CanvasManager } from '@sentry-internal/rrweb'; import { _replayCanvasIntegration, replayCanvasIntegration } from '../src/canvas'; -jest.mock('@sentry-internal/rrweb'); +vi.mock('@sentry-internal/rrweb'); beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('initializes with default options', () => { diff --git a/packages/replay-canvas/tsconfig.test.json b/packages/replay-canvas/tsconfig.test.json index 3995d3e18e59..f4e8a1624f08 100644 --- a/packages/replay-canvas/tsconfig.test.json +++ b/packages/replay-canvas/tsconfig.test.json @@ -1,11 +1,11 @@ { "extends": "./tsconfig.json", - "include": ["test/**/*.ts", "jest.config.ts", "jest.setup.ts"], + "include": ["test/**/*.ts", "vite.config.ts"], "compilerOptions": { "lib": ["DOM", "ES2018"], - "types": ["node", "jest"], + "types": ["node"], "esModuleInterop": true, "allowJs": true, "noImplicitAny": true, diff --git a/packages/replay-canvas/vite.config.ts b/packages/replay-canvas/vite.config.ts new file mode 100644 index 000000000000..0582a58f479a --- /dev/null +++ b/packages/replay-canvas/vite.config.ts @@ -0,0 +1,5 @@ +import baseConfig from '../../vite/vite.config'; + +export default { + ...baseConfig, +}; From ce1e7c7ff158a80c22ae4dd484a0cef98ea92ed7 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Thu, 18 Jul 2024 10:16:50 +0200 Subject: [PATCH 14/21] docs(nestjs): Update README to include SentryCron (#12972) SentryCron was missing from the nest readme --- packages/nestjs/README.md | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/packages/nestjs/README.md b/packages/nestjs/README.md index b9ac0c9371c2..e336a856c03e 100644 --- a/packages/nestjs/README.md +++ b/packages/nestjs/README.md @@ -24,7 +24,7 @@ yarn add @sentry/nestjs ## Usage -```js +```typescript // CJS Syntax const Sentry = require('@sentry/nestjs'); // ESM Syntax @@ -38,12 +38,12 @@ Sentry.init({ Note that it is necessary to initialize Sentry **before you import any package that may be instrumented by us**. -## Span Decorator +## SentryTraced -Use the @SentryTraced() decorator to gain additional performance insights for any function within your NestJS +Use the `@SentryTraced()` decorator to gain additional performance insights for any function within your NestJS applications. -```js +```typescript import { Injectable } from '@nestjs/common'; import { SentryTraced } from '@sentry/nestjs'; @@ -56,6 +56,35 @@ export class ExampleService { } ``` +## SentryCron + +Use the `@SentryCron()` decorator to augment the native NestJS `@Cron` decorator to send check-ins to Sentry before and +after each cron job run. + +```typescript +import { Cron } from '@nestjs/schedule'; +import { SentryCron, MonitorConfig } from '@sentry/nestjs'; +import type { MonitorConfig } from '@sentry/types'; + +const monitorConfig: MonitorConfig = { + schedule: { + type: 'crontab', + value: '* * * * *', + }, + checkinMargin: 2, // In minutes. Optional. + maxRuntime: 10, // In minutes. Optional. + timezone: 'America/Los_Angeles', // Optional. +}; + +export class MyCronService { + @Cron('* * * * *') + @SentryCron('my-monitor-slug', monitorConfig) + handleCron() { + // Your cron job logic here + } +} +``` + ## Links - [Official SDK Docs](https://docs.sentry.io/platforms/javascript/guides/nestjs/) From 935bb617c7220191c545a585165c8f3a3e8a76ae Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Thu, 18 Jul 2024 11:21:57 +0200 Subject: [PATCH 15/21] feat(core): Align Span interface with OTEL (#12898) --- .../src/tracing/sentryNonRecordingSpan.ts | 33 +++++++++++++++ packages/core/src/tracing/sentrySpan.ts | 40 ++++++++++++++++++- packages/opentelemetry/src/trace.ts | 4 +- packages/types/src/span.ts | 19 ++++++++- 4 files changed, 89 insertions(+), 7 deletions(-) diff --git a/packages/core/src/tracing/sentryNonRecordingSpan.ts b/packages/core/src/tracing/sentryNonRecordingSpan.ts index 1debb45aa282..867b5684d1da 100644 --- a/packages/core/src/tracing/sentryNonRecordingSpan.ts +++ b/packages/core/src/tracing/sentryNonRecordingSpan.ts @@ -68,4 +68,37 @@ export class SentryNonRecordingSpan implements Span { ): this { return this; } + + /** + * This should generally not be used, + * but we need it for being comliant with the OTEL Span interface. + * + * @hidden + * @internal + */ + public addLink(_link: unknown): this { + return this; + } + + /** + * This should generally not be used, + * but we need it for being comliant with the OTEL Span interface. + * + * @hidden + * @internal + */ + public addLinks(_links: unknown[]): this { + return this; + } + + /** + * This should generally not be used, + * but we need it for being comliant with the OTEL Span interface. + * + * @hidden + * @internal + */ + public recordException(_exception: unknown, _time?: number | undefined): void { + // noop + } } diff --git a/packages/core/src/tracing/sentrySpan.ts b/packages/core/src/tracing/sentrySpan.ts index d7c800e338a7..7e1083142314 100644 --- a/packages/core/src/tracing/sentrySpan.ts +++ b/packages/core/src/tracing/sentrySpan.ts @@ -107,6 +107,39 @@ export class SentrySpan implements Span { } } + /** + * This should generally not be used, + * but it is needed for being compliant with the OTEL Span interface. + * + * @hidden + * @internal + */ + public addLink(_link: unknown): this { + return this; + } + + /** + * This should generally not be used, + * but it is needed for being compliant with the OTEL Span interface. + * + * @hidden + * @internal + */ + public addLinks(_links: unknown[]): this { + return this; + } + + /** + * This should generally not be used, + * but it is needed for being compliant with the OTEL Span interface. + * + * @hidden + * @internal + */ + public recordException(_exception: unknown, _time?: number | undefined): void { + // noop + } + /** @inheritdoc */ public spanContext(): SpanContextData { const { _spanId: spanId, _traceId: traceId, _sampled: sampled } = this; @@ -118,18 +151,21 @@ export class SentrySpan implements Span { } /** @inheritdoc */ - public setAttribute(key: string, value: SpanAttributeValue | undefined): void { + public setAttribute(key: string, value: SpanAttributeValue | undefined): this { if (value === undefined) { // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete this._attributes[key]; } else { this._attributes[key] = value; } + + return this; } /** @inheritdoc */ - public setAttributes(attributes: SpanAttributes): void { + public setAttributes(attributes: SpanAttributes): this { Object.keys(attributes).forEach(key => this.setAttribute(key, attributes[key])); + return this; } /** diff --git a/packages/opentelemetry/src/trace.ts b/packages/opentelemetry/src/trace.ts index 6f9fe5dad6d1..6a2acd874e67 100644 --- a/packages/opentelemetry/src/trace.ts +++ b/packages/opentelemetry/src/trace.ts @@ -289,9 +289,7 @@ export function continueTrace(options: Parameters[0 function getActiveSpanWrapper(parentSpan: Span | SentrySpan | undefined | null): (callback: () => T) => T { return parentSpan !== undefined ? (callback: () => T) => { - // We cast this, because the OTEL Span has a few more methods than our Span interface - // TODO: Add these missing methods to the Span interface - return withActiveSpan(parentSpan as Span, callback); + return withActiveSpan(parentSpan, callback); } : (callback: () => T) => callback(); } diff --git a/packages/types/src/span.ts b/packages/types/src/span.ts index cc5cb45213b9..a2ee74fd7cfa 100644 --- a/packages/types/src/span.ts +++ b/packages/types/src/span.ts @@ -219,13 +219,13 @@ export interface Span { * Set a single attribute on the span. * Set it to `undefined` to remove the attribute. */ - setAttribute(key: string, value: SpanAttributeValue | undefined): void; + setAttribute(key: string, value: SpanAttributeValue | undefined): this; /** * Set multiple attributes on the span. * Any attribute set to `undefined` will be removed. */ - setAttributes(attributes: SpanAttributes): void; + setAttributes(attributes: SpanAttributes): this; /** * Sets the status attribute on the current span. @@ -247,4 +247,19 @@ export interface Span { * Adds an event to the Span. */ addEvent(name: string, attributesOrStartTime?: SpanAttributes | SpanTimeInput, startTime?: SpanTimeInput): this; + + /** + * NOT USED IN SENTRY, only added for compliance with OTEL Span interface + */ + addLink(link: unknown): this; + + /** + * NOT USED IN SENTRY, only added for compliance with OTEL Span interface + */ + addLinks(links: unknown): this; + + /** + * NOT USED IN SENTRY, only added for compliance with OTEL Span interface + */ + recordException(exception: unknown, time?: number): void; } From d629991be0894d2ee9440ac194d5e3478550d4f4 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Thu, 18 Jul 2024 12:04:16 +0200 Subject: [PATCH 16/21] tests(e2e): Refactor nestjs e2e applications into multiple smaller test applications (#12948) Refactor of the existing nestjs test applications. Before we had one sample application testing everything nest-related. This PR splits them up into three applications to make it more readable and easier to understand what is being tested. It also allows for iterating a bit quicker in local development. No new functionality was added. Will add more tests in a follow-up. The three new services are: - nestjs-basic: Simple nestjs application with no submodules and tests for basic functionality of the SDK like error monitoring and span instrumentation. - nestjs-with-submodules: NestJS application that is bit more complex including a submodule (and potentially multiple in the future) to have a more realistic setup for more advanced testing. - nestjs-distributed-tracing: Includes tests for trace propagation with multiple services. --- .github/workflows/build.yml | 4 +- .../{nestjs => nestjs-basic}/.gitignore | 0 .../{nestjs => nestjs-basic}/.npmrc | 0 .../{nestjs => nestjs-basic}/nest-cli.json | 0 .../{nestjs => nestjs-basic}/package.json | 2 +- .../playwright.config.mjs | 0 .../nestjs-basic/src/app.controller.ts | 37 +++++++ .../nestjs-basic/src/app.module.ts | 11 ++ .../src/app.service.ts | 78 +------------- .../nestjs-basic/src/instrument.ts | 8 ++ .../nestjs-basic/src/main.ts | 20 ++++ .../start-event-proxy.mjs | 0 .../tests/cron-decorator.test.ts | 0 .../tests/errors.test.ts | 29 ----- .../tests/span-decorator.test.ts | 0 .../tests/transactions.test.ts | 0 .../tsconfig.build.json | 0 .../{nestjs => nestjs-basic}/tsconfig.json | 0 .../nestjs-distributed-tracing/.gitignore | 56 ++++++++++ .../nestjs-distributed-tracing/.npmrc | 2 + .../nestjs-distributed-tracing/nest-cli.json | 8 ++ .../nestjs-distributed-tracing/package.json | 47 ++++++++ .../playwright.config.mjs | 7 ++ .../src/instrument.ts | 0 .../nestjs-distributed-tracing/src/main.ts | 25 +++++ .../src/trace-initiator.controller.ts | 42 ++++++++ .../src/trace-initiator.module.ts | 10 ++ .../src/trace-initiator.service.ts | 47 ++++++++ .../src/trace-receiver.controller.ts | 17 +++ .../src/trace-receiver.module.ts | 10 ++ .../src/trace-receiver.service.ts | 18 ++++ .../src/utils.ts | 0 .../start-event-proxy.mjs | 6 ++ .../tests/propagation.test.ts | 0 .../tsconfig.build.json | 4 + .../nestjs-distributed-tracing/tsconfig.json | 21 ++++ .../nestjs-with-submodules/.gitignore | 56 ++++++++++ .../nestjs-with-submodules/.npmrc | 2 + .../nestjs-with-submodules/nest-cli.json | 8 ++ .../nestjs-with-submodules/package.json | 47 ++++++++ .../playwright.config.mjs | 7 ++ .../src/app.controller.ts | 17 +++ .../nestjs-with-submodules/src/app.module.ts | 11 ++ .../nestjs-with-submodules/src/app.service.ts | 14 +++ .../src/example-module/example.controller.ts | 12 +++ .../src/example-module/example.exception.ts | 5 + .../src/example-module/example.filter.ts} | 8 +- .../src/example-module/example.module.ts | 16 +++ .../nestjs-with-submodules/src/instrument.ts | 8 ++ .../nestjs-with-submodules/src/main.ts | 20 ++++ .../start-event-proxy.mjs | 6 ++ .../tests/errors.test.ts | 31 ++++++ .../tsconfig.build.json | 4 + .../nestjs-with-submodules/tsconfig.json | 21 ++++ .../nestjs/src/app.controller.ts | 102 ------------------ .../nestjs/src/app.module.ts | 19 ---- .../test-applications/nestjs/src/main.ts | 24 ----- .../nestjs/src/test-module/test.controller.ts | 12 --- .../nestjs/src/test-module/test.exception.ts | 5 - .../nestjs/src/test-module/test.module.ts | 16 --- 60 files changed, 690 insertions(+), 290 deletions(-) rename dev-packages/e2e-tests/test-applications/{nestjs => nestjs-basic}/.gitignore (100%) rename dev-packages/e2e-tests/test-applications/{nestjs => nestjs-basic}/.npmrc (100%) rename dev-packages/e2e-tests/test-applications/{nestjs => nestjs-basic}/nest-cli.json (100%) rename dev-packages/e2e-tests/test-applications/{nestjs => nestjs-basic}/package.json (98%) rename dev-packages/e2e-tests/test-applications/{nestjs => nestjs-basic}/playwright.config.mjs (100%) create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.controller.ts create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.module.ts rename dev-packages/e2e-tests/test-applications/{nestjs => nestjs-basic}/src/app.service.ts (51%) create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-basic/src/instrument.ts create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-basic/src/main.ts rename dev-packages/e2e-tests/test-applications/{nestjs => nestjs-basic}/start-event-proxy.mjs (100%) rename dev-packages/e2e-tests/test-applications/{nestjs => nestjs-basic}/tests/cron-decorator.test.ts (100%) rename dev-packages/e2e-tests/test-applications/{nestjs => nestjs-basic}/tests/errors.test.ts (63%) rename dev-packages/e2e-tests/test-applications/{nestjs => nestjs-basic}/tests/span-decorator.test.ts (100%) rename dev-packages/e2e-tests/test-applications/{nestjs => nestjs-basic}/tests/transactions.test.ts (100%) rename dev-packages/e2e-tests/test-applications/{nestjs => nestjs-basic}/tsconfig.build.json (100%) rename dev-packages/e2e-tests/test-applications/{nestjs => nestjs-basic}/tsconfig.json (100%) create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/.gitignore create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/.npmrc create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/nest-cli.json create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/package.json create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/playwright.config.mjs rename dev-packages/e2e-tests/test-applications/{nestjs => nestjs-distributed-tracing}/src/instrument.ts (100%) create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/main.ts create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/trace-initiator.controller.ts create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/trace-initiator.module.ts create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/trace-initiator.service.ts create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/trace-receiver.controller.ts create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/trace-receiver.module.ts create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/trace-receiver.service.ts rename dev-packages/e2e-tests/test-applications/{nestjs => nestjs-distributed-tracing}/src/utils.ts (100%) create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/start-event-proxy.mjs rename dev-packages/e2e-tests/test-applications/{nestjs => nestjs-distributed-tracing}/tests/propagation.test.ts (100%) create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/tsconfig.build.json create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/tsconfig.json create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-with-submodules/.gitignore create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-with-submodules/.npmrc create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-with-submodules/nest-cli.json create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-with-submodules/package.json create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-with-submodules/playwright.config.mjs create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/app.controller.ts create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/app.module.ts create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/app.service.ts create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module/example.controller.ts create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module/example.exception.ts rename dev-packages/e2e-tests/test-applications/{nestjs/src/test-module/test.filter.ts => nestjs-with-submodules/src/example-module/example.filter.ts} (61%) create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module/example.module.ts create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/instrument.ts create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/main.ts create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-with-submodules/start-event-proxy.mjs create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tests/errors.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tsconfig.build.json create mode 100644 dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tsconfig.json delete mode 100644 dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts delete mode 100644 dev-packages/e2e-tests/test-applications/nestjs/src/app.module.ts delete mode 100644 dev-packages/e2e-tests/test-applications/nestjs/src/main.ts delete mode 100644 dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.controller.ts delete mode 100644 dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.exception.ts delete mode 100644 dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.module.ts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cbf2fe9030da..4cf711bd42ff 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1037,7 +1037,9 @@ jobs: 'generic-ts3.8', 'node-fastify', 'node-hapi', - 'nestjs', + 'nestjs-basic', + 'nestjs-distributed-tracing', + 'nestjs-with-submodules', 'node-exports-test-app', 'node-koa', 'node-connect', diff --git a/dev-packages/e2e-tests/test-applications/nestjs/.gitignore b/dev-packages/e2e-tests/test-applications/nestjs-basic/.gitignore similarity index 100% rename from dev-packages/e2e-tests/test-applications/nestjs/.gitignore rename to dev-packages/e2e-tests/test-applications/nestjs-basic/.gitignore diff --git a/dev-packages/e2e-tests/test-applications/nestjs/.npmrc b/dev-packages/e2e-tests/test-applications/nestjs-basic/.npmrc similarity index 100% rename from dev-packages/e2e-tests/test-applications/nestjs/.npmrc rename to dev-packages/e2e-tests/test-applications/nestjs-basic/.npmrc diff --git a/dev-packages/e2e-tests/test-applications/nestjs/nest-cli.json b/dev-packages/e2e-tests/test-applications/nestjs-basic/nest-cli.json similarity index 100% rename from dev-packages/e2e-tests/test-applications/nestjs/nest-cli.json rename to dev-packages/e2e-tests/test-applications/nestjs-basic/nest-cli.json diff --git a/dev-packages/e2e-tests/test-applications/nestjs/package.json b/dev-packages/e2e-tests/test-applications/nestjs-basic/package.json similarity index 98% rename from dev-packages/e2e-tests/test-applications/nestjs/package.json rename to dev-packages/e2e-tests/test-applications/nestjs-basic/package.json index 94c4e445bfe0..f4c44ff7cef3 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/package.json +++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/package.json @@ -1,5 +1,5 @@ { - "name": "nestjs", + "name": "nestjs-basic", "version": "0.0.1", "private": true, "scripts": { diff --git a/dev-packages/e2e-tests/test-applications/nestjs/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/nestjs-basic/playwright.config.mjs similarity index 100% rename from dev-packages/e2e-tests/test-applications/nestjs/playwright.config.mjs rename to dev-packages/e2e-tests/test-applications/nestjs-basic/playwright.config.mjs diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.controller.ts new file mode 100644 index 000000000000..b54604d999cb --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.controller.ts @@ -0,0 +1,37 @@ +import { Controller, Get, Param } from '@nestjs/common'; +import { AppService } from './app.service'; + +@Controller() +export class AppController { + constructor(private readonly appService: AppService) {} + + @Get('test-transaction') + testTransaction() { + return this.appService.testTransaction(); + } + + @Get('test-exception/:id') + async testException(@Param('id') id: string) { + return this.appService.testException(id); + } + + @Get('test-expected-exception/:id') + async testExpectedException(@Param('id') id: string) { + return this.appService.testExpectedException(id); + } + + @Get('test-span-decorator-async') + async testSpanDecoratorAsync() { + return { result: await this.appService.testSpanDecoratorAsync() }; + } + + @Get('test-span-decorator-sync') + async testSpanDecoratorSync() { + return { result: await this.appService.testSpanDecoratorSync() }; + } + + @Get('kill-test-cron') + async killTestCron() { + this.appService.killTestCron(); + } +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.module.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.module.ts new file mode 100644 index 000000000000..ceb7199a99cf --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { ScheduleModule } from '@nestjs/schedule'; +import { AppController } from './app.controller'; +import { AppService } from './app.service'; + +@Module({ + imports: [ScheduleModule.forRoot()], + controllers: [AppController], + providers: [AppService], +}) +export class AppModule {} diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.service.ts similarity index 51% rename from dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts rename to dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.service.ts index f5666bffeb46..3afb7b5147bd 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.service.ts @@ -3,7 +3,6 @@ import { Cron, SchedulerRegistry } from '@nestjs/schedule'; import * as Sentry from '@sentry/nestjs'; import { SentryCron, SentryTraced } from '@sentry/nestjs'; import type { MonitorConfig } from '@sentry/types'; -import { makeHttpRequest } from './utils'; const monitorConfig: MonitorConfig = { schedule: { @@ -13,53 +12,15 @@ const monitorConfig: MonitorConfig = { }; @Injectable() -export class AppService1 { +export class AppService { constructor(private schedulerRegistry: SchedulerRegistry) {} - testSuccess() { - return { version: 'v1' }; - } - - testParam(id: string) { - return { - paramWas: id, - }; - } - - testInboundHeaders(headers: Record, id: string) { - return { - headers, - id, - }; - } - - async testOutgoingHttp(id: string) { - const data = await makeHttpRequest(`http://localhost:3030/test-inbound-headers/${id}`); - - return data; - } - - async testOutgoingFetch(id: string) { - const response = await fetch(`http://localhost:3030/test-inbound-headers/${id}`); - const data = await response.json(); - - return data; - } - testTransaction() { Sentry.startSpan({ name: 'test-span' }, () => { Sentry.startSpan({ name: 'child-span' }, () => {}); }); } - async testError() { - const exceptionId = Sentry.captureException(new Error('This is an error')); - - await Sentry.flush(2000); - - return { exceptionId }; - } - testException(id: string) { throw new Error(`This is an exception with id ${id}`); } @@ -68,26 +29,6 @@ export class AppService1 { throw new HttpException(`This is an expected exception with id ${id}`, HttpStatus.FORBIDDEN); } - async testOutgoingFetchExternalAllowed() { - const fetchResponse = await fetch('http://localhost:3040/external-allowed'); - - return fetchResponse.json(); - } - - async testOutgoingFetchExternalDisallowed() { - const fetchResponse = await fetch('http://localhost:3040/external-disallowed'); - - return fetchResponse.json(); - } - - async testOutgoingHttpExternalAllowed() { - return makeHttpRequest('http://localhost:3040/external-allowed'); - } - - async testOutgoingHttpExternalDisallowed() { - return makeHttpRequest('http://localhost:3040/external-disallowed'); - } - @SentryTraced('wait and return a string') async wait() { await new Promise(resolve => setTimeout(resolve, 500)); @@ -124,20 +65,3 @@ export class AppService1 { this.schedulerRegistry.deleteCronJob('test-cron-job'); } } - -@Injectable() -export class AppService2 { - externalAllowed(headers: Record) { - return { - headers, - route: 'external-allowed', - }; - } - - externalDisallowed(headers: Record) { - return { - headers, - route: 'external-disallowed', - }; - } -} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/instrument.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/instrument.ts new file mode 100644 index 000000000000..f1f4de865435 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/instrument.ts @@ -0,0 +1,8 @@ +import * as Sentry from '@sentry/nestjs'; + +Sentry.init({ + environment: 'qa', // dynamic sampling bias to keep transactions + dsn: process.env.E2E_TEST_DSN, + tunnel: `http://localhost:3031/`, // proxy server + tracesSampleRate: 1, +}); diff --git a/dev-packages/e2e-tests/test-applications/nestjs-basic/src/main.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/main.ts new file mode 100644 index 000000000000..3a7b5ded8645 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/src/main.ts @@ -0,0 +1,20 @@ +// Import this first +import './instrument'; + +// Import other modules +import { BaseExceptionFilter, HttpAdapterHost, NestFactory } from '@nestjs/core'; +import * as Sentry from '@sentry/nestjs'; +import { AppModule } from './app.module'; + +const PORT = 3030; + +async function bootstrap() { + const app = await NestFactory.create(AppModule); + + const { httpAdapter } = app.get(HttpAdapterHost); + Sentry.setupNestErrorHandler(app, new BaseExceptionFilter(httpAdapter)); + + await app.listen(PORT); +} + +bootstrap(); diff --git a/dev-packages/e2e-tests/test-applications/nestjs/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/nestjs-basic/start-event-proxy.mjs similarity index 100% rename from dev-packages/e2e-tests/test-applications/nestjs/start-event-proxy.mjs rename to dev-packages/e2e-tests/test-applications/nestjs-basic/start-event-proxy.mjs diff --git a/dev-packages/e2e-tests/test-applications/nestjs/tests/cron-decorator.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/cron-decorator.test.ts similarity index 100% rename from dev-packages/e2e-tests/test-applications/nestjs/tests/cron-decorator.test.ts rename to dev-packages/e2e-tests/test-applications/nestjs-basic/tests/cron-decorator.test.ts diff --git a/dev-packages/e2e-tests/test-applications/nestjs/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/errors.test.ts similarity index 63% rename from dev-packages/e2e-tests/test-applications/nestjs/tests/errors.test.ts rename to dev-packages/e2e-tests/test-applications/nestjs-basic/tests/errors.test.ts index ffb48f4e5e70..349b25b0eee9 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/errors.test.ts @@ -53,32 +53,3 @@ test('Does not send expected exception to Sentry', async ({ baseURL }) => { expect(errorEventOccurred).toBe(false); }); - -test('Does not handle expected exception if exception is thrown in module', async ({ baseURL }) => { - const errorEventPromise = waitForError('nestjs', event => { - return !event.type && event.exception?.values?.[0]?.value === 'Something went wrong in the test module!'; - }); - - const response = await fetch(`${baseURL}/test-module`); - expect(response.status).toBe(500); // should be 400 - - // should never arrive, but does because the exception is not handled properly - const errorEvent = await errorEventPromise; - - expect(errorEvent.exception?.values).toHaveLength(1); - expect(errorEvent.exception?.values?.[0]?.value).toBe('Something went wrong in the test module!'); - - expect(errorEvent.request).toEqual({ - method: 'GET', - cookies: {}, - headers: expect.any(Object), - url: 'http://localhost:3030/test-module', - }); - - expect(errorEvent.transaction).toEqual('GET /test-module'); - - expect(errorEvent.contexts?.trace).toEqual({ - trace_id: expect.any(String), - span_id: expect.any(String), - }); -}); diff --git a/dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/span-decorator.test.ts similarity index 100% rename from dev-packages/e2e-tests/test-applications/nestjs/tests/span-decorator.test.ts rename to dev-packages/e2e-tests/test-applications/nestjs-basic/tests/span-decorator.test.ts diff --git a/dev-packages/e2e-tests/test-applications/nestjs/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-basic/tests/transactions.test.ts similarity index 100% rename from dev-packages/e2e-tests/test-applications/nestjs/tests/transactions.test.ts rename to dev-packages/e2e-tests/test-applications/nestjs-basic/tests/transactions.test.ts diff --git a/dev-packages/e2e-tests/test-applications/nestjs/tsconfig.build.json b/dev-packages/e2e-tests/test-applications/nestjs-basic/tsconfig.build.json similarity index 100% rename from dev-packages/e2e-tests/test-applications/nestjs/tsconfig.build.json rename to dev-packages/e2e-tests/test-applications/nestjs-basic/tsconfig.build.json diff --git a/dev-packages/e2e-tests/test-applications/nestjs/tsconfig.json b/dev-packages/e2e-tests/test-applications/nestjs-basic/tsconfig.json similarity index 100% rename from dev-packages/e2e-tests/test-applications/nestjs/tsconfig.json rename to dev-packages/e2e-tests/test-applications/nestjs-basic/tsconfig.json diff --git a/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/.gitignore b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/.gitignore new file mode 100644 index 000000000000..4b56acfbebf4 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/.gitignore @@ -0,0 +1,56 @@ +# compiled output +/dist +/node_modules +/build + +# Logs +logs +*.log +npm-debug.log* +pnpm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# OS +.DS_Store + +# Tests +/coverage +/.nyc_output + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# temp directory +.temp +.tmp + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json diff --git a/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/.npmrc b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/.npmrc new file mode 100644 index 000000000000..070f80f05092 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/.npmrc @@ -0,0 +1,2 @@ +@sentry:registry=http://127.0.0.1:4873 +@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/nest-cli.json b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/nest-cli.json new file mode 100644 index 000000000000..f9aa683b1ad5 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/nest-cli.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "deleteOutDir": true + } +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/package.json b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/package.json new file mode 100644 index 000000000000..b4d0ead875f9 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/package.json @@ -0,0 +1,47 @@ +{ + "name": "nestjs-distributed-tracing", + "version": "0.0.1", + "private": true, + "scripts": { + "build": "nest build", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "start": "nest start", + "start:dev": "nest start --watch", + "start:debug": "nest start --debug --watch", + "start:prod": "node dist/main", + "clean": "npx rimraf node_modules pnpm-lock.yaml", + "test": "playwright test", + "test:build": "pnpm install", + "test:assert": "pnpm test" + }, + "dependencies": { + "@nestjs/common": "^10.0.0", + "@nestjs/core": "^10.0.0", + "@nestjs/platform-express": "^10.0.0", + "@sentry/nestjs": "latest || *", + "@sentry/types": "latest || *", + "reflect-metadata": "^0.2.0", + "rxjs": "^7.8.1" + }, + "devDependencies": { + "@playwright/test": "^1.44.1", + "@sentry-internal/test-utils": "link:../../../test-utils", + "@nestjs/cli": "^10.0.0", + "@nestjs/schematics": "^10.0.0", + "@nestjs/testing": "^10.0.0", + "@types/express": "^4.17.17", + "@types/node": "18.15.1", + "@types/supertest": "^6.0.0", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "eslint": "^8.42.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0", + "prettier": "^3.0.0", + "source-map-support": "^0.5.21", + "supertest": "^6.3.3", + "ts-loader": "^9.4.3", + "tsconfig-paths": "^4.2.0", + "typescript": "^4.9.5" + } +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/playwright.config.mjs new file mode 100644 index 000000000000..31f2b913b58b --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/playwright.config.mjs @@ -0,0 +1,7 @@ +import { getPlaywrightConfig } from '@sentry-internal/test-utils'; + +const config = getPlaywrightConfig({ + startCommand: `pnpm start`, +}); + +export default config; diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/instrument.ts b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/instrument.ts similarity index 100% rename from dev-packages/e2e-tests/test-applications/nestjs/src/instrument.ts rename to dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/instrument.ts diff --git a/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/main.ts b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/main.ts new file mode 100644 index 000000000000..83d0b33d687d --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/main.ts @@ -0,0 +1,25 @@ +// Import this first +import './instrument'; + +// Import other modules +import { BaseExceptionFilter, HttpAdapterHost, NestFactory } from '@nestjs/core'; +import * as Sentry from '@sentry/nestjs'; +import { TraceInitiatorModule } from './trace-initiator.module'; +import { TraceReceiverModule } from './trace-receiver.module'; + +const TRACE_INITIATOR_PORT = 3030; +const TRACE_RECEIVER_PORT = 3040; + +async function bootstrap() { + const trace_initiator_app = await NestFactory.create(TraceInitiatorModule); + + const { httpAdapter } = trace_initiator_app.get(HttpAdapterHost); + Sentry.setupNestErrorHandler(trace_initiator_app, new BaseExceptionFilter(httpAdapter)); + + await trace_initiator_app.listen(TRACE_INITIATOR_PORT); + + const trace_receiver_app = await NestFactory.create(TraceReceiverModule); + await trace_receiver_app.listen(TRACE_RECEIVER_PORT); +} + +bootstrap(); diff --git a/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/trace-initiator.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/trace-initiator.controller.ts new file mode 100644 index 000000000000..62e0c299a239 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/trace-initiator.controller.ts @@ -0,0 +1,42 @@ +import { Controller, Get, Headers, Param } from '@nestjs/common'; +import { TraceInitiatorService } from './trace-initiator.service'; + +@Controller() +export class TraceInitiatorController { + constructor(private readonly traceInitiatorService: TraceInitiatorService) {} + + @Get('test-inbound-headers/:id') + testInboundHeaders(@Headers() headers, @Param('id') id: string) { + return this.traceInitiatorService.testInboundHeaders(headers, id); + } + + @Get('test-outgoing-http/:id') + async testOutgoingHttp(@Param('id') id: string) { + return this.traceInitiatorService.testOutgoingHttp(id); + } + + @Get('test-outgoing-fetch/:id') + async testOutgoingFetch(@Param('id') id: string) { + return this.traceInitiatorService.testOutgoingFetch(id); + } + + @Get('test-outgoing-fetch-external-allowed') + async testOutgoingFetchExternalAllowed() { + return this.traceInitiatorService.testOutgoingFetchExternalAllowed(); + } + + @Get('test-outgoing-fetch-external-disallowed') + async testOutgoingFetchExternalDisallowed() { + return this.traceInitiatorService.testOutgoingFetchExternalDisallowed(); + } + + @Get('test-outgoing-http-external-allowed') + async testOutgoingHttpExternalAllowed() { + return this.traceInitiatorService.testOutgoingHttpExternalAllowed(); + } + + @Get('test-outgoing-http-external-disallowed') + async testOutgoingHttpExternalDisallowed() { + return this.traceInitiatorService.testOutgoingHttpExternalDisallowed(); + } +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/trace-initiator.module.ts b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/trace-initiator.module.ts new file mode 100644 index 000000000000..9256f29928ab --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/trace-initiator.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { TraceInitiatorController } from './trace-initiator.controller'; +import { TraceInitiatorService } from './trace-initiator.service'; + +@Module({ + imports: [], + controllers: [TraceInitiatorController], + providers: [TraceInitiatorService], +}) +export class TraceInitiatorModule {} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/trace-initiator.service.ts b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/trace-initiator.service.ts new file mode 100644 index 000000000000..67c5333cedaf --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/trace-initiator.service.ts @@ -0,0 +1,47 @@ +import { Injectable } from '@nestjs/common'; +import { makeHttpRequest } from './utils'; + +@Injectable() +export class TraceInitiatorService { + constructor() {} + + testInboundHeaders(headers: Record, id: string) { + return { + headers, + id, + }; + } + + async testOutgoingHttp(id: string) { + const data = await makeHttpRequest(`http://localhost:3030/test-inbound-headers/${id}`); + + return data; + } + + async testOutgoingFetch(id: string) { + const response = await fetch(`http://localhost:3030/test-inbound-headers/${id}`); + const data = await response.json(); + + return data; + } + + async testOutgoingFetchExternalAllowed() { + const fetchResponse = await fetch('http://localhost:3040/external-allowed'); + + return fetchResponse.json(); + } + + async testOutgoingFetchExternalDisallowed() { + const fetchResponse = await fetch('http://localhost:3040/external-disallowed'); + + return fetchResponse.json(); + } + + async testOutgoingHttpExternalAllowed() { + return makeHttpRequest('http://localhost:3040/external-allowed'); + } + + async testOutgoingHttpExternalDisallowed() { + return makeHttpRequest('http://localhost:3040/external-disallowed'); + } +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/trace-receiver.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/trace-receiver.controller.ts new file mode 100644 index 000000000000..2a1899f1097d --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/trace-receiver.controller.ts @@ -0,0 +1,17 @@ +import { Controller, Get, Headers } from '@nestjs/common'; +import { TraceReceiverService } from './trace-receiver.service'; + +@Controller() +export class TraceReceiverController { + constructor(private readonly traceReceiverService: TraceReceiverService) {} + + @Get('external-allowed') + externalAllowed(@Headers() headers) { + return this.traceReceiverService.externalAllowed(headers); + } + + @Get('external-disallowed') + externalDisallowed(@Headers() headers) { + return this.traceReceiverService.externalDisallowed(headers); + } +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/trace-receiver.module.ts b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/trace-receiver.module.ts new file mode 100644 index 000000000000..2680b3071fb7 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/trace-receiver.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { TraceReceiverController } from './trace-receiver.controller'; +import { TraceReceiverService } from './trace-receiver.service'; + +@Module({ + imports: [], + controllers: [TraceReceiverController], + providers: [TraceReceiverService], +}) +export class TraceReceiverModule {} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/trace-receiver.service.ts b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/trace-receiver.service.ts new file mode 100644 index 000000000000..a40b28ad0778 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/trace-receiver.service.ts @@ -0,0 +1,18 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class TraceReceiverService { + externalAllowed(headers: Record) { + return { + headers, + route: 'external-allowed', + }; + } + + externalDisallowed(headers: Record) { + return { + headers, + route: 'external-disallowed', + }; + } +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/utils.ts b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/utils.ts similarity index 100% rename from dev-packages/e2e-tests/test-applications/nestjs/src/utils.ts rename to dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/src/utils.ts diff --git a/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/start-event-proxy.mjs new file mode 100644 index 000000000000..e9917b9273da --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/start-event-proxy.mjs @@ -0,0 +1,6 @@ +import { startEventProxyServer } from '@sentry-internal/test-utils'; + +startEventProxyServer({ + port: 3031, + proxyServerName: 'nestjs', +}); diff --git a/dev-packages/e2e-tests/test-applications/nestjs/tests/propagation.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/tests/propagation.test.ts similarity index 100% rename from dev-packages/e2e-tests/test-applications/nestjs/tests/propagation.test.ts rename to dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/tests/propagation.test.ts diff --git a/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/tsconfig.build.json b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/tsconfig.build.json new file mode 100644 index 000000000000..26c30d4eddf2 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "test", "dist"] +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/tsconfig.json b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/tsconfig.json new file mode 100644 index 000000000000..95f5641cf7f3 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-distributed-tracing/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "module": "commonjs", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "target": "ES2021", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./", + "incremental": true, + "skipLibCheck": true, + "strictNullChecks": false, + "noImplicitAny": false, + "strictBindCallApply": false, + "forceConsistentCasingInFileNames": false, + "noFallthroughCasesInSwitch": false + } +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/.gitignore b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/.gitignore new file mode 100644 index 000000000000..4b56acfbebf4 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/.gitignore @@ -0,0 +1,56 @@ +# compiled output +/dist +/node_modules +/build + +# Logs +logs +*.log +npm-debug.log* +pnpm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# OS +.DS_Store + +# Tests +/coverage +/.nyc_output + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# temp directory +.temp +.tmp + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/.npmrc b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/.npmrc new file mode 100644 index 000000000000..070f80f05092 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/.npmrc @@ -0,0 +1,2 @@ +@sentry:registry=http://127.0.0.1:4873 +@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/nest-cli.json b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/nest-cli.json new file mode 100644 index 000000000000..f9aa683b1ad5 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/nest-cli.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "deleteOutDir": true + } +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/package.json b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/package.json new file mode 100644 index 000000000000..dfbe5e83e640 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/package.json @@ -0,0 +1,47 @@ +{ + "name": "nestjs-with-submodules", + "version": "0.0.1", + "private": true, + "scripts": { + "build": "nest build", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "start": "nest start", + "start:dev": "nest start --watch", + "start:debug": "nest start --debug --watch", + "start:prod": "node dist/main", + "clean": "npx rimraf node_modules pnpm-lock.yaml", + "test": "playwright test", + "test:build": "pnpm install", + "test:assert": "pnpm test" + }, + "dependencies": { + "@nestjs/common": "^10.0.0", + "@nestjs/core": "^10.0.0", + "@nestjs/platform-express": "^10.0.0", + "@sentry/nestjs": "latest || *", + "@sentry/types": "latest || *", + "reflect-metadata": "^0.2.0", + "rxjs": "^7.8.1" + }, + "devDependencies": { + "@playwright/test": "^1.44.1", + "@sentry-internal/test-utils": "link:../../../test-utils", + "@nestjs/cli": "^10.0.0", + "@nestjs/schematics": "^10.0.0", + "@nestjs/testing": "^10.0.0", + "@types/express": "^4.17.17", + "@types/node": "18.15.1", + "@types/supertest": "^6.0.0", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "eslint": "^8.42.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-prettier": "^5.0.0", + "prettier": "^3.0.0", + "source-map-support": "^0.5.21", + "supertest": "^6.3.3", + "ts-loader": "^9.4.3", + "tsconfig-paths": "^4.2.0", + "typescript": "^4.9.5" + } +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/playwright.config.mjs new file mode 100644 index 000000000000..31f2b913b58b --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/playwright.config.mjs @@ -0,0 +1,7 @@ +import { getPlaywrightConfig } from '@sentry-internal/test-utils'; + +const config = getPlaywrightConfig({ + startCommand: `pnpm start`, +}); + +export default config; diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/app.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/app.controller.ts new file mode 100644 index 000000000000..71a410e8d0a8 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/app.controller.ts @@ -0,0 +1,17 @@ +import { Controller, Get, Param } from '@nestjs/common'; +import { AppService } from './app.service'; + +@Controller() +export class AppController { + constructor(private readonly appService: AppService) {} + + @Get('test-exception/:id') + async testException(@Param('id') id: string) { + return this.appService.testException(id); + } + + @Get('test-expected-exception/:id') + async testExpectedException(@Param('id') id: string) { + return this.appService.testExpectedException(id); + } +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/app.module.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/app.module.ts new file mode 100644 index 000000000000..944b84e66d27 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/app.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { AppController } from './app.controller'; +import { AppService } from './app.service'; +import { ExampleModule } from './example-module/example.module'; + +@Module({ + imports: [ExampleModule], + controllers: [AppController], + providers: [AppService], +}) +export class AppModule {} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/app.service.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/app.service.ts new file mode 100644 index 000000000000..242408023586 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/app.service.ts @@ -0,0 +1,14 @@ +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; + +@Injectable() +export class AppService { + constructor() {} + + testException(id: string) { + throw new Error(`This is an exception with id ${id}`); + } + + testExpectedException(id: string) { + throw new HttpException(`This is an expected exception with id ${id}`, HttpStatus.FORBIDDEN); + } +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module/example.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module/example.controller.ts new file mode 100644 index 000000000000..b71179c195cb --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module/example.controller.ts @@ -0,0 +1,12 @@ +import { Controller, Get } from '@nestjs/common'; +import { ExampleException } from './example.exception'; + +@Controller('example-module') +export class ExampleController { + constructor() {} + + @Get() + getTest(): string { + throw new ExampleException(); + } +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module/example.exception.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module/example.exception.ts new file mode 100644 index 000000000000..ac43dddfa8dc --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module/example.exception.ts @@ -0,0 +1,5 @@ +export class ExampleException extends Error { + constructor() { + super('Something went wrong in the example module!'); + } +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.filter.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module/example.filter.ts similarity index 61% rename from dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.filter.ts rename to dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module/example.filter.ts index 87a4ca0920e5..848441caf855 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.filter.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module/example.filter.ts @@ -1,11 +1,11 @@ import { ArgumentsHost, BadRequestException, Catch } from '@nestjs/common'; import { BaseExceptionFilter } from '@nestjs/core'; -import { TestException } from './test.exception'; +import { ExampleException } from './example.exception'; -@Catch(TestException) -export class TestExceptionFilter extends BaseExceptionFilter { +@Catch(ExampleException) +export class ExampleExceptionFilter extends BaseExceptionFilter { catch(exception: unknown, host: ArgumentsHost) { - if (exception instanceof TestException) { + if (exception instanceof ExampleException) { return super.catch(new BadRequestException(exception.message), host); } return super.catch(exception, host); diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module/example.module.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module/example.module.ts new file mode 100644 index 000000000000..fabd71c4df90 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/example-module/example.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common'; +import { APP_FILTER } from '@nestjs/core'; +import { ExampleController } from './example.controller'; +import { ExampleExceptionFilter } from './example.filter'; + +@Module({ + imports: [], + controllers: [ExampleController], + providers: [ + { + provide: APP_FILTER, + useClass: ExampleExceptionFilter, + }, + ], +}) +export class ExampleModule {} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/instrument.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/instrument.ts new file mode 100644 index 000000000000..f1f4de865435 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/instrument.ts @@ -0,0 +1,8 @@ +import * as Sentry from '@sentry/nestjs'; + +Sentry.init({ + environment: 'qa', // dynamic sampling bias to keep transactions + dsn: process.env.E2E_TEST_DSN, + tunnel: `http://localhost:3031/`, // proxy server + tracesSampleRate: 1, +}); diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/main.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/main.ts new file mode 100644 index 000000000000..3a7b5ded8645 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/src/main.ts @@ -0,0 +1,20 @@ +// Import this first +import './instrument'; + +// Import other modules +import { BaseExceptionFilter, HttpAdapterHost, NestFactory } from '@nestjs/core'; +import * as Sentry from '@sentry/nestjs'; +import { AppModule } from './app.module'; + +const PORT = 3030; + +async function bootstrap() { + const app = await NestFactory.create(AppModule); + + const { httpAdapter } = app.get(HttpAdapterHost); + Sentry.setupNestErrorHandler(app, new BaseExceptionFilter(httpAdapter)); + + await app.listen(PORT); +} + +bootstrap(); diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/start-event-proxy.mjs new file mode 100644 index 000000000000..e9917b9273da --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/start-event-proxy.mjs @@ -0,0 +1,6 @@ +import { startEventProxyServer } from '@sentry-internal/test-utils'; + +startEventProxyServer({ + port: 3031, + proxyServerName: 'nestjs', +}); diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tests/errors.test.ts new file mode 100644 index 000000000000..3711cbe8fd0f --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tests/errors.test.ts @@ -0,0 +1,31 @@ +import { expect, test } from '@playwright/test'; +import { waitForError } from '@sentry-internal/test-utils'; + +test('Does not handle expected exception if exception is thrown in module', async ({ baseURL }) => { + const errorEventPromise = waitForError('nestjs', event => { + return !event.type && event.exception?.values?.[0]?.value === 'Something went wrong in the example module!'; + }); + + const response = await fetch(`${baseURL}/example-module`); + expect(response.status).toBe(500); // should be 400 + + // should never arrive, but does because the exception is not handled properly + const errorEvent = await errorEventPromise; + + expect(errorEvent.exception?.values).toHaveLength(1); + expect(errorEvent.exception?.values?.[0]?.value).toBe('Something went wrong in the example module!'); + + expect(errorEvent.request).toEqual({ + method: 'GET', + cookies: {}, + headers: expect.any(Object), + url: 'http://localhost:3030/example-module', + }); + + expect(errorEvent.transaction).toEqual('GET /example-module'); + + expect(errorEvent.contexts?.trace).toEqual({ + trace_id: expect.any(String), + span_id: expect.any(String), + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tsconfig.build.json b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tsconfig.build.json new file mode 100644 index 000000000000..26c30d4eddf2 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "test", "dist"] +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tsconfig.json b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tsconfig.json new file mode 100644 index 000000000000..95f5641cf7f3 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs-with-submodules/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "module": "commonjs", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "target": "ES2021", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./", + "incremental": true, + "skipLibCheck": true, + "strictNullChecks": false, + "noImplicitAny": false, + "strictBindCallApply": false, + "forceConsistentCasingInFileNames": false, + "noFallthroughCasesInSwitch": false + } +} diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts deleted file mode 100644 index 7fda9eef768e..000000000000 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { Controller, Get, Headers, Param } from '@nestjs/common'; -import { AppService1, AppService2 } from './app.service'; - -@Controller() -export class AppController1 { - constructor(private readonly appService: AppService1) {} - - @Get('test-success') - testSuccess() { - return this.appService.testSuccess(); - } - - @Get('test-param/:param') - testParam(@Param() params) { - return this.appService.testParam(params.param); - } - - @Get('test-inbound-headers/:id') - testInboundHeaders(@Headers() headers, @Param('id') id: string) { - return this.appService.testInboundHeaders(headers, id); - } - - @Get('test-outgoing-http/:id') - async testOutgoingHttp(@Param('id') id: string) { - return this.appService.testOutgoingHttp(id); - } - - @Get('test-outgoing-fetch/:id') - async testOutgoingFetch(@Param('id') id: string) { - return this.appService.testOutgoingFetch(id); - } - - @Get('test-transaction') - testTransaction() { - return this.appService.testTransaction(); - } - - @Get('test-error') - async testError() { - return this.appService.testError(); - } - - @Get('test-exception/:id') - async testException(@Param('id') id: string) { - return this.appService.testException(id); - } - - @Get('test-expected-exception/:id') - async testExpectedException(@Param('id') id: string) { - return this.appService.testExpectedException(id); - } - - @Get('test-outgoing-fetch-external-allowed') - async testOutgoingFetchExternalAllowed() { - return this.appService.testOutgoingFetchExternalAllowed(); - } - - @Get('test-outgoing-fetch-external-disallowed') - async testOutgoingFetchExternalDisallowed() { - return this.appService.testOutgoingFetchExternalDisallowed(); - } - - @Get('test-outgoing-http-external-allowed') - async testOutgoingHttpExternalAllowed() { - return this.appService.testOutgoingHttpExternalAllowed(); - } - - @Get('test-outgoing-http-external-disallowed') - async testOutgoingHttpExternalDisallowed() { - return this.appService.testOutgoingHttpExternalDisallowed(); - } - - @Get('test-span-decorator-async') - async testSpanDecoratorAsync() { - return { result: await this.appService.testSpanDecoratorAsync() }; - } - - @Get('test-span-decorator-sync') - async testSpanDecoratorSync() { - return { result: await this.appService.testSpanDecoratorSync() }; - } - - @Get('kill-test-cron') - async killTestCron() { - this.appService.killTestCron(); - } -} - -@Controller() -export class AppController2 { - constructor(private readonly appService: AppService2) {} - - @Get('external-allowed') - externalAllowed(@Headers() headers) { - return this.appService.externalAllowed(headers); - } - - @Get('external-disallowed') - externalDisallowed(@Headers() headers) { - return this.appService.externalDisallowed(headers); - } -} diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/app.module.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/app.module.ts deleted file mode 100644 index 932d1af99611..000000000000 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/app.module.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Module } from '@nestjs/common'; -import { ScheduleModule } from '@nestjs/schedule'; -import { AppController1, AppController2 } from './app.controller'; -import { AppService1, AppService2 } from './app.service'; -import { TestModule } from './test-module/test.module'; - -@Module({ - imports: [ScheduleModule.forRoot(), TestModule], - controllers: [AppController1], - providers: [AppService1], -}) -export class AppModule1 {} - -@Module({ - imports: [], - controllers: [AppController2], - providers: [AppService2], -}) -export class AppModule2 {} diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/main.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/main.ts deleted file mode 100644 index c2682662154d..000000000000 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/main.ts +++ /dev/null @@ -1,24 +0,0 @@ -// Import this first -import './instrument'; - -// Import other modules -import { BaseExceptionFilter, HttpAdapterHost, NestFactory } from '@nestjs/core'; -import * as Sentry from '@sentry/nestjs'; -import { AppModule1, AppModule2 } from './app.module'; - -const app1Port = 3030; -const app2Port = 3040; - -async function bootstrap() { - const app1 = await NestFactory.create(AppModule1); - - const { httpAdapter } = app1.get(HttpAdapterHost); - Sentry.setupNestErrorHandler(app1, new BaseExceptionFilter(httpAdapter)); - - await app1.listen(app1Port); - - const app2 = await NestFactory.create(AppModule2); - await app2.listen(app2Port); -} - -bootstrap(); diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.controller.ts deleted file mode 100644 index 150fb0e07546..000000000000 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.controller.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Controller, Get } from '@nestjs/common'; -import { TestException } from './test.exception'; - -@Controller('test-module') -export class TestController { - constructor() {} - - @Get() - getTest(): string { - throw new TestException(); - } -} diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.exception.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.exception.ts deleted file mode 100644 index b736596b6717..000000000000 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.exception.ts +++ /dev/null @@ -1,5 +0,0 @@ -export class TestException extends Error { - constructor() { - super('Something went wrong in the test module!'); - } -} diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.module.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.module.ts deleted file mode 100644 index 37b6dbe7e819..000000000000 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/test-module/test.module.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Module } from '@nestjs/common'; -import { APP_FILTER } from '@nestjs/core'; -import { TestController } from './test.controller'; -import { TestExceptionFilter } from './test.filter'; - -@Module({ - imports: [], - controllers: [TestController], - providers: [ - { - provide: APP_FILTER, - useClass: TestExceptionFilter, - }, - ], -}) -export class TestModule {} From 1619efcc0614e0948f80fc9a9c623d4927f4b1c7 Mon Sep 17 00:00:00 2001 From: Jaspreet Singh Date: Thu, 18 Jul 2024 17:26:12 +0530 Subject: [PATCH 17/21] fix(feedback): add a missing call of Actor.appendToDom method when DOMContentLoaded event is triggered (#12973) Fixing a missing paranthesis to call `appendToDom` on `DOMContentLoaded` event. Fixes #12970 --- packages/feedback/src/core/integration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/feedback/src/core/integration.ts b/packages/feedback/src/core/integration.ts index 8917644cebfe..4e8caa85a135 100644 --- a/packages/feedback/src/core/integration.ts +++ b/packages/feedback/src/core/integration.ts @@ -282,7 +282,7 @@ export const buildFeedbackIntegration = ({ } if (DOCUMENT.readyState === 'loading') { - DOCUMENT.addEventListener('DOMContentLoaded', () => _createActor().appendToDom); + DOCUMENT.addEventListener('DOMContentLoaded', () => _createActor().appendToDom()); } else { _createActor().appendToDom(); } From 803220e44bab45c6cb955d910886b5ec5ef5ddc2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2024 14:29:08 +0200 Subject: [PATCH 18/21] ref: Add external contributor to CHANGELOG.md (#12974) This PR adds the external contributor to the CHANGELOG.md file, so that they are credited for their contribution. See #12973 --------- Co-authored-by: andreiborza <168741329+andreiborza@users.noreply.github.com> Co-authored-by: Andrei Borza --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 336529b9ff23..46d4c95e9603 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +Work in this release was contributed by @jaspreet57. Thank you for your contribution! + ## 8.18.0 ### Important Changes From 7500b06481ace968978847ca551c1db415861d8a Mon Sep 17 00:00:00 2001 From: Artur Androsovych Date: Thu, 18 Jul 2024 13:30:55 +0100 Subject: [PATCH 19/21] fix(angular): Remove `afterSendEvent` listener once root injector is destroyed (#12786) Add cleanup logic to handle the removal of afterSendEvent, which is set up within the Angular error handler. This fixes memory leaks that occur when the event is still being handled after the root view is removed --------- Co-authored-by: Lukas Stracke --- packages/angular/src/errorhandler.ts | 28 ++++++++------ packages/angular/test/errorhandler.test.ts | 44 ++++++++++++++++++++++ 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/packages/angular/src/errorhandler.ts b/packages/angular/src/errorhandler.ts index 28c06e1e6bfd..14ca380ea3ea 100644 --- a/packages/angular/src/errorhandler.ts +++ b/packages/angular/src/errorhandler.ts @@ -1,5 +1,5 @@ import { HttpErrorResponse } from '@angular/common/http'; -import type { ErrorHandler as AngularErrorHandler } from '@angular/core'; +import type { ErrorHandler as AngularErrorHandler, OnDestroy } from '@angular/core'; import { Inject, Injectable } from '@angular/core'; import * as Sentry from '@sentry/browser'; import type { ReportDialogOptions } from '@sentry/browser'; @@ -81,21 +81,28 @@ function isErrorOrErrorLikeObject(value: unknown): value is Error { * Implementation of Angular's ErrorHandler provider that can be used as a drop-in replacement for the stock one. */ @Injectable({ providedIn: 'root' }) -class SentryErrorHandler implements AngularErrorHandler { +class SentryErrorHandler implements AngularErrorHandler, OnDestroy { protected readonly _options: ErrorHandlerOptions; - /* indicates if we already registered our the afterSendEvent handler */ - private _registeredAfterSendEventHandler; + /** The cleanup function is executed when the injector is destroyed. */ + private _removeAfterSendEventListener?: () => void; public constructor(@Inject('errorHandlerOptions') options?: ErrorHandlerOptions) { - this._registeredAfterSendEventHandler = false; - this._options = { logErrors: true, ...options, }; } + /** + * Method executed when the injector is destroyed. + */ + public ngOnDestroy(): void { + if (this._removeAfterSendEventListener) { + this._removeAfterSendEventListener(); + } + } + /** * Method called for every value captured through the ErrorHandler */ @@ -119,17 +126,14 @@ class SentryErrorHandler implements AngularErrorHandler { if (this._options.showDialog) { const client = Sentry.getClient(); - if (client && !this._registeredAfterSendEventHandler) { - client.on('afterSendEvent', (event: Event) => { + if (client && !this._removeAfterSendEventListener) { + this._removeAfterSendEventListener = client.on('afterSendEvent', (event: Event) => { if (!event.type && event.event_id) { runOutsideAngular(() => { - Sentry.showReportDialog({ ...this._options.dialogOptions, eventId: event.event_id! }); + Sentry.showReportDialog({ ...this._options.dialogOptions, eventId: event.event_id }); }); } }); - - // We only want to register this hook once in the lifetime of the error handler - this._registeredAfterSendEventHandler = true; } else if (!client) { runOutsideAngular(() => { Sentry.showReportDialog({ ...this._options.dialogOptions, eventId }); diff --git a/packages/angular/test/errorhandler.test.ts b/packages/angular/test/errorhandler.test.ts index c30a7a87efc9..1ae415c5706d 100644 --- a/packages/angular/test/errorhandler.test.ts +++ b/packages/angular/test/errorhandler.test.ts @@ -546,5 +546,49 @@ describe('SentryErrorHandler', () => { expect(showReportDialogSpy).toBeCalledTimes(1); }); }); + + it('only registers the client "afterSendEvent" listener to open the dialog once', () => { + const unsubScribeSpy = vi.fn(); + const client = { + cbs: [] as ((event: Event) => void)[], + on: vi.fn((_, cb) => { + client.cbs.push(cb); + return unsubScribeSpy; + }), + }; + + vi.spyOn(SentryBrowser, 'getClient').mockImplementation(() => client as unknown as Client); + + const errorhandler = createErrorHandler({ showDialog: true }); + expect(client.cbs).toHaveLength(0); + + errorhandler.handleError(new Error('error 1')); + expect(client.cbs).toHaveLength(1); + + errorhandler.handleError(new Error('error 2')); + errorhandler.handleError(new Error('error 3')); + expect(client.cbs).toHaveLength(1); + }); + + it('cleans up the "afterSendEvent" listener once the ErrorHandler is destroyed', () => { + const unsubScribeSpy = vi.fn(); + const client = { + cbs: [] as ((event: Event) => void)[], + on: vi.fn((_, cb) => { + client.cbs.push(cb); + return unsubScribeSpy; + }), + }; + + vi.spyOn(SentryBrowser, 'getClient').mockImplementation(() => client as unknown as Client); + + const errorhandler = createErrorHandler({ showDialog: true }); + + errorhandler.handleError(new Error('error 1')); + expect(client.cbs).toHaveLength(1); + + errorhandler.ngOnDestroy(); + expect(unsubScribeSpy).toHaveBeenCalledTimes(1); + }); }); }); From 7d7164a43016595b59bb2427abd089dfa6e3f646 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2024 14:40:42 +0200 Subject: [PATCH 20/21] ref: Add external contributor to CHANGELOG.md (#12975) This PR adds the external contributor to the CHANGELOG.md file, so that they are credited for their contribution. See #12786 --------- Co-authored-by: Lms24 <8420481+Lms24@users.noreply.github.com> Co-authored-by: Andrei Borza --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46d4c95e9603..a894d4ea4f83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott -Work in this release was contributed by @jaspreet57. Thank you for your contribution! +Work in this release was contributed by @jaspreet57 and @arturovt. Thank you for your contribution! ## 8.18.0 From c7ff1a9d1e768d0af9d21690ba8b4fe5db24965d Mon Sep 17 00:00:00 2001 From: Andrei Borza Date: Thu, 18 Jul 2024 14:44:27 +0200 Subject: [PATCH 21/21] meta(changelog): Update changelog for 8.19.0 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a894d4ea4f83..3f6617a2bcc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,13 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +## 8.19.0 + +- feat(core): Align Span interface with OTEL (#12898) +- fix(angular): Remove `afterSendEvent` listener once root injector is destroyed (#12786) +- fix(browser): Fix bug causing unintentional dropping of transactions (#12933) +- fix(feedback): Add a missing call of Actor.appendToDom method when DOMContentLoaded event is triggered (#12973) + Work in this release was contributed by @jaspreet57 and @arturovt. Thank you for your contribution! ## 8.18.0