added action arg to the before Dao hook to allow skipping the default persist behavior
This commit is contained in:
		
							parent
							
								
									6da94aef8d
								
							
						
					
					
						commit
						cdeb9a94ed
					
				| 
						 | 
				
			
			@ -119,6 +119,7 @@
 | 
			
		|||
  There is a system migration that will convert the existing view `relation` fields to `json` (multiple) and `text` (single) fields.
 | 
			
		||||
  This could be a breaking change if you have `relation` to view and use `expand` or some of the `relation` view fields as part of a collection rule.
 | 
			
		||||
 | 
			
		||||
- **!** (@todo docs) Added action argument to the Dao hooks to allow skipping the default persist behavior.
 | 
			
		||||
 | 
			
		||||
## v0.16.10
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										36
									
								
								core/base.go
								
								
								
								
							
							
						
						
									
										36
									
								
								core/base.go
								
								
								
								
							| 
						 | 
				
			
			@ -1049,58 +1049,58 @@ func (app *BaseApp) initDataDB() error {
 | 
			
		|||
func (app *BaseApp) createDaoWithHooks(concurrentDB, nonconcurrentDB dbx.Builder) *daos.Dao {
 | 
			
		||||
	dao := daos.NewMultiDB(concurrentDB, nonconcurrentDB)
 | 
			
		||||
 | 
			
		||||
	dao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
	dao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
		e := new(ModelEvent)
 | 
			
		||||
		e.Dao = eventDao
 | 
			
		||||
		e.Model = m
 | 
			
		||||
 | 
			
		||||
		return app.OnModelBeforeCreate().Trigger(e)
 | 
			
		||||
		return app.OnModelBeforeCreate().Trigger(e, func(e *ModelEvent) error {
 | 
			
		||||
			return action()
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dao.AfterCreateFunc = func(eventDao *daos.Dao, m models.Model) {
 | 
			
		||||
	dao.AfterCreateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
		e := new(ModelEvent)
 | 
			
		||||
		e.Dao = eventDao
 | 
			
		||||
		e.Model = m
 | 
			
		||||
 | 
			
		||||
		if err := app.OnModelAfterCreate().Trigger(e); err != nil && app.isDebug {
 | 
			
		||||
			log.Println(err)
 | 
			
		||||
		}
 | 
			
		||||
		return app.OnModelAfterCreate().Trigger(e)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dao.BeforeUpdateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
	dao.BeforeUpdateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
		e := new(ModelEvent)
 | 
			
		||||
		e.Dao = eventDao
 | 
			
		||||
		e.Model = m
 | 
			
		||||
 | 
			
		||||
		return app.OnModelBeforeUpdate().Trigger(e)
 | 
			
		||||
		return app.OnModelBeforeUpdate().Trigger(e, func(e *ModelEvent) error {
 | 
			
		||||
			return action()
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dao.AfterUpdateFunc = func(eventDao *daos.Dao, m models.Model) {
 | 
			
		||||
	dao.AfterUpdateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
		e := new(ModelEvent)
 | 
			
		||||
		e.Dao = eventDao
 | 
			
		||||
		e.Model = m
 | 
			
		||||
 | 
			
		||||
		if err := app.OnModelAfterUpdate().Trigger(e); err != nil && app.isDebug {
 | 
			
		||||
			log.Println(err)
 | 
			
		||||
		}
 | 
			
		||||
		return app.OnModelAfterUpdate().Trigger(e)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dao.BeforeDeleteFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
	dao.BeforeDeleteFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
		e := new(ModelEvent)
 | 
			
		||||
		e.Dao = eventDao
 | 
			
		||||
		e.Model = m
 | 
			
		||||
 | 
			
		||||
		return app.OnModelBeforeDelete().Trigger(e)
 | 
			
		||||
		return app.OnModelBeforeDelete().Trigger(e, func(e *ModelEvent) error {
 | 
			
		||||
			return action()
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dao.AfterDeleteFunc = func(eventDao *daos.Dao, m models.Model) {
 | 
			
		||||
	dao.AfterDeleteFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
		e := new(ModelEvent)
 | 
			
		||||
		e.Dao = eventDao
 | 
			
		||||
		e.Model = m
 | 
			
		||||
 | 
			
		||||
		if err := app.OnModelAfterDelete().Trigger(e); err != nil && app.isDebug {
 | 
			
		||||
			log.Println(err)
 | 
			
		||||
		}
 | 
			
		||||
		return app.OnModelAfterDelete().Trigger(e)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return dao
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										119
									
								
								daos/base.go
								
								
								
								
							
							
						
						
									
										119
									
								
								daos/base.go
								
								
								
								
							| 
						 | 
				
			
			@ -5,6 +5,8 @@ package daos
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/pocketbase/dbx"
 | 
			
		||||
| 
						 | 
				
			
			@ -45,12 +47,12 @@ type Dao struct {
 | 
			
		|||
	ModelQueryTimeout time.Duration
 | 
			
		||||
 | 
			
		||||
	// write hooks
 | 
			
		||||
	BeforeCreateFunc func(eventDao *Dao, m models.Model) error
 | 
			
		||||
	AfterCreateFunc  func(eventDao *Dao, m models.Model)
 | 
			
		||||
	BeforeUpdateFunc func(eventDao *Dao, m models.Model) error
 | 
			
		||||
	AfterUpdateFunc  func(eventDao *Dao, m models.Model)
 | 
			
		||||
	BeforeDeleteFunc func(eventDao *Dao, m models.Model) error
 | 
			
		||||
	AfterDeleteFunc  func(eventDao *Dao, m models.Model)
 | 
			
		||||
	BeforeCreateFunc func(eventDao *Dao, m models.Model, action func() error) error
 | 
			
		||||
	AfterCreateFunc  func(eventDao *Dao, m models.Model) error
 | 
			
		||||
	BeforeUpdateFunc func(eventDao *Dao, m models.Model, action func() error) error
 | 
			
		||||
	AfterUpdateFunc  func(eventDao *Dao, m models.Model) error
 | 
			
		||||
	BeforeDeleteFunc func(eventDao *Dao, m models.Model, action func() error) error
 | 
			
		||||
	AfterDeleteFunc  func(eventDao *Dao, m models.Model) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DB returns the default dao db builder (*dbx.DB or *dbx.TX).
 | 
			
		||||
| 
						 | 
				
			
			@ -151,56 +153,75 @@ func (dao *Dao) RunInTransaction(fn func(txDao *Dao) error) error {
 | 
			
		|||
			txDao := New(tx)
 | 
			
		||||
 | 
			
		||||
			if dao.BeforeCreateFunc != nil {
 | 
			
		||||
				txDao.BeforeCreateFunc = func(eventDao *Dao, m models.Model) error {
 | 
			
		||||
					return dao.BeforeCreateFunc(eventDao, m)
 | 
			
		||||
				txDao.BeforeCreateFunc = func(eventDao *Dao, m models.Model, action func() error) error {
 | 
			
		||||
					return dao.BeforeCreateFunc(eventDao, m, action)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if dao.BeforeUpdateFunc != nil {
 | 
			
		||||
				txDao.BeforeUpdateFunc = func(eventDao *Dao, m models.Model) error {
 | 
			
		||||
					return dao.BeforeUpdateFunc(eventDao, m)
 | 
			
		||||
				txDao.BeforeUpdateFunc = func(eventDao *Dao, m models.Model, action func() error) error {
 | 
			
		||||
					return dao.BeforeUpdateFunc(eventDao, m, action)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if dao.BeforeDeleteFunc != nil {
 | 
			
		||||
				txDao.BeforeDeleteFunc = func(eventDao *Dao, m models.Model) error {
 | 
			
		||||
					return dao.BeforeDeleteFunc(eventDao, m)
 | 
			
		||||
				txDao.BeforeDeleteFunc = func(eventDao *Dao, m models.Model, action func() error) error {
 | 
			
		||||
					return dao.BeforeDeleteFunc(eventDao, m, action)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if dao.AfterCreateFunc != nil {
 | 
			
		||||
				txDao.AfterCreateFunc = func(eventDao *Dao, m models.Model) {
 | 
			
		||||
				txDao.AfterCreateFunc = func(eventDao *Dao, m models.Model) error {
 | 
			
		||||
					afterCalls = append(afterCalls, afterCallGroup{"create", eventDao, m})
 | 
			
		||||
					return nil
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if dao.AfterUpdateFunc != nil {
 | 
			
		||||
				txDao.AfterUpdateFunc = func(eventDao *Dao, m models.Model) {
 | 
			
		||||
				txDao.AfterUpdateFunc = func(eventDao *Dao, m models.Model) error {
 | 
			
		||||
					afterCalls = append(afterCalls, afterCallGroup{"update", eventDao, m})
 | 
			
		||||
					return nil
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if dao.AfterDeleteFunc != nil {
 | 
			
		||||
				txDao.AfterDeleteFunc = func(eventDao *Dao, m models.Model) {
 | 
			
		||||
				txDao.AfterDeleteFunc = func(eventDao *Dao, m models.Model) error {
 | 
			
		||||
					afterCalls = append(afterCalls, afterCallGroup{"delete", eventDao, m})
 | 
			
		||||
					return nil
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return fn(txDao)
 | 
			
		||||
		})
 | 
			
		||||
		if txError != nil {
 | 
			
		||||
			return txError
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if txError == nil {
 | 
			
		||||
		// execute after event calls on successful transaction
 | 
			
		||||
		// (note: using the non-transaction dao to allow following queries in the after hooks)
 | 
			
		||||
		var errs []error
 | 
			
		||||
		for _, call := range afterCalls {
 | 
			
		||||
			var err error
 | 
			
		||||
			switch call.Action {
 | 
			
		||||
			case "create":
 | 
			
		||||
					dao.AfterCreateFunc(dao, call.Model)
 | 
			
		||||
				err = dao.AfterCreateFunc(dao, call.Model)
 | 
			
		||||
			case "update":
 | 
			
		||||
					dao.AfterUpdateFunc(dao, call.Model)
 | 
			
		||||
				err = dao.AfterUpdateFunc(dao, call.Model)
 | 
			
		||||
			case "delete":
 | 
			
		||||
					dao.AfterDeleteFunc(dao, call.Model)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
				err = dao.AfterDeleteFunc(dao, call.Model)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		return txError
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				errs = append(errs, err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if len(errs) > 0 {
 | 
			
		||||
			// @todo after go 1.20+ upgrade consider replacing with errors.Join()
 | 
			
		||||
			var errsMsg strings.Builder
 | 
			
		||||
			for _, err := range errs {
 | 
			
		||||
				errsMsg.WriteString(err.Error())
 | 
			
		||||
				errsMsg.WriteString("; ")
 | 
			
		||||
			}
 | 
			
		||||
			return fmt.Errorf("after transaction errors: %s", errsMsg.String())
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return errors.New("failed to start transaction (unknown dao.NonconcurrentDB() instance)")
 | 
			
		||||
| 
						 | 
				
			
			@ -213,12 +234,7 @@ func (dao *Dao) Delete(m models.Model) error {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	return dao.lockRetry(func(retryDao *Dao) error {
 | 
			
		||||
		if retryDao.BeforeDeleteFunc != nil {
 | 
			
		||||
			if err := retryDao.BeforeDeleteFunc(retryDao, m); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		action := func() error {
 | 
			
		||||
			if err := retryDao.NonconcurrentDB().Model(m).Delete(); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -228,6 +244,13 @@ func (dao *Dao) Delete(m models.Model) error {
 | 
			
		|||
			}
 | 
			
		||||
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if retryDao.BeforeDeleteFunc != nil {
 | 
			
		||||
			return retryDao.BeforeDeleteFunc(retryDao, m, action)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return action()
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -258,12 +281,7 @@ func (dao *Dao) update(m models.Model) error {
 | 
			
		|||
 | 
			
		||||
	m.RefreshUpdated()
 | 
			
		||||
 | 
			
		||||
	if dao.BeforeUpdateFunc != nil {
 | 
			
		||||
		if err := dao.BeforeUpdateFunc(dao, m); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	action := func() error {
 | 
			
		||||
		if v, ok := any(m).(models.ColumnValueMapper); ok {
 | 
			
		||||
			dataMap := v.ColumnValueMap()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -276,17 +294,22 @@ func (dao *Dao) update(m models.Model) error {
 | 
			
		|||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
	} else {
 | 
			
		||||
		if err := dao.NonconcurrentDB().Model(m).Update(); err != nil {
 | 
			
		||||
		} else if err := dao.NonconcurrentDB().Model(m).Update(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		if dao.AfterUpdateFunc != nil {
 | 
			
		||||
		dao.AfterUpdateFunc(dao, m)
 | 
			
		||||
			return dao.AfterUpdateFunc(dao, m)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if dao.BeforeUpdateFunc != nil {
 | 
			
		||||
		return dao.BeforeUpdateFunc(dao, m, action)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return action()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dao *Dao) create(m models.Model) error {
 | 
			
		||||
| 
						 | 
				
			
			@ -306,12 +329,7 @@ func (dao *Dao) create(m models.Model) error {
 | 
			
		|||
		m.RefreshUpdated()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if dao.BeforeCreateFunc != nil {
 | 
			
		||||
		if err := dao.BeforeCreateFunc(dao, m); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	action := func() error {
 | 
			
		||||
		if v, ok := any(m).(models.ColumnValueMapper); ok {
 | 
			
		||||
			dataMap := v.ColumnValueMap()
 | 
			
		||||
			if _, ok := dataMap["id"]; !ok {
 | 
			
		||||
| 
						 | 
				
			
			@ -322,20 +340,25 @@ func (dao *Dao) create(m models.Model) error {
 | 
			
		|||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
	} else {
 | 
			
		||||
		if err := dao.NonconcurrentDB().Model(m).Insert(); err != nil {
 | 
			
		||||
		} else if err := dao.NonconcurrentDB().Model(m).Insert(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		// clears the "new" model flag
 | 
			
		||||
		m.MarkAsNotNew()
 | 
			
		||||
 | 
			
		||||
		if dao.AfterCreateFunc != nil {
 | 
			
		||||
		dao.AfterCreateFunc(dao, m)
 | 
			
		||||
			return dao.AfterCreateFunc(dao, m)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if dao.BeforeCreateFunc != nil {
 | 
			
		||||
		return dao.BeforeCreateFunc(dao, m, action)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return action()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dao *Dao) lockRetry(op func(retryDao *Dao) error) error {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -49,33 +49,37 @@ func TestDaoClone(t *testing.T) {
 | 
			
		|||
	dao := daos.NewMultiDB(testApp.Dao().ConcurrentDB(), testApp.Dao().NonconcurrentDB())
 | 
			
		||||
	dao.MaxLockRetries = 1
 | 
			
		||||
	dao.ModelQueryTimeout = 2
 | 
			
		||||
	dao.BeforeDeleteFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
	dao.BeforeDeleteFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
		hookCalls["BeforeDeleteFunc"]++
 | 
			
		||||
		return nil
 | 
			
		||||
		return action()
 | 
			
		||||
	}
 | 
			
		||||
	dao.BeforeUpdateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
	dao.BeforeUpdateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
		hookCalls["BeforeUpdateFunc"]++
 | 
			
		||||
		return nil
 | 
			
		||||
		return action()
 | 
			
		||||
	}
 | 
			
		||||
	dao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
	dao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
		hookCalls["BeforeCreateFunc"]++
 | 
			
		||||
		return action()
 | 
			
		||||
	}
 | 
			
		||||
	dao.AfterDeleteFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
		hookCalls["AfterDeleteFunc"]++
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	dao.AfterDeleteFunc = func(eventDao *daos.Dao, m models.Model) {
 | 
			
		||||
		hookCalls["AfterDeleteFunc"]++
 | 
			
		||||
	}
 | 
			
		||||
	dao.AfterUpdateFunc = func(eventDao *daos.Dao, m models.Model) {
 | 
			
		||||
	dao.AfterUpdateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
		hookCalls["AfterUpdateFunc"]++
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	dao.AfterCreateFunc = func(eventDao *daos.Dao, m models.Model) {
 | 
			
		||||
	dao.AfterCreateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
		hookCalls["AfterCreateFunc"]++
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	clone := dao.Clone()
 | 
			
		||||
	clone.MaxLockRetries = 3
 | 
			
		||||
	clone.ModelQueryTimeout = 4
 | 
			
		||||
	clone.AfterCreateFunc = func(eventDao *daos.Dao, m models.Model) {
 | 
			
		||||
	clone.AfterCreateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
		hookCalls["NewAfterCreateFunc"]++
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if dao.MaxLockRetries == clone.MaxLockRetries {
 | 
			
		||||
| 
						 | 
				
			
			@ -86,16 +90,18 @@ func TestDaoClone(t *testing.T) {
 | 
			
		|||
		t.Fatal("Expected different ModelQueryTimeout")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	emptyAction := func() error { return nil }
 | 
			
		||||
 | 
			
		||||
	// trigger hooks
 | 
			
		||||
	dao.BeforeDeleteFunc(nil, nil)
 | 
			
		||||
	dao.BeforeUpdateFunc(nil, nil)
 | 
			
		||||
	dao.BeforeCreateFunc(nil, nil)
 | 
			
		||||
	dao.BeforeDeleteFunc(nil, nil, emptyAction)
 | 
			
		||||
	dao.BeforeUpdateFunc(nil, nil, emptyAction)
 | 
			
		||||
	dao.BeforeCreateFunc(nil, nil, emptyAction)
 | 
			
		||||
	dao.AfterDeleteFunc(nil, nil)
 | 
			
		||||
	dao.AfterUpdateFunc(nil, nil)
 | 
			
		||||
	dao.AfterCreateFunc(nil, nil)
 | 
			
		||||
	clone.BeforeDeleteFunc(nil, nil)
 | 
			
		||||
	clone.BeforeUpdateFunc(nil, nil)
 | 
			
		||||
	clone.BeforeCreateFunc(nil, nil)
 | 
			
		||||
	clone.BeforeDeleteFunc(nil, nil, emptyAction)
 | 
			
		||||
	clone.BeforeUpdateFunc(nil, nil, emptyAction)
 | 
			
		||||
	clone.BeforeCreateFunc(nil, nil, emptyAction)
 | 
			
		||||
	clone.AfterDeleteFunc(nil, nil)
 | 
			
		||||
	clone.AfterUpdateFunc(nil, nil)
 | 
			
		||||
	clone.AfterCreateFunc(nil, nil)
 | 
			
		||||
| 
						 | 
				
			
			@ -129,26 +135,29 @@ func TestDaoWithoutHooks(t *testing.T) {
 | 
			
		|||
	dao := daos.NewMultiDB(testApp.Dao().ConcurrentDB(), testApp.Dao().NonconcurrentDB())
 | 
			
		||||
	dao.MaxLockRetries = 1
 | 
			
		||||
	dao.ModelQueryTimeout = 2
 | 
			
		||||
	dao.BeforeDeleteFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
	dao.BeforeDeleteFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
		hookCalls["BeforeDeleteFunc"]++
 | 
			
		||||
		return nil
 | 
			
		||||
		return action()
 | 
			
		||||
	}
 | 
			
		||||
	dao.BeforeUpdateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
	dao.BeforeUpdateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
		hookCalls["BeforeUpdateFunc"]++
 | 
			
		||||
		return nil
 | 
			
		||||
		return action()
 | 
			
		||||
	}
 | 
			
		||||
	dao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
	dao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
		hookCalls["BeforeCreateFunc"]++
 | 
			
		||||
		return action()
 | 
			
		||||
	}
 | 
			
		||||
	dao.AfterDeleteFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
		hookCalls["AfterDeleteFunc"]++
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	dao.AfterDeleteFunc = func(eventDao *daos.Dao, m models.Model) {
 | 
			
		||||
		hookCalls["AfterDeleteFunc"]++
 | 
			
		||||
	}
 | 
			
		||||
	dao.AfterUpdateFunc = func(eventDao *daos.Dao, m models.Model) {
 | 
			
		||||
	dao.AfterUpdateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
		hookCalls["AfterUpdateFunc"]++
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	dao.AfterCreateFunc = func(eventDao *daos.Dao, m models.Model) {
 | 
			
		||||
	dao.AfterCreateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
		hookCalls["AfterCreateFunc"]++
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	new := dao.WithoutHooks()
 | 
			
		||||
| 
						 | 
				
			
			@ -481,12 +490,13 @@ func TestDaoRetryCreate(t *testing.T) {
 | 
			
		|||
	retryBeforeCreateHookCalls := 0
 | 
			
		||||
	retryAfterCreateHookCalls := 0
 | 
			
		||||
	retryDao := daos.New(testApp.DB())
 | 
			
		||||
	retryDao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
	retryDao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
		retryBeforeCreateHookCalls++
 | 
			
		||||
		return errors.New("database is locked")
 | 
			
		||||
	}
 | 
			
		||||
	retryDao.AfterCreateFunc = func(eventDao *daos.Dao, m models.Model) {
 | 
			
		||||
	retryDao.AfterCreateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
		retryAfterCreateHookCalls++
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	model := &models.Admin{Email: "new@example.com"}
 | 
			
		||||
| 
						 | 
				
			
			@ -507,7 +517,7 @@ func TestDaoRetryCreate(t *testing.T) {
 | 
			
		|||
	// with non-locking error
 | 
			
		||||
	retryBeforeCreateHookCalls = 0
 | 
			
		||||
	retryAfterCreateHookCalls = 0
 | 
			
		||||
	retryDao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
	retryDao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
		retryBeforeCreateHookCalls++
 | 
			
		||||
		return errors.New("non-locking error")
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -539,12 +549,13 @@ func TestDaoRetryUpdate(t *testing.T) {
 | 
			
		|||
	retryBeforeUpdateHookCalls := 0
 | 
			
		||||
	retryAfterUpdateHookCalls := 0
 | 
			
		||||
	retryDao := daos.New(testApp.DB())
 | 
			
		||||
	retryDao.BeforeUpdateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
	retryDao.BeforeUpdateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
		retryBeforeUpdateHookCalls++
 | 
			
		||||
		return errors.New("database is locked")
 | 
			
		||||
	}
 | 
			
		||||
	retryDao.AfterUpdateFunc = func(eventDao *daos.Dao, m models.Model) {
 | 
			
		||||
	retryDao.AfterUpdateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
		retryAfterUpdateHookCalls++
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := retryDao.Save(model); err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -564,7 +575,7 @@ func TestDaoRetryUpdate(t *testing.T) {
 | 
			
		|||
	// with non-locking error
 | 
			
		||||
	retryBeforeUpdateHookCalls = 0
 | 
			
		||||
	retryAfterUpdateHookCalls = 0
 | 
			
		||||
	retryDao.BeforeUpdateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
	retryDao.BeforeUpdateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
		retryBeforeUpdateHookCalls++
 | 
			
		||||
		return errors.New("non-locking error")
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -590,12 +601,13 @@ func TestDaoRetryDelete(t *testing.T) {
 | 
			
		|||
	retryBeforeDeleteHookCalls := 0
 | 
			
		||||
	retryAfterDeleteHookCalls := 0
 | 
			
		||||
	retryDao := daos.New(testApp.DB())
 | 
			
		||||
	retryDao.BeforeDeleteFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
	retryDao.BeforeDeleteFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
		retryBeforeDeleteHookCalls++
 | 
			
		||||
		return errors.New("database is locked")
 | 
			
		||||
	}
 | 
			
		||||
	retryDao.AfterDeleteFunc = func(eventDao *daos.Dao, m models.Model) {
 | 
			
		||||
	retryDao.AfterDeleteFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
		retryAfterDeleteHookCalls++
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	model, _ := retryDao.FindAdminByEmail("test@example.com")
 | 
			
		||||
| 
						 | 
				
			
			@ -616,7 +628,7 @@ func TestDaoRetryDelete(t *testing.T) {
 | 
			
		|||
	// with non-locking error
 | 
			
		||||
	retryBeforeDeleteHookCalls = 0
 | 
			
		||||
	retryAfterDeleteHookCalls = 0
 | 
			
		||||
	retryDao.BeforeDeleteFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
	retryDao.BeforeDeleteFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
		retryBeforeDeleteHookCalls++
 | 
			
		||||
		return errors.New("non-locking error")
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -643,13 +655,13 @@ func TestDaoBeforeHooksError(t *testing.T) {
 | 
			
		|||
 | 
			
		||||
	baseDao := testApp.Dao()
 | 
			
		||||
 | 
			
		||||
	baseDao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
	baseDao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
		return errors.New("before_create")
 | 
			
		||||
	}
 | 
			
		||||
	baseDao.BeforeUpdateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
	baseDao.BeforeUpdateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
		return errors.New("before_update")
 | 
			
		||||
	}
 | 
			
		||||
	baseDao.BeforeDeleteFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
	baseDao.BeforeDeleteFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
		return errors.New("before_delete")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -688,27 +700,30 @@ func TestDaoTransactionHooksCallsOnFailure(t *testing.T) {
 | 
			
		|||
 | 
			
		||||
	baseDao := testApp.Dao()
 | 
			
		||||
 | 
			
		||||
	baseDao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
	baseDao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
		beforeCreateFuncCalls++
 | 
			
		||||
		return nil
 | 
			
		||||
		return action()
 | 
			
		||||
	}
 | 
			
		||||
	baseDao.BeforeUpdateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
	baseDao.BeforeUpdateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
		beforeUpdateFuncCalls++
 | 
			
		||||
		return nil
 | 
			
		||||
		return action()
 | 
			
		||||
	}
 | 
			
		||||
	baseDao.BeforeDeleteFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
	baseDao.BeforeDeleteFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
		beforeDeleteFuncCalls++
 | 
			
		||||
		return nil
 | 
			
		||||
		return action()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	baseDao.AfterCreateFunc = func(eventDao *daos.Dao, m models.Model) {
 | 
			
		||||
	baseDao.AfterCreateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
		afterCreateFuncCalls++
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	baseDao.AfterUpdateFunc = func(eventDao *daos.Dao, m models.Model) {
 | 
			
		||||
	baseDao.AfterUpdateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
		afterUpdateFuncCalls++
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	baseDao.AfterDeleteFunc = func(eventDao *daos.Dao, m models.Model) {
 | 
			
		||||
	baseDao.AfterDeleteFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
		afterDeleteFuncCalls++
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	existingModel, _ := testApp.Dao().FindAdminByEmail("test@example.com")
 | 
			
		||||
| 
						 | 
				
			
			@ -776,27 +791,30 @@ func TestDaoTransactionHooksCallsOnSuccess(t *testing.T) {
 | 
			
		|||
 | 
			
		||||
	baseDao := testApp.Dao()
 | 
			
		||||
 | 
			
		||||
	baseDao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
	baseDao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
		beforeCreateFuncCalls++
 | 
			
		||||
		return nil
 | 
			
		||||
		return action()
 | 
			
		||||
	}
 | 
			
		||||
	baseDao.BeforeUpdateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
	baseDao.BeforeUpdateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
		beforeUpdateFuncCalls++
 | 
			
		||||
		return nil
 | 
			
		||||
		return action()
 | 
			
		||||
	}
 | 
			
		||||
	baseDao.BeforeDeleteFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
	baseDao.BeforeDeleteFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
		beforeDeleteFuncCalls++
 | 
			
		||||
		return nil
 | 
			
		||||
		return action()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	baseDao.AfterCreateFunc = func(eventDao *daos.Dao, m models.Model) {
 | 
			
		||||
	baseDao.AfterCreateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
		afterCreateFuncCalls++
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	baseDao.AfterUpdateFunc = func(eventDao *daos.Dao, m models.Model) {
 | 
			
		||||
	baseDao.AfterUpdateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
		afterUpdateFuncCalls++
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	baseDao.AfterDeleteFunc = func(eventDao *daos.Dao, m models.Model) {
 | 
			
		||||
	baseDao.AfterDeleteFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
		afterDeleteFuncCalls++
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	existingModel, _ := testApp.Dao().FindAdminByEmail("test@example.com")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -744,36 +744,44 @@ func (form *RecordUpsert) Submit(interceptors ...InterceptorFunc[*models.Record]
 | 
			
		|||
 | 
			
		||||
		// upload new files (if any)
 | 
			
		||||
		//
 | 
			
		||||
		// note: executed after the default BeforeCreateFunc and BeforeUpdateFunc hooks
 | 
			
		||||
		// note: executed after the default BeforeCreateFunc and BeforeUpdateFunc hook actions
 | 
			
		||||
		// to allow uploading AFTER the before app model hooks (eg. in case of an id change)
 | 
			
		||||
		// but BEFORE the actual record db persistence
 | 
			
		||||
		// ---
 | 
			
		||||
		dao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
		dao.BeforeCreateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
			newAction := func() error {
 | 
			
		||||
				if m.TableName() == form.record.TableName() && m.GetId() == form.record.GetId() {
 | 
			
		||||
					if err := form.processFilesToUpload(); err != nil {
 | 
			
		||||
						return err
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				return action()
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if form.dao.BeforeCreateFunc != nil {
 | 
			
		||||
				if err := form.dao.BeforeCreateFunc(eventDao, m); err != nil {
 | 
			
		||||
				return form.dao.BeforeCreateFunc(eventDao, m, newAction)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return newAction()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		dao.BeforeUpdateFunc = func(eventDao *daos.Dao, m models.Model, action func() error) error {
 | 
			
		||||
			newAction := func() error {
 | 
			
		||||
				if m.TableName() == form.record.TableName() && m.GetId() == form.record.GetId() {
 | 
			
		||||
					if err := form.processFilesToUpload(); err != nil {
 | 
			
		||||
						return err
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			if m.TableName() == form.record.TableName() && m.GetId() == form.record.GetId() {
 | 
			
		||||
				return form.processFilesToUpload()
 | 
			
		||||
				return action()
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		dao.BeforeUpdateFunc = func(eventDao *daos.Dao, m models.Model) error {
 | 
			
		||||
			if form.dao.BeforeUpdateFunc != nil {
 | 
			
		||||
				if err := form.dao.BeforeUpdateFunc(eventDao, m); err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
				return form.dao.BeforeUpdateFunc(eventDao, m, newAction)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if m.TableName() == form.record.TableName() && m.GetId() == form.record.GetId() {
 | 
			
		||||
				return form.processFilesToUpload()
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return nil
 | 
			
		||||
			return newAction()
 | 
			
		||||
		}
 | 
			
		||||
		// ---
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue