executor_actions.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  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.Entity != "" {
  87. registerActionsFromEntities(e, configOrder, action.Entity, action, req)
  88. } else {
  89. registerAction(e, configOrder, action, req)
  90. }
  91. }
  92. e.MapActionBindingsLock.Unlock()
  93. configcheck.Rebuild(e.Cfg, defaultIssues...)
  94. for _, l := range e.copyListeners() {
  95. l.OnActionMapRebuilt()
  96. }
  97. }
  98. func registerAction(e *Executor, configOrder int, action *config.Action, req *RebuildActionMapRequest) {
  99. bindingId := generateActionBindingId(action, "")
  100. e.MapActionBindings[bindingId] = &ActionBinding{
  101. ID: bindingId,
  102. Action: action,
  103. Entity: nil,
  104. ConfigOrder: configOrder,
  105. OnDashboards: resolveOnDashboards(req.dashboardTargets, action.Title, ""),
  106. }
  107. }
  108. func registerActionsFromEntities(e *Executor, configOrder int, entityTitle string, tpl *config.Action, req *RebuildActionMapRequest) {
  109. for _, ent := range entities.GetEntityInstancesOrdered(entityTitle) {
  110. registerActionFromEntity(e, configOrder, tpl, ent, req)
  111. }
  112. }
  113. func registerActionFromEntity(e *Executor, configOrder int, tpl *config.Action, ent *entities.Entity, req *RebuildActionMapRequest) {
  114. virtualActionId := generateActionBindingId(tpl, ent.UniqueKey)
  115. e.MapActionBindings[virtualActionId] = &ActionBinding{
  116. ID: virtualActionId,
  117. Action: tpl,
  118. Entity: ent,
  119. ConfigOrder: configOrder,
  120. OnDashboards: resolveOnDashboards(req.dashboardTargets, tpl.Title, ent.UniqueKey),
  121. }
  122. }
  123. func generateActionBindingId(action *config.Action, entityPrefix string) string {
  124. if action.ID != "" && entityPrefix == "" {
  125. return action.ID
  126. }
  127. h := sha256.New()
  128. if entityPrefix == "" {
  129. h.Write([]byte(action.Title))
  130. } else {
  131. // Include the entity data to make each entity instance unique
  132. h.Write([]byte(action.Title + "." + entityPrefix))
  133. }
  134. return fmt.Sprintf("%x", h.Sum(nil))
  135. }