entities.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. package entities
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "os"
  7. "path/filepath"
  8. "strings"
  9. config "github.com/OliveTin/OliveTin/internal/config"
  10. "github.com/OliveTin/OliveTin/internal/filehelper"
  11. log "github.com/sirupsen/logrus"
  12. "gopkg.in/yaml.v3"
  13. )
  14. var (
  15. EntityChangedSender chan bool
  16. listeners []func()
  17. )
  18. type Entity struct {
  19. Data any
  20. UniqueKey string
  21. Title string
  22. }
  23. func AddListener(l func()) {
  24. listeners = append(listeners, l)
  25. }
  26. func SetupEntityFileWatchers(cfg *config.Config) {
  27. baseDir := resolveEntitiesBaseDir(cfg.GetDir())
  28. for i := range cfg.Entities { // #337 - iterate by key, not by value
  29. ef := cfg.Entities[i]
  30. watchAndLoadEntity(baseDir, ef)
  31. }
  32. }
  33. //gocyclo:ignore
  34. func resolveEntitiesBaseDir(configDir string) string {
  35. absConfigDir, err := filepath.Abs(configDir)
  36. if err != nil {
  37. log.Errorf("Error getting absolute path for %s: %v", configDir, err)
  38. return configDir
  39. }
  40. if strings.Contains(absConfigDir, "integration-tests") {
  41. return configDir
  42. }
  43. devVar := filepath.Join(configDir, "var")
  44. if _, err := os.Stat(devVar); err == nil {
  45. return devVar
  46. }
  47. return absConfigDir
  48. }
  49. func watchAndLoadEntity(baseDir string, ef *config.EntityFile) {
  50. p := ef.File
  51. if !filepath.IsAbs(p) {
  52. p = filepath.Join(baseDir, p)
  53. log.WithFields(log.Fields{"entityFile": p}).Debugf("Adding config dir to entity file path")
  54. }
  55. go filehelper.WatchFileWrite(p, func(filename string) { loadEntityFile(p, ef.Name) })
  56. loadEntityFile(p, ef.Name)
  57. }
  58. func loadEntityFile(filename string, entityname string) {
  59. if strings.HasSuffix(filename, ".json") {
  60. loadEntityFileJson(filename, entityname)
  61. } else {
  62. loadEntityFileYaml(filename, entityname)
  63. }
  64. }
  65. func loadEntityFileJson(filename string, entityname string) {
  66. log.WithFields(log.Fields{
  67. "file": filename,
  68. "name": entityname,
  69. }).Infof("Loading entity file with JSON format")
  70. jfile, err := os.ReadFile(filename)
  71. if err != nil {
  72. log.Errorf("ReadIn: %v", err)
  73. return
  74. }
  75. data := make([]map[string]any, 0)
  76. decoder := json.NewDecoder(bytes.NewReader(jfile))
  77. for decoder.More() {
  78. d := make(map[string]any)
  79. err := decoder.Decode(&d)
  80. if err != nil {
  81. log.Errorf("%v", err)
  82. return
  83. }
  84. data = append(data, d)
  85. }
  86. updateSvFromFile(entityname, data)
  87. }
  88. func loadEntityFileYaml(filename string, entityname string) {
  89. log.WithFields(log.Fields{
  90. "file": filename,
  91. "name": entityname,
  92. }).Infof("Loading entity file with YAML format")
  93. yfile, err := os.ReadFile(filename)
  94. if err != nil {
  95. log.Errorf("ReadIn: %v", err)
  96. return
  97. }
  98. var data []map[string]any
  99. err = yaml.Unmarshal(yfile, &data)
  100. if err != nil {
  101. log.Errorf("Unmarshal: %v", err)
  102. return
  103. }
  104. updateSvFromFile(entityname, data)
  105. }
  106. func updateSvFromFile(entityname string, data []map[string]any) {
  107. ClearEntitiesOfType(entityname)
  108. for i, mapp := range data {
  109. AddEntity(entityname, fmt.Sprintf("%d", i), mapp)
  110. }
  111. for _, l := range listeners {
  112. l()
  113. }
  114. }
  115. /*
  116. //gocyclo:ignore
  117. func serializeValueToSv(prefix string, value any) {
  118. if m, ok := value.(map[string]any); ok { // if value is a map we need to flatten it
  119. serializeMapToSv(prefix, m)
  120. } else if s, ok := value.([]any); ok { // if value is a slice we need to flatten it
  121. serializeSliceToSv(prefix, s)
  122. } else if f, ok := value.(float64); ok {
  123. if canConvertToInt64(f) {
  124. s := int64(f)
  125. sv.Set(prefix, fmt.Sprintf("%d", s))
  126. } else {
  127. sv.Set(prefix, fmt.Sprintf("%f", f))
  128. }
  129. } else {
  130. sv.Set(prefix, fmt.Sprintf("%v", value))
  131. }
  132. }
  133. func canConvertToInt64(f float64) bool {
  134. return f >= math.MinInt64 && f <= math.MaxInt64 && f == math.Trunc(f)
  135. }
  136. func serializeMapToSv(prefix string, m map[string]any) {
  137. for k, v := range m {
  138. serializeValueToSv(prefix+"."+k, v)
  139. }
  140. }
  141. func serializeSliceToSv(prefix string, s []any) {
  142. sv.Set(prefix+".count", fmt.Sprintf("%v", len(s)))
  143. for i, v := range s {
  144. serializeValueToSv(prefix+"."+fmt.Sprintf("%v", i), v)
  145. }
  146. }
  147. */