From 04223e501dbf02e7d281a15bea11c4965a2c800e Mon Sep 17 00:00:00 2001 From: andig Date: Wed, 5 Mar 2025 14:06:04 +0100 Subject: [PATCH] Revert "chore: simplify config serialisation" This reverts commit 14fdabbcb66415b9745697927b88bbd9963d01bf. --- cmd/config.go | 2 +- cmd/config_delete.go | 2 +- cmd/device.go | 2 +- cmd/helper.go | 17 ++++--- util/config/config.go | 110 ++++++++++++++++++++++++++++++++++++++---- util/config/device.go | 2 + 6 files changed, 116 insertions(+), 19 deletions(-) diff --git a/cmd/config.go b/cmd/config.go index 19675e394..1cd776da1 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -54,7 +54,7 @@ func runConfig(cmd *cobra.Command, args []string) { } for _, c := range configurable { - fmt.Println(config.NameForID(c.ID), "type:"+c.Type, redactMap(c.Data)) + fmt.Println(config.NameForID(c.ID), "type:"+c.Type, redactJson(c.Value)) } fmt.Println("") diff --git a/cmd/config_delete.go b/cmd/config_delete.go index a65064431..6410a71c1 100644 --- a/cmd/config_delete.go +++ b/cmd/config_delete.go @@ -43,7 +43,7 @@ func runConfigDelete(cmd *cobra.Command, args []string) { log.FATAL.Fatal(err) } - fmt.Println(config.NameForID(c.ID), "type:"+c.Type, c.Data) + fmt.Println(config.NameForID(c.ID), "type:"+c.Type, c.Value) switch c.Class { case templates.Charger: diff --git a/cmd/device.go b/cmd/device.go index 4142de8b2..6a7380239 100644 --- a/cmd/device.go +++ b/cmd/device.go @@ -46,7 +46,7 @@ func runDevice(cmd *cobra.Command, args []string) { for _, d := range devs { fmt.Printf("%d. %s\n", d.ID, d.Type) - fmt.Println(d.Data) + fmt.Println(d.Value) fmt.Println() } } diff --git a/cmd/helper.go b/cmd/helper.go index 21e9c0539..9a7284ef6 100644 --- a/cmd/helper.go +++ b/cmd/helper.go @@ -1,9 +1,9 @@ package cmd import ( + "encoding/json" "errors" "fmt" - "maps" "net" "os" "regexp" @@ -64,15 +64,20 @@ func redact(src string) string { ReplaceAllString(src, "$1: *****") } -func redactMap(src map[string]any) map[string]any { - res := maps.Clone(src) - for k := range res { +func redactJson(src string) string { + var j map[string]any + if err := json.Unmarshal([]byte(src), &j); err != nil { + panic(err) + } + + for k := range j { if slices.Contains(redactSecrets, k) { - res[k] = "*****" + j[k] = "*****" } } - return res + res, _ := json.Marshal(j) + return string(res) } // fatal logs a fatal error and runs shutdown functions before terminating diff --git a/util/config/config.go b/util/config/config.go index 725935b70..17e62207e 100644 --- a/util/config/config.go +++ b/util/config/config.go @@ -1,8 +1,8 @@ package config import ( + "encoding/json" "fmt" - "maps" "strconv" "strings" @@ -15,7 +15,7 @@ type Config struct { ID int `gorm:"primarykey"` Class templates.Class Type string - Data map[string]any `gorm:"value,type:string,serializer:json"` + Value string } // TODO remove- migration only @@ -30,7 +30,7 @@ func (d *Config) Named() Named { res := Named{ Name: NameForID(d.ID), Type: d.Type, - Other: maps.Clone(d.Data), + Other: d.detailsAsMap(), } return res } @@ -39,11 +39,26 @@ func (d *Config) Named() Named { func (d *Config) Typed() Typed { res := Typed{ Type: d.Type, - Other: maps.Clone(d.Data), + Other: d.detailsAsMap(), } return res } +// detailsAsMap converts device details to map +func (d *Config) detailsAsMap() map[string]any { + res := make(map[string]any) + if err := json.Unmarshal([]byte(d.Value), &res); err != nil { + panic(err) + } + return res +} + +// detailsFromMap converts map to device details +func detailsFromMap(config map[string]any) (string, error) { + b, err := json.Marshal(config) + return string(b), err +} + // Update updates a config's details to the database func (d *Config) Update(conf map[string]any) error { return db.Transaction(func(tx *gorm.DB) error { @@ -52,7 +67,11 @@ func (d *Config) Update(conf map[string]any) error { return err } - d.Data = conf + val, err := detailsFromMap(conf) + if err != nil { + return err + } + d.Value = val return tx.Save(&d).Error }) @@ -66,10 +85,17 @@ func (d *Config) PartialUpdate(conf map[string]any) error { return err } - if err := mergo.Merge(&config.Data, conf, mergo.WithOverride); err != nil { + actual := d.detailsAsMap() + if err := mergo.Merge(&actual, conf, mergo.WithOverride); err != nil { return err } + val, err := detailsFromMap(actual) + if err != nil { + return err + } + d.Value = val + return tx.Save(&d).Error }) } @@ -84,7 +110,66 @@ var db *gorm.DB func Init(instance *gorm.DB) error { db = instance m := db.Migrator() - return m.AutoMigrate(new(Config)) + + for old, new := range map[string]string{ + "devices": "configs", + "device_details": "config_details", + } { + if m.HasTable(old) { + if err := m.RenameTable(old, new); err != nil { + return err + } + } + } + + err := m.AutoMigrate(new(Config)) + + if err == nil && m.HasTable("config_details") { + err = m.AutoMigrate(new(ConfigDetails)) + + if err == nil && m.HasConstraint(new(ConfigDetails), "fk_devices_details") { + err = m.DropConstraint(new(ConfigDetails), "fk_devices_details") + } + if err == nil && m.HasColumn(new(ConfigDetails), "device_id") { + err = m.DropColumn(new(ConfigDetails), "device_id") + } + } + + if err == nil && m.HasTable("config_details") { + var devices []Config + if err := db.Where(&Config{}).Find(&devices).Error; err != nil { + return err + } + + // migrate ConfigDetails into Config.Value + for _, dev := range devices { + var details []ConfigDetails + if err := db.Where(&ConfigDetails{ConfigID: dev.ID}).Find(&details).Error; err != nil { + return err + } + + res := make(map[string]any) + for _, detail := range details { + res[detail.Key] = detail.Value + } + + if len(res) > 0 { + val, err := detailsFromMap(res) + if err != nil { + return err + } + dev.Value = val + + if err := db.Save(&dev).Error; err != nil { + return err + } + } + } + + err = m.DropTable("config_details") + } + + return err } // NameForID returns a unique config name for the given id @@ -105,7 +190,7 @@ func ConfigurationsByClass(class templates.Class) ([]Config, error) { // remove devices without details res := make([]Config, 0, len(devices)) for _, dev := range devices { - if len(dev.Data) > 0 { + if len(dev.Value) > 0 { res = append(res, dev) } } @@ -122,13 +207,18 @@ func ConfigByID(id int) (Config, error) { // AddConfig adds a new config to the database func AddConfig(class templates.Class, typ string, conf map[string]any) (Config, error) { + val, err := detailsFromMap(conf) + if err != nil { + return Config{}, err + } + config := Config{ Class: class, Type: typ, - Data: conf, + Value: val, } - err := db.Create(&config).Error + err = db.Create(&config).Error return config, err } diff --git a/util/config/device.go b/util/config/device.go index 8d60b1b5f..0c1ec1f14 100644 --- a/util/config/device.go +++ b/util/config/device.go @@ -7,6 +7,8 @@ type Device[T any] interface { type ConfigurableDevice[T any] interface { Device[T] ID() int + // Assign(T) + // Update1(map[string]any) error Update(map[string]any, T) error PartialUpdate(map[string]any, T) error Delete() error