From 2ecdd90b63884d85c86bb5a13f91ba256d18cbac Mon Sep 17 00:00:00 2001 From: Andrei Borza Date: Tue, 17 Dec 2024 10:58:03 +0100 Subject: [PATCH 1/2] feat(nestjs): Move `nestIntegration` into nest sdk and remove `setupNestErrorHandler` --- .../node-nestjs-basic/.gitignore | 56 -- .../node-nestjs-basic/.npmrc | 2 - .../node-nestjs-basic/nest-cli.json | 8 - .../node-nestjs-basic/package.json | 48 -- .../node-nestjs-basic/playwright.config.mjs | 7 - .../node-nestjs-basic/src/app.controller.ts | 105 --- .../node-nestjs-basic/src/app.module.ts | 16 - .../node-nestjs-basic/src/app.service.ts | 97 --- .../src/async-example.interceptor.ts | 17 - .../src/example-1.interceptor.ts | 15 - .../src/example-2.interceptor.ts | 10 - .../node-nestjs-basic/src/example.guard.ts | 10 - .../src/example.middleware.ts | 12 - .../node-nestjs-basic/src/instrument.ts | 12 - .../node-nestjs-basic/src/main.ts | 20 - .../node-nestjs-basic/start-event-proxy.mjs | 6 - .../tests/cron-decorator.test.ts | 55 -- .../node-nestjs-basic/tests/errors.test.ts | 96 --- .../tests/span-decorator.test.ts | 72 -- .../tests/transactions.test.ts | 727 ------------------ .../node-nestjs-basic/tsconfig.build.json | 4 - .../node-nestjs-basic/tsconfig.json | 21 - .../.gitignore | 56 -- .../node-nestjs-distributed-tracing/.npmrc | 2 - .../nest-cli.json | 8 - .../package.json | 46 -- .../playwright.config.mjs | 7 - .../src/instrument.ts | 13 - .../src/main.ts | 23 - .../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 | 26 - .../start-event-proxy.mjs | 6 - .../tests/propagation.test.ts | 356 --------- .../tsconfig.build.json | 4 - .../tsconfig.json | 21 - .../nestjs-errors-no-express/scenario.ts | 58 -- .../tracing/nestjs-errors-no-express/test.ts | 37 - .../nestjs-errors-no-express/tsconfig.json | 9 - .../suites/tracing/nestjs-errors/scenario.ts | 56 -- .../suites/tracing/nestjs-errors/test.ts | 37 - .../tracing/nestjs-errors/tsconfig.json | 9 - .../tracing/nestjs-no-express/scenario.ts | 58 -- .../suites/tracing/nestjs-no-express/test.ts | 38 - .../tracing/nestjs-no-express/tsconfig.json | 9 - .../suites/tracing/nestjs/scenario.ts | 57 -- .../suites/tracing/nestjs/test.ts | 51 -- .../suites/tracing/nestjs/tsconfig.json | 9 - packages/astro/src/index.server.ts | 4 - packages/aws-serverless/src/index.ts | 4 - packages/bun/src/index.ts | 4 - packages/google-cloud-serverless/src/index.ts | 4 - packages/nestjs/package.json | 3 + packages/nestjs/src/index.ts | 12 +- .../src/integrations}/helpers.ts | 0 packages/nestjs/src/integrations/nest.ts | 40 + .../sentry-nest-event-instrumentation.ts | 0 .../sentry-nest-instrumentation.ts | 0 .../nest => nestjs/src/integrations}/types.ts | 0 packages/nestjs/src/sdk.ts | 10 +- packages/node/package.json | 1 - packages/node/src/index.ts | 2 - .../node/src/integrations/tracing/index.ts | 5 - .../src/integrations/tracing/nest/nest.ts | 152 ---- packages/remix/src/index.server.ts | 4 - packages/solidstart/src/server/index.ts | 4 - packages/sveltekit/src/server/index.ts | 4 - 71 files changed, 54 insertions(+), 2755 deletions(-) delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/.gitignore delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/nest-cli.json delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/package.json delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/playwright.config.mjs delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/app.controller.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/app.module.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/app.service.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/async-example.interceptor.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example-1.interceptor.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example-2.interceptor.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example.guard.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example.middleware.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/instrument.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/main.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/start-event-proxy.mjs delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/tests/cron-decorator.test.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/tests/errors.test.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/tests/span-decorator.test.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/tests/transactions.test.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/tsconfig.build.json delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-basic/tsconfig.json delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/.gitignore delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/.npmrc delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/nest-cli.json delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/package.json delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/playwright.config.mjs delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/instrument.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/main.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/trace-initiator.controller.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/trace-initiator.module.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/trace-initiator.service.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/trace-receiver.controller.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/trace-receiver.module.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/trace-receiver.service.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/utils.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/start-event-proxy.mjs delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/tests/propagation.test.ts delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/tsconfig.build.json delete mode 100644 dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/tsconfig.json delete mode 100644 dev-packages/node-integration-tests/suites/tracing/nestjs-errors-no-express/scenario.ts delete mode 100644 dev-packages/node-integration-tests/suites/tracing/nestjs-errors-no-express/test.ts delete mode 100644 dev-packages/node-integration-tests/suites/tracing/nestjs-errors-no-express/tsconfig.json delete mode 100644 dev-packages/node-integration-tests/suites/tracing/nestjs-errors/scenario.ts delete mode 100644 dev-packages/node-integration-tests/suites/tracing/nestjs-errors/test.ts delete mode 100644 dev-packages/node-integration-tests/suites/tracing/nestjs-errors/tsconfig.json delete mode 100644 dev-packages/node-integration-tests/suites/tracing/nestjs-no-express/scenario.ts delete mode 100644 dev-packages/node-integration-tests/suites/tracing/nestjs-no-express/test.ts delete mode 100644 dev-packages/node-integration-tests/suites/tracing/nestjs-no-express/tsconfig.json delete mode 100644 dev-packages/node-integration-tests/suites/tracing/nestjs/scenario.ts delete mode 100644 dev-packages/node-integration-tests/suites/tracing/nestjs/test.ts delete mode 100644 dev-packages/node-integration-tests/suites/tracing/nestjs/tsconfig.json rename packages/{node/src/integrations/tracing/nest => nestjs/src/integrations}/helpers.ts (100%) create mode 100644 packages/nestjs/src/integrations/nest.ts rename packages/{node/src/integrations/tracing/nest => nestjs/src/integrations}/sentry-nest-event-instrumentation.ts (100%) rename packages/{node/src/integrations/tracing/nest => nestjs/src/integrations}/sentry-nest-instrumentation.ts (100%) rename packages/{node/src/integrations/tracing/nest => nestjs/src/integrations}/types.ts (100%) delete mode 100644 packages/node/src/integrations/tracing/nest/nest.ts diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/.gitignore b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/.gitignore deleted file mode 100644 index 4b56acfbebf4..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/.gitignore +++ /dev/null @@ -1,56 +0,0 @@ -# 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/node-nestjs-basic/.npmrc b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@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/node-nestjs-basic/nest-cli.json b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/nest-cli.json deleted file mode 100644 index f9aa683b1ad5..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/nest-cli.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/nest-cli", - "collection": "@nestjs/schematics", - "sourceRoot": "src", - "compilerOptions": { - "deleteOutDir": true - } -} diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/package.json b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/package.json deleted file mode 100644 index ed286d4c6886..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/package.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "name": "node-nestjs-basic", - "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/microservices": "^10.0.0", - "@nestjs/schedule": "^4.1.0", - "@nestjs/platform-express": "^10.0.0", - "@sentry/nestjs": "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/node-nestjs-basic/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/playwright.config.mjs deleted file mode 100644 index 31f2b913b58b..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/playwright.config.mjs +++ /dev/null @@ -1,7 +0,0 @@ -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/node-nestjs-basic/src/app.controller.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/app.controller.ts deleted file mode 100644 index 70c734c61d73..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/app.controller.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { Controller, Get, Param, ParseIntPipe, UseGuards, UseInterceptors } from '@nestjs/common'; -import { flush } from '@sentry/nestjs'; -import { AppService } from './app.service'; -import { AsyncInterceptor } from './async-example.interceptor'; -import { ExampleInterceptor1 } from './example-1.interceptor'; -import { ExampleInterceptor2 } from './example-2.interceptor'; -import { ExampleGuard } from './example.guard'; - -@Controller() -export class AppController { - constructor(private readonly appService: AppService) {} - - @Get('test-transaction') - testTransaction() { - return this.appService.testTransaction(); - } - - @Get('test-middleware-instrumentation') - testMiddlewareInstrumentation() { - return this.appService.testSpan(); - } - - @Get('test-guard-instrumentation') - @UseGuards(ExampleGuard) - testGuardInstrumentation() { - return {}; - } - - @Get('test-interceptor-instrumentation') - @UseInterceptors(ExampleInterceptor1, ExampleInterceptor2) - testInterceptorInstrumentation() { - return this.appService.testSpan(); - } - - @Get('test-async-interceptor-instrumentation') - @UseInterceptors(AsyncInterceptor) - testAsyncInterceptorInstrumentation() { - return this.appService.testSpan(); - } - - @Get('test-pipe-instrumentation/:id') - testPipeInstrumentation(@Param('id', ParseIntPipe) id: number) { - return { value: id }; - } - - @Get('test-exception/:id') - async testException(@Param('id') id: string) { - return this.appService.testException(id); - } - - @Get('test-expected-400-exception/:id') - async testExpected400Exception(@Param('id') id: string) { - return this.appService.testExpected400Exception(id); - } - - @Get('test-expected-500-exception/:id') - async testExpected500Exception(@Param('id') id: string) { - return this.appService.testExpected500Exception(id); - } - - @Get('test-expected-rpc-exception/:id') - async testExpectedRpcException(@Param('id') id: string) { - return this.appService.testExpectedRpcException(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(); - } - - @Get('flush') - async flush() { - await flush(); - } - - @Get('test-service-use') - testServiceWithUseMethod() { - return this.appService.use(); - } - - @Get('test-service-transform') - testServiceWithTransform() { - return this.appService.transform(); - } - - @Get('test-service-intercept') - testServiceWithIntercept() { - return this.appService.intercept(); - } - - @Get('test-service-canActivate') - testServiceWithCanActivate() { - return this.appService.canActivate(); - } -} diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/app.module.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/app.module.ts deleted file mode 100644 index 567dbefeadb7..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/app.module.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { MiddlewareConsumer, Module } from '@nestjs/common'; -import { ScheduleModule } from '@nestjs/schedule'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; -import { ExampleMiddleware } from './example.middleware'; - -@Module({ - imports: [ScheduleModule.forRoot()], - controllers: [AppController], - providers: [AppService], -}) -export class AppModule { - configure(consumer: MiddlewareConsumer): void { - consumer.apply(ExampleMiddleware).forRoutes('test-middleware-instrumentation'); - } -} diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/app.service.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/app.service.ts deleted file mode 100644 index 3241e05768ec..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/app.service.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { RpcException } from '@nestjs/microservices'; -import { Cron, SchedulerRegistry } from '@nestjs/schedule'; -import type { MonitorConfig } from '@sentry/core'; -import * as Sentry from '@sentry/nestjs'; -import { SentryCron, SentryTraced } from '@sentry/nestjs'; - -const monitorConfig: MonitorConfig = { - schedule: { - type: 'crontab', - value: '* * * * *', - }, -}; - -@Injectable() -export class AppService { - constructor(private schedulerRegistry: SchedulerRegistry) {} - - testTransaction() { - Sentry.startSpan({ name: 'test-span' }, () => { - Sentry.startSpan({ name: 'child-span' }, () => {}); - }); - } - - testSpan() { - // span that should not be a child span of the middleware span - Sentry.startSpan({ name: 'test-controller-span' }, () => {}); - } - - testException(id: string) { - throw new Error(`This is an exception with id ${id}`); - } - - testExpected400Exception(id: string) { - throw new HttpException(`This is an expected 400 exception with id ${id}`, HttpStatus.BAD_REQUEST); - } - - testExpected500Exception(id: string) { - throw new HttpException(`This is an expected 500 exception with id ${id}`, HttpStatus.INTERNAL_SERVER_ERROR); - } - - testExpectedRpcException(id: string) { - throw new RpcException(`This is an expected RPC exception with id ${id}`); - } - - @SentryTraced('wait and return a string') - async wait() { - await new Promise(resolve => setTimeout(resolve, 500)); - return 'test'; - } - - async testSpanDecoratorAsync() { - return await this.wait(); - } - - @SentryTraced('return a string') - getString(): { result: string } { - return { result: 'test' }; - } - - async testSpanDecoratorSync() { - const returned = this.getString(); - // Will fail if getString() is async, because returned will be a Promise<> - return returned.result; - } - - /* - Actual cron schedule differs from schedule defined in config because Sentry - only supports minute granularity, but we don't want to wait (worst case) a - full minute for the tests to finish. - */ - @Cron('*/5 * * * * *', { name: 'test-cron-job' }) - @SentryCron('test-cron-slug', monitorConfig) - async testCron() { - console.log('Test cron!'); - } - - async killTestCron() { - this.schedulerRegistry.deleteCronJob('test-cron-job'); - } - - use() { - console.log('Test use!'); - } - - transform() { - console.log('Test transform!'); - } - - intercept() { - console.log('Test intercept!'); - } - - canActivate() { - console.log('Test canActivate!'); - } -} diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/async-example.interceptor.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/async-example.interceptor.ts deleted file mode 100644 index ac0ee60acc51..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/async-example.interceptor.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common'; -import * as Sentry from '@sentry/nestjs'; -import { tap } from 'rxjs'; - -@Injectable() -export class AsyncInterceptor implements NestInterceptor { - intercept(context: ExecutionContext, next: CallHandler) { - Sentry.startSpan({ name: 'test-async-interceptor-span' }, () => {}); - return Promise.resolve( - next.handle().pipe( - tap(() => { - Sentry.startSpan({ name: 'test-async-interceptor-span-after-route' }, () => {}); - }), - ), - ); - } -} diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example-1.interceptor.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example-1.interceptor.ts deleted file mode 100644 index 81c9f70d30e2..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example-1.interceptor.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common'; -import * as Sentry from '@sentry/nestjs'; -import { tap } from 'rxjs'; - -@Injectable() -export class ExampleInterceptor1 implements NestInterceptor { - intercept(context: ExecutionContext, next: CallHandler) { - Sentry.startSpan({ name: 'test-interceptor-span-1' }, () => {}); - return next.handle().pipe( - tap(() => { - Sentry.startSpan({ name: 'test-interceptor-span-after-route' }, () => {}); - }), - ); - } -} diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example-2.interceptor.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example-2.interceptor.ts deleted file mode 100644 index 2cf9dfb9e043..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example-2.interceptor.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common'; -import * as Sentry from '@sentry/nestjs'; - -@Injectable() -export class ExampleInterceptor2 implements NestInterceptor { - intercept(context: ExecutionContext, next: CallHandler) { - Sentry.startSpan({ name: 'test-interceptor-span-2' }, () => {}); - return next.handle().pipe(); - } -} diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example.guard.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example.guard.ts deleted file mode 100644 index e12bbdc4e994..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example.guard.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; -import * as Sentry from '@sentry/nestjs'; - -@Injectable() -export class ExampleGuard implements CanActivate { - canActivate(context: ExecutionContext): boolean { - Sentry.startSpan({ name: 'test-guard-span' }, () => {}); - return true; - } -} diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example.middleware.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example.middleware.ts deleted file mode 100644 index 31d15c9372ea..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/example.middleware.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Injectable, NestMiddleware } from '@nestjs/common'; -import * as Sentry from '@sentry/nestjs'; -import { NextFunction, Request, Response } from 'express'; - -@Injectable() -export class ExampleMiddleware implements NestMiddleware { - use(req: Request, res: Response, next: NextFunction) { - // span that should be a child span of the middleware span - Sentry.startSpan({ name: 'test-middleware-span' }, () => {}); - next(); - } -} diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/instrument.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/instrument.ts deleted file mode 100644 index 4f16ebb36d11..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/instrument.ts +++ /dev/null @@ -1,12 +0,0 @@ -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, - transportOptions: { - // We expect the app to send a lot of events in a short time - bufferSize: 1000, - }, -}); diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/main.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/main.ts deleted file mode 100644 index 3a7b5ded8645..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/src/main.ts +++ /dev/null @@ -1,20 +0,0 @@ -// 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/node-nestjs-basic/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/start-event-proxy.mjs deleted file mode 100644 index a521d4f7d4fc..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/start-event-proxy.mjs +++ /dev/null @@ -1,6 +0,0 @@ -import { startEventProxyServer } from '@sentry-internal/test-utils'; - -startEventProxyServer({ - port: 3031, - proxyServerName: 'node-nestjs-basic', -}); diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tests/cron-decorator.test.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tests/cron-decorator.test.ts deleted file mode 100644 index dc4c98aebf40..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tests/cron-decorator.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { expect, test } from '@playwright/test'; -import { waitForEnvelopeItem } from '@sentry-internal/test-utils'; - -test('Cron job triggers send of in_progress envelope', async ({ baseURL }) => { - const inProgressEnvelopePromise = waitForEnvelopeItem('node-nestjs-basic', envelope => { - return envelope[0].type === 'check_in' && envelope[1]['status'] === 'in_progress'; - }); - - const okEnvelopePromise = waitForEnvelopeItem('node-nestjs-basic', envelope => { - return envelope[0].type === 'check_in' && envelope[1]['status'] === 'ok'; - }); - - const inProgressEnvelope = await inProgressEnvelopePromise; - const okEnvelope = await okEnvelopePromise; - - expect(inProgressEnvelope[1]).toEqual( - expect.objectContaining({ - check_in_id: expect.any(String), - monitor_slug: 'test-cron-slug', - status: 'in_progress', - environment: 'qa', - monitor_config: { - schedule: { - type: 'crontab', - value: '* * * * *', - }, - }, - contexts: { - trace: { - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - }, - }, - }), - ); - - expect(okEnvelope[1]).toEqual( - expect.objectContaining({ - check_in_id: expect.any(String), - monitor_slug: 'test-cron-slug', - status: 'ok', - environment: 'qa', - duration: expect.any(Number), - contexts: { - trace: { - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - }, - }, - }), - ); - - // kill cron so tests don't get stuck - await fetch(`${baseURL}/kill-test-cron`); -}); diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tests/errors.test.ts deleted file mode 100644 index 491a64e7166c..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tests/errors.test.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { expect, test } from '@playwright/test'; -import { waitForError, waitForTransaction } from '@sentry-internal/test-utils'; - -test('Sends exception to Sentry', async ({ baseURL }) => { - const errorEventPromise = waitForError('node-nestjs-basic', event => { - return !event.type && event.exception?.values?.[0]?.value === 'This is an exception with id 123'; - }); - - const response = await fetch(`${baseURL}/test-exception/123`); - expect(response.status).toBe(500); - - const errorEvent = await errorEventPromise; - - expect(errorEvent.exception?.values).toHaveLength(1); - expect(errorEvent.exception?.values?.[0]?.value).toBe('This is an exception with id 123'); - - expect(errorEvent.request).toEqual({ - method: 'GET', - cookies: {}, - headers: expect.any(Object), - url: 'http://localhost:3030/test-exception/123', - }); - - expect(errorEvent.transaction).toEqual('GET /test-exception/:id'); - - expect(errorEvent.contexts?.trace).toEqual({ - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - span_id: expect.stringMatching(/[a-f0-9]{16}/), - }); -}); - -test('Does not send HttpExceptions to Sentry', async ({ baseURL }) => { - let errorEventOccurred = false; - - waitForError('node-nestjs-basic', event => { - if (!event.type && event.exception?.values?.[0]?.value === 'This is an expected 400 exception with id 123') { - errorEventOccurred = true; - } - - return event?.transaction === 'GET /test-expected-400-exception/:id'; - }); - - waitForError('node-nestjs-basic', event => { - if (!event.type && event.exception?.values?.[0]?.value === 'This is an expected 500 exception with id 123') { - errorEventOccurred = true; - } - - return event?.transaction === 'GET /test-expected-500-exception/:id'; - }); - - const transactionEventPromise400 = waitForTransaction('node-nestjs-basic', transactionEvent => { - return transactionEvent?.transaction === 'GET /test-expected-400-exception/:id'; - }); - - const transactionEventPromise500 = waitForTransaction('node-nestjs-basic', transactionEvent => { - return transactionEvent?.transaction === 'GET /test-expected-500-exception/:id'; - }); - - const response400 = await fetch(`${baseURL}/test-expected-400-exception/123`); - expect(response400.status).toBe(400); - - const response500 = await fetch(`${baseURL}/test-expected-500-exception/123`); - expect(response500.status).toBe(500); - - await transactionEventPromise400; - await transactionEventPromise500; - - (await fetch(`${baseURL}/flush`)).text(); - - expect(errorEventOccurred).toBe(false); -}); - -test('Does not send RpcExceptions to Sentry', async ({ baseURL }) => { - let errorEventOccurred = false; - - waitForError('node-nestjs-basic', event => { - if (!event.type && event.exception?.values?.[0]?.value === 'This is an expected RPC exception with id 123') { - errorEventOccurred = true; - } - - return event?.transaction === 'GET /test-expected-rpc-exception/:id'; - }); - - const transactionEventPromise = waitForTransaction('node-nestjs-basic', transactionEvent => { - return transactionEvent?.transaction === 'GET /test-expected-rpc-exception/:id'; - }); - - const response = await fetch(`${baseURL}/test-expected-rpc-exception/123`); - expect(response.status).toBe(500); - - await transactionEventPromise; - - (await fetch(`${baseURL}/flush`)).text(); - - expect(errorEventOccurred).toBe(false); -}); diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tests/span-decorator.test.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tests/span-decorator.test.ts deleted file mode 100644 index fa155b6dd674..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tests/span-decorator.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { expect, test } from '@playwright/test'; -import { waitForTransaction } from '@sentry-internal/test-utils'; - -test('Transaction includes span and correct value for decorated async function', async ({ baseURL }) => { - const transactionEventPromise = waitForTransaction('node-nestjs-basic', transactionEvent => { - return ( - transactionEvent?.contexts?.trace?.op === 'http.server' && - transactionEvent?.transaction === 'GET /test-span-decorator-async' - ); - }); - - const response = await fetch(`${baseURL}/test-span-decorator-async`); - const body = await response.json(); - - expect(body.result).toEqual('test'); - - const transactionEvent = await transactionEventPromise; - - expect(transactionEvent.spans).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - data: { - 'sentry.origin': 'manual', - 'sentry.op': 'wait and return a string', - }, - description: 'wait', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - status: 'ok', - op: 'wait and return a string', - origin: 'manual', - }), - ]), - ); -}); - -test('Transaction includes span and correct value for decorated sync function', async ({ baseURL }) => { - const transactionEventPromise = waitForTransaction('node-nestjs-basic', transactionEvent => { - return ( - transactionEvent?.contexts?.trace?.op === 'http.server' && - transactionEvent?.transaction === 'GET /test-span-decorator-sync' - ); - }); - - const response = await fetch(`${baseURL}/test-span-decorator-sync`); - const body = await response.json(); - - expect(body.result).toEqual('test'); - - const transactionEvent = await transactionEventPromise; - - expect(transactionEvent.spans).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - data: { - 'sentry.origin': 'manual', - 'sentry.op': 'return a string', - }, - description: 'getString', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - status: 'ok', - op: 'return a string', - origin: 'manual', - }), - ]), - ); -}); diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tests/transactions.test.ts deleted file mode 100644 index 0245b7fdb7b3..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tests/transactions.test.ts +++ /dev/null @@ -1,727 +0,0 @@ -import { expect, test } from '@playwright/test'; -import { waitForTransaction } from '@sentry-internal/test-utils'; - -test('Sends an API route transaction', async ({ baseURL }) => { - const pageloadTransactionEventPromise = waitForTransaction('node-nestjs-basic', transactionEvent => { - return ( - transactionEvent?.contexts?.trace?.op === 'http.server' && - transactionEvent?.transaction === 'GET /test-transaction' - ); - }); - - await fetch(`${baseURL}/test-transaction`); - - const transactionEvent = await pageloadTransactionEventPromise; - - expect(transactionEvent.contexts?.trace).toEqual({ - data: { - 'sentry.source': 'route', - 'sentry.origin': 'auto.http.otel.http', - 'sentry.op': 'http.server', - 'sentry.sample_rate': 1, - url: 'http://localhost:3030/test-transaction', - 'otel.kind': 'SERVER', - 'http.response.status_code': 200, - 'http.url': 'http://localhost:3030/test-transaction', - 'http.host': 'localhost:3030', - 'net.host.name': 'localhost', - 'http.method': 'GET', - 'http.scheme': 'http', - 'http.target': '/test-transaction', - 'http.user_agent': 'node', - 'http.flavor': '1.1', - 'net.transport': 'ip_tcp', - 'net.host.ip': expect.any(String), - 'net.host.port': expect.any(Number), - 'net.peer.ip': expect.any(String), - 'net.peer.port': expect.any(Number), - 'http.status_code': 200, - 'http.status_text': 'OK', - 'http.route': '/test-transaction', - }, - op: 'http.server', - span_id: expect.stringMatching(/[a-f0-9]{16}/), - status: 'ok', - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - origin: 'auto.http.otel.http', - }); - - expect(transactionEvent).toEqual( - expect.objectContaining({ - spans: expect.arrayContaining([ - { - data: { - 'express.name': '/test-transaction', - 'express.type': 'request_handler', - 'http.route': '/test-transaction', - 'sentry.origin': 'auto.http.otel.express', - 'sentry.op': 'request_handler.express', - }, - op: 'request_handler.express', - description: '/test-transaction', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - status: 'ok', - timestamp: expect.any(Number), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - origin: 'auto.http.otel.express', - }, - { - data: { - 'sentry.origin': 'manual', - }, - description: 'test-span', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - status: 'ok', - timestamp: expect.any(Number), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - origin: 'manual', - }, - { - data: { - 'sentry.origin': 'manual', - }, - description: 'child-span', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - status: 'ok', - timestamp: expect.any(Number), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - origin: 'manual', - }, - { - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - data: { - 'sentry.origin': 'auto.http.otel.nestjs', - 'sentry.op': 'handler.nestjs', - component: '@nestjs/core', - 'nestjs.version': expect.any(String), - 'nestjs.type': 'handler', - 'nestjs.callback': 'testTransaction', - }, - description: 'testTransaction', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - status: 'ok', - origin: 'auto.http.otel.nestjs', - op: 'handler.nestjs', - }, - ]), - transaction: 'GET /test-transaction', - type: 'transaction', - transaction_info: { - source: 'route', - }, - }), - ); -}); - -test('API route transaction includes nest middleware span. Spans created in and after middleware are nested correctly', async ({ - baseURL, -}) => { - const transactionEventPromise = waitForTransaction('node-nestjs-basic', transactionEvent => { - return ( - transactionEvent?.contexts?.trace?.op === 'http.server' && - transactionEvent?.transaction === 'GET /test-middleware-instrumentation' - ); - }); - - const response = await fetch(`${baseURL}/test-middleware-instrumentation`); - expect(response.status).toBe(200); - - const transactionEvent = await transactionEventPromise; - - expect(transactionEvent).toEqual( - expect.objectContaining({ - spans: expect.arrayContaining([ - { - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - data: { - 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', - }, - description: 'ExampleMiddleware', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - status: 'ok', - op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', - }, - ]), - }), - ); - - const exampleMiddlewareSpan = transactionEvent.spans.find(span => span.description === 'ExampleMiddleware'); - const exampleMiddlewareSpanId = exampleMiddlewareSpan?.span_id; - - expect(transactionEvent).toEqual( - expect.objectContaining({ - spans: expect.arrayContaining([ - { - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - data: expect.any(Object), - description: 'test-controller-span', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - status: 'ok', - origin: 'manual', - }, - { - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - data: expect.any(Object), - description: 'test-middleware-span', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - status: 'ok', - origin: 'manual', - }, - ]), - }), - ); - - // verify correct span parent-child relationships - const testMiddlewareSpan = transactionEvent.spans.find(span => span.description === 'test-middleware-span'); - const testControllerSpan = transactionEvent.spans.find(span => span.description === 'test-controller-span'); - - // 'ExampleMiddleware' is the parent of 'test-middleware-span' - expect(testMiddlewareSpan.parent_span_id).toBe(exampleMiddlewareSpanId); - - // 'ExampleMiddleware' is NOT the parent of 'test-controller-span' - expect(testControllerSpan.parent_span_id).not.toBe(exampleMiddlewareSpanId); -}); - -test('API route transaction includes nest guard span and span started in guard is nested correctly', async ({ - baseURL, -}) => { - const transactionEventPromise = waitForTransaction('node-nestjs-basic', transactionEvent => { - return ( - transactionEvent?.contexts?.trace?.op === 'http.server' && - transactionEvent?.transaction === 'GET /test-guard-instrumentation' - ); - }); - - const response = await fetch(`${baseURL}/test-guard-instrumentation`); - expect(response.status).toBe(200); - - const transactionEvent = await transactionEventPromise; - - expect(transactionEvent).toEqual( - expect.objectContaining({ - spans: expect.arrayContaining([ - { - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - data: { - 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', - }, - description: 'ExampleGuard', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - status: 'ok', - op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', - }, - ]), - }), - ); - - const exampleGuardSpan = transactionEvent.spans.find(span => span.description === 'ExampleGuard'); - const exampleGuardSpanId = exampleGuardSpan?.span_id; - - expect(transactionEvent).toEqual( - expect.objectContaining({ - spans: expect.arrayContaining([ - { - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - data: expect.any(Object), - description: 'test-guard-span', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - status: 'ok', - origin: 'manual', - }, - ]), - }), - ); - - // verify correct span parent-child relationships - const testGuardSpan = transactionEvent.spans.find(span => span.description === 'test-guard-span'); - - // 'ExampleGuard' is the parent of 'test-guard-span' - expect(testGuardSpan.parent_span_id).toBe(exampleGuardSpanId); -}); - -test('API route transaction includes nest pipe span for valid request', async ({ baseURL }) => { - const transactionEventPromise = waitForTransaction('node-nestjs-basic', transactionEvent => { - return ( - transactionEvent?.contexts?.trace?.op === 'http.server' && - transactionEvent?.transaction === 'GET /test-pipe-instrumentation/:id' - ); - }); - - const response = await fetch(`${baseURL}/test-pipe-instrumentation/123`); - expect(response.status).toBe(200); - - const transactionEvent = await transactionEventPromise; - - expect(transactionEvent).toEqual( - expect.objectContaining({ - spans: expect.arrayContaining([ - { - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - data: { - 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', - }, - description: 'ParseIntPipe', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - status: 'ok', - op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', - }, - ]), - }), - ); -}); - -test('API route transaction includes nest pipe span for invalid request', async ({ baseURL }) => { - const transactionEventPromise = waitForTransaction('node-nestjs-basic', transactionEvent => { - return ( - transactionEvent?.contexts?.trace?.op === 'http.server' && - transactionEvent?.transaction === 'GET /test-pipe-instrumentation/:id' - ); - }); - - const response = await fetch(`${baseURL}/test-pipe-instrumentation/abc`); - expect(response.status).toBe(400); - - const transactionEvent = await transactionEventPromise; - - expect(transactionEvent).toEqual( - expect.objectContaining({ - spans: expect.arrayContaining([ - { - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - data: { - 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', - }, - description: 'ParseIntPipe', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - status: 'unknown_error', - op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', - }, - ]), - }), - ); -}); - -test('API route transaction includes nest interceptor spans before route execution. Spans created in and after interceptor are nested correctly', async ({ - baseURL, -}) => { - const pageloadTransactionEventPromise = waitForTransaction('node-nestjs-basic', transactionEvent => { - return ( - transactionEvent?.contexts?.trace?.op === 'http.server' && - transactionEvent?.transaction === 'GET /test-interceptor-instrumentation' - ); - }); - - const response = await fetch(`${baseURL}/test-interceptor-instrumentation`); - expect(response.status).toBe(200); - - const transactionEvent = await pageloadTransactionEventPromise; - - // check if interceptor spans before route execution exist - expect(transactionEvent).toEqual( - expect.objectContaining({ - spans: expect.arrayContaining([ - { - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - data: { - 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', - }, - description: 'ExampleInterceptor1', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - status: 'ok', - op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', - }, - { - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - data: { - 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', - }, - description: 'ExampleInterceptor2', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - status: 'ok', - op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', - }, - ]), - }), - ); - - // get interceptor spans - const exampleInterceptor1Span = transactionEvent.spans.find(span => span.description === 'ExampleInterceptor1'); - const exampleInterceptor1SpanId = exampleInterceptor1Span?.span_id; - const exampleInterceptor2Span = transactionEvent.spans.find(span => span.description === 'ExampleInterceptor2'); - const exampleInterceptor2SpanId = exampleInterceptor2Span?.span_id; - - // check if manually started spans exist - expect(transactionEvent).toEqual( - expect.objectContaining({ - spans: expect.arrayContaining([ - { - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - data: expect.any(Object), - description: 'test-controller-span', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - status: 'ok', - origin: 'manual', - }, - { - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - data: expect.any(Object), - description: 'test-interceptor-span-1', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - status: 'ok', - origin: 'manual', - }, - { - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - data: expect.any(Object), - description: 'test-interceptor-span-2', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - status: 'ok', - origin: 'manual', - }, - ]), - }), - ); - - // verify correct span parent-child relationships - const testInterceptor1Span = transactionEvent.spans.find(span => span.description === 'test-interceptor-span-1'); - const testInterceptor2Span = transactionEvent.spans.find(span => span.description === 'test-interceptor-span-2'); - const testControllerSpan = transactionEvent.spans.find(span => span.description === 'test-controller-span'); - - // 'ExampleInterceptor1' is the parent of 'test-interceptor-span-1' - expect(testInterceptor1Span.parent_span_id).toBe(exampleInterceptor1SpanId); - - // 'ExampleInterceptor1' is NOT the parent of 'test-controller-span' - expect(testControllerSpan.parent_span_id).not.toBe(exampleInterceptor1SpanId); - - // 'ExampleInterceptor2' is the parent of 'test-interceptor-span-2' - expect(testInterceptor2Span.parent_span_id).toBe(exampleInterceptor2SpanId); - - // 'ExampleInterceptor2' is NOT the parent of 'test-controller-span' - expect(testControllerSpan.parent_span_id).not.toBe(exampleInterceptor2SpanId); -}); - -test('API route transaction includes exactly one nest interceptor span after route execution. Spans created in controller and in interceptor are nested correctly', async ({ - baseURL, -}) => { - const pageloadTransactionEventPromise = waitForTransaction('node-nestjs-basic', transactionEvent => { - return ( - transactionEvent?.contexts?.trace?.op === 'http.server' && - transactionEvent?.transaction === 'GET /test-interceptor-instrumentation' - ); - }); - - const response = await fetch(`${baseURL}/test-interceptor-instrumentation`); - expect(response.status).toBe(200); - - const transactionEvent = await pageloadTransactionEventPromise; - - // check if interceptor spans after route execution exist - expect(transactionEvent).toEqual( - expect.objectContaining({ - spans: expect.arrayContaining([ - { - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - data: { - 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', - }, - description: 'Interceptors - After Route', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - status: 'ok', - op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', - }, - ]), - }), - ); - - // check that exactly one after route span is sent - const allInterceptorSpansAfterRoute = transactionEvent.spans.filter( - span => span.description === 'Interceptors - After Route', - ); - expect(allInterceptorSpansAfterRoute.length).toBe(1); - - // get interceptor span - const exampleInterceptorSpanAfterRoute = transactionEvent.spans.find( - span => span.description === 'Interceptors - After Route', - ); - const exampleInterceptorSpanAfterRouteId = exampleInterceptorSpanAfterRoute?.span_id; - - // check if manually started span in interceptor after route exists - expect(transactionEvent).toEqual( - expect.objectContaining({ - spans: expect.arrayContaining([ - { - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - data: expect.any(Object), - description: 'test-interceptor-span-after-route', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - status: 'ok', - origin: 'manual', - }, - ]), - }), - ); - - // verify correct span parent-child relationships - const testInterceptorSpanAfterRoute = transactionEvent.spans.find( - span => span.description === 'test-interceptor-span-after-route', - ); - const testControllerSpan = transactionEvent.spans.find(span => span.description === 'test-controller-span'); - - // 'Interceptor - After Route' is the parent of 'test-interceptor-span-after-route' - expect(testInterceptorSpanAfterRoute.parent_span_id).toBe(exampleInterceptorSpanAfterRouteId); - - // 'Interceptor - After Route' is NOT the parent of 'test-controller-span' - expect(testControllerSpan.parent_span_id).not.toBe(exampleInterceptorSpanAfterRouteId); -}); - -test('API route transaction includes nest async interceptor spans before route execution. Spans created in and after async interceptor are nested correctly', async ({ - baseURL, -}) => { - const pageloadTransactionEventPromise = waitForTransaction('node-nestjs-basic', transactionEvent => { - return ( - transactionEvent?.contexts?.trace?.op === 'http.server' && - transactionEvent?.transaction === 'GET /test-async-interceptor-instrumentation' - ); - }); - - const response = await fetch(`${baseURL}/test-async-interceptor-instrumentation`); - expect(response.status).toBe(200); - - const transactionEvent = await pageloadTransactionEventPromise; - - // check if interceptor spans before route execution exist - expect(transactionEvent).toEqual( - expect.objectContaining({ - spans: expect.arrayContaining([ - { - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - data: { - 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', - }, - description: 'AsyncInterceptor', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - status: 'ok', - op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', - }, - ]), - }), - ); - - // get interceptor spans - const exampleAsyncInterceptor = transactionEvent.spans.find(span => span.description === 'AsyncInterceptor'); - const exampleAsyncInterceptorSpanId = exampleAsyncInterceptor?.span_id; - - // check if manually started spans exist - expect(transactionEvent).toEqual( - expect.objectContaining({ - spans: expect.arrayContaining([ - { - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - data: expect.any(Object), - description: 'test-controller-span', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - status: 'ok', - origin: 'manual', - }, - { - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - data: expect.any(Object), - description: 'test-async-interceptor-span', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - status: 'ok', - origin: 'manual', - }, - ]), - }), - ); - - // verify correct span parent-child relationships - const testAsyncInterceptorSpan = transactionEvent.spans.find( - span => span.description === 'test-async-interceptor-span', - ); - const testControllerSpan = transactionEvent.spans.find(span => span.description === 'test-controller-span'); - - // 'AsyncInterceptor' is the parent of 'test-async-interceptor-span' - expect(testAsyncInterceptorSpan.parent_span_id).toBe(exampleAsyncInterceptorSpanId); - - // 'AsyncInterceptor' is NOT the parent of 'test-controller-span' - expect(testControllerSpan.parent_span_id).not.toBe(exampleAsyncInterceptorSpanId); -}); - -test('API route transaction includes exactly one nest async interceptor span after route execution. Spans created in controller and in async interceptor are nested correctly', async ({ - baseURL, -}) => { - const pageloadTransactionEventPromise = waitForTransaction('node-nestjs-basic', transactionEvent => { - return ( - transactionEvent?.contexts?.trace?.op === 'http.server' && - transactionEvent?.transaction === 'GET /test-async-interceptor-instrumentation' - ); - }); - - const response = await fetch(`${baseURL}/test-async-interceptor-instrumentation`); - expect(response.status).toBe(200); - - const transactionEvent = await pageloadTransactionEventPromise; - - // check if interceptor spans after route execution exist - expect(transactionEvent).toEqual( - expect.objectContaining({ - spans: expect.arrayContaining([ - { - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - data: { - 'sentry.op': 'middleware.nestjs', - 'sentry.origin': 'auto.middleware.nestjs', - }, - description: 'Interceptors - After Route', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - status: 'ok', - op: 'middleware.nestjs', - origin: 'auto.middleware.nestjs', - }, - ]), - }), - ); - - // check that exactly one after route span is sent - const allInterceptorSpansAfterRoute = transactionEvent.spans.filter( - span => span.description === 'Interceptors - After Route', - ); - expect(allInterceptorSpansAfterRoute.length).toBe(1); - - // get interceptor span - const exampleInterceptorSpanAfterRoute = transactionEvent.spans.find( - span => span.description === 'Interceptors - After Route', - ); - const exampleInterceptorSpanAfterRouteId = exampleInterceptorSpanAfterRoute?.span_id; - - // check if manually started span in interceptor after route exists - expect(transactionEvent).toEqual( - expect.objectContaining({ - spans: expect.arrayContaining([ - { - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: expect.stringMatching(/[a-f0-9]{32}/), - data: expect.any(Object), - description: 'test-async-interceptor-span-after-route', - parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), - start_timestamp: expect.any(Number), - timestamp: expect.any(Number), - status: 'ok', - origin: 'manual', - }, - ]), - }), - ); - - // verify correct span parent-child relationships - const testInterceptorSpanAfterRoute = transactionEvent.spans.find( - span => span.description === 'test-async-interceptor-span-after-route', - ); - const testControllerSpan = transactionEvent.spans.find(span => span.description === 'test-controller-span'); - - // 'Interceptor - After Route' is the parent of 'test-interceptor-span-after-route' - expect(testInterceptorSpanAfterRoute.parent_span_id).toBe(exampleInterceptorSpanAfterRouteId); - - // 'Interceptor - After Route' is NOT the parent of 'test-controller-span' - expect(testControllerSpan.parent_span_id).not.toBe(exampleInterceptorSpanAfterRouteId); -}); - -test('Calling use method on service with Injectable decorator returns 200', async ({ baseURL }) => { - const response = await fetch(`${baseURL}/test-service-use`); - expect(response.status).toBe(200); -}); - -test('Calling transform method on service with Injectable decorator returns 200', async ({ baseURL }) => { - const response = await fetch(`${baseURL}/test-service-transform`); - expect(response.status).toBe(200); -}); - -test('Calling intercept method on service with Injectable decorator returns 200', async ({ baseURL }) => { - const response = await fetch(`${baseURL}/test-service-intercept`); - expect(response.status).toBe(200); -}); - -test('Calling canActivate method on service with Injectable decorator returns 200', async ({ baseURL }) => { - const response = await fetch(`${baseURL}/test-service-canActivate`); - expect(response.status).toBe(200); -}); diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tsconfig.build.json b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tsconfig.build.json deleted file mode 100644 index 26c30d4eddf2..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tsconfig.build.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "exclude": ["node_modules", "test", "dist"] -} diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tsconfig.json b/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tsconfig.json deleted file mode 100644 index 95f5641cf7f3..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-basic/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "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/node-nestjs-distributed-tracing/.gitignore b/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/.gitignore deleted file mode 100644 index 4b56acfbebf4..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/.gitignore +++ /dev/null @@ -1,56 +0,0 @@ -# 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/node-nestjs-distributed-tracing/.npmrc b/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/.npmrc deleted file mode 100644 index 070f80f05092..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/.npmrc +++ /dev/null @@ -1,2 +0,0 @@ -@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/node-nestjs-distributed-tracing/nest-cli.json b/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/nest-cli.json deleted file mode 100644 index f9aa683b1ad5..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/nest-cli.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/nest-cli", - "collection": "@nestjs/schematics", - "sourceRoot": "src", - "compilerOptions": { - "deleteOutDir": true - } -} diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/package.json b/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/package.json deleted file mode 100644 index f6fbc916ad68..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/package.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "node-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 || *", - "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/node-nestjs-distributed-tracing/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/playwright.config.mjs deleted file mode 100644 index 31f2b913b58b..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/playwright.config.mjs +++ /dev/null @@ -1,7 +0,0 @@ -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/node-nestjs-distributed-tracing/src/instrument.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/instrument.ts deleted file mode 100644 index 1cf7b8ee1f76..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/instrument.ts +++ /dev/null @@ -1,13 +0,0 @@ -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, - tracePropagationTargets: ['http://localhost:3030', '/external-allowed'], - transportOptions: { - // We expect the app to send a lot of events in a short time - bufferSize: 1000, - }, -}); diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/main.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/main.ts deleted file mode 100644 index 7e3f7e0e2b52..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/main.ts +++ /dev/null @@ -1,23 +0,0 @@ -// 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/node-nestjs-distributed-tracing/src/trace-initiator.controller.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/trace-initiator.controller.ts deleted file mode 100644 index 62e0c299a239..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/trace-initiator.controller.ts +++ /dev/null @@ -1,42 +0,0 @@ -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/node-nestjs-distributed-tracing/src/trace-initiator.module.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/trace-initiator.module.ts deleted file mode 100644 index 9256f29928ab..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/trace-initiator.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -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/node-nestjs-distributed-tracing/src/trace-initiator.service.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/trace-initiator.service.ts deleted file mode 100644 index 67c5333cedaf..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/trace-initiator.service.ts +++ /dev/null @@ -1,47 +0,0 @@ -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/node-nestjs-distributed-tracing/src/trace-receiver.controller.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/trace-receiver.controller.ts deleted file mode 100644 index 2a1899f1097d..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/trace-receiver.controller.ts +++ /dev/null @@ -1,17 +0,0 @@ -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/node-nestjs-distributed-tracing/src/trace-receiver.module.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/trace-receiver.module.ts deleted file mode 100644 index 2680b3071fb7..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/trace-receiver.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -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/node-nestjs-distributed-tracing/src/trace-receiver.service.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/trace-receiver.service.ts deleted file mode 100644 index a40b28ad0778..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/trace-receiver.service.ts +++ /dev/null @@ -1,18 +0,0 @@ -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/node-nestjs-distributed-tracing/src/utils.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/utils.ts deleted file mode 100644 index 27639ef26349..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/src/utils.ts +++ /dev/null @@ -1,26 +0,0 @@ -import * as http from 'http'; - -export function makeHttpRequest(url) { - return new Promise(resolve => { - const data = []; - - http - .request(url, httpRes => { - httpRes.on('data', chunk => { - data.push(chunk); - }); - httpRes.on('error', error => { - resolve({ error: error.message, url }); - }); - httpRes.on('end', () => { - try { - const json = JSON.parse(Buffer.concat(data).toString()); - resolve(json); - } catch { - resolve({ data: Buffer.concat(data).toString(), url }); - } - }); - }) - .end(); - }); -} diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/start-event-proxy.mjs deleted file mode 100644 index 1db7d30f8680..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/start-event-proxy.mjs +++ /dev/null @@ -1,6 +0,0 @@ -import { startEventProxyServer } from '@sentry-internal/test-utils'; - -startEventProxyServer({ - port: 3031, - proxyServerName: 'node-nestjs-distributed-tracing', -}); diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/tests/propagation.test.ts b/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/tests/propagation.test.ts deleted file mode 100644 index d252cdad5fa3..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/tests/propagation.test.ts +++ /dev/null @@ -1,356 +0,0 @@ -import crypto from 'crypto'; -import { expect, test } from '@playwright/test'; -import { waitForTransaction } from '@sentry-internal/test-utils'; -import { SpanJSON } from '@sentry/core'; - -test('Propagates trace for outgoing http requests', async ({ baseURL }) => { - const id = crypto.randomUUID(); - - const inboundTransactionPromise = waitForTransaction('node-nestjs-distributed-tracing', transactionEvent => { - return ( - transactionEvent.contexts?.trace?.op === 'http.server' && - transactionEvent.contexts?.trace?.data?.['http.target'] === `/test-inbound-headers/${id}` - ); - }); - - const outboundTransactionPromise = waitForTransaction('node-nestjs-distributed-tracing', transactionEvent => { - return ( - transactionEvent.contexts?.trace?.op === 'http.server' && - transactionEvent.contexts?.trace?.data?.['http.target'] === `/test-outgoing-http/${id}` - ); - }); - - const response = await fetch(`${baseURL}/test-outgoing-http/${id}`); - const data = await response.json(); - - const inboundTransaction = await inboundTransactionPromise; - const outboundTransaction = await outboundTransactionPromise; - - const traceId = outboundTransaction?.contexts?.trace?.trace_id; - const outgoingHttpSpan = outboundTransaction?.spans?.find(span => span.op === 'http.client') as SpanJSON | undefined; - - expect(outgoingHttpSpan).toBeDefined(); - - const outgoingHttpSpanId = outgoingHttpSpan?.span_id; - - expect(traceId).toEqual(expect.any(String)); - - // data is passed through from the inbound request, to verify we have the correct headers set - const inboundHeaderSentryTrace = data.headers?.['sentry-trace']; - const inboundHeaderBaggage = data.headers?.['baggage']; - - expect(inboundHeaderSentryTrace).toEqual(`${traceId}-${outgoingHttpSpanId}-1`); - expect(inboundHeaderBaggage).toBeDefined(); - - const baggage = (inboundHeaderBaggage || '').split(','); - expect(baggage).toEqual( - expect.arrayContaining([ - 'sentry-environment=qa', - `sentry-trace_id=${traceId}`, - expect.stringMatching(/sentry-public_key=/), - ]), - ); - - expect(outboundTransaction.contexts?.trace).toEqual({ - data: { - 'sentry.source': 'route', - 'sentry.origin': 'auto.http.otel.http', - 'sentry.op': 'http.server', - 'sentry.sample_rate': 1, - url: `http://localhost:3030/test-outgoing-http/${id}`, - 'otel.kind': 'SERVER', - 'http.response.status_code': 200, - 'http.url': `http://localhost:3030/test-outgoing-http/${id}`, - 'http.host': 'localhost:3030', - 'net.host.name': 'localhost', - 'http.method': 'GET', - 'http.scheme': 'http', - 'http.target': `/test-outgoing-http/${id}`, - 'http.user_agent': 'node', - 'http.flavor': '1.1', - 'net.transport': 'ip_tcp', - 'net.host.ip': expect.any(String), - 'net.host.port': expect.any(Number), - 'net.peer.ip': expect.any(String), - 'net.peer.port': expect.any(Number), - 'http.status_code': 200, - 'http.status_text': 'OK', - 'http.route': '/test-outgoing-http/:id', - }, - op: 'http.server', - span_id: expect.stringMatching(/[a-f0-9]{16}/), - status: 'ok', - trace_id: traceId, - origin: 'auto.http.otel.http', - }); - - expect(inboundTransaction.contexts?.trace).toEqual({ - data: { - 'sentry.source': 'route', - 'sentry.origin': 'auto.http.otel.http', - 'sentry.op': 'http.server', - 'sentry.sample_rate': 1, - url: `http://localhost:3030/test-inbound-headers/${id}`, - 'otel.kind': 'SERVER', - 'http.response.status_code': 200, - 'http.url': `http://localhost:3030/test-inbound-headers/${id}`, - 'http.host': 'localhost:3030', - 'net.host.name': 'localhost', - 'http.method': 'GET', - 'http.scheme': 'http', - 'http.target': `/test-inbound-headers/${id}`, - 'http.flavor': '1.1', - 'net.transport': 'ip_tcp', - 'net.host.ip': expect.any(String), - 'net.host.port': expect.any(Number), - 'net.peer.ip': expect.any(String), - 'net.peer.port': expect.any(Number), - 'http.status_code': 200, - 'http.status_text': 'OK', - 'http.route': '/test-inbound-headers/:id', - }, - op: 'http.server', - parent_span_id: outgoingHttpSpanId, - span_id: expect.stringMatching(/[a-f0-9]{16}/), - status: 'ok', - trace_id: traceId, - origin: 'auto.http.otel.http', - }); -}); - -test('Propagates trace for outgoing fetch requests', async ({ baseURL }) => { - const id = crypto.randomUUID(); - - const inboundTransactionPromise = waitForTransaction('node-nestjs-distributed-tracing', transactionEvent => { - return ( - transactionEvent?.contexts?.trace?.op === 'http.server' && - transactionEvent.contexts?.trace?.data?.['http.target'] === `/test-inbound-headers/${id}` - ); - }); - - const outboundTransactionPromise = waitForTransaction('node-nestjs-distributed-tracing', transactionEvent => { - return ( - transactionEvent?.contexts?.trace?.op === 'http.server' && - transactionEvent.contexts?.trace?.data?.['http.target'] === `/test-outgoing-fetch/${id}` - ); - }); - - const response = await fetch(`${baseURL}/test-outgoing-fetch/${id}`); - const data = await response.json(); - - const inboundTransaction = await inboundTransactionPromise; - const outboundTransaction = await outboundTransactionPromise; - - const traceId = outboundTransaction?.contexts?.trace?.trace_id; - const outgoingHttpSpan = outboundTransaction?.spans?.find(span => span.op === 'http.client') as SpanJSON | undefined; - - expect(outgoingHttpSpan).toBeDefined(); - - const outgoingHttpSpanId = outgoingHttpSpan?.span_id; - - expect(traceId).toEqual(expect.any(String)); - - // data is passed through from the inbound request, to verify we have the correct headers set - const inboundHeaderSentryTrace = data.headers?.['sentry-trace']; - const inboundHeaderBaggage = data.headers?.['baggage']; - - expect(inboundHeaderSentryTrace).toEqual(`${traceId}-${outgoingHttpSpanId}-1`); - expect(inboundHeaderBaggage).toBeDefined(); - - const baggage = (inboundHeaderBaggage || '').split(','); - expect(baggage).toEqual( - expect.arrayContaining([ - 'sentry-environment=qa', - `sentry-trace_id=${traceId}`, - expect.stringMatching(/sentry-public_key=/), - ]), - ); - - expect(outboundTransaction.contexts?.trace).toEqual({ - data: { - 'sentry.source': 'route', - 'sentry.origin': 'auto.http.otel.http', - 'sentry.op': 'http.server', - 'sentry.sample_rate': 1, - url: `http://localhost:3030/test-outgoing-fetch/${id}`, - 'otel.kind': 'SERVER', - 'http.response.status_code': 200, - 'http.url': `http://localhost:3030/test-outgoing-fetch/${id}`, - 'http.host': 'localhost:3030', - 'net.host.name': 'localhost', - 'http.method': 'GET', - 'http.scheme': 'http', - 'http.target': `/test-outgoing-fetch/${id}`, - 'http.user_agent': 'node', - 'http.flavor': '1.1', - 'net.transport': 'ip_tcp', - 'net.host.ip': expect.any(String), - 'net.host.port': expect.any(Number), - 'net.peer.ip': expect.any(String), - 'net.peer.port': expect.any(Number), - 'http.status_code': 200, - 'http.status_text': 'OK', - 'http.route': '/test-outgoing-fetch/:id', - }, - op: 'http.server', - span_id: expect.stringMatching(/[a-f0-9]{16}/), - status: 'ok', - trace_id: traceId, - origin: 'auto.http.otel.http', - }); - - expect(inboundTransaction.contexts?.trace).toEqual({ - data: expect.objectContaining({ - 'sentry.source': 'route', - 'sentry.origin': 'auto.http.otel.http', - 'sentry.op': 'http.server', - 'sentry.sample_rate': 1, - url: `http://localhost:3030/test-inbound-headers/${id}`, - 'otel.kind': 'SERVER', - 'http.response.status_code': 200, - 'http.url': `http://localhost:3030/test-inbound-headers/${id}`, - 'http.host': 'localhost:3030', - 'net.host.name': 'localhost', - 'http.method': 'GET', - 'http.scheme': 'http', - 'http.target': `/test-inbound-headers/${id}`, - 'http.flavor': '1.1', - 'net.transport': 'ip_tcp', - 'net.host.ip': expect.any(String), - 'net.host.port': expect.any(Number), - 'net.peer.ip': expect.any(String), - 'net.peer.port': expect.any(Number), - 'http.status_code': 200, - 'http.status_text': 'OK', - 'http.route': '/test-inbound-headers/:id', - }), - op: 'http.server', - parent_span_id: outgoingHttpSpanId, - span_id: expect.stringMatching(/[a-f0-9]{16}/), - status: 'ok', - trace_id: traceId, - origin: 'auto.http.otel.http', - }); -}); - -test('Propagates trace for outgoing external http requests', async ({ baseURL }) => { - const inboundTransactionPromise = waitForTransaction('node-nestjs-distributed-tracing', transactionEvent => { - return ( - transactionEvent?.contexts?.trace?.op === 'http.server' && - transactionEvent.contexts?.trace?.data?.['http.target'] === `/test-outgoing-http-external-allowed` - ); - }); - - const response = await fetch(`${baseURL}/test-outgoing-http-external-allowed`); - const data = await response.json(); - - const inboundTransaction = await inboundTransactionPromise; - - const traceId = inboundTransaction?.contexts?.trace?.trace_id; - const spanId = inboundTransaction?.spans?.find(span => span.op === 'http.client')?.span_id; - - expect(traceId).toEqual(expect.any(String)); - expect(spanId).toEqual(expect.any(String)); - - expect(data).toEqual({ - headers: expect.objectContaining({ - 'sentry-trace': `${traceId}-${spanId}-1`, - baggage: expect.any(String), - }), - route: 'external-allowed', - }); - - const baggage = (data.headers.baggage || '').split(','); - expect(baggage).toEqual( - expect.arrayContaining([ - 'sentry-environment=qa', - `sentry-trace_id=${traceId}`, - expect.stringMatching(/sentry-public_key=/), - ]), - ); -}); - -test('Does not propagate outgoing http requests not covered by tracePropagationTargets', async ({ baseURL }) => { - const inboundTransactionPromise = waitForTransaction('node-nestjs-distributed-tracing', transactionEvent => { - return ( - transactionEvent?.contexts?.trace?.op === 'http.server' && - transactionEvent.contexts?.trace?.data?.['http.target'] === `/test-outgoing-http-external-disallowed` - ); - }); - - const response = await fetch(`${baseURL}/test-outgoing-http-external-disallowed`); - const data = await response.json(); - - const inboundTransaction = await inboundTransactionPromise; - - const traceId = inboundTransaction?.contexts?.trace?.trace_id; - const spanId = inboundTransaction?.spans?.find(span => span.op === 'http.client')?.span_id; - - expect(traceId).toEqual(expect.any(String)); - expect(spanId).toEqual(expect.any(String)); - - expect(data.route).toBe('external-disallowed'); - expect(data.headers?.['sentry-trace']).toBeUndefined(); - expect(data.headers?.baggage).toBeUndefined(); -}); - -test('Propagates trace for outgoing external fetch requests', async ({ baseURL }) => { - const inboundTransactionPromise = waitForTransaction('node-nestjs-distributed-tracing', transactionEvent => { - return ( - transactionEvent?.contexts?.trace?.op === 'http.server' && - transactionEvent.contexts?.trace?.data?.['http.target'] === `/test-outgoing-fetch-external-allowed` - ); - }); - - const response = await fetch(`${baseURL}/test-outgoing-fetch-external-allowed`); - const data = await response.json(); - - const inboundTransaction = await inboundTransactionPromise; - - const traceId = inboundTransaction?.contexts?.trace?.trace_id; - const spanId = inboundTransaction?.spans?.find(span => span.op === 'http.client')?.span_id; - - expect(traceId).toEqual(expect.any(String)); - expect(spanId).toEqual(expect.any(String)); - - expect(data).toEqual({ - headers: expect.objectContaining({ - 'sentry-trace': `${traceId}-${spanId}-1`, - baggage: expect.any(String), - }), - route: 'external-allowed', - }); - - const baggage = (data.headers.baggage || '').split(','); - expect(baggage).toEqual( - expect.arrayContaining([ - 'sentry-environment=qa', - `sentry-trace_id=${traceId}`, - expect.stringMatching(/sentry-public_key=/), - ]), - ); -}); - -test('Does not propagate outgoing fetch requests not covered by tracePropagationTargets', async ({ baseURL }) => { - const inboundTransactionPromise = waitForTransaction('node-nestjs-distributed-tracing', transactionEvent => { - return ( - transactionEvent?.contexts?.trace?.op === 'http.server' && - transactionEvent.contexts?.trace?.data?.['http.target'] === `/test-outgoing-fetch-external-disallowed` - ); - }); - - const response = await fetch(`${baseURL}/test-outgoing-fetch-external-disallowed`); - const data = await response.json(); - - const inboundTransaction = await inboundTransactionPromise; - - const traceId = inboundTransaction?.contexts?.trace?.trace_id; - const spanId = inboundTransaction?.spans?.find(span => span.op === 'http.client')?.span_id; - - expect(traceId).toEqual(expect.any(String)); - expect(spanId).toEqual(expect.any(String)); - - expect(data.route).toBe('external-disallowed'); - expect(data.headers?.['sentry-trace']).toBeUndefined(); - expect(data.headers?.baggage).toBeUndefined(); -}); diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/tsconfig.build.json b/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/tsconfig.build.json deleted file mode 100644 index 26c30d4eddf2..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/tsconfig.build.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "exclude": ["node_modules", "test", "dist"] -} diff --git a/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/tsconfig.json b/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/tsconfig.json deleted file mode 100644 index 95f5641cf7f3..000000000000 --- a/dev-packages/e2e-tests/test-applications/node-nestjs-distributed-tracing/tsconfig.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "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/node-integration-tests/suites/tracing/nestjs-errors-no-express/scenario.ts b/dev-packages/node-integration-tests/suites/tracing/nestjs-errors-no-express/scenario.ts deleted file mode 100644 index 3dcf30f97b20..000000000000 --- a/dev-packages/node-integration-tests/suites/tracing/nestjs-errors-no-express/scenario.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -/* eslint-disable @typescript-eslint/explicit-member-accessibility */ -import { loggingTransport, sendPortToRunner } from '@sentry-internal/node-integration-tests'; -import * as Sentry from '@sentry/node'; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - tracesSampleRate: 0, - transport: loggingTransport, - integrations: integrations => integrations.filter(i => i.name !== 'Express'), - debug: true, -}); - -import { Controller, Get, Injectable, Module, Param } from '@nestjs/common'; -import { BaseExceptionFilter, HttpAdapterHost, NestFactory } from '@nestjs/core'; - -const port = 3480; - -// Stop the process from exiting before the transaction is sent -// eslint-disable-next-line @typescript-eslint/no-empty-function -setInterval(() => {}, 1000); - -@Injectable() -class AppService { - getHello(): string { - return 'Hello World!'; - } -} - -@Controller() -class AppController { - constructor(private readonly appService: AppService) {} - - @Get('test-exception/:id') - async testException(@Param('id') id: string): Promise { - Sentry.captureException(new Error(`error with id ${id}`)); - } -} - -@Module({ - imports: [], - controllers: [AppController], - providers: [AppService], -}) -class AppModule {} - -async function run(): Promise { - const app = await NestFactory.create(AppModule); - const { httpAdapter } = app.get(HttpAdapterHost); - // eslint-disable-next-line deprecation/deprecation - Sentry.setupNestErrorHandler(app, new BaseExceptionFilter(httpAdapter)); - await app.listen(port); - sendPortToRunner(port); -} - -// eslint-disable-next-line @typescript-eslint/no-floating-promises -run(); diff --git a/dev-packages/node-integration-tests/suites/tracing/nestjs-errors-no-express/test.ts b/dev-packages/node-integration-tests/suites/tracing/nestjs-errors-no-express/test.ts deleted file mode 100644 index 84b1aeef40c4..000000000000 --- a/dev-packages/node-integration-tests/suites/tracing/nestjs-errors-no-express/test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { conditionalTest } from '../../../utils'; -import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; - -const { TS_VERSION } = process.env; -const isOldTS = TS_VERSION && TS_VERSION.startsWith('3.'); - -// This is required to run the test with ts-node and decorators -process.env.TS_NODE_PROJECT = `${__dirname}/tsconfig.json`; - -conditionalTest({ min: 16 })('nestjs auto instrumentation', () => { - afterAll(async () => { - cleanupChildProcesses(); - }); - - test("should assign scope's transactionName if spans are not sampled and express integration is disabled", done => { - if (isOldTS) { - // Skipping test on old TypeScript - return done(); - } - - createRunner(__dirname, 'scenario.ts') - .expect({ - event: { - exception: { - values: [ - { - value: 'error with id 456', - }, - ], - }, - transaction: 'GET /test-exception/:id', - }, - }) - .start(done) - .makeRequest('get', '/test-exception/456'); - }); -}); diff --git a/dev-packages/node-integration-tests/suites/tracing/nestjs-errors-no-express/tsconfig.json b/dev-packages/node-integration-tests/suites/tracing/nestjs-errors-no-express/tsconfig.json deleted file mode 100644 index 84b8f8d6c44e..000000000000 --- a/dev-packages/node-integration-tests/suites/tracing/nestjs-errors-no-express/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "include": ["scenario.ts"], - "compilerOptions": { - "module": "commonjs", - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "target": "ES2021", - } -} diff --git a/dev-packages/node-integration-tests/suites/tracing/nestjs-errors/scenario.ts b/dev-packages/node-integration-tests/suites/tracing/nestjs-errors/scenario.ts deleted file mode 100644 index 6f4c9fa6955e..000000000000 --- a/dev-packages/node-integration-tests/suites/tracing/nestjs-errors/scenario.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -/* eslint-disable @typescript-eslint/explicit-member-accessibility */ -import { loggingTransport, sendPortToRunner } from '@sentry-internal/node-integration-tests'; -import * as Sentry from '@sentry/node'; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - tracesSampleRate: 0, - transport: loggingTransport, -}); - -import { Controller, Get, Injectable, Module, Param } from '@nestjs/common'; -import { BaseExceptionFilter, HttpAdapterHost, NestFactory } from '@nestjs/core'; - -const port = 3460; - -// Stop the process from exiting before the transaction is sent -// eslint-disable-next-line @typescript-eslint/no-empty-function -setInterval(() => {}, 1000); - -@Injectable() -class AppService { - getHello(): string { - return 'Hello World!'; - } -} - -@Controller() -class AppController { - constructor(private readonly appService: AppService) {} - - @Get('test-exception/:id') - async testException(@Param('id') id: string): Promise { - Sentry.captureException(new Error(`error with id ${id}`)); - } -} - -@Module({ - imports: [], - controllers: [AppController], - providers: [AppService], -}) -class AppModule {} - -async function run(): Promise { - const app = await NestFactory.create(AppModule); - const { httpAdapter } = app.get(HttpAdapterHost); - // eslint-disable-next-line deprecation/deprecation - Sentry.setupNestErrorHandler(app, new BaseExceptionFilter(httpAdapter)); - await app.listen(port); - sendPortToRunner(port); -} - -// eslint-disable-next-line @typescript-eslint/no-floating-promises -run(); diff --git a/dev-packages/node-integration-tests/suites/tracing/nestjs-errors/test.ts b/dev-packages/node-integration-tests/suites/tracing/nestjs-errors/test.ts deleted file mode 100644 index 1b366307eac6..000000000000 --- a/dev-packages/node-integration-tests/suites/tracing/nestjs-errors/test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { conditionalTest } from '../../../utils'; -import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; - -const { TS_VERSION } = process.env; -const isOldTS = TS_VERSION && TS_VERSION.startsWith('3.'); - -// This is required to run the test with ts-node and decorators -process.env.TS_NODE_PROJECT = `${__dirname}/tsconfig.json`; - -conditionalTest({ min: 16 })('nestjs auto instrumentation', () => { - afterAll(async () => { - cleanupChildProcesses(); - }); - - test("should assign scope's transactionName if spans are not sampled", done => { - if (isOldTS) { - // Skipping test on old TypeScript - return done(); - } - - createRunner(__dirname, 'scenario.ts') - .expect({ - event: { - exception: { - values: [ - { - value: 'error with id 123', - }, - ], - }, - transaction: 'GET /test-exception/:id', - }, - }) - .start(done) - .makeRequest('get', '/test-exception/123'); - }); -}); diff --git a/dev-packages/node-integration-tests/suites/tracing/nestjs-errors/tsconfig.json b/dev-packages/node-integration-tests/suites/tracing/nestjs-errors/tsconfig.json deleted file mode 100644 index 84b8f8d6c44e..000000000000 --- a/dev-packages/node-integration-tests/suites/tracing/nestjs-errors/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "include": ["scenario.ts"], - "compilerOptions": { - "module": "commonjs", - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "target": "ES2021", - } -} diff --git a/dev-packages/node-integration-tests/suites/tracing/nestjs-no-express/scenario.ts b/dev-packages/node-integration-tests/suites/tracing/nestjs-no-express/scenario.ts deleted file mode 100644 index 62e042a4bf7a..000000000000 --- a/dev-packages/node-integration-tests/suites/tracing/nestjs-no-express/scenario.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -/* eslint-disable @typescript-eslint/explicit-member-accessibility */ -import { loggingTransport, sendPortToRunner } from '@sentry-internal/node-integration-tests'; -import * as Sentry from '@sentry/node'; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - tracesSampleRate: 1, - transport: loggingTransport, - integrations: integrations => integrations.filter(i => i.name !== 'Express'), - debug: true, -}); - -import { Controller, Get, Injectable, Module, Param } from '@nestjs/common'; -import { BaseExceptionFilter, HttpAdapterHost, NestFactory } from '@nestjs/core'; - -const port = 3470; - -// Stop the process from exiting before the transaction is sent -// eslint-disable-next-line @typescript-eslint/no-empty-function -setInterval(() => {}, 1000); - -@Injectable() -class AppService { - getHello(): string { - return 'Hello World!'; - } -} - -@Controller() -class AppController { - constructor(private readonly appService: AppService) {} - - @Get('test-exception/:id') - async testException(@Param('id') id: string): Promise { - Sentry.captureException(new Error(`error with id ${id}`)); - } -} - -@Module({ - imports: [], - controllers: [AppController], - providers: [AppService], -}) -class AppModule {} - -async function run(): Promise { - const app = await NestFactory.create(AppModule); - const { httpAdapter } = app.get(HttpAdapterHost); - // eslint-disable-next-line deprecation/deprecation - Sentry.setupNestErrorHandler(app, new BaseExceptionFilter(httpAdapter)); - await app.listen(port); - sendPortToRunner(port); -} - -// eslint-disable-next-line @typescript-eslint/no-floating-promises -run(); diff --git a/dev-packages/node-integration-tests/suites/tracing/nestjs-no-express/test.ts b/dev-packages/node-integration-tests/suites/tracing/nestjs-no-express/test.ts deleted file mode 100644 index 59532ef989da..000000000000 --- a/dev-packages/node-integration-tests/suites/tracing/nestjs-no-express/test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { conditionalTest } from '../../../utils'; -import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; - -const { TS_VERSION } = process.env; -const isOldTS = TS_VERSION && TS_VERSION.startsWith('3.'); - -// This is required to run the test with ts-node and decorators -process.env.TS_NODE_PROJECT = `${__dirname}/tsconfig.json`; - -conditionalTest({ min: 16 })('nestjs auto instrumentation', () => { - afterAll(async () => { - cleanupChildProcesses(); - }); - - test("should assign scope's transactionName if express integration is disabled", done => { - if (isOldTS) { - // Skipping test on old TypeScript - return done(); - } - - createRunner(__dirname, 'scenario.ts') - .ignore('transaction') - .expect({ - event: { - exception: { - values: [ - { - value: 'error with id 456', - }, - ], - }, - transaction: 'GET /test-exception/:id', - }, - }) - .start(done) - .makeRequest('get', '/test-exception/456'); - }); -}); diff --git a/dev-packages/node-integration-tests/suites/tracing/nestjs-no-express/tsconfig.json b/dev-packages/node-integration-tests/suites/tracing/nestjs-no-express/tsconfig.json deleted file mode 100644 index 84b8f8d6c44e..000000000000 --- a/dev-packages/node-integration-tests/suites/tracing/nestjs-no-express/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "include": ["scenario.ts"], - "compilerOptions": { - "module": "commonjs", - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "target": "ES2021", - } -} diff --git a/dev-packages/node-integration-tests/suites/tracing/nestjs/scenario.ts b/dev-packages/node-integration-tests/suites/tracing/nestjs/scenario.ts deleted file mode 100644 index 449dc82fd070..000000000000 --- a/dev-packages/node-integration-tests/suites/tracing/nestjs/scenario.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ -/* eslint-disable @typescript-eslint/explicit-member-accessibility */ -import { loggingTransport, sendPortToRunner } from '@sentry-internal/node-integration-tests'; -import * as Sentry from '@sentry/node'; - -Sentry.init({ - dsn: 'https://public@dsn.ingest.sentry.io/1337', - release: '1.0', - tracesSampleRate: 1.0, - transport: loggingTransport, -}); - -import { Controller, Get, Injectable, Module } from '@nestjs/common'; -import { BaseExceptionFilter, HttpAdapterHost, NestFactory } from '@nestjs/core'; - -const port = 3450; - -// Stop the process from exiting before the transaction is sent -// eslint-disable-next-line @typescript-eslint/no-empty-function -setInterval(() => {}, 1000); - -@Injectable() -class AppService { - getHello(): string { - return 'Hello World!'; - } -} - -@Controller() -class AppController { - constructor(private readonly appService: AppService) {} - - @Get() - getHello(): string { - return this.appService.getHello(); - } -} - -@Module({ - imports: [], - controllers: [AppController], - providers: [AppService], -}) -class AppModule {} - -async function run(): Promise { - const app = await NestFactory.create(AppModule); - await app.listen(port); - - const { httpAdapter } = app.get(HttpAdapterHost); - // eslint-disable-next-line deprecation/deprecation - Sentry.setupNestErrorHandler(app, new BaseExceptionFilter(httpAdapter)); - sendPortToRunner(port); -} - -// eslint-disable-next-line @typescript-eslint/no-floating-promises -run(); diff --git a/dev-packages/node-integration-tests/suites/tracing/nestjs/test.ts b/dev-packages/node-integration-tests/suites/tracing/nestjs/test.ts deleted file mode 100644 index 80570044d64d..000000000000 --- a/dev-packages/node-integration-tests/suites/tracing/nestjs/test.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { conditionalTest } from '../../../utils'; -import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; - -const { TS_VERSION } = process.env; -const isOldTS = TS_VERSION && TS_VERSION.startsWith('3.'); - -// This is required to run the test with ts-node and decorators -process.env.TS_NODE_PROJECT = `${__dirname}/tsconfig.json`; - -conditionalTest({ min: 16 })('nestjs auto instrumentation', () => { - afterAll(async () => { - cleanupChildProcesses(); - }); - - const CREATION_TRANSACTION = { - transaction: 'Create Nest App', - }; - - const GET_TRANSACTION = { - transaction: 'GET /', - spans: expect.arrayContaining([ - expect.objectContaining({ - description: 'GET /', - data: expect.objectContaining({ - 'nestjs.callback': 'getHello', - 'nestjs.controller': 'AppController', - 'nestjs.type': 'request_context', - 'sentry.op': 'request_context.nestjs', - 'sentry.origin': 'auto.http.otel.nestjs', - component: '@nestjs/core', - 'http.method': 'GET', - 'http.route': '/', - 'http.url': '/', - }), - }), - ]), - }; - - test('should auto-instrument `nestjs` package', done => { - if (isOldTS) { - // Skipping test on old TypeScript - return done(); - } - - createRunner(__dirname, 'scenario.ts') - .expect({ transaction: CREATION_TRANSACTION }) - .expect({ transaction: GET_TRANSACTION }) - .start(done) - .makeRequest('get', '/'); - }); -}); diff --git a/dev-packages/node-integration-tests/suites/tracing/nestjs/tsconfig.json b/dev-packages/node-integration-tests/suites/tracing/nestjs/tsconfig.json deleted file mode 100644 index 84b8f8d6c44e..000000000000 --- a/dev-packages/node-integration-tests/suites/tracing/nestjs/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "include": ["scenario.ts"], - "compilerOptions": { - "module": "commonjs", - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "target": "ES2021", - } -} diff --git a/packages/astro/src/index.server.ts b/packages/astro/src/index.server.ts index 32f89c174751..8557cfa4eb48 100644 --- a/packages/astro/src/index.server.ts +++ b/packages/astro/src/index.server.ts @@ -88,8 +88,6 @@ export { mysql2Integration, mysqlIntegration, nativeNodeFetchIntegration, - // eslint-disable-next-line deprecation/deprecation - nestIntegration, NodeClient, nodeContextIntegration, onUncaughtExceptionIntegration, @@ -121,8 +119,6 @@ export { setupExpressErrorHandler, setupHapiErrorHandler, setupKoaErrorHandler, - // eslint-disable-next-line deprecation/deprecation - setupNestErrorHandler, setUser, spanToBaggageHeader, spanToJSON, diff --git a/packages/aws-serverless/src/index.ts b/packages/aws-serverless/src/index.ts index 5a6894dc50f7..856d77d62be7 100644 --- a/packages/aws-serverless/src/index.ts +++ b/packages/aws-serverless/src/index.ts @@ -104,10 +104,6 @@ export { mysql2Integration, redisIntegration, tediousIntegration, - // eslint-disable-next-line deprecation/deprecation - nestIntegration, - // eslint-disable-next-line deprecation/deprecation - setupNestErrorHandler, postgresIntegration, prismaIntegration, childProcessIntegration, diff --git a/packages/bun/src/index.ts b/packages/bun/src/index.ts index dcae6e98aa8d..96190169169e 100644 --- a/packages/bun/src/index.ts +++ b/packages/bun/src/index.ts @@ -127,10 +127,6 @@ export { mysql2Integration, redisIntegration, tediousIntegration, - // eslint-disable-next-line deprecation/deprecation - nestIntegration, - // eslint-disable-next-line deprecation/deprecation - setupNestErrorHandler, postgresIntegration, prismaIntegration, hapiIntegration, diff --git a/packages/google-cloud-serverless/src/index.ts b/packages/google-cloud-serverless/src/index.ts index ad0e081af9da..8f7fdcfb24d0 100644 --- a/packages/google-cloud-serverless/src/index.ts +++ b/packages/google-cloud-serverless/src/index.ts @@ -104,10 +104,6 @@ export { mysql2Integration, redisIntegration, tediousIntegration, - // eslint-disable-next-line deprecation/deprecation - nestIntegration, - // eslint-disable-next-line deprecation/deprecation - setupNestErrorHandler, postgresIntegration, prismaIntegration, hapiIntegration, diff --git a/packages/nestjs/package.json b/packages/nestjs/package.json index 1bef4cc56c2f..56ab66b31cd5 100644 --- a/packages/nestjs/package.json +++ b/packages/nestjs/package.json @@ -44,6 +44,9 @@ "access": "public" }, "dependencies": { + "@opentelemetry/core": "^1.29.0", + "@opentelemetry/instrumentation": "^0.56.0", + "@opentelemetry/instrumentation-nestjs-core": "0.43.0", "@sentry/core": "8.45.0", "@sentry/node": "8.45.0" }, diff --git a/packages/nestjs/src/index.ts b/packages/nestjs/src/index.ts index c26edc3b4941..dc8815477910 100644 --- a/packages/nestjs/src/index.ts +++ b/packages/nestjs/src/index.ts @@ -1,16 +1,8 @@ -import { nestIntegration as nestIntegrationAlias } from '@sentry/node'; - export * from '@sentry/node'; -/** - * Integration capturing tracing data for NestJS. - */ -// eslint-disable-next-line deprecation/deprecation -export const nestIntegration = nestIntegrationAlias; - -// TODO(v9): Export custom `getDefaultIntegrations` from this SDK that automatically registers the `nestIntegration`. +export { nestIntegration } from './integrations/nest'; -export { init } from './sdk'; +export { init, getDefaultIntegrations } from './sdk'; export { SentryTraced, diff --git a/packages/node/src/integrations/tracing/nest/helpers.ts b/packages/nestjs/src/integrations/helpers.ts similarity index 100% rename from packages/node/src/integrations/tracing/nest/helpers.ts rename to packages/nestjs/src/integrations/helpers.ts diff --git a/packages/nestjs/src/integrations/nest.ts b/packages/nestjs/src/integrations/nest.ts new file mode 100644 index 000000000000..b7f1a8ef1485 --- /dev/null +++ b/packages/nestjs/src/integrations/nest.ts @@ -0,0 +1,40 @@ +import { NestInstrumentation } from '@opentelemetry/instrumentation-nestjs-core'; +import { defineIntegration } from '@sentry/core'; +import { generateInstrumentOnce } from '@sentry/node'; +import { SentryNestEventInstrumentation } from './sentry-nest-event-instrumentation'; +import { SentryNestInstrumentation } from './sentry-nest-instrumentation'; + +const INTEGRATION_NAME = 'Nest'; + +const instrumentNestCore = generateInstrumentOnce('Nest-Core', () => { + return new NestInstrumentation(); +}); + +const instrumentNestCommon = generateInstrumentOnce('Nest-Common', () => { + return new SentryNestInstrumentation(); +}); + +const instrumentNestEvent = generateInstrumentOnce('Nest-Event', () => { + return new SentryNestEventInstrumentation(); +}); + +export const instrumentNest = Object.assign( + (): void => { + instrumentNestCore(); + instrumentNestCommon(); + instrumentNestEvent(); + }, + { id: INTEGRATION_NAME }, +); + +/** + * Integration capturing tracing data for NestJS. + */ +export const nestIntegration = defineIntegration(() => { + return { + name: INTEGRATION_NAME, + setupOnce() { + instrumentNest(); + }, + }; +}); diff --git a/packages/node/src/integrations/tracing/nest/sentry-nest-event-instrumentation.ts b/packages/nestjs/src/integrations/sentry-nest-event-instrumentation.ts similarity index 100% rename from packages/node/src/integrations/tracing/nest/sentry-nest-event-instrumentation.ts rename to packages/nestjs/src/integrations/sentry-nest-event-instrumentation.ts diff --git a/packages/node/src/integrations/tracing/nest/sentry-nest-instrumentation.ts b/packages/nestjs/src/integrations/sentry-nest-instrumentation.ts similarity index 100% rename from packages/node/src/integrations/tracing/nest/sentry-nest-instrumentation.ts rename to packages/nestjs/src/integrations/sentry-nest-instrumentation.ts diff --git a/packages/node/src/integrations/tracing/nest/types.ts b/packages/nestjs/src/integrations/types.ts similarity index 100% rename from packages/node/src/integrations/tracing/nest/types.ts rename to packages/nestjs/src/integrations/types.ts diff --git a/packages/nestjs/src/sdk.ts b/packages/nestjs/src/sdk.ts index 7bc3a856f093..d9c00369e8b3 100644 --- a/packages/nestjs/src/sdk.ts +++ b/packages/nestjs/src/sdk.ts @@ -4,14 +4,17 @@ import { applySdkMetadata, spanToJSON, } from '@sentry/core'; +import type { Integration } from '@sentry/core'; import type { NodeClient, NodeOptions, Span } from '@sentry/node'; -import { init as nodeInit } from '@sentry/node'; +import { getDefaultIntegrations as getDefaultNodeIntegrations, init as nodeInit } from '@sentry/node'; +import { nestIntegration } from './integrations/nest'; /** * Initializes the NestJS SDK */ export function init(options: NodeOptions | undefined = {}): NodeClient | undefined { const opts: NodeOptions = { + defaultIntegrations: getDefaultIntegrations(options), ...options, }; @@ -29,6 +32,11 @@ export function init(options: NodeOptions | undefined = {}): NodeClient | undefi return client; } +/** Get the default integrations for the NestJS SDK. */ +export function getDefaultIntegrations(options: NodeOptions): Integration[] | undefined { + return [nestIntegration(), ...getDefaultNodeIntegrations(options)]; +} + function addNestSpanAttributes(span: Span): void { const attributes = spanToJSON(span).data; diff --git a/packages/node/package.json b/packages/node/package.json index 618b9aa89725..dc35fdb8eda9 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -88,7 +88,6 @@ "@opentelemetry/instrumentation-mongoose": "0.45.0", "@opentelemetry/instrumentation-mysql": "0.44.0", "@opentelemetry/instrumentation-mysql2": "0.44.0", - "@opentelemetry/instrumentation-nestjs-core": "0.43.0", "@opentelemetry/instrumentation-pg": "0.49.0", "@opentelemetry/instrumentation-redis-4": "0.45.0", "@opentelemetry/instrumentation-tedious": "0.17.0", diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 43aa9258253b..82c7e94c7e2e 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -21,8 +21,6 @@ export { mongooseIntegration } from './integrations/tracing/mongoose'; export { mysqlIntegration } from './integrations/tracing/mysql'; export { mysql2Integration } from './integrations/tracing/mysql2'; export { redisIntegration } from './integrations/tracing/redis'; -// eslint-disable-next-line deprecation/deprecation -export { nestIntegration, setupNestErrorHandler } from './integrations/tracing/nest/nest'; export { postgresIntegration } from './integrations/tracing/postgres'; export { prismaIntegration } from './integrations/tracing/prisma'; export { hapiIntegration, setupHapiErrorHandler } from './integrations/tracing/hapi'; diff --git a/packages/node/src/integrations/tracing/index.ts b/packages/node/src/integrations/tracing/index.ts index 6faf403511b9..7d06689f250d 100644 --- a/packages/node/src/integrations/tracing/index.ts +++ b/packages/node/src/integrations/tracing/index.ts @@ -15,7 +15,6 @@ import { instrumentMongo, mongoIntegration } from './mongo'; import { instrumentMongoose, mongooseIntegration } from './mongoose'; import { instrumentMysql, mysqlIntegration } from './mysql'; import { instrumentMysql2, mysql2Integration } from './mysql2'; -import { instrumentNest, nestIntegration } from './nest/nest'; import { instrumentPostgres, postgresIntegration } from './postgres'; import { instrumentRedis, redisIntegration } from './redis'; import { instrumentTedious, tediousIntegration } from './tedious'; @@ -39,8 +38,6 @@ export function getAutoPerformanceIntegrations(): Integration[] { // See https://github.com/prisma/prisma/issues/23410 // TODO v8: Figure out a better solution for this, maybe only disable in ESM mode? // prismaIntegration(), - // eslint-disable-next-line deprecation/deprecation - nestIntegration(), hapiIntegration(), koaIntegration(), connectIntegration(), @@ -67,8 +64,6 @@ export function getOpenTelemetryInstrumentationToPreload(): (((options?: any) => instrumentKafka, instrumentKoa, instrumentLruMemoizer, - // eslint-disable-next-line deprecation/deprecation - instrumentNest, instrumentMongo, instrumentMongoose, instrumentMysql, diff --git a/packages/node/src/integrations/tracing/nest/nest.ts b/packages/node/src/integrations/tracing/nest/nest.ts deleted file mode 100644 index 8ba3cb2ac98d..000000000000 --- a/packages/node/src/integrations/tracing/nest/nest.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { NestInstrumentation } from '@opentelemetry/instrumentation-nestjs-core'; -import type { Span } from '@sentry/core'; -import { - SEMANTIC_ATTRIBUTE_SENTRY_OP, - SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, - captureException, - consoleSandbox, - defineIntegration, - getClient, - getDefaultIsolationScope, - getIsolationScope, - logger, - spanToJSON, -} from '@sentry/core'; -import { generateInstrumentOnce } from '../../../otel/instrument'; -import { SentryNestEventInstrumentation } from './sentry-nest-event-instrumentation'; -import { SentryNestInstrumentation } from './sentry-nest-instrumentation'; -import type { MinimalNestJsApp, NestJsErrorFilter } from './types'; - -const INTEGRATION_NAME = 'Nest'; - -const instrumentNestCore = generateInstrumentOnce('Nest-Core', () => { - return new NestInstrumentation(); -}); - -const instrumentNestCommon = generateInstrumentOnce('Nest-Common', () => { - return new SentryNestInstrumentation(); -}); - -const instrumentNestEvent = generateInstrumentOnce('Nest-Event', () => { - return new SentryNestEventInstrumentation(); -}); - -export const instrumentNest = Object.assign( - (): void => { - instrumentNestCore(); - instrumentNestCommon(); - instrumentNestEvent(); - }, - { id: INTEGRATION_NAME }, -); - -/** - * Integration capturing tracing data for NestJS. - * - * @deprecated The `nestIntegration` is deprecated. Instead, use the NestJS SDK directly (`@sentry/nestjs`), or use the `nestIntegration` export from `@sentry/nestjs`. - */ -export const nestIntegration = defineIntegration(() => { - return { - name: INTEGRATION_NAME, - setupOnce() { - instrumentNest(); - }, - }; -}); - -/** - * Setup an error handler for Nest. - * - * @deprecated `setupNestErrorHandler` is deprecated. - * Instead use the `@sentry/nestjs` package, which has more functional APIs for capturing errors. - * See the [`@sentry/nestjs` Setup Guide](https://docs.sentry.io/platforms/javascript/guides/nestjs/) for how to set up the Sentry NestJS SDK. - */ -export function setupNestErrorHandler(app: MinimalNestJsApp, baseFilter: NestJsErrorFilter): void { - consoleSandbox(() => { - // eslint-disable-next-line no-console - console.warn( - '[Sentry] Warning: You used the `setupNestErrorHandler()` method to set up Sentry error monitoring. This function is deprecated and will be removed in the next major version. Instead, it is recommended to use the `@sentry/nestjs` package. To set up the NestJS SDK see: https://docs.sentry.io/platforms/javascript/guides/nestjs/', - ); - }); - - // Sadly, NestInstrumentation has no requestHook, so we need to add the attributes here - // We register this hook in this method, because if we register it in the integration `setup`, - // it would always run even for users that are not even using Nest.js - const client = getClient(); - if (client) { - client.on('spanStart', span => { - addNestSpanAttributes(span); - }); - } - - app.useGlobalInterceptors({ - intercept(context, next) { - if (getIsolationScope() === getDefaultIsolationScope()) { - logger.warn('Isolation scope is still the default isolation scope, skipping setting transactionName.'); - return next.handle(); - } - - if (context.getType() === 'http') { - // getRequest() returns either a FastifyRequest or ExpressRequest, depending on the used adapter - const req = context.switchToHttp().getRequest(); - if ('routeOptions' in req && req.routeOptions && req.routeOptions.url) { - // fastify case - getIsolationScope().setTransactionName( - `${req.routeOptions.method?.toUpperCase() || 'GET'} ${req.routeOptions.url}`, - ); - } else if ('route' in req && req.route && req.route.path) { - // express case - getIsolationScope().setTransactionName(`${req.method?.toUpperCase() || 'GET'} ${req.route.path}`); - } - } - - return next.handle(); - }, - }); - - const wrappedFilter = new Proxy(baseFilter, { - get(target, prop, receiver) { - if (prop === 'catch') { - const originalCatch = Reflect.get(target, prop, receiver); - - return (exception: unknown, host: unknown) => { - const exceptionIsObject = typeof exception === 'object' && exception !== null; - const exceptionStatusCode = exceptionIsObject && 'status' in exception ? exception.status : null; - const exceptionErrorProperty = exceptionIsObject && 'error' in exception ? exception.error : null; - - /* - Don't report expected NestJS control flow errors - - `HttpException` errors will have a `status` property - - `RpcException` errors will have an `error` property - */ - if (exceptionStatusCode !== null || exceptionErrorProperty !== null) { - return originalCatch.apply(target, [exception, host]); - } - - captureException(exception); - return originalCatch.apply(target, [exception, host]); - }; - } - return Reflect.get(target, prop, receiver); - }, - }); - - app.useGlobalFilters(wrappedFilter); -} - -function addNestSpanAttributes(span: Span): void { - const attributes = spanToJSON(span).data; - - // this is one of: app_creation, request_context, handler - const type = attributes['nestjs.type']; - - // If this is already set, or we have no nest.js span, no need to process again... - if (attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP] || !type) { - return; - } - - span.setAttributes({ - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.otel.nestjs', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: `${type}.nestjs`, - }); -} diff --git a/packages/remix/src/index.server.ts b/packages/remix/src/index.server.ts index 4bb6539dbd33..5a935bd1d4d7 100644 --- a/packages/remix/src/index.server.ts +++ b/packages/remix/src/index.server.ts @@ -87,8 +87,6 @@ export { mysql2Integration, mysqlIntegration, nativeNodeFetchIntegration, - // eslint-disable-next-line deprecation/deprecation - nestIntegration, NodeClient, nodeContextIntegration, onUncaughtExceptionIntegration, @@ -119,8 +117,6 @@ export { setupExpressErrorHandler, setupHapiErrorHandler, setupKoaErrorHandler, - // eslint-disable-next-line deprecation/deprecation - setupNestErrorHandler, setUser, spanToBaggageHeader, spanToJSON, diff --git a/packages/solidstart/src/server/index.ts b/packages/solidstart/src/server/index.ts index 4c1f192b0c36..66aa39a37eae 100644 --- a/packages/solidstart/src/server/index.ts +++ b/packages/solidstart/src/server/index.ts @@ -79,8 +79,6 @@ export { mysql2Integration, mysqlIntegration, nativeNodeFetchIntegration, - // eslint-disable-next-line deprecation/deprecation - nestIntegration, NodeClient, nodeContextIntegration, onUncaughtExceptionIntegration, @@ -111,8 +109,6 @@ export { setupExpressErrorHandler, setupHapiErrorHandler, setupKoaErrorHandler, - // eslint-disable-next-line deprecation/deprecation - setupNestErrorHandler, setUser, spanToBaggageHeader, spanToJSON, diff --git a/packages/sveltekit/src/server/index.ts b/packages/sveltekit/src/server/index.ts index 4996dcc0e7ca..44a362deddce 100644 --- a/packages/sveltekit/src/server/index.ts +++ b/packages/sveltekit/src/server/index.ts @@ -81,8 +81,6 @@ export { mysql2Integration, mysqlIntegration, nativeNodeFetchIntegration, - // eslint-disable-next-line deprecation/deprecation - nestIntegration, NodeClient, nodeContextIntegration, onUncaughtExceptionIntegration, @@ -113,8 +111,6 @@ export { setupExpressErrorHandler, setupHapiErrorHandler, setupKoaErrorHandler, - // eslint-disable-next-line deprecation/deprecation - setupNestErrorHandler, setUser, spanToBaggageHeader, spanToJSON, From 4c448ce51344243482e5ec8b0cf4671afbc9a948 Mon Sep 17 00:00:00 2001 From: Andrei Borza Date: Tue, 17 Dec 2024 12:32:42 +0100 Subject: [PATCH 2/2] Move unit tests --- .../test/integrations}/nest.test.ts | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) rename packages/{node/test/integrations/tracing => nestjs/test/integrations}/nest.test.ts (83%) diff --git a/packages/node/test/integrations/tracing/nest.test.ts b/packages/nestjs/test/integrations/nest.test.ts similarity index 83% rename from packages/node/test/integrations/tracing/nest.test.ts rename to packages/nestjs/test/integrations/nest.test.ts index 7f592a93f341..b7ad5c041616 100644 --- a/packages/node/test/integrations/tracing/nest.test.ts +++ b/packages/nestjs/test/integrations/nest.test.ts @@ -1,8 +1,9 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + import * as core from '@sentry/core'; -import { isPatched } from '../../../src/integrations/tracing/nest/helpers'; -import { SentryNestEventInstrumentation } from '../../../src/integrations/tracing/nest/sentry-nest-event-instrumentation'; -import type { InjectableTarget } from '../../../src/integrations/tracing/nest/types'; -import type { OnEventTarget } from '../../../src/integrations/tracing/nest/types'; +import { isPatched } from '../../src/integrations/helpers'; +import { SentryNestEventInstrumentation } from '../../src/integrations/sentry-nest-event-instrumentation'; +import type { InjectableTarget, OnEventTarget } from '../../src/integrations/types'; describe('Nest', () => { describe('isPatched', () => { @@ -20,13 +21,13 @@ describe('Nest', () => { describe('EventInstrumentation', () => { let instrumentation: SentryNestEventInstrumentation; - let mockOnEvent: jest.Mock; + let mockOnEvent: vi.Mock; let mockTarget: OnEventTarget; beforeEach(() => { instrumentation = new SentryNestEventInstrumentation(); // Mock OnEvent to return a function that applies the descriptor - mockOnEvent = jest.fn().mockImplementation(() => { + mockOnEvent = vi.fn().mockImplementation(() => { return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => { return descriptor; }; @@ -35,12 +36,12 @@ describe('Nest', () => { name: 'TestClass', prototype: {}, } as OnEventTarget; - jest.spyOn(core, 'startSpan'); - jest.spyOn(core, 'captureException'); + vi.spyOn(core, 'startSpan'); + vi.spyOn(core, 'captureException'); }); afterEach(() => { - jest.restoreAllMocks(); + vi.restoreAllMocks(); }); describe('init()', () => { @@ -53,10 +54,10 @@ describe('Nest', () => { describe('OnEvent decorator wrapping', () => { let wrappedOnEvent: any; let descriptor: PropertyDescriptor; - let originalHandler: jest.Mock; + let originalHandler: vi.Mock; beforeEach(() => { - originalHandler = jest.fn().mockResolvedValue('result'); + originalHandler = vi.fn().mockResolvedValue('result'); descriptor = { value: originalHandler, };