This is an automated email from the ASF dual-hosted git repository.

hsluoyz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/casbin.git


The following commit(s) were added to refs/heads/master by this push:
     new 9a94fde  feat: expose Transaction.GetAdapter() to share the tx with 
GORM
9a94fde is described below

commit 9a94fde4757836d1327ac8f8df0877ffc97ca66b
Author: Yang Luo <[email protected]>
AuthorDate: Thu Aug 6 23:14:57 2026 +0800

    feat: expose Transaction.GetAdapter() to share the tx with GORM
---
 transaction.go              | 22 +++++++++++++++++++
 transaction_adapter_test.go | 51 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 73 insertions(+)

diff --git a/transaction.go b/transaction.go
index 7c921ca..387432c 100644
--- a/transaction.go
+++ b/transaction.go
@@ -48,6 +48,28 @@ type Transaction struct {
        mutex       sync.RWMutex               // Protects transaction state.
 }
 
+// GetAdapter returns the adapter that operates within this transaction.
+// Writes done through it join the same database transaction as the buffered
+// policy changes, so non-policy operations can be made atomic together with 
the
+// policy changes, e.g. with the GORM adapter:
+//
+//     err := e.WithTransaction(ctx, func(tx *casbin.Transaction) error {
+//             db := tx.GetAdapter().(*gormadapter.Adapter).GetDb().
+//                     Session(&gorm.Session{NewDB: true})
+//             if err := db.Where("name = ?", "admin").Delete(&Role{}).Error; 
err != nil {
+//                     return err
+//             }
+//             _, err := tx.RemoveGroupingPolicy("alice", "admin")
+//             return err
+//     })
+//
+// Mind the Session(&gorm.Session{NewDB: true}): the GORM adapter pins its
+// *gorm.DB to the policy table, and without resetting the statement every 
query
+// would silently run against casbin_rule instead of the caller's own table.
+func (tx *Transaction) GetAdapter() persist.Adapter {
+       return tx.txContext.GetAdapter()
+}
+
 // AddPolicy adds a policy within the transaction.
 // The policy is buffered and will be applied when the transaction is 
committed.
 func (tx *Transaction) AddPolicy(params ...interface{}) (bool, error) {
diff --git a/transaction_adapter_test.go b/transaction_adapter_test.go
new file mode 100644
index 0000000..e38fa4d
--- /dev/null
+++ b/transaction_adapter_test.go
@@ -0,0 +1,51 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package casbin
+
+import (
+       "context"
+       "testing"
+)
+
+// GetAdapter hands out the transaction scoped adapter, so the caller can run
+// its own database operations in the same transaction as the policy changes.
+// See https://github.com/casbin/casbin/issues/1706.
+func TestTransactionGetAdapter(t *testing.T) {
+       adapter := NewMockTransactionalAdapter()
+       e, err := NewTransactionalEnforcer("examples/rbac_model.conf", adapter)
+       if err != nil {
+               t.Fatalf("Failed to create transactional enforcer: %v", err)
+       }
+
+       err = e.WithTransaction(context.Background(), func(tx *Transaction) 
error {
+               // The adapter of the transaction, not the one of the enforcer: 
for a
+               // real adapter this is the one bound to the open database 
transaction.
+               if tx.GetAdapter() == nil {
+                       t.Error("The transaction scoped adapter should be 
reachable")
+               }
+               _, addErr := tx.AddPolicy("admin", "data1", "read")
+               return addErr
+       })
+       if err != nil {
+               t.Fatalf("Failed to run transaction: %v", err)
+       }
+
+       if ok, err := e.HasPolicy("admin", "data1", "read"); err != nil || !ok {
+               t.Fatalf("The policy added in the transaction should be in 
effect, got %v, %v", ok, err)
+       }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to