Merged
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
# compiled output
/dist
/node_modules
/build

# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# OS
.DS_Store

# Tests
/coverage
/.nyc_output

# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# temp directory
.temp
.tmp

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
{
"name": "nestjs-11",
"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": "^11.0.0",
"@nestjs/core": "^11.0.0",
"@nestjs/microservices": "^11.0.0",
"@nestjs/schedule": "^5.0.0",
"@nestjs/platform-express": "^11.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": "^11.0.0",
"@nestjs/schematics": "^11.0.0",
"@nestjs/testing": "^11.0.0",
"@types/express": "^4.17.17",
"@types/node": "^18.19.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": "~5.0.0"
}
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
import { MiddlewareConsumer, Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
import { ScheduleModule } from '@nestjs/schedule';
import { SentryGlobalFilter, SentryModule } from '@sentry/nestjs/setup';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ExampleGlobalFilter } from './example-global.filter';
import { ExampleMiddleware } from './example.middleware';

@Module({
imports: [SentryModule.forRoot(), ScheduleModule.forRoot()],
controllers: [AppController],
providers: [
AppService,
{
provide: APP_FILTER,
useClass: SentryGlobalFilter,
},
{
provide: APP_FILTER,
useClass: ExampleGlobalFilter,
},
],
})
export class AppModule {
configure(consumer: MiddlewareConsumer): void {
consumer.apply(ExampleMiddleware).forRoutes('test-middleware-instrumentation');
}
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
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' };
}

@SentryTraced('return the function name')
getFunctionName(): { result: string } {
return { result: this.getFunctionName.name };
}

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!');
}

/*
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-error' })
@SentryCron('test-cron-error-slug', monitorConfig)
async testCronError() {
throw new Error('Test error from cron job');
}

async killTestCron(job: string) {
this.schedulerRegistry.deleteCronJob(job);
}

use() {
console.log('Test use!');
}

transform() {
console.log('Test transform!');
}

intercept() {
console.log('Test intercept!');
}

canActivate() {
console.log('Test canActivate!');
}
}
Loading
Loading