diff --git a/dev-packages/node-integration-tests/package.json b/dev-packages/node-integration-tests/package.json index 5480445e69bb..1594d8e837d0 100644 --- a/dev-packages/node-integration-tests/package.json +++ b/dev-packages/node-integration-tests/package.json @@ -16,9 +16,11 @@ "build:types": "tsc -p tsconfig.types.json", "clean": "rimraf -g **/node_modules && run-p clean:script", "clean:script": "node scripts/clean.js", + "prisma:init": "(cd suites/tracing-experimental/prisma-orm && ts-node ./setup.ts)", "lint": "eslint . --format stylish", "fix": "eslint . --format stylish --fix", "type-check": "tsc", + "pretest": "run-s --silent prisma:init", "test": "ts-node ./utils/run-tests.ts", "jest": "jest --config ./jest.config.js", "test:watch": "yarn test --watch" @@ -28,7 +30,7 @@ "@nestjs/core": "^10.3.3", "@nestjs/common": "^10.3.3", "@nestjs/platform-express": "^10.3.3", - "@prisma/client": "3.15.2", + "@prisma/client": "5.9.1", "@sentry/node": "7.100.0", "@sentry/tracing": "7.100.0", "@sentry/types": "7.100.0", diff --git a/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/docker-compose.yml new file mode 100644 index 000000000000..45caa4bb3179 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/docker-compose.yml @@ -0,0 +1,13 @@ +version: '3.9' + +services: + db: + image: postgres:13 + restart: always + container_name: integration-tests-prisma + ports: + - '5433:5432' + environment: + POSTGRES_USER: prisma + POSTGRES_PASSWORD: prisma + POSTGRES_DB: tests diff --git a/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/package.json b/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/package.json new file mode 100644 index 000000000000..b9a5e7998269 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/package.json @@ -0,0 +1,22 @@ +{ + "name": "sentry-prisma-test", + "version": "1.0.0", + "description": "", + "main": "index.js", + "engines": { + "node": ">=16" + }, + "scripts": { + "db-up": "docker-compose up -d", + "generate": "prisma generate", + "migrate": "prisma migrate dev -n sentry-test", + "setup": "run-s --silent db-up generate migrate" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@prisma/client": "5.9.1", + "prisma": "^5.9.1" + } +} diff --git a/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/prisma/migrations/migration_lock.toml b/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/prisma/migrations/migration_lock.toml new file mode 100644 index 000000000000..fbffa92c2bb7 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "postgresql" \ No newline at end of file diff --git a/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/prisma/migrations/sentry_test/migration.sql b/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/prisma/migrations/sentry_test/migration.sql new file mode 100644 index 000000000000..8619aaceb2b0 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/prisma/migrations/sentry_test/migration.sql @@ -0,0 +1,12 @@ +-- CreateTable +CREATE TABLE "User" ( + "id" SERIAL NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "email" TEXT NOT NULL, + "name" TEXT, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); diff --git a/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/prisma/schema.prisma b/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/prisma/schema.prisma new file mode 100644 index 000000000000..52682f1b6cf5 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/prisma/schema.prisma @@ -0,0 +1,16 @@ +datasource db { + url = "postgresql://prisma:prisma@localhost:5433/tests" + provider = "postgresql" +} + +generator client { + provider = "prisma-client-js" + previewFeatures = ["tracing"] +} + +model User { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) + email String @unique + name String? +} diff --git a/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/scenario.js b/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/scenario.js new file mode 100644 index 000000000000..58b46ac1cf3a --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/scenario.js @@ -0,0 +1,52 @@ +const { randomBytes } = require('crypto'); +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +const { PrismaClient } = require('@prisma/client'); +const Sentry = require('@sentry/node'); +const { loggingTransport } = require('@sentry-internal/node-integration-tests'); + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0', + tracesSampleRate: 1.0, + transport: loggingTransport, +}); + +// Stop the process from exiting before the transaction is sent +setInterval(() => {}, 1000); + +async function run() { + const client = new PrismaClient(); + + await Sentry.startSpan( + { + name: 'Test Transaction', + op: 'transaction', + }, + async span => { + await client.user.create({ + data: { + name: 'Tilda', + email: `tilda_${randomBytes(4).toString('hex')}@sentry.io`, + }, + }); + + await client.user.findMany(); + + await client.user.deleteMany({ + where: { + email: { + contains: 'sentry.io', + }, + }, + }); + + setTimeout(async () => { + span.end(); + await client.$disconnect(); + }, 500); + }, + ); +} + +// eslint-disable-next-line @typescript-eslint/no-floating-promises +run(); diff --git a/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/setup.ts b/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/setup.ts new file mode 100755 index 000000000000..a71bec82f893 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/setup.ts @@ -0,0 +1,18 @@ +import { execSync } from 'child_process'; +import { parseSemver } from '@sentry/utils'; + +const NODE_VERSION = parseSemver(process.versions.node); + +// Prisma v5 requires Node.js v16+ +// https://www.prisma.io/docs/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-5#nodejs-minimum-version-change +if (NODE_VERSION.major && NODE_VERSION.major < 16) { + // eslint-disable-next-line no-console + console.warn(`Skipping Prisma tests on Node: ${NODE_VERSION.major}`); + process.exit(0); +} + +try { + execSync('yarn && yarn setup'); +} catch (_) { + process.exit(1); +} diff --git a/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/test.ts b/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/test.ts new file mode 100644 index 000000000000..32bcdf168555 --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/test.ts @@ -0,0 +1,112 @@ +import { conditionalTest } from '../../../utils'; +import { createRunner } from '../../../utils/runner'; + +conditionalTest({ min: 16 })('Prisma ORM Tests', () => { + test('CJS - should instrument PostgreSQL queries from Prisma ORM', done => { + const EXPECTED_TRANSACTION = { + transaction: 'Test Transaction', + spans: expect.arrayContaining([ + expect.objectContaining({ + data: expect.objectContaining({ + method: 'create', + model: 'User', + name: 'User.create', + 'otel.kind': 'INTERNAL', + 'sentry.origin': 'manual', + }), + description: 'prisma:client:operation', + status: 'ok', + }), + expect.objectContaining({ + data: expect.objectContaining({ + 'otel.kind': 'INTERNAL', + 'sentry.origin': 'manual', + }), + description: 'prisma:client:serialize', + status: 'ok', + }), + expect.objectContaining({ + data: expect.objectContaining({ + 'otel.kind': 'INTERNAL', + 'sentry.origin': 'manual', + }), + description: 'prisma:client:connect', + status: 'ok', + }), + expect.objectContaining({ + data: expect.objectContaining({ + 'otel.kind': 'INTERNAL', + 'sentry.origin': 'manual', + }), + description: 'prisma:engine', + status: 'ok', + }), + expect.objectContaining({ + data: expect.objectContaining({ + 'db.type': 'postgres', + 'otel.kind': 'INTERNAL', + 'sentry.origin': 'manual', + }), + description: 'prisma:engine:connection', + status: 'ok', + }), + expect.objectContaining({ + data: expect.objectContaining({ + 'db.statement': expect.stringContaining( + 'INSERT INTO "public"."User" ("createdAt","email","name") VALUES ($1,$2,$3) RETURNING "public"."User"."id", "public"."User"."createdAt", "public"."User"."email", "public"."User"."name" /* traceparent', + ), + 'otel.kind': 'INTERNAL', + 'sentry.origin': 'manual', + }), + description: 'prisma:engine:db_query', + status: 'ok', + }), + expect.objectContaining({ + data: expect.objectContaining({ + 'otel.kind': 'INTERNAL', + 'sentry.origin': 'manual', + }), + description: 'prisma:engine:serialize', + status: 'ok', + }), + expect.objectContaining({ + data: expect.objectContaining({ + 'otel.kind': 'INTERNAL', + 'sentry.origin': 'manual', + }), + description: 'prisma:engine:response_json_serialization', + status: 'ok', + }), + expect.objectContaining({ + data: expect.objectContaining({ + method: 'findMany', + model: 'User', + name: 'User.findMany', + 'otel.kind': 'INTERNAL', + 'sentry.origin': 'manual', + }), + description: 'prisma:client:operation', + status: 'ok', + }), + expect.objectContaining({ + data: expect.objectContaining({ + 'otel.kind': 'INTERNAL', + 'sentry.origin': 'manual', + }), + description: 'prisma:client:serialize', + status: 'ok', + }), + expect.objectContaining({ + data: expect.objectContaining({ + 'otel.kind': 'INTERNAL', + 'sentry.origin': 'manual', + }), + description: 'prisma:engine', + status: 'ok', + }), + ]), + }; + + createRunner(__dirname, 'scenario.js').expect({ transaction: EXPECTED_TRANSACTION }).start(done); + }); +}); diff --git a/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/yarn.lock b/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/yarn.lock new file mode 100644 index 000000000000..9c0fc47be4be --- /dev/null +++ b/dev-packages/node-integration-tests/suites/tracing-experimental/prisma-orm/yarn.lock @@ -0,0 +1,51 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@prisma/client@5.9.1": + version "5.9.1" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.9.1.tgz#d92bd2f7f006e0316cb4fda9d73f235965cf2c64" + integrity sha512-caSOnG4kxcSkhqC/2ShV7rEoWwd3XrftokxJqOCMVvia4NYV/TPtJlS9C2os3Igxw/Qyxumj9GBQzcStzECvtQ== + +"@prisma/debug@5.9.1": + version "5.9.1" + resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-5.9.1.tgz#906274e73d3267f88b69459199fa3c51cd9511a3" + integrity sha512-yAHFSFCg8KVoL0oRUno3m60GAjsUKYUDkQ+9BA2X2JfVR3kRVSJFc/GpQ2fSORi4pSHZR9orfM4UC9OVXIFFTA== + +"@prisma/engines-version@5.9.0-32.23fdc5965b1e05fc54e5f26ed3de66776b93de64": + version "5.9.0-32.23fdc5965b1e05fc54e5f26ed3de66776b93de64" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.9.0-32.23fdc5965b1e05fc54e5f26ed3de66776b93de64.tgz#54d2164f28d23e09d41cf9eb0bddbbe7f3aaa660" + integrity sha512-HFl7275yF0FWbdcNvcSRbbu9JCBSLMcurYwvWc8WGDnpu7APxQo2ONtZrUggU3WxLxUJ2uBX+0GOFIcJeVeOOQ== + +"@prisma/engines@5.9.1": + version "5.9.1" + resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.9.1.tgz#767539afc6f193a182d0495b30b027f61f279073" + integrity sha512-gkdXmjxQ5jktxWNdDA5aZZ6R8rH74JkoKq6LD5mACSvxd2vbqWeWIOV0Py5wFC8vofOYShbt6XUeCIUmrOzOnQ== + dependencies: + "@prisma/debug" "5.9.1" + "@prisma/engines-version" "5.9.0-32.23fdc5965b1e05fc54e5f26ed3de66776b93de64" + "@prisma/fetch-engine" "5.9.1" + "@prisma/get-platform" "5.9.1" + +"@prisma/fetch-engine@5.9.1": + version "5.9.1" + resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-5.9.1.tgz#5d3b2c9af54a242e37b3f9561b59ab72f8e92818" + integrity sha512-l0goQOMcNVOJs1kAcwqpKq3ylvkD9F04Ioe1oJoCqmz05mw22bNAKKGWuDd3zTUoUZr97va0c/UfLNru+PDmNA== + dependencies: + "@prisma/debug" "5.9.1" + "@prisma/engines-version" "5.9.0-32.23fdc5965b1e05fc54e5f26ed3de66776b93de64" + "@prisma/get-platform" "5.9.1" + +"@prisma/get-platform@5.9.1": + version "5.9.1" + resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-5.9.1.tgz#a66bb46ab4d30db786c84150ef074ab0aad4549e" + integrity sha512-6OQsNxTyhvG+T2Ksr8FPFpuPeL4r9u0JF0OZHUBI/Uy9SS43sPyAIutt4ZEAyqWQt104ERh70EZedkHZKsnNbg== + dependencies: + "@prisma/debug" "5.9.1" + +prisma@^5.9.1: + version "5.9.1" + resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.9.1.tgz#baa3dd635fbf71504980978f10f55ea11068f6aa" + integrity sha512-Hy/8KJZz0ELtkw4FnG9MS9rNWlXcJhf98Z2QMqi0QiVMoS8PzsBkpla0/Y5hTlob8F3HeECYphBjqmBxrluUrQ== + dependencies: + "@prisma/engines" "5.9.1"