forked from evcc-io/evcc
/
config.go
158 lines (130 loc) · 3.72 KB
/
config.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package config
import (
"fmt"
"github.com/connectorjs/evcm/util/templates"
"gorm.io/gorm"
)
type Config struct {
ID int `gorm:"primarykey"`
Class templates.Class
Type string
Details []ConfigDetail `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
}
type ConfigDetail struct {
ConfigID int `gorm:"index:idx_unique"`
Key string `gorm:"index:idx_unique"`
Value string
}
// Named converts device details to named config
func (d *Config) Named() Named {
res := Named{
Name: NameForID(d.ID),
Type: d.Type,
Other: d.detailsAsMap(),
}
return res
}
// Typed converts device details to typed config
func (d *Config) Typed() Typed {
res := Typed{
Type: d.Type,
Other: d.detailsAsMap(),
}
return res
}
// detailsAsMap converts device details to map
func (d *Config) detailsAsMap() map[string]any {
res := make(map[string]any, len(d.Details))
for _, detail := range d.Details {
res[detail.Key] = detail.Value
}
return res
}
// detailsFromMap converts map to device details
func detailsFromMap(config map[string]any) []ConfigDetail {
res := make([]ConfigDetail, 0, len(config))
for k, v := range config {
res = append(res, ConfigDetail{Key: k, Value: fmt.Sprintf("%v", v)})
}
return res
}
// 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 {
var config Config
if err := tx.Where(Config{Class: d.Class, ID: d.ID}).First(&config).Error; err != nil {
return err
}
if err := tx.Delete(new(ConfigDetail), ConfigDetail{ConfigID: d.ID}).Error; err != nil {
return err
}
d.Details = detailsFromMap(conf)
return tx.Save(&d).Error
})
}
// Delete deletes a config from the database
func (d *Config) Delete() error {
return db.Transaction(func(tx *gorm.DB) error {
if err := tx.Delete(new(ConfigDetail), ConfigDetail{ConfigID: d.ID}).Error; err != nil {
return err
}
return tx.Delete(Config{ID: d.ID}).Error
})
}
var db *gorm.DB
func Init(instance *gorm.DB) error {
db = instance
for old, new := range map[string]string{
"devices": "configs",
"device_details": "config_details",
} {
if m := db.Migrator(); m.HasTable(old) {
if err := m.RenameTable(old, new); err != nil {
return err
}
}
}
err := db.AutoMigrate(new(Config), new(ConfigDetail))
if err == nil && db.Migrator().HasConstraint(new(ConfigDetail), "fk_devices_details") {
err = db.Migrator().DropConstraint(new(ConfigDetail), "fk_devices_details")
}
if err == nil && db.Migrator().HasColumn(new(ConfigDetail), "device_id") {
err = db.Migrator().DropColumn(new(ConfigDetail), "device_id")
}
return err
}
// NameForID returns a unique config name for the given id
func NameForID(id int) string {
return fmt.Sprintf("db:%d", id)
}
// ConfigurationsByClass returns devices by class from the database
func ConfigurationsByClass(class templates.Class) ([]Config, error) {
var devices []Config
tx := db.Where(&Config{Class: class}).Preload("Details").Order("id").Find(&devices)
// remove devices without details
res := make([]Config, 0, len(devices))
for _, dev := range devices {
if len(dev.Details) > 0 {
res = append(res, dev)
}
}
return res, tx.Error
}
// ConfigByID returns device by id from the database
func ConfigByID(id int) (Config, error) {
var config Config
tx := db.Where(&Config{ID: id}).Preload("Details").First(&config)
return config, tx.Error
}
// AddConfig adds a new config to the database
func AddConfig(class templates.Class, typ string, conf map[string]any) (Config, error) {
config := Config{
Class: class,
Type: typ,
Details: detailsFromMap(conf),
}
err := db.Transaction(func(tx *gorm.DB) error {
return tx.Create(&config).Error
})
return config, err
}