executor_actions.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. package executor
  2. import (
  3. "crypto/sha256"
  4. "fmt"
  5. "strings"
  6. "github.com/OliveTin/OliveTin/internal/config"
  7. "github.com/OliveTin/OliveTin/internal/configcheck"
  8. "github.com/OliveTin/OliveTin/internal/configissues"
  9. "github.com/OliveTin/OliveTin/internal/entities"
  10. )
  11. func (e *Executor) FindBindingByID(id string) *ActionBinding {
  12. e.MapActionBindingsLock.RLock()
  13. pair, found := e.MapActionBindings[id]
  14. e.MapActionBindingsLock.RUnlock()
  15. if !found {
  16. return nil
  17. }
  18. return pair
  19. }
  20. func (e *Executor) FindBindingWithNoEntity(action *config.Action) *ActionBinding {
  21. e.MapActionBindingsLock.RLock()
  22. defer e.MapActionBindingsLock.RUnlock()
  23. for _, binding := range e.MapActionBindings {
  24. if binding.Action == action && binding.Entity == nil {
  25. return binding
  26. }
  27. }
  28. return nil
  29. }
  30. type RebuildActionMapRequest struct {
  31. Cfg *config.Config
  32. dashboardTargets *dashboardTargetIndex
  33. }
  34. func validateArgumentDefaults(cfg *config.Config) []configissues.Issue {
  35. if cfg == nil {
  36. return nil
  37. }
  38. out := make([]configissues.Issue, 0)
  39. for _, action := range cfg.Actions {
  40. out = append(out, validateActionArgumentDefaults(action)...)
  41. }
  42. return out
  43. }
  44. func validateActionArgumentDefaults(action *config.Action) []configissues.Issue {
  45. if action == nil {
  46. return nil
  47. }
  48. out := make([]configissues.Issue, 0)
  49. for i := range action.Arguments {
  50. if issue := validateArgumentDefault(action, &action.Arguments[i]); issue != nil {
  51. out = append(out, *issue)
  52. }
  53. }
  54. return out
  55. }
  56. func validateArgumentDefault(action *config.Action, arg *config.ActionArgument) *configissues.Issue {
  57. if arg.Default == "" {
  58. return nil
  59. }
  60. if strings.Contains(arg.Default, "{{") {
  61. return nil
  62. }
  63. if err := ValidateArgument(arg, arg.Default, action); err != nil {
  64. return &configissues.Issue{
  65. Severity: configissues.SeverityWarning,
  66. Code: configissues.CodeArgDefaultInvalid,
  67. Message: fmt.Sprintf("Argument default value failed validation: %v", err),
  68. ActionID: action.ID,
  69. ActionTitle: action.Title,
  70. ArgumentName: arg.Name,
  71. Source: arg.Default,
  72. ConfigFile: action.SourceFile,
  73. }
  74. }
  75. return nil
  76. }
  77. func (e *Executor) RebuildActionMap() {
  78. defaultIssues := validateArgumentDefaults(e.Cfg)
  79. e.MapActionBindingsLock.Lock()
  80. clear(e.MapActionBindings)
  81. req := &RebuildActionMapRequest{
  82. Cfg: e.Cfg,
  83. dashboardTargets: buildDashboardTargetIndex(e.Cfg),
  84. }
  85. for configOrder, action := range e.Cfg.Actions {
  86. if action == nil {
  87. continue
  88. }
  89. if action.Entity != "" {
  90. registerActionsFromEntities(e, configOrder, action.Entity, action, req)
  91. } else {
  92. registerAction(e, configOrder, action, req)
  93. }
  94. }
  95. e.MapActionBindingsLock.Unlock()
  96. configcheck.Rebuild(e.Cfg, defaultIssues...)
  97. for _, l := range e.copyListeners() {
  98. l.OnActionMapRebuilt()
  99. }
  100. }
  101. func registerAction(e *Executor, configOrder int, action *config.Action, req *RebuildActionMapRequest) {
  102. bindingId := generateActionBindingId(action, "")
  103. e.MapActionBindings[bindingId] = &ActionBinding{
  104. ID: bindingId,
  105. Action: action,
  106. Entity: nil,
  107. ConfigOrder: configOrder,
  108. OnDashboards: resolveOnDashboards(req.dashboardTargets, action.Title, ""),
  109. }
  110. }
  111. func registerActionsFromEntities(e *Executor, configOrder int, entityTitle string, tpl *config.Action, req *RebuildActionMapRequest) {
  112. for _, ent := range entities.GetEntityInstancesOrdered(entityTitle) {
  113. registerActionFromEntity(e, configOrder, tpl, ent, req)
  114. }
  115. }
  116. func registerActionFromEntity(e *Executor, configOrder int, tpl *config.Action, ent *entities.Entity, req *RebuildActionMapRequest) {
  117. virtualActionId := generateActionBindingId(tpl, ent.UniqueKey)
  118. e.MapActionBindings[virtualActionId] = &ActionBinding{
  119. ID: virtualActionId,
  120. Action: tpl,
  121. Entity: ent,
  122. ConfigOrder: configOrder,
  123. OnDashboards: resolveOnDashboards(req.dashboardTargets, tpl.Title, ent.UniqueKey),
  124. }
  125. }
  126. func generateActionBindingId(action *config.Action, entityPrefix string) string {
  127. if action.ID != "" && entityPrefix == "" {
  128. return action.ID
  129. }
  130. h := sha256.New()
  131. if entityPrefix == "" {
  132. h.Write([]byte(action.Title))
  133. } else {
  134. // Include the entity data to make each entity instance unique
  135. h.Write([]byte(action.Title + "." + entityPrefix))
  136. }
  137. return fmt.Sprintf("%x", h.Sum(nil))
  138. }