loggable

package module
v4.2.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 30, 2020 License: MIT Imports: 12 Imported by: 0

README

Loggable

Loggable is used to helps tracking changes and history of your GORM models.

It creates change_logs table in your database and writes to all loggable models when they are changed.

More documentation is available in godoc.

Usage

  1. Register plugin using loggable.Register(db).
plugin, err := Register(database) // database is a *gorm.DB
if err != nil {
	panic(err)
}
  1. Add (embed) loggable.LoggableModel to your GORM model.
type User struct{
    Id        uint
    CreatedAt time.Time
    // some other stuff...
    
    loggable.LoggableModel
}
  1. Changes after calling Create, Save, Update, Delete will be tracked.

Customization

You may add additional fields to change logs, that should be saved.
First, embed loggable.LoggableModel to your model wrapper or directly to GORM model.

type CreatedByLog struct {
	// Public field will be catches by GORM and will be saved to main table.
	CreatedBy     string
	// Hided field because we do not want to write this to main table,
	// only to change_logs.
	createdByPass string 
	loggable.LoggableModel
}

After that, shadow LoggableModel's Meta() method by writing your realization, that should return structure with your information.

type CreatedByLog struct {
	CreatedBy     string
	createdByPass string 
	loggable.LoggableModel
}

func (m CreatedByLog) Meta() interface{} {
	return struct { // You may define special type for this purposes, here we use unnamed one.
		CreatedBy     string
		CreatedByPass string // CreatedByPass is a public because we want to track this field. 
	}{
		CreatedBy:     m.CreatedBy,
		CreatedByPass: m.createdByPass,
	}
}

Options

LazyUpdate

Option LazyUpdate allows save changes only if they big enough to be saved.
Plugin compares the last saved object and the new one, but ignores changes was made in fields from provided list.

ComputeDiff

Option ComputeDiff allows to only save the changes into the RawDiff field. This options is only relevant during update operations. Only fields tagged with gorm-loggable:true will be taken in account. If the object does not have any field tagged with gorm-loggable:true then the column will always be NULL.

e.g.

type Person struct {
	FirstName string `gorm-loggable:true`
	LastName  string `gorm-loggable:true`
	Age       int    `gorm-loggable:true`
}

Let's say you change person FirstName from John to Jack and its Age from 30 to 40. ChangeLog.RawDiff will be populated with the following:

{
  "FirstName": "Jack",
  "Age": 40,
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ToSnakeCase = toSomeCase("_")

Functions

func StringMap

func StringMap(strs []string, fn func(string) string) []string

func ToLowerFirst

func ToLowerFirst(s string) string

Types

type ChangeLog

type ChangeLog struct {
	// Primary key of change logs.
	ID uuid.UUID `gorm:"primary_key;"`
	// Timestamp, when change log was created.
	CreatedAt time.Time `sql:"DEFAULT:current_timestamp"`
	// Action type.
	// On write, supports only 'create', 'update', 'delete',
	// but on read can be anything.
	Action string
	// ID of tracking object.
	// By this ID later you can find all object (database row) changes.
	ObjectID string `gorm:"index"`
	// Reflect name of tracking object.
	// It does not use package or module name, so
	// it may be not unique when use multiple types from different packages but with the same name.
	ObjectType string `gorm:"index"`
	// Raw representation of tracking object.
	// todo(@sas1024): Replace with []byte, to reduce allocations. Would be major version.
	RawObject string `sql:"type:JSON"`
	// Raw representation of tracking object's meta.
	// todo(@sas1024): Replace with []byte, to reduce allocations. Would be major version.
	RawMeta string `sql:"type:JSON"`
	// Raw representation of diff's.
	// todo(@sas1024): Replace with []byte, to reduce allocations. Would be major version.
	RawDiff string `sql:"type:JSON"`
	// Free field to store something you want, e.g. who creates change log.
	// Not used field in gorm-loggable, but gorm tracks this field.
	CreatedBy string `gorm:"index"`
	// Field Object would contain prepared structure, parsed from RawObject as json.
	// Use RegObjectType to register object types.
	Object interface{} `sql:"-"`
	// Field Meta would contain prepared structure, parsed from RawMeta as json.
	// Use RegMetaType to register object's meta types.
	Meta interface{} `sql:"-"`
}

ChangeLog is a main entity, which used to log changes. Commonly, ChangeLog is stored in 'change_logs' table.

func (ChangeLog) Diff

func (l ChangeLog) Diff() (UpdateDiff, error)

Diff returns parsed to map[string]interface{} diff representation from field RawDiff. To unmarshal diff to own structure, manually use field RawDiff.

type Interface

type Interface interface {
	// Meta should return structure, that can be converted to json.
	Meta() interface{}

	// enable/disable loggable
	Enable(v bool)
	// contains filtered or unexported methods
}

Interface is used to get metadata from your models.

type LoggableModel

type LoggableModel struct {
	Disabled bool `sql:"-" json:"-"`
}

LoggableModel is a root structure, which implement Interface. Embed LoggableModel to your model so that Plugin starts tracking changes.

func (LoggableModel) Enable

func (l LoggableModel) Enable(v bool)

func (LoggableModel) Meta

func (LoggableModel) Meta() interface{}

type Option

type Option func(options *options)

Option is a generic options pattern.

func ComputeDiff

func ComputeDiff() Option

Option ComputeDiff allows you also write differences between objects on update operations. ComputeDiff not reads records from db, it used only as cache on plugin side. So it does not track changes outside plugin.

func LazyUpdate

func LazyUpdate(fields ...string) Option

Option LazyUpdate allows you to skip update operations when nothing was changed. Parameter 'fields' is list of sql field names that should be ignored on updates.

func RegMetaType

func RegMetaType(objectType string, metaType interface{}) Option

RegMetaType works like RegObjectType, but for field RawMeta. RegMetaType maps object to type name, that is used in field Type of ChangeLog struct. On read change log operations, if plugin finds registered object type, by its name from db, it unmarshal field RawMeta to Meta field via json.Unmarshal.

To access decoded object, e.g. `MyClientMeta`, use type casting: `changeLog.Meta.(MyClientMeta)`.

func RegObjectType

func RegObjectType(objectType string, objectStruct interface{}) Option

RegObjectType maps object to type name, that is used in field Type of ChangeLog struct. On read change log operations, if plugin finds registered object type, by its name from db, it unmarshal field RawObject to Object field via json.Unmarshal.

To access decoded object, e.g. `ReallyFunnyClient`, use type casting: `changeLog.Object.(ReallyFunnyClient)`.

type Plugin

type Plugin struct {
	// contains filtered or unexported fields
}

Plugin is a hook for gorm.

func Register

func Register(db *gorm.DB, opts ...Option) (Plugin, error)

Register initializes Plugin for provided gorm.DB. There is also available some options, that should be passed there. Options cannot be set after initialization.

func (*Plugin) GetLastRecord

func (p *Plugin) GetLastRecord(objectId string, prepare bool) (change ChangeLog, err error)

GetLastRecord returns last by creation time (CreatedAt field) change log by provided object id. Flag prepare allows to decode content of Raw* fields to direct fields, e.g. RawObject to Object.

func (*Plugin) GetRecords

func (p *Plugin) GetRecords(objectId string, prepare bool) (changes []ChangeLog, err error)

GetRecords returns all records by objectId. Flag prepare allows to decode content of Raw* fields to direct fields, e.g. RawObject to Object.

type UpdateDiff

type UpdateDiff map[string]interface{}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL