From 1afdd81d432be0f803d7cd2dde47d261074dd83c Mon Sep 17 00:00:00 2001 From: Harminder Virk Date: Mon, 9 Dec 2024 12:23:16 +0530 Subject: [PATCH] fix: Model.transaction always create a transaction and do not re-use existing one --- package.json | 8 ++++---- src/orm/base_model/index.ts | 25 +++++++++++++++++++------ src/types/model.ts | 5 ++--- test/orm/base_model.spec.ts | 26 ++++++++++++++++++++++++++ 4 files changed, 51 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index ffb40dca..6ba62c3a 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ }, "devDependencies": { "@adonisjs/assembler": "^7.8.2", - "@adonisjs/core": "^6.15.2", + "@adonisjs/core": "^6.16.0", "@adonisjs/eslint-config": "^2.0.0-beta.7", "@adonisjs/prettier-config": "^1.4.0", "@adonisjs/tsconfig": "^1.4.0", @@ -69,14 +69,14 @@ "@japa/runner": "^3.1.4", "@libsql/sqlite3": "^0.3.1", "@release-it/conventional-changelog": "^9.0.3", - "@swc/core": "^1.9.3", + "@swc/core": "^1.10.0", "@types/chance": "^1.1.6", "@types/luxon": "^3.4.2", "@types/node": "^22.10.1", "@types/pretty-hrtime": "^1.0.3", "@types/qs": "^6.9.17", "@vinejs/vine": "^3.0.0", - "better-sqlite3": "^11.6.0", + "better-sqlite3": "^11.7.0", "c8": "^10.1.2", "chance": "^1.1.12", "copyfiles": "^2.4.1", @@ -88,7 +88,7 @@ "luxon": "^3.5.0", "mysql2": "^3.11.5", "pg": "^8.13.1", - "prettier": "^3.4.1", + "prettier": "^3.4.2", "reflect-metadata": "^0.2.2", "release-it": "^17.10.0", "sqlite3": "^5.1.7", diff --git a/src/orm/base_model/index.ts b/src/orm/base_model/index.ts index beb472bc..f1a1cdbc 100644 --- a/src/orm/base_model/index.ts +++ b/src/orm/base_model/index.ts @@ -248,15 +248,28 @@ class BaseModelImpl implements LucidRow { /** * Returns the model query instance for the given model */ - static async transaction( + static transaction( + callback: (trx: TransactionClientContract) => Promise, options?: ModelAdapterOptions & { isolationLevel?: IsolationLevels } - ): Promise { - const client = this.$adapter.modelConstructorClient(this, options) - if (client.isTransaction) { - return client as TransactionClientContract + ): Promise + static transaction( + options?: ModelAdapterOptions & { + isolationLevel?: IsolationLevels + } + ): Promise + static async transaction( + callbackOrOptions?: + | ((trx: TransactionClientContract) => Promise) + | (ModelAdapterOptions & { isolationLevel?: IsolationLevels }), + options?: ModelAdapterOptions & { isolationLevel?: IsolationLevels } + ): Promise { + if (typeof callbackOrOptions === 'function') { + const client = this.$adapter.modelConstructorClient(this, options) + return client.transaction(callbackOrOptions, options) } - return client.transaction() + const client = this.$adapter.modelConstructorClient(this, callbackOrOptions) + return client.transaction(callbackOrOptions) } /** diff --git a/src/types/model.ts b/src/types/model.ts index e178a945..a191d37f 100644 --- a/src/types/model.ts +++ b/src/types/model.ts @@ -14,6 +14,7 @@ import { IsolationLevels, QueryClientContract, TransactionClientContract, + TransactionFn, } from './database.js' import { @@ -1154,9 +1155,7 @@ export interface LucidModel { * Returns transaction client from the model. It is same as * calling "db.transaction" */ - transaction( - options?: ModelAdapterOptions & { isolationLevel?: IsolationLevels } - ): Promise + transaction: TransactionFn /** * Truncate model table diff --git a/test/orm/base_model.spec.ts b/test/orm/base_model.spec.ts index 630a10a2..9668da8c 100644 --- a/test/orm/base_model.spec.ts +++ b/test/orm/base_model.spec.ts @@ -8346,4 +8346,30 @@ test.group('Base Model | transaction', (group) => { assert.isNull(user) }) + + test('auto manage transaction when callback is provided', async ({ fs, assert }) => { + const app = new AppFactory().create(fs.baseUrl, () => {}) + await app.init() + const db = getDb() + const adapter = ormAdapter(db) + + const BaseModel = getBaseModel(adapter) + + class User extends BaseModel { + @column({ isPrimary: true }) + declare id: number + + @column() + declare username: string + + @column() + declare email: string + } + + const [userId] = await User.transaction(async (trx) => { + return trx.insertQuery().table('users').insert({ username: 'virk' }).returning('id') + }) + const user = await User.find(userId) + assert.equal(user!.username, 'virk') + }) })