Select Git revision
audit.go 3.34 KiB
package audit
import (
"reflect"
"regexp"
"strings"
"github.com/r3labs/diff/v2"
"gitlab.com/uafrica/go-utils/reflection"
"gitlab.com/uafrica/go-utils/string_utils"
)
type FieldChange struct {
From interface{} `json:"change_from"`
To interface{} `json:"change_to"`
}
func GetChanges(original interface{}, new interface{}) (map[string]interface{}, error) {
changes := map[string]interface{}{}
changelog, err := diff.Diff(original, new)
if err != nil {
return changes, err
}
for _, change := range changelog {
if len(change.Path) == 1 {
// Root object change
field := ToSnakeCase(change.Path[0])
changes[field] = FieldChange{
From: change.From,
To: change.To,
}
} else if len(change.Path) == 2 {
// Child object changed
// ["Account", "ID"]
// 0 = Object
// 1 = field
objectKey := ToSnakeCase(change.Path[0])
field := ToSnakeCase(change.Path[1])
existingObject, present := changes[objectKey]
if present {
if object, ok := existingObject.(map[string]interface{}); ok {
object[field] = FieldChange{
From: change.From,
To: change.To,
}
changes[objectKey] = object
}
} else {
fieldChange := map[string]interface{}{
field: FieldChange{
From: change.From,
To: change.To,
},
}
changes[objectKey] = fieldChange
}
} else if len(change.Path) == 3 {
// Array of objects
// ["Parcel", "0", "ActualWeight"]
// 0 = Object
// 1 = Index of object
// 2 = field
objectKey := ToSnakeCase(change.Path[0])
indexString := change.Path[1]
index, _ := string_utils.StringToInt64(indexString)
field := ToSnakeCase(change.Path[2])
arrayObject, present := changes[objectKey]
if present {
if arrayOfObjects, ok := arrayObject.([]map[string]interface{}); ok {
if len(arrayOfObjects) > int(index) {
// Add field to existing object in array
object := arrayOfObjects[index]
object[field] = FieldChange{
From: change.From,
To: change.To,
}
} else {
// new object, append to existing array
fieldChange := map[string]interface{}{
field: FieldChange{
From: change.From,
To: change.To,
},
}
changes[objectKey] = append(arrayOfObjects, fieldChange)
}
}
} else {
// Create array of objects
fieldChange := map[string]interface{}{
field: FieldChange{
From: change.From,
To: change.To,
},
}
changes[objectKey] = []map[string]interface{}{
fieldChange,
}
}
}
}
return changes, nil
}
var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)")
var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])")
func ToSnakeCase(str string) string {
snake := matchFirstCap.ReplaceAllString(str, "${1}_${2}")
snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}")
return strings.ToLower(snake)
}
func GetIntValue(object interface{}, key string) int64 {
structValue := reflect.ValueOf(object)
if structValue.Kind() == reflect.Struct {
field := structValue.FieldByName(key)
id := reflection.GetInt64Value(field)
return id
}
return 0
}
func GetStringValue(object interface{}, key string) string {
structValue := reflect.ValueOf(object)
if structValue.Kind() == reflect.Struct {
field := structValue.FieldByName(key)
id := reflection.GetStringValue(field)
return id
}
return ""
}