From ad796f38fd9a06a789e99416d8ae8e0c4be98432 Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Fri, 5 Jul 2024 12:44:33 +0200 Subject: [PATCH 01/15] Add poc implementation of nest cron decorator --- packages/nestjs/package.json | 3 +- packages/nestjs/src/cron-decorator.ts | 46 +++++++++++++++++++++++++++ yarn.lock | 26 +++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 packages/nestjs/src/cron-decorator.ts diff --git a/packages/nestjs/package.json b/packages/nestjs/package.json index 322c44d937f9..f9cceec7583a 100644 --- a/packages/nestjs/package.json +++ b/packages/nestjs/package.json @@ -40,7 +40,8 @@ }, "dependencies": { "@sentry/core": "8.15.0", - "@sentry/node": "8.15.0" + "@sentry/node": "8.15.0", + "@nestjs/schedule": "4.1.0" }, "scripts": { "build": "run-p build:transpile build:types", diff --git a/packages/nestjs/src/cron-decorator.ts b/packages/nestjs/src/cron-decorator.ts new file mode 100644 index 000000000000..ba26a4e933ca --- /dev/null +++ b/packages/nestjs/src/cron-decorator.ts @@ -0,0 +1,46 @@ +import type { CronExpression } from '@nestjs/schedule'; +import { Cron } from '@nestjs/schedule'; +import * as Sentry from '@sentry/node'; + +/** + * A decorator wrapping the native nest Cron decorator, sending check-ins to Sentry. + */ +export const SentryCron = ( + cronTime: string | CronExpression, + monitorSlug: string, +): MethodDecorator => { + return (target, propertyKey, descriptor: PropertyDescriptor) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const originalMethod = descriptor.value as (...args: any[]) => Promise; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + descriptor.value = async function (...args: any[]) { + const checkInId = Sentry.captureCheckIn({ + monitorSlug, + status: 'in_progress', + }); + + try { + await originalMethod.apply(this, args); + + // cron job successful + Sentry.captureCheckIn({ + checkInId, + monitorSlug, + status: 'ok', + }); + } catch (error) { + // cron job failed + Sentry.captureCheckIn({ + checkInId, + monitorSlug, + status: 'error', + }); + throw error; + } + }; + + // apply native nest cron decorator with instrumented method + Cron(cronTime)(target, propertyKey, descriptor); + }; +}; diff --git a/yarn.lock b/yarn.lock index 676d0b52bbf0..5e7bb5741448 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5914,6 +5914,14 @@ multer "1.4.4-lts.1" tslib "2.6.2" +"@nestjs/schedule@4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@nestjs/schedule/-/schedule-4.1.0.tgz#b0ae64519365821f4186416915e502d225836048" + integrity sha512-WEc96WTXZW+VI/Ng+uBpiBUwm6TWtAbQ4RKWkfbmzKvmbRGzA/9k/UyAWDS9k0pp+ZcbC+MaZQtt7TjQHrwX6g== + dependencies: + cron "3.1.7" + uuid "10.0.0" + "@netlify/functions@^2.6.0": version "2.7.0" resolved "https://registry.yarnpkg.com/@netlify/functions/-/functions-2.7.0.tgz#a9506191dc2c6fe888a50de28bcab1537b5b4161" @@ -9623,6 +9631,11 @@ resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.3.8.tgz#84dbf2d020a9209a272058725e168f21d331a67e" integrity sha512-jYvz8UMLDgy3a5SkGJne8H7VA7zPV2Lwohjx0V8V31+SqAjNmurWMkk9cQhfvlcnXWudBpK9xPM1n4rljOcHYQ== +"@types/luxon@~3.4.0": + version "3.4.2" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.4.2.tgz#e4fc7214a420173cea47739c33cdf10874694db7" + integrity sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA== + "@types/mdast@^3.0.0": version "3.0.13" resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.13.tgz#b7ba6e52d0faeb9c493e32c205f3831022be4e1b" @@ -15145,6 +15158,14 @@ cron-parser@^4.2.0: dependencies: luxon "^3.2.1" +cron@3.1.7: + version "3.1.7" + resolved "https://registry.yarnpkg.com/cron/-/cron-3.1.7.tgz#3423d618ba625e78458fff8cb67001672d49ba0d" + integrity sha512-tlBg7ARsAMQLzgwqVxy8AZl/qlTc5nibqYwtNGoCrd+cV+ugI+tvZC1oT/8dFH8W455YrywGykx/KMmAqOr7Jw== + dependencies: + "@types/luxon" "~3.4.0" + luxon "~3.4.0" + cron@^3.1.6: version "3.1.6" resolved "https://registry.yarnpkg.com/cron/-/cron-3.1.6.tgz#e7e1798a468e017c8d31459ecd7c2d088f97346c" @@ -33646,6 +33667,11 @@ uuid-v4@^0.1.0: resolved "https://registry.yarnpkg.com/uuid-v4/-/uuid-v4-0.1.0.tgz#62d7b310406f6cecfea1528c69f1e8e0bcec5a3a" integrity sha512-m11RYDtowtAIihBXMoGajOEKpAXrKbpKlpmxqyztMYQNGSY5nZAZ/oYch/w2HNS1RMA4WLGcZvuD8/wFMuCEzA== +uuid@10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294" + integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ== + uuid@8.3.2, uuid@^8.0.0, uuid@^8.3.1, uuid@^8.3.2: version "8.3.2" resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" From 20153abfc2da1f979954d72948a05aed5063399f Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Fri, 5 Jul 2024 12:47:19 +0200 Subject: [PATCH 02/15] Lint --- packages/nestjs/src/cron-decorator.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/nestjs/src/cron-decorator.ts b/packages/nestjs/src/cron-decorator.ts index ba26a4e933ca..4b0221113573 100644 --- a/packages/nestjs/src/cron-decorator.ts +++ b/packages/nestjs/src/cron-decorator.ts @@ -5,10 +5,7 @@ import * as Sentry from '@sentry/node'; /** * A decorator wrapping the native nest Cron decorator, sending check-ins to Sentry. */ -export const SentryCron = ( - cronTime: string | CronExpression, - monitorSlug: string, -): MethodDecorator => { +export const SentryCron = (cronTime: string | CronExpression, monitorSlug: string): MethodDecorator => { return (target, propertyKey, descriptor: PropertyDescriptor) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const originalMethod = descriptor.value as (...args: any[]) => Promise; From 18c936680a30af6113a1925bb3776d0c778c3795 Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Fri, 5 Jul 2024 12:58:04 +0200 Subject: [PATCH 03/15] Export cron decorator --- packages/nestjs/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/nestjs/src/index.ts b/packages/nestjs/src/index.ts index 668187a21e29..00519cf49b9e 100644 --- a/packages/nestjs/src/index.ts +++ b/packages/nestjs/src/index.ts @@ -3,3 +3,4 @@ export * from '@sentry/node'; export { init } from './sdk'; export { SentryTraced } from './span-decorator'; +export { SentryCron } from './cron-decorator'; From a029b354a59d24adb361b0ef3db2c2bc29dac4e6 Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Fri, 5 Jul 2024 13:18:24 +0200 Subject: [PATCH 04/15] Update dependencies --- packages/nestjs/package.json | 7 +++++++ yarn.lock | 26 ++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/packages/nestjs/package.json b/packages/nestjs/package.json index f9cceec7583a..fa3adb539ec6 100644 --- a/packages/nestjs/package.json +++ b/packages/nestjs/package.json @@ -41,6 +41,13 @@ "dependencies": { "@sentry/core": "8.15.0", "@sentry/node": "8.15.0", + "@nestjs/common": "10.3.10", + "@nestjs/core": "10.3.10", + "@nestjs/schedule": "4.1.0" + }, + "peerDependencies": { + "@nestjs/common": "10.3.10", + "@nestjs/core": "10.3.10", "@nestjs/schedule": "4.1.0" }, "scripts": { diff --git a/yarn.lock b/yarn.lock index 5e7bb5741448..f33d94c40cd3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5882,6 +5882,15 @@ semver "^7.3.5" tar "^6.1.11" +"@nestjs/common@10.3.10": + version "10.3.10" + resolved "https://registry.yarnpkg.com/@nestjs/common/-/common-10.3.10.tgz#d8825d55a50a04e33080c9188e6a5b03235d19f2" + integrity sha512-H8k0jZtxk1IdtErGDmxFRy0PfcOAUg41Prrqpx76DQusGGJjsaovs1zjXVD1rZWaVYchfT1uczJ6L4Kio10VNg== + dependencies: + uid "2.0.2" + iterare "1.2.1" + tslib "2.6.3" + "@nestjs/common@^10.3.7": version "10.3.7" resolved "https://registry.yarnpkg.com/@nestjs/common/-/common-10.3.7.tgz#38ab5ff92277cf1f26f4749c264524e76962cfff" @@ -5891,6 +5900,18 @@ iterare "1.2.1" tslib "2.6.2" +"@nestjs/core@10.3.10": + version "10.3.10" + resolved "https://registry.yarnpkg.com/@nestjs/core/-/core-10.3.10.tgz#508090c3ca36488a8e24a9e5939c2f37426e48f4" + integrity sha512-ZbQ4jovQyzHtCGCrzK5NdtW1SYO2fHSsgSY1+/9WdruYCUra+JDkWEXgZ4M3Hv480Dl3OXehAmY1wCOojeMyMQ== + dependencies: + uid "2.0.2" + "@nuxtjs/opencollective" "0.3.2" + fast-safe-stringify "2.1.1" + iterare "1.2.1" + path-to-regexp "3.2.0" + tslib "2.6.3" + "@nestjs/core@^10.3.3": version "10.3.3" resolved "https://registry.yarnpkg.com/@nestjs/core/-/core-10.3.3.tgz#f957068ddda59252b7c36fcdb07a0fb323b52bcf" @@ -32769,6 +32790,11 @@ tslib@2.6.2, tslib@^2.6.2: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== +tslib@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" + integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== + tslib@^1.11.1, tslib@^1.8.1, tslib@^1.9.0: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" From c5c16c4c7ca6e6dbc693bace5e8e924deaf1b8aa Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Fri, 5 Jul 2024 13:31:38 +0200 Subject: [PATCH 05/15] Move nest dependencies to devDependencies --- packages/nestjs/package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/nestjs/package.json b/packages/nestjs/package.json index fa3adb539ec6..acd7e11c0d35 100644 --- a/packages/nestjs/package.json +++ b/packages/nestjs/package.json @@ -40,7 +40,9 @@ }, "dependencies": { "@sentry/core": "8.15.0", - "@sentry/node": "8.15.0", + "@sentry/node": "8.15.0" + }, + "devDependencies": { "@nestjs/common": "10.3.10", "@nestjs/core": "10.3.10", "@nestjs/schedule": "4.1.0" From 01c5c6b7dd1da58d5b96a0fb70d71a2c0aebc11e Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Mon, 8 Jul 2024 11:06:29 +0200 Subject: [PATCH 06/15] Allow to supply options to cron decorator --- packages/nestjs/src/cron-decorator.ts | 47 +++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/packages/nestjs/src/cron-decorator.ts b/packages/nestjs/src/cron-decorator.ts index 4b0221113573..c6a7c4fec62e 100644 --- a/packages/nestjs/src/cron-decorator.ts +++ b/packages/nestjs/src/cron-decorator.ts @@ -2,10 +2,53 @@ import type { CronExpression } from '@nestjs/schedule'; import { Cron } from '@nestjs/schedule'; import * as Sentry from '@sentry/node'; +/** + * @ref https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/cron/index.d.ts + */ +export type CronOptions = { + /** + * Specify the name of your cron job. This will allow to inject your cron job reference through `@InjectCronRef`. + */ + name?: string; + + /** + * Specify the timezone for the execution. This will modify the actual time relative to your timezone. If the timezone is invalid, an error is thrown. You can check all timezones available at [Moment Timezone Website](http://momentjs.com/timezone/). Probably don't use both ```timeZone``` and ```utcOffset``` together or weird things may happen. + */ + timeZone?: unknown; + /** + * This allows you to specify the offset of your timezone rather than using the ```timeZone``` param. Probably don't use both ```timeZone``` and ```utcOffset``` together or weird things may happen. + */ + utcOffset?: unknown; + + /** + * If you have code that keeps the event loop running and want to stop the node process when that finishes regardless of the state of your cronjob, you can do so making use of this parameter. This is off by default and cron will run as if it needs to control the event loop. For more information take a look at [timers#timers_timeout_unref](https://nodejs.org/api/timers.html#timers_timeout_unref) from the NodeJS docs. + */ + unrefTimeout?: boolean; + + /** + * This flag indicates whether the job will be executed at all. + * @default false + */ + disabled?: boolean; +} & ( // make timeZone & utcOffset mutually exclusive + | { + timeZone?: string; + utcOffset?: never; + } + | { + timeZone?: never; + utcOffset?: number; + } +); + /** * A decorator wrapping the native nest Cron decorator, sending check-ins to Sentry. */ -export const SentryCron = (cronTime: string | CronExpression, monitorSlug: string): MethodDecorator => { +export const SentryCron = ( + cronTime: string | CronExpression, + monitorSlug: string, + options: CronOptions = {}, +): MethodDecorator => { return (target, propertyKey, descriptor: PropertyDescriptor) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const originalMethod = descriptor.value as (...args: any[]) => Promise; @@ -38,6 +81,6 @@ export const SentryCron = (cronTime: string | CronExpression, monitorSlug: strin }; // apply native nest cron decorator with instrumented method - Cron(cronTime)(target, propertyKey, descriptor); + Cron(cronTime, options)(target, propertyKey, descriptor); }; }; From 3bf9471de1db7d5f0b1de5aeefa77617b2eca47d Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Mon, 8 Jul 2024 13:53:42 +0200 Subject: [PATCH 07/15] Update decorator + remove dependecies --- packages/nestjs/package.json | 10 ---- packages/nestjs/src/cron-decorator.ts | 79 ++++----------------------- yarn.lock | 52 ------------------ 3 files changed, 10 insertions(+), 131 deletions(-) diff --git a/packages/nestjs/package.json b/packages/nestjs/package.json index acd7e11c0d35..322c44d937f9 100644 --- a/packages/nestjs/package.json +++ b/packages/nestjs/package.json @@ -42,16 +42,6 @@ "@sentry/core": "8.15.0", "@sentry/node": "8.15.0" }, - "devDependencies": { - "@nestjs/common": "10.3.10", - "@nestjs/core": "10.3.10", - "@nestjs/schedule": "4.1.0" - }, - "peerDependencies": { - "@nestjs/common": "10.3.10", - "@nestjs/core": "10.3.10", - "@nestjs/schedule": "4.1.0" - }, "scripts": { "build": "run-p build:transpile build:types", "build:dev": "yarn build", diff --git a/packages/nestjs/src/cron-decorator.ts b/packages/nestjs/src/cron-decorator.ts index c6a7c4fec62e..7ec933d7e175 100644 --- a/packages/nestjs/src/cron-decorator.ts +++ b/packages/nestjs/src/cron-decorator.ts @@ -1,86 +1,27 @@ -import type { CronExpression } from '@nestjs/schedule'; -import { Cron } from '@nestjs/schedule'; import * as Sentry from '@sentry/node'; - -/** - * @ref https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/cron/index.d.ts - */ -export type CronOptions = { - /** - * Specify the name of your cron job. This will allow to inject your cron job reference through `@InjectCronRef`. - */ - name?: string; - - /** - * Specify the timezone for the execution. This will modify the actual time relative to your timezone. If the timezone is invalid, an error is thrown. You can check all timezones available at [Moment Timezone Website](http://momentjs.com/timezone/). Probably don't use both ```timeZone``` and ```utcOffset``` together or weird things may happen. - */ - timeZone?: unknown; - /** - * This allows you to specify the offset of your timezone rather than using the ```timeZone``` param. Probably don't use both ```timeZone``` and ```utcOffset``` together or weird things may happen. - */ - utcOffset?: unknown; - - /** - * If you have code that keeps the event loop running and want to stop the node process when that finishes regardless of the state of your cronjob, you can do so making use of this parameter. This is off by default and cron will run as if it needs to control the event loop. For more information take a look at [timers#timers_timeout_unref](https://nodejs.org/api/timers.html#timers_timeout_unref) from the NodeJS docs. - */ - unrefTimeout?: boolean; - - /** - * This flag indicates whether the job will be executed at all. - * @default false - */ - disabled?: boolean; -} & ( // make timeZone & utcOffset mutually exclusive - | { - timeZone?: string; - utcOffset?: never; - } - | { - timeZone?: never; - utcOffset?: number; - } -); +import type { MonitorConfig } from '@sentry/types'; /** * A decorator wrapping the native nest Cron decorator, sending check-ins to Sentry. */ export const SentryCron = ( - cronTime: string | CronExpression, monitorSlug: string, - options: CronOptions = {}, + monitorConfig: MonitorConfig | undefined, ): MethodDecorator => { - return (target, propertyKey, descriptor: PropertyDescriptor) => { + return (target : unknown, propertyKey, descriptor: PropertyDescriptor) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const originalMethod = descriptor.value as (...args: any[]) => Promise; // eslint-disable-next-line @typescript-eslint/no-explicit-any descriptor.value = async function (...args: any[]) { - const checkInId = Sentry.captureCheckIn({ + Sentry.withMonitor( monitorSlug, - status: 'in_progress', - }); - - try { - await originalMethod.apply(this, args); - - // cron job successful - Sentry.captureCheckIn({ - checkInId, - monitorSlug, - status: 'ok', - }); - } catch (error) { - // cron job failed - Sentry.captureCheckIn({ - checkInId, - monitorSlug, - status: 'error', - }); - throw error; - } + () => { + return originalMethod.apply(this, args); + }, + monitorConfig, + ) }; - - // apply native nest cron decorator with instrumented method - Cron(cronTime, options)(target, propertyKey, descriptor); + return descriptor; }; }; diff --git a/yarn.lock b/yarn.lock index f33d94c40cd3..676d0b52bbf0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5882,15 +5882,6 @@ semver "^7.3.5" tar "^6.1.11" -"@nestjs/common@10.3.10": - version "10.3.10" - resolved "https://registry.yarnpkg.com/@nestjs/common/-/common-10.3.10.tgz#d8825d55a50a04e33080c9188e6a5b03235d19f2" - integrity sha512-H8k0jZtxk1IdtErGDmxFRy0PfcOAUg41Prrqpx76DQusGGJjsaovs1zjXVD1rZWaVYchfT1uczJ6L4Kio10VNg== - dependencies: - uid "2.0.2" - iterare "1.2.1" - tslib "2.6.3" - "@nestjs/common@^10.3.7": version "10.3.7" resolved "https://registry.yarnpkg.com/@nestjs/common/-/common-10.3.7.tgz#38ab5ff92277cf1f26f4749c264524e76962cfff" @@ -5900,18 +5891,6 @@ iterare "1.2.1" tslib "2.6.2" -"@nestjs/core@10.3.10": - version "10.3.10" - resolved "https://registry.yarnpkg.com/@nestjs/core/-/core-10.3.10.tgz#508090c3ca36488a8e24a9e5939c2f37426e48f4" - integrity sha512-ZbQ4jovQyzHtCGCrzK5NdtW1SYO2fHSsgSY1+/9WdruYCUra+JDkWEXgZ4M3Hv480Dl3OXehAmY1wCOojeMyMQ== - dependencies: - uid "2.0.2" - "@nuxtjs/opencollective" "0.3.2" - fast-safe-stringify "2.1.1" - iterare "1.2.1" - path-to-regexp "3.2.0" - tslib "2.6.3" - "@nestjs/core@^10.3.3": version "10.3.3" resolved "https://registry.yarnpkg.com/@nestjs/core/-/core-10.3.3.tgz#f957068ddda59252b7c36fcdb07a0fb323b52bcf" @@ -5935,14 +5914,6 @@ multer "1.4.4-lts.1" tslib "2.6.2" -"@nestjs/schedule@4.1.0": - version "4.1.0" - resolved "https://registry.yarnpkg.com/@nestjs/schedule/-/schedule-4.1.0.tgz#b0ae64519365821f4186416915e502d225836048" - integrity sha512-WEc96WTXZW+VI/Ng+uBpiBUwm6TWtAbQ4RKWkfbmzKvmbRGzA/9k/UyAWDS9k0pp+ZcbC+MaZQtt7TjQHrwX6g== - dependencies: - cron "3.1.7" - uuid "10.0.0" - "@netlify/functions@^2.6.0": version "2.7.0" resolved "https://registry.yarnpkg.com/@netlify/functions/-/functions-2.7.0.tgz#a9506191dc2c6fe888a50de28bcab1537b5b4161" @@ -9652,11 +9623,6 @@ resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.3.8.tgz#84dbf2d020a9209a272058725e168f21d331a67e" integrity sha512-jYvz8UMLDgy3a5SkGJne8H7VA7zPV2Lwohjx0V8V31+SqAjNmurWMkk9cQhfvlcnXWudBpK9xPM1n4rljOcHYQ== -"@types/luxon@~3.4.0": - version "3.4.2" - resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.4.2.tgz#e4fc7214a420173cea47739c33cdf10874694db7" - integrity sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA== - "@types/mdast@^3.0.0": version "3.0.13" resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.13.tgz#b7ba6e52d0faeb9c493e32c205f3831022be4e1b" @@ -15179,14 +15145,6 @@ cron-parser@^4.2.0: dependencies: luxon "^3.2.1" -cron@3.1.7: - version "3.1.7" - resolved "https://registry.yarnpkg.com/cron/-/cron-3.1.7.tgz#3423d618ba625e78458fff8cb67001672d49ba0d" - integrity sha512-tlBg7ARsAMQLzgwqVxy8AZl/qlTc5nibqYwtNGoCrd+cV+ugI+tvZC1oT/8dFH8W455YrywGykx/KMmAqOr7Jw== - dependencies: - "@types/luxon" "~3.4.0" - luxon "~3.4.0" - cron@^3.1.6: version "3.1.6" resolved "https://registry.yarnpkg.com/cron/-/cron-3.1.6.tgz#e7e1798a468e017c8d31459ecd7c2d088f97346c" @@ -32790,11 +32748,6 @@ tslib@2.6.2, tslib@^2.6.2: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== -tslib@2.6.3: - version "2.6.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" - integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== - tslib@^1.11.1, tslib@^1.8.1, tslib@^1.9.0: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" @@ -33693,11 +33646,6 @@ uuid-v4@^0.1.0: resolved "https://registry.yarnpkg.com/uuid-v4/-/uuid-v4-0.1.0.tgz#62d7b310406f6cecfea1528c69f1e8e0bcec5a3a" integrity sha512-m11RYDtowtAIihBXMoGajOEKpAXrKbpKlpmxqyztMYQNGSY5nZAZ/oYch/w2HNS1RMA4WLGcZvuD8/wFMuCEzA== -uuid@10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294" - integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ== - uuid@8.3.2, uuid@^8.0.0, uuid@^8.3.1, uuid@^8.3.2: version "8.3.2" resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" From 8e79b96baa180bf9509a2682bb2ac89ae3277b3f Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Mon, 8 Jul 2024 13:58:35 +0200 Subject: [PATCH 08/15] Update decorator --- packages/nestjs/src/cron-decorator.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/nestjs/src/cron-decorator.ts b/packages/nestjs/src/cron-decorator.ts index 7ec933d7e175..ca956254fd8e 100644 --- a/packages/nestjs/src/cron-decorator.ts +++ b/packages/nestjs/src/cron-decorator.ts @@ -4,23 +4,20 @@ import type { MonitorConfig } from '@sentry/types'; /** * A decorator wrapping the native nest Cron decorator, sending check-ins to Sentry. */ -export const SentryCron = ( - monitorSlug: string, - monitorConfig: MonitorConfig | undefined, -): MethodDecorator => { - return (target : unknown, propertyKey, descriptor: PropertyDescriptor) => { +export const SentryCron = (monitorSlug: string, monitorConfig: MonitorConfig | undefined): MethodDecorator => { + return (target: unknown, propertyKey, descriptor: PropertyDescriptor) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const originalMethod = descriptor.value as (...args: any[]) => Promise; // eslint-disable-next-line @typescript-eslint/no-explicit-any descriptor.value = async function (...args: any[]) { - Sentry.withMonitor( + return Sentry.withMonitor( monitorSlug, - () => { + async () => { return originalMethod.apply(this, args); }, monitorConfig, - ) + ); }; return descriptor; }; From 67bc9a7fb60d1ae8dde6d678a73e2ffd46fcd2da Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Mon, 8 Jul 2024 14:57:46 +0200 Subject: [PATCH 09/15] . --- packages/nestjs/src/cron-decorator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nestjs/src/cron-decorator.ts b/packages/nestjs/src/cron-decorator.ts index ca956254fd8e..2d22dfcd43c7 100644 --- a/packages/nestjs/src/cron-decorator.ts +++ b/packages/nestjs/src/cron-decorator.ts @@ -4,7 +4,7 @@ import type { MonitorConfig } from '@sentry/types'; /** * A decorator wrapping the native nest Cron decorator, sending check-ins to Sentry. */ -export const SentryCron = (monitorSlug: string, monitorConfig: MonitorConfig | undefined): MethodDecorator => { +export const SentryCron = (monitorSlug: string, monitorConfig?: MonitorConfig): MethodDecorator => { return (target: unknown, propertyKey, descriptor: PropertyDescriptor) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const originalMethod = descriptor.value as (...args: any[]) => Promise; From f7de1f9dca2b5a6cf8b133389e1ef64f9f5bdd47 Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Mon, 8 Jul 2024 16:15:37 +0200 Subject: [PATCH 10/15] Add e2e test --- .../test-applications/nestjs/package.json | 1 + .../nestjs/src/app.module.ts | 5 +++- .../nestjs/src/app.service.ts | 22 +++++++++++++- .../nestjs/tests/cron-decorator.test.ts | 29 +++++++++++++++++++ 4 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 dev-packages/e2e-tests/test-applications/nestjs/tests/cron-decorator.test.ts diff --git a/dev-packages/e2e-tests/test-applications/nestjs/package.json b/dev-packages/e2e-tests/test-applications/nestjs/package.json index 6ad2576fc3cc..94c4e445bfe0 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/package.json +++ b/dev-packages/e2e-tests/test-applications/nestjs/package.json @@ -17,6 +17,7 @@ "dependencies": { "@nestjs/common": "^10.0.0", "@nestjs/core": "^10.0.0", + "@nestjs/schedule": "^4.1.0", "@nestjs/platform-express": "^10.0.0", "@sentry/nestjs": "latest || *", "@sentry/types": "latest || *", diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/app.module.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/app.module.ts index 5fda2f1e209f..b006d685bcca 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/app.module.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/src/app.module.ts @@ -1,9 +1,12 @@ import { Module } from '@nestjs/common'; import { AppController1, AppController2 } from './app.controller'; import { AppService1, AppService2 } from './app.service'; +import { ScheduleModule } from '@nestjs/schedule'; @Module({ - imports: [], + imports: [ + ScheduleModule.forRoot(), + ], controllers: [AppController1], providers: [AppService1], }) diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts index 7e0df6b7e1c8..06f8949699f8 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts @@ -1,7 +1,16 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import * as Sentry from '@sentry/nestjs'; -import { SentryTraced } from '@sentry/nestjs'; +import { SentryTraced, SentryCron } from '@sentry/nestjs'; import { makeHttpRequest } from './utils'; +import { Cron } from '@nestjs/schedule'; +import type { MonitorConfig } from '@sentry/types'; + +const monitorConfig: MonitorConfig = { + schedule: { + type: 'crontab', + value: '* * * * *', + } +}; @Injectable() export class AppService1 { @@ -95,6 +104,17 @@ export class AppService1 { async testSpanDecoratorSync() { return this.getString(); } + + /* + 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 * * * * *') + @SentryCron('test-cron-slug', monitorConfig) + async testCron() { + await new Promise(resolve => setTimeout(resolve, 500)); + } } @Injectable() diff --git a/dev-packages/e2e-tests/test-applications/nestjs/tests/cron-decorator.test.ts b/dev-packages/e2e-tests/test-applications/nestjs/tests/cron-decorator.test.ts new file mode 100644 index 000000000000..63b99e9ac974 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/nestjs/tests/cron-decorator.test.ts @@ -0,0 +1,29 @@ +import { expect, test } from '@playwright/test'; +import { waitForEnvelopeItem } from '@sentry-internal/test-utils'; + +test('Cron job triggers send of in_progress envelope', async () => { + const inProgressEnvelopePromise = waitForEnvelopeItem('nestjs', envelope => { + return envelope[0].type === 'check_in'; + }); + + const inProgressEnvelope = await inProgressEnvelopePromise; + + expect(inProgressEnvelope[1]).toEqual({ + 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.any(String), + trace_id: expect.any(String), + } + } + }); +}); From d3016fb4097c299718f321ced86cc03b771f38c0 Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Mon, 8 Jul 2024 16:16:49 +0200 Subject: [PATCH 11/15] Lint --- .../e2e-tests/test-applications/nestjs/src/app.module.ts | 6 ++---- .../e2e-tests/test-applications/nestjs/src/app.service.ts | 8 ++++---- .../test-applications/nestjs/tests/cron-decorator.test.ts | 6 +++--- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/app.module.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/app.module.ts index b006d685bcca..b4f9d5588dda 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/app.module.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/src/app.module.ts @@ -1,12 +1,10 @@ import { Module } from '@nestjs/common'; +import { ScheduleModule } from '@nestjs/schedule'; import { AppController1, AppController2 } from './app.controller'; import { AppService1, AppService2 } from './app.service'; -import { ScheduleModule } from '@nestjs/schedule'; @Module({ - imports: [ - ScheduleModule.forRoot(), - ], + imports: [ScheduleModule.forRoot()], controllers: [AppController1], providers: [AppService1], }) diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts index 06f8949699f8..84d78ce147f5 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts @@ -1,15 +1,15 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import * as Sentry from '@sentry/nestjs'; -import { SentryTraced, SentryCron } from '@sentry/nestjs'; -import { makeHttpRequest } from './utils'; import { Cron } from '@nestjs/schedule'; +import * as Sentry from '@sentry/nestjs'; +import { SentryCron, SentryTraced } from '@sentry/nestjs'; import type { MonitorConfig } from '@sentry/types'; +import { makeHttpRequest } from './utils'; const monitorConfig: MonitorConfig = { schedule: { type: 'crontab', value: '* * * * *', - } + }, }; @Injectable() diff --git a/dev-packages/e2e-tests/test-applications/nestjs/tests/cron-decorator.test.ts b/dev-packages/e2e-tests/test-applications/nestjs/tests/cron-decorator.test.ts index 63b99e9ac974..c98f1d6f8547 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/tests/cron-decorator.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/tests/cron-decorator.test.ts @@ -17,13 +17,13 @@ test('Cron job triggers send of in_progress envelope', async () => { schedule: { type: 'crontab', value: '* * * * *', - } + }, }, contexts: { trace: { span_id: expect.any(String), trace_id: expect.any(String), - } - } + }, + }, }); }); From 0b3c8070795407eeeb959c3a352b24c86865d236 Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Mon, 8 Jul 2024 16:59:03 +0200 Subject: [PATCH 12/15] Fix tests + lint --- .../nestjs/src/app.service.ts | 2 +- .../nestjs/tests/cron-decorator.test.ts | 34 ++++++++++--------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts index 84d78ce147f5..6a4a13e3fde1 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts @@ -113,7 +113,7 @@ export class AppService1 { @Cron('*/5 * * * * *') @SentryCron('test-cron-slug', monitorConfig) async testCron() { - await new Promise(resolve => setTimeout(resolve, 500)); + console.log('Test cron!'); } } diff --git a/dev-packages/e2e-tests/test-applications/nestjs/tests/cron-decorator.test.ts b/dev-packages/e2e-tests/test-applications/nestjs/tests/cron-decorator.test.ts index c98f1d6f8547..ec136ff32248 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/tests/cron-decorator.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/tests/cron-decorator.test.ts @@ -8,22 +8,24 @@ test('Cron job triggers send of in_progress envelope', async () => { const inProgressEnvelope = await inProgressEnvelopePromise; - expect(inProgressEnvelope[1]).toEqual({ - check_in_id: expect.any(String), - monitor_slug: 'test-cron-slug', - status: 'in_progress', - environment: 'qa', - monitor_config: { - schedule: { - type: 'crontab', - value: '* * * * *', + 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.any(String), - trace_id: expect.any(String), + contexts: { + trace: { + span_id: expect.any(String), + trace_id: expect.any(String), + }, }, - }, - }); + }), + ); }); From 40490c27e51d15ed6231c6050ff7c340aa780bc8 Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Tue, 9 Jul 2024 09:15:38 +0200 Subject: [PATCH 13/15] Manually kill test cron at the end of cron test to unstuck test --- .../test-applications/nestjs/src/app.controller.ts | 5 +++++ .../test-applications/nestjs/src/app.service.ts | 10 ++++++++-- .../nestjs/tests/cron-decorator.test.ts | 5 ++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts index 5ba6bcb2a68e..7fda9eef768e 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts @@ -79,6 +79,11 @@ export class AppController1 { async testSpanDecoratorSync() { return { result: await this.appService.testSpanDecoratorSync() }; } + + @Get('kill-test-cron') + async killTestCron() { + this.appService.killTestCron(); + } } @Controller() diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts index 6a4a13e3fde1..b6fd70769e1f 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts @@ -1,5 +1,5 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; -import { Cron } from '@nestjs/schedule'; +import { Cron, SchedulerRegistry } from '@nestjs/schedule'; import * as Sentry from '@sentry/nestjs'; import { SentryCron, SentryTraced } from '@sentry/nestjs'; import type { MonitorConfig } from '@sentry/types'; @@ -14,6 +14,8 @@ const monitorConfig: MonitorConfig = { @Injectable() export class AppService1 { + constructor(private schedulerRegistry: SchedulerRegistry) {} + testSuccess() { return { version: 'v1' }; } @@ -110,11 +112,15 @@ export class AppService1 { only supports minute granularity, but we don't want to wait (worst case) a full minute for the tests to finish. */ - @Cron('*/5 * * * * *') + @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'); + } } @Injectable() diff --git a/dev-packages/e2e-tests/test-applications/nestjs/tests/cron-decorator.test.ts b/dev-packages/e2e-tests/test-applications/nestjs/tests/cron-decorator.test.ts index ec136ff32248..c13623337343 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/tests/cron-decorator.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/tests/cron-decorator.test.ts @@ -1,7 +1,7 @@ import { expect, test } from '@playwright/test'; import { waitForEnvelopeItem } from '@sentry-internal/test-utils'; -test('Cron job triggers send of in_progress envelope', async () => { +test('Cron job triggers send of in_progress envelope', async ({ baseURL }) => { const inProgressEnvelopePromise = waitForEnvelopeItem('nestjs', envelope => { return envelope[0].type === 'check_in'; }); @@ -28,4 +28,7 @@ test('Cron job triggers send of in_progress envelope', async () => { }, }), ); + + // kill cron so tests don't get stuck + await fetch(`${baseURL}/kill-test-cron`); }); From c17925077a3a994debb1c5264323fe02aa5f40d0 Mon Sep 17 00:00:00 2001 From: Nicolas Hrubec Date: Tue, 9 Jul 2024 11:30:25 +0200 Subject: [PATCH 14/15] Update packages/nestjs/src/cron-decorator.ts Co-authored-by: Luca Forstner --- packages/nestjs/src/cron-decorator.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/nestjs/src/cron-decorator.ts b/packages/nestjs/src/cron-decorator.ts index 2d22dfcd43c7..d3c97105cbdd 100644 --- a/packages/nestjs/src/cron-decorator.ts +++ b/packages/nestjs/src/cron-decorator.ts @@ -10,12 +10,10 @@ export const SentryCron = (monitorSlug: string, monitorConfig?: MonitorConfig): const originalMethod = descriptor.value as (...args: any[]) => Promise; // eslint-disable-next-line @typescript-eslint/no-explicit-any - descriptor.value = async function (...args: any[]) { + descriptor.value = function (...args: any[]) { return Sentry.withMonitor( monitorSlug, - async () => { - return originalMethod.apply(this, args); - }, + () => return originalMethod.apply(this, args), monitorConfig, ); }; From 87f2f6dcfa4e2158d9bbd567fbb607603860d933 Mon Sep 17 00:00:00 2001 From: nicohrubec Date: Tue, 9 Jul 2024 12:26:44 +0200 Subject: [PATCH 15/15] Fix syntax error --- packages/nestjs/src/cron-decorator.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/nestjs/src/cron-decorator.ts b/packages/nestjs/src/cron-decorator.ts index d3c97105cbdd..8cb86c6d66cc 100644 --- a/packages/nestjs/src/cron-decorator.ts +++ b/packages/nestjs/src/cron-decorator.ts @@ -13,7 +13,9 @@ export const SentryCron = (monitorSlug: string, monitorConfig?: MonitorConfig): descriptor.value = function (...args: any[]) { return Sentry.withMonitor( monitorSlug, - () => return originalMethod.apply(this, args), + () => { + return originalMethod.apply(this, args); + }, monitorConfig, ); };