From ba763bedf30521d013f1ece7d440d35193446768 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Tue, 9 Jan 2024 23:49:52 +0800 Subject: [PATCH 1/3] fix: bug in enhancement proxy for detecting nested transactions --- packages/runtime/src/enhancements/proxy.ts | 25 +++++++++++++++++++ .../enhancements/with-policy/postgres.test.ts | 4 +-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/packages/runtime/src/enhancements/proxy.ts b/packages/runtime/src/enhancements/proxy.ts index 358bff153..b49307c79 100644 --- a/packages/runtime/src/enhancements/proxy.ts +++ b/packages/runtime/src/enhancements/proxy.ts @@ -183,6 +183,10 @@ export function makeProxy( errorTransformer?: ErrorTransformer ) { const models = Object.keys(modelMeta.fields).map((k) => k.toLowerCase()); + + // a store for saving fields that belong to the proxy (not the target) + const proxyStorage: Record = {}; + const proxy = new Proxy(prisma, { get: (target: any, prop: string | symbol, receiver: any) => { // enhancer metadata @@ -194,6 +198,10 @@ export function makeProxy( return () => `$zenstack_${name}[${target.toString()}]`; } + if (typeof prop === 'string' && prop in proxyStorage) { + return proxyStorage[prop]; + } + if (prop === '$transaction') { // for interactive transactions, we need to proxy the transaction function so that // when it runs the callback, it provides a proxy to the Prisma client wrapped with @@ -213,8 +221,14 @@ export function makeProxy( const txFunc = input; return $transaction.bind(target)((tx: any) => { + // create a proxy for the transaction function const txProxy = makeProxy(tx, modelMeta, makeHandler, name + '$tx'); + + // record in-transaction flag on the proxy (not the target) + // see logic in "set" handler below txProxy[PRISMA_TX_FLAG] = true; + + // call the transaction function with the proxy return txFunc(txProxy); }, ...rest); }; @@ -235,6 +249,17 @@ export function makeProxy( return createHandlerProxy(makeHandler(target, prop), propVal, errorTransformer); }, + + set: (target: any, prop: string | symbol, value: any) => { + if (prop === PRISMA_TX_FLAG) { + // set to the proxy store + proxyStorage[prop] = value; + } else { + // pass through to the original target + target[prop] = value; + } + return true; + }, }); return proxy; diff --git a/tests/integration/tests/enhancements/with-policy/postgres.test.ts b/tests/integration/tests/enhancements/with-policy/postgres.test.ts index caed6a5ce..7653b89f7 100644 --- a/tests/integration/tests/enhancements/with-policy/postgres.test.ts +++ b/tests/integration/tests/enhancements/with-policy/postgres.test.ts @@ -16,14 +16,14 @@ describe('With Policy: with postgres', () => { beforeEach(async () => { dbUrl = await createPostgresDb(DB_NAME); - const { prisma: _prisma, withPolicy } = await loadSchemaFromFile( + const { prisma: _prisma, enhance } = await loadSchemaFromFile( path.join(__dirname, '../../schema/todo-pg.zmodel'), { provider: 'postgresql', dbUrl, } ); - getDb = withPolicy; + getDb = enhance; prisma = _prisma; }); From f292d29fda0bd1f82cd5add984b7af6a0ec05e3e Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Wed, 10 Jan 2024 09:28:58 +0800 Subject: [PATCH 2/3] fix transaction detection --- packages/runtime/src/constants.ts | 5 ----- .../runtime/src/enhancements/policy/handler.ts | 6 +++--- packages/runtime/src/enhancements/proxy.ts | 15 ++------------- 3 files changed, 5 insertions(+), 21 deletions(-) diff --git a/packages/runtime/src/constants.ts b/packages/runtime/src/constants.ts index 3c12b5a88..36143621f 100644 --- a/packages/runtime/src/constants.ts +++ b/packages/runtime/src/constants.ts @@ -53,11 +53,6 @@ export enum PrismaErrorCode { DEPEND_ON_RECORD_NOT_FOUND = 'P2025', } -/** - * Field name for storing in-transaction flag - */ -export const PRISMA_TX_FLAG = '$__zenstack_tx'; - /** * Field name for getting current enhancer */ diff --git a/packages/runtime/src/enhancements/policy/handler.ts b/packages/runtime/src/enhancements/policy/handler.ts index f002002d2..ac2ce50e2 100644 --- a/packages/runtime/src/enhancements/policy/handler.ts +++ b/packages/runtime/src/enhancements/policy/handler.ts @@ -1232,11 +1232,11 @@ export class PolicyProxyHandler implements Pr } private transaction(action: (tx: Record) => Promise) { - if (this.prisma[PRISMA_TX_FLAG]) { + if (this.prisma['$transaction']) { + return this.prisma.$transaction((tx) => action(tx), { maxWait: 100000, timeout: 100000 }); + } else { // already in transaction, don't nest return action(this.prisma); - } else { - return this.prisma.$transaction((tx) => action(tx), { maxWait: 100000, timeout: 100000 }); } } diff --git a/packages/runtime/src/enhancements/proxy.ts b/packages/runtime/src/enhancements/proxy.ts index b49307c79..2a97c0371 100644 --- a/packages/runtime/src/enhancements/proxy.ts +++ b/packages/runtime/src/enhancements/proxy.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { PRISMA_PROXY_ENHANCER, PRISMA_TX_FLAG } from '../constants'; +import { PRISMA_PROXY_ENHANCER } from '../constants'; import type { ModelMeta } from '../cross'; import type { DbClientContract } from '../types'; import { createDeferredPromise } from './policy/promise'; @@ -184,9 +184,6 @@ export function makeProxy( ) { const models = Object.keys(modelMeta.fields).map((k) => k.toLowerCase()); - // a store for saving fields that belong to the proxy (not the target) - const proxyStorage: Record = {}; - const proxy = new Proxy(prisma, { get: (target: any, prop: string | symbol, receiver: any) => { // enhancer metadata @@ -195,11 +192,7 @@ export function makeProxy( } if (prop === 'toString') { - return () => `$zenstack_${name}[${target.toString()}]`; - } - - if (typeof prop === 'string' && prop in proxyStorage) { - return proxyStorage[prop]; + return () => `$zenstack_prisma_${prisma._clientVersion}`; } if (prop === '$transaction') { @@ -224,10 +217,6 @@ export function makeProxy( // create a proxy for the transaction function const txProxy = makeProxy(tx, modelMeta, makeHandler, name + '$tx'); - // record in-transaction flag on the proxy (not the target) - // see logic in "set" handler below - txProxy[PRISMA_TX_FLAG] = true; - // call the transaction function with the proxy return txFunc(txProxy); }, ...rest); From 24ec71b5776809ee407b8ba0b60d8e983b0060e3 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Wed, 10 Jan 2024 09:41:06 +0800 Subject: [PATCH 3/3] fix build --- packages/runtime/src/enhancements/policy/handler.ts | 2 +- packages/runtime/src/enhancements/proxy.ts | 11 ----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/packages/runtime/src/enhancements/policy/handler.ts b/packages/runtime/src/enhancements/policy/handler.ts index ac2ce50e2..9268c24b3 100644 --- a/packages/runtime/src/enhancements/policy/handler.ts +++ b/packages/runtime/src/enhancements/policy/handler.ts @@ -4,7 +4,7 @@ import { lowerCaseFirst } from 'lower-case-first'; import invariant from 'tiny-invariant'; import { upperCaseFirst } from 'upper-case-first'; import { fromZodError } from 'zod-validation-error'; -import { CrudFailureReason, PRISMA_TX_FLAG } from '../../constants'; +import { CrudFailureReason } from '../../constants'; import { ModelDataVisitor, NestedWriteVisitor, diff --git a/packages/runtime/src/enhancements/proxy.ts b/packages/runtime/src/enhancements/proxy.ts index 2a97c0371..c735d595a 100644 --- a/packages/runtime/src/enhancements/proxy.ts +++ b/packages/runtime/src/enhancements/proxy.ts @@ -238,17 +238,6 @@ export function makeProxy( return createHandlerProxy(makeHandler(target, prop), propVal, errorTransformer); }, - - set: (target: any, prop: string | symbol, value: any) => { - if (prop === PRISMA_TX_FLAG) { - // set to the proxy store - proxyStorage[prop] = value; - } else { - // pass through to the original target - target[prop] = value; - } - return true; - }, }); return proxy;