From 9df0b38a59b1b14cd9f63d6061ed45d4768067fc Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Tue, 15 Jun 2021 08:10:26 -0500 Subject: [PATCH] fix(client): delete transactionId field in middleware (#7662) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: William Luke Co-authored-by: Joël Galeran --- .../happy/middlewares-transaction/.gitignore | 1 + .../happy/middlewares-transaction/dev.db | Bin 0 -> 53248 bytes .../middlewares-transaction/package.json | 12 +++ .../middlewares-transaction/schema.prisma | 28 +++++++ .../happy/middlewares-transaction/test.ts | 35 +++++++++ .../client/src/runtime/getPrismaClient.ts | 71 ++++++++---------- 6 files changed, 109 insertions(+), 38 deletions(-) create mode 100644 src/packages/client/src/__tests__/integration/happy/middlewares-transaction/.gitignore create mode 100644 src/packages/client/src/__tests__/integration/happy/middlewares-transaction/dev.db create mode 100644 src/packages/client/src/__tests__/integration/happy/middlewares-transaction/package.json create mode 100644 src/packages/client/src/__tests__/integration/happy/middlewares-transaction/schema.prisma create mode 100644 src/packages/client/src/__tests__/integration/happy/middlewares-transaction/test.ts diff --git a/src/packages/client/src/__tests__/integration/happy/middlewares-transaction/.gitignore b/src/packages/client/src/__tests__/integration/happy/middlewares-transaction/.gitignore new file mode 100644 index 000000000000..97952752a72b --- /dev/null +++ b/src/packages/client/src/__tests__/integration/happy/middlewares-transaction/.gitignore @@ -0,0 +1 @@ +!dev.db \ No newline at end of file diff --git a/src/packages/client/src/__tests__/integration/happy/middlewares-transaction/dev.db b/src/packages/client/src/__tests__/integration/happy/middlewares-transaction/dev.db new file mode 100644 index 0000000000000000000000000000000000000000..ea3cf06fb5a665d57c0267f469b011ba55d3dcfe GIT binary patch literal 53248 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 { + 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() + }) +}) diff --git a/src/packages/client/src/runtime/getPrismaClient.ts b/src/packages/client/src/runtime/getPrismaClient.ts index eb784432a7d4..e9c79851af3d 100644 --- a/src/packages/client/src/runtime/getPrismaClient.ts +++ b/src/packages/client/src/runtime/getPrismaClient.ts @@ -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(middleware: QueryMiddleware) + $use(namespace: 'all', cb: QueryMiddleware) + $use(namespace: 'engine', cb: EngineMiddleware) + $use( + arg0: Namespace | QueryMiddleware, + arg1?: QueryMiddleware | EngineMiddleware, ) { // 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') { @@ -915,45 +915,40 @@ new PrismaClient({ * @param middlewareIndex * @returns */ - private _request( - internalParams: InternalRequestParams, - middlewareIndex = 0, - ): Promise { + private _request(internalParams: InternalRequestParams): Promise { 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