While trying to insert logs to detect my models' values changes using gorm in golang, I'm doing it using plugins:
type MyModel struct {
Id
Name
}
type Log struct {
Id
NewValue
OldValue
CreatedAt
}
My plugin definition is something like this:
func ChangelogCreatePlugin(db *gorm.DB) {
log := &Log{NewValue: "the new value", OldValue: "the old value", CreatedAt: "current time"}
// Here is the problem
db.Save(log) <- this is not acceptable
}
Inserting a different data model using the db *gorm.DB
argument in the plugin is not acceptable as that argument is initialized to accept data from the same model which triggered the plugin.
My requirement is to store my log in the same db transaction, so if one of them failed they should both fail. How to do such thing in gorm?
I'm aware of hooks. Hooks are not useful in my case as I want my logs to track different models, so I'm looking for more "reusable" solution, rather than copy/paste hooks implementations in my models.
After a lot of digging and debugging, I came up with this solution:
You first need to register your plugin execution in the right order, in my case it should be something like this:
gormDb.Callback().Create().Before("gorm:commit_or_rollback_transaction").Register("changelog_create", ChangelogCreatePlugin)
This order will guarantee that your plugin will be activated or triggered before the transaction commit of any insert clause of your models.
To have more insights about where you can register your plugins, have a look inside the default callbacks registered in gorm
After doing so, I needed to adjust the plugin code:
func ChangelogCreatePlugin(db *gorm.DB) {
// first make a check that the model insert transaction doesn't have any error
if db.Error != nil {
return
}
log := &Log{NewValue: "the new value", OldValue: "the old value", CreatedAt: "current time"}
// get a new db session for the new model to work
logDb := db.Session(&gorm.Session{})
// if an error ocurred while saving the log
// push it into original model db instance errors, so it will be rolledback eventually
logErr := logDb.Save(log)
if logErr != nil {
db.AddError(logErr)
}
}
Hope this may help someone someday!