Skip to content

Commit

Permalink
fix(client): delete transactionId field in middleware (prisma#7662)
Browse files Browse the repository at this point in the history
Co-authored-by: William Luke <william@ordino.ai>
Co-authored-by: Joël Galeran <Jolg42@users.noreply.github.com>
  • Loading branch information
3 people authored and Andrew-Colman committed Aug 7, 2021
1 parent 881800b commit 9df0b38
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 38 deletions.
@@ -0,0 +1 @@
!dev.db
Binary file not shown.
@@ -0,0 +1,12 @@
{
"name": "my-prisma-project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
@@ -0,0 +1,28 @@
datasource db {
provider = "sqlite"
url = "file:dev.db"
}

generator client {
provider = "prisma-client-js"
}

// / User model comment
model User {
id String @id @default(uuid())
email String @unique
// / name comment
name String?
posts Post[]
}

model Post {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
published Boolean
title String
content String?
authorId String?
author User? @relation(fields: [authorId], references: [id])
}
@@ -0,0 +1,35 @@
import { getTestClient } from '../../../../utils/getTestClient'

describe('middleware and transaction', () => {
test('typeof await next()', async () => {
const PrismaClient = await getTestClient()

const prisma = new PrismaClient()
await prisma.user.deleteMany()

const responses: any[] = []
prisma.$use(async (params, next) => {
const response = await next(params)
responses.push(response)
return response
})

await prisma.$transaction([
prisma.user.create({
data: {
email: 'test@test.com',
name: 'test',
},
}),
])
expect(typeof responses[0]).toEqual(`object`)
expect(responses[0].email).toMatchInlineSnapshot(`test@test.com`)

const users = await prisma.user.findMany()
expect(users[0].email).toMatchInlineSnapshot(`test@test.com`)

await prisma.user.deleteMany()

prisma.$disconnect()
})
})
71 changes: 33 additions & 38 deletions src/packages/client/src/runtime/getPrismaClient.ts
Expand Up @@ -435,16 +435,16 @@ export function getPrismaClient(config: GetPrismaClientOptions): any {
* Hook a middleware into the client
* @param middleware to hook
*/
$use(middleware: QueryMiddleware)
$use(namespace: 'all', cb: QueryMiddleware)
$use(namespace: 'engine', cb: EngineMiddleware)
$use(
arg0: Namespace | QueryMiddleware,
arg1?: QueryMiddleware | EngineMiddleware,
$use<T>(middleware: QueryMiddleware<T>)
$use<T>(namespace: 'all', cb: QueryMiddleware<T>)
$use<T>(namespace: 'engine', cb: EngineMiddleware<T>)
$use<T>(
arg0: Namespace | QueryMiddleware<T>,
arg1?: QueryMiddleware | EngineMiddleware<T>,
) {
// TODO use a mixin and move this into MiddlewareHandler
if (typeof arg0 === 'function') {
this._middlewares.query.use(arg0)
this._middlewares.query.use(arg0 as QueryMiddleware)
} else if (arg0 === 'all') {
this._middlewares.query.use(arg1 as QueryMiddleware)
} else if (arg0 === 'engine') {
Expand Down Expand Up @@ -915,45 +915,40 @@ new PrismaClient({
* @param middlewareIndex
* @returns
*/
private _request(
internalParams: InternalRequestParams,
middlewareIndex = 0,
): Promise<any> {
private _request(internalParams: InternalRequestParams): Promise<any> {
try {
// in this recursion, we check for our terminating condition
const middleware = this._middlewares.query.get(middlewareIndex)
let index = -1
// async scope https://github.com/prisma/prisma/issues/3148
const resource = new AsyncResource('prisma-client-request')
// make sure that we don't leak extra properties to users
const params: QueryMiddlewareParams = {
args: internalParams.args,
dataPath: internalParams.dataPath,
runInTransaction: internalParams.runInTransaction,
action: internalParams.action,
model: internalParams.model,
}

// prepare recursive fn that will pipe params through middlewares
const consumer = (changedParams: QueryMiddlewareParams) => {
// if this `next` was called and there's some more middlewares
const nextMiddleware = this._middlewares.query.get(++index)

if (middleware) {
// make sure that we don't leak extra properties to users
const params: QueryMiddlewareParams = {
args: internalParams.args,
dataPath: internalParams.dataPath,
runInTransaction: internalParams.runInTransaction,
action: internalParams.action,
model: internalParams.model,
if (nextMiddleware) {
// we pass the modfied params down to the next one, & repeat
return nextMiddleware(changedParams, consumer)
}

return resource.runInAsyncScope(() => {
// call the middleware of the user & get their changes
return middleware(params, (changedParams) => {
// this middleware returns the value of the next one 🐛
return this._request(
{
...internalParams,
...changedParams,
},
++middlewareIndex,
) // recursion happens over here
})
})
const changedInternalParams = { ...internalParams, ...params }

// TODO remove this, because transactionId should be passed?
if (index > 0) delete changedInternalParams['transactionId']

// no middleware? then we just proceed with request execution
return this._executeRequest(changedInternalParams)
}

// they're finished, or there's none, then execute request
return resource.runInAsyncScope(() => {
return this._executeRequest(internalParams)
})
return resource.runInAsyncScope(() => consumer(params))
} catch (e) {
e.clientVersion = this._clientVersion

Expand Down

0 comments on commit 9df0b38

Please sign in to comment.