From 340ca49b7ffe4b38881eff247c24602223077d41 Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Thu, 21 Apr 2022 19:28:00 +0300 Subject: [PATCH 1/3] test(tracing): Add Prisma ORM integration tests. --- packages/node-integration-tests/.gitignore | 1 + packages/node-integration-tests/package.json | 4 ++ .../tracing/prisma-orm/docker-compose.yml | 13 +++++ .../suites/tracing/prisma-orm/package.json | 22 +++++++++ .../prisma/migrations/migration_lock.toml | 3 ++ .../migrations/sentry_test/migration.sql | 12 +++++ .../tracing/prisma-orm/prisma/schema.prisma | 15 ++++++ .../suites/tracing/prisma-orm/scenario.ts | 48 +++++++++++++++++++ .../suites/tracing/prisma-orm/setup.sh | 10 ++++ .../suites/tracing/prisma-orm/test.ts | 17 +++++++ .../suites/tracing/prisma-orm/yarn.lock | 27 +++++++++++ yarn.lock | 12 +++++ 12 files changed, 184 insertions(+) create mode 100644 packages/node-integration-tests/.gitignore create mode 100644 packages/node-integration-tests/suites/tracing/prisma-orm/docker-compose.yml create mode 100644 packages/node-integration-tests/suites/tracing/prisma-orm/package.json create mode 100644 packages/node-integration-tests/suites/tracing/prisma-orm/prisma/migrations/migration_lock.toml create mode 100644 packages/node-integration-tests/suites/tracing/prisma-orm/prisma/migrations/sentry_test/migration.sql create mode 100644 packages/node-integration-tests/suites/tracing/prisma-orm/prisma/schema.prisma create mode 100644 packages/node-integration-tests/suites/tracing/prisma-orm/scenario.ts create mode 100755 packages/node-integration-tests/suites/tracing/prisma-orm/setup.sh create mode 100644 packages/node-integration-tests/suites/tracing/prisma-orm/test.ts create mode 100644 packages/node-integration-tests/suites/tracing/prisma-orm/yarn.lock diff --git a/packages/node-integration-tests/.gitignore b/packages/node-integration-tests/.gitignore new file mode 100644 index 000000000000..3c3629e647f5 --- /dev/null +++ b/packages/node-integration-tests/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/packages/node-integration-tests/package.json b/packages/node-integration-tests/package.json index 002031983f52..53bac132f9c4 100644 --- a/packages/node-integration-tests/package.json +++ b/packages/node-integration-tests/package.json @@ -7,14 +7,18 @@ }, "private": true, "scripts": { + "clean": "rimraf -g **/node_modules", + "prisma:init": "(cd suites/tracing/prisma-orm && ./setup.sh)", "lint": "run-s lint:prettier lint:eslint", "lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish", "lint:prettier": "prettier --check \"{suites,utils}/**/*.ts\"", "type-check": "tsc", + "pretest": "run-s --silent prisma:init", "test": "jest --runInBand --forceExit", "test:watch": "yarn test --watch" }, "dependencies": { + "@prisma/client": "^3.12.0", "@types/mongodb": "^3.6.20", "@types/mysql": "^2.15.21", "@types/pg": "^8.6.5", diff --git a/packages/node-integration-tests/suites/tracing/prisma-orm/docker-compose.yml b/packages/node-integration-tests/suites/tracing/prisma-orm/docker-compose.yml new file mode 100644 index 000000000000..45caa4bb3179 --- /dev/null +++ b/packages/node-integration-tests/suites/tracing/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/packages/node-integration-tests/suites/tracing/prisma-orm/package.json b/packages/node-integration-tests/suites/tracing/prisma-orm/package.json new file mode 100644 index 000000000000..f8b24d7d0465 --- /dev/null +++ b/packages/node-integration-tests/suites/tracing/prisma-orm/package.json @@ -0,0 +1,22 @@ +{ + "name": "sentry-prisma-test", + "version": "1.0.0", + "description": "", + "main": "index.js", + "engines": { + "node": ">=12" + }, + "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": "3.12.0", + "prisma": "^3.12.0" + } +} diff --git a/packages/node-integration-tests/suites/tracing/prisma-orm/prisma/migrations/migration_lock.toml b/packages/node-integration-tests/suites/tracing/prisma-orm/prisma/migrations/migration_lock.toml new file mode 100644 index 000000000000..fbffa92c2bb7 --- /dev/null +++ b/packages/node-integration-tests/suites/tracing/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/packages/node-integration-tests/suites/tracing/prisma-orm/prisma/migrations/sentry_test/migration.sql b/packages/node-integration-tests/suites/tracing/prisma-orm/prisma/migrations/sentry_test/migration.sql new file mode 100644 index 000000000000..8619aaceb2b0 --- /dev/null +++ b/packages/node-integration-tests/suites/tracing/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/packages/node-integration-tests/suites/tracing/prisma-orm/prisma/schema.prisma b/packages/node-integration-tests/suites/tracing/prisma-orm/prisma/schema.prisma new file mode 100644 index 000000000000..4363c97738ee --- /dev/null +++ b/packages/node-integration-tests/suites/tracing/prisma-orm/prisma/schema.prisma @@ -0,0 +1,15 @@ +datasource db { + url = "postgresql://prisma:prisma@localhost:5433/tests" + provider = "postgresql" +} + +generator client { + provider = "prisma-client-js" +} + +model User { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) + email String @unique + name String? +} diff --git a/packages/node-integration-tests/suites/tracing/prisma-orm/scenario.ts b/packages/node-integration-tests/suites/tracing/prisma-orm/scenario.ts new file mode 100644 index 000000000000..047166a9e136 --- /dev/null +++ b/packages/node-integration-tests/suites/tracing/prisma-orm/scenario.ts @@ -0,0 +1,48 @@ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +import { PrismaClient } from '@prisma/client'; +import * as Sentry from '@sentry/node'; +import * as Tracing from '@sentry/tracing'; +import { randomBytes } from 'crypto'; + +const client = new PrismaClient(); + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + release: '1.0', + tracesSampleRate: 1.0, + integrations: [new Tracing.Integrations.Prisma({ client })], +}); + +async function run(): Promise { + const transaction = Sentry.startTransaction({ + name: 'Test Transaction', + op: 'transaction', + }); + + Sentry.configureScope(scope => { + scope.setSpan(transaction); + }); + + try { + 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', + }, + }, + }); + } finally { + if (transaction) transaction.finish(); + } +} + +void run(); diff --git a/packages/node-integration-tests/suites/tracing/prisma-orm/setup.sh b/packages/node-integration-tests/suites/tracing/prisma-orm/setup.sh new file mode 100755 index 000000000000..7e68da11012b --- /dev/null +++ b/packages/node-integration-tests/suites/tracing/prisma-orm/setup.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +NODE_MAJOR=$(node -v | cut -c2- | cut -d. -f1) + +if [ "$NODE_MAJOR" -lt "12" ]; then + echo "Skipping Prisma tests on Node: $NODE_MAJOR" + exit 0 +fi + +yarn && yarn setup diff --git a/packages/node-integration-tests/suites/tracing/prisma-orm/test.ts b/packages/node-integration-tests/suites/tracing/prisma-orm/test.ts new file mode 100644 index 000000000000..91e6f39da889 --- /dev/null +++ b/packages/node-integration-tests/suites/tracing/prisma-orm/test.ts @@ -0,0 +1,17 @@ +import { assertSentryTransaction, conditionalTest, getEnvelopeRequest, runServer } from '../../../utils'; + +conditionalTest({ min: 12 })('Prisma ORM Integration', () => { + test('should instrument Prisma client for tracing.', async () => { + const url = await runServer(__dirname); + const envelope = await getEnvelopeRequest(url); + + assertSentryTransaction(envelope[2], { + transaction: 'Test Transaction', + spans: [ + { description: 'User create', op: 'db.prisma' }, + { description: 'User findMany', op: 'db.prisma' }, + { description: 'User deleteMany', op: 'db.prisma' }, + ], + }); + }); +}); diff --git a/packages/node-integration-tests/suites/tracing/prisma-orm/yarn.lock b/packages/node-integration-tests/suites/tracing/prisma-orm/yarn.lock new file mode 100644 index 000000000000..d228adebd621 --- /dev/null +++ b/packages/node-integration-tests/suites/tracing/prisma-orm/yarn.lock @@ -0,0 +1,27 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@prisma/client@3.12.0": + version "3.12.0" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-3.12.0.tgz#a0eb49ffea5c128dd11dffb896d7139a60073d12" + integrity sha512-4NEQjUcWja/NVBvfuDFscWSk1/rXg3+wj+TSkqXCb1tKlx/bsUE00rxsvOvGg7VZ6lw1JFpGkwjwmsOIc4zvQw== + dependencies: + "@prisma/engines-version" "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980" + +"@prisma/engines-version@3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980": + version "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980.tgz#829ca3d9d0d92555f44644606d4edfd45b2f5886" + integrity sha512-o+jo8d7ZEiVpcpNWUDh3fj2uPQpBxl79XE9ih9nkogJbhw6P33274SHnqheedZ7PyvPIK/mvU8MLNYgetgXPYw== + +"@prisma/engines@3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980": + version "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980" + resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980.tgz#e52e364084c4d05278f62768047b788665e64a45" + integrity sha512-zULjkN8yhzS7B3yeEz4aIym4E2w1ChrV12i14pht3ePFufvsAvBSoZ+tuXMvfSoNTgBS5E4bolRzLbMmbwkkMQ== + +prisma@^3.12.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/prisma/-/prisma-3.12.0.tgz#9675e0e72407122759d3eadcb6d27cdccd3497bd" + integrity sha512-ltCMZAx1i0i9xuPM692Srj8McC665h6E5RqJom999sjtVSccHSD8Z+HSdBN2183h9PJKvC5dapkn78dd0NWMBg== + dependencies: + "@prisma/engines" "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980" diff --git a/yarn.lock b/yarn.lock index 6b21e6baa196..f6ba304563d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4079,6 +4079,18 @@ resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.12.tgz#431ec342a7195622f86688bbda82e3166ce8cb28" integrity sha512-6RglhutqrGFMO1MNUXp95RBuYIuc8wTnMAV5MUhLmjTOy78ncwOw7RgeQ/HeymkKXRhZd0s2DNrM1rL7unk3MQ== +"@prisma/client@^3.12.0": + version "3.12.0" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-3.12.0.tgz#a0eb49ffea5c128dd11dffb896d7139a60073d12" + integrity sha512-4NEQjUcWja/NVBvfuDFscWSk1/rXg3+wj+TSkqXCb1tKlx/bsUE00rxsvOvGg7VZ6lw1JFpGkwjwmsOIc4zvQw== + dependencies: + "@prisma/engines-version" "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980" + +"@prisma/engines-version@3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980": + version "3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-3.12.0-37.22b822189f46ef0dc5c5b503368d1bee01213980.tgz#829ca3d9d0d92555f44644606d4edfd45b2f5886" + integrity sha512-o+jo8d7ZEiVpcpNWUDh3fj2uPQpBxl79XE9ih9nkogJbhw6P33274SHnqheedZ7PyvPIK/mvU8MLNYgetgXPYw== + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" From 679265c886ee1e150b052520b9fbcef82eea3e2f Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Thu, 21 Apr 2022 19:54:13 +0300 Subject: [PATCH 2/3] Convert shell script to TS. --- packages/node-integration-tests/package.json | 2 +- .../suites/tracing/prisma-orm/setup.sh | 10 ---------- .../suites/tracing/prisma-orm/setup.ts | 16 ++++++++++++++++ 3 files changed, 17 insertions(+), 11 deletions(-) delete mode 100755 packages/node-integration-tests/suites/tracing/prisma-orm/setup.sh create mode 100755 packages/node-integration-tests/suites/tracing/prisma-orm/setup.ts diff --git a/packages/node-integration-tests/package.json b/packages/node-integration-tests/package.json index 53bac132f9c4..87920c697cad 100644 --- a/packages/node-integration-tests/package.json +++ b/packages/node-integration-tests/package.json @@ -8,7 +8,7 @@ "private": true, "scripts": { "clean": "rimraf -g **/node_modules", - "prisma:init": "(cd suites/tracing/prisma-orm && ./setup.sh)", + "prisma:init": "(cd suites/tracing/prisma-orm && ts-node ./setup.ts)", "lint": "run-s lint:prettier lint:eslint", "lint:eslint": "eslint . --cache --cache-location '../../eslintcache/' --format stylish", "lint:prettier": "prettier --check \"{suites,utils}/**/*.ts\"", diff --git a/packages/node-integration-tests/suites/tracing/prisma-orm/setup.sh b/packages/node-integration-tests/suites/tracing/prisma-orm/setup.sh deleted file mode 100755 index 7e68da11012b..000000000000 --- a/packages/node-integration-tests/suites/tracing/prisma-orm/setup.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -NODE_MAJOR=$(node -v | cut -c2- | cut -d. -f1) - -if [ "$NODE_MAJOR" -lt "12" ]; then - echo "Skipping Prisma tests on Node: $NODE_MAJOR" - exit 0 -fi - -yarn && yarn setup diff --git a/packages/node-integration-tests/suites/tracing/prisma-orm/setup.ts b/packages/node-integration-tests/suites/tracing/prisma-orm/setup.ts new file mode 100755 index 000000000000..3c40d12f7337 --- /dev/null +++ b/packages/node-integration-tests/suites/tracing/prisma-orm/setup.ts @@ -0,0 +1,16 @@ +import { parseSemver } from '@sentry/utils'; +import { execSync } from 'child_process'; + +const NODE_VERSION = parseSemver(process.versions.node); + +if (NODE_VERSION.major && NODE_VERSION.major < 12) { + // 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); +} From d87b92c984d0ef0542aa091926bc3514208ec203 Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Fri, 22 Apr 2022 09:46:18 +0000 Subject: [PATCH 3/3] Use a type-guard for Prisma clients. --- packages/tracing/src/integrations/node/prisma.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/tracing/src/integrations/node/prisma.ts b/packages/tracing/src/integrations/node/prisma.ts index e70fce9f0f60..3aa7a07f4da3 100644 --- a/packages/tracing/src/integrations/node/prisma.ts +++ b/packages/tracing/src/integrations/node/prisma.ts @@ -38,6 +38,10 @@ interface PrismaClient { $use: (cb: PrismaMiddleware) => void; } +function isValidPrismaClient(possibleClient: unknown): possibleClient is PrismaClient { + return possibleClient && !!(possibleClient as PrismaClient)['$use']; +} + /** Tracing integration for @prisma/client package */ export class Prisma implements Integration { /** @@ -58,8 +62,14 @@ export class Prisma implements Integration { /** * @inheritDoc */ - public constructor(options: { client?: PrismaClient } = {}) { - this._client = options.client; + public constructor(options: { client?: unknown } = {}) { + if (isValidPrismaClient(options.client)) { + this._client = options.client; + } else { + logger.warn( + `Unsupported Prisma client provided to PrismaIntegration. Provided client: ${JSON.stringify(options.client)}`, + ); + } } /** @@ -71,7 +81,7 @@ export class Prisma implements Integration { return; } - this._client.$use((params: PrismaMiddlewareParams, next: (params: PrismaMiddlewareParams) => Promise) => { + this._client.$use((params, next: (params: PrismaMiddlewareParams) => Promise) => { const scope = getCurrentHub().getScope(); const parentSpan = scope?.getSpan();