4
0

executor_dashboards.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. package executor
  2. import (
  3. "fmt"
  4. config "github.com/OliveTin/OliveTin/internal/config"
  5. "github.com/OliveTin/OliveTin/internal/entities"
  6. )
  7. type DashboardNavigationTarget struct {
  8. Title string
  9. EntityType string
  10. EntityKey string
  11. Path string
  12. }
  13. func (target DashboardNavigationTarget) key() string {
  14. return target.Title + "\x00" + target.EntityType + "\x00" + target.EntityKey
  15. }
  16. func (b *ActionBinding) IsOnConfiguredDashboard() bool {
  17. for _, dashboard := range b.OnDashboards {
  18. if dashboard.Title != "Actions" {
  19. return true
  20. }
  21. }
  22. return false
  23. }
  24. type dashboardTargetIndex struct {
  25. byTitle map[string][]DashboardNavigationTarget
  26. byTitleEntityKey map[string]map[string][]DashboardNavigationTarget
  27. }
  28. func buildDashboardTargetIndex(cfg *config.Config) *dashboardTargetIndex {
  29. index := &dashboardTargetIndex{
  30. byTitle: make(map[string][]DashboardNavigationTarget),
  31. byTitleEntityKey: make(map[string]map[string][]DashboardNavigationTarget),
  32. }
  33. for _, dashboard := range cfg.Dashboards {
  34. walkDashboardContents(dashboard.Contents, dashboard.Title, index)
  35. }
  36. return index
  37. }
  38. func walkDashboardContents(contents []*config.DashboardComponent, rootDashboardTitle string, index *dashboardTargetIndex) {
  39. for _, component := range contents {
  40. walkDashboardComponent(component, rootDashboardTitle, index)
  41. }
  42. }
  43. func walkDashboardComponent(component *config.DashboardComponent, rootDashboardTitle string, index *dashboardTargetIndex) {
  44. if component.Type == "fieldset" && component.Entity != "" {
  45. walkEntityFieldset(component, rootDashboardTitle, component.Entity, index)
  46. return
  47. }
  48. recordActionTarget(component, rootDashboardTitle, "", "", index)
  49. if len(component.Contents) > 0 {
  50. walkDashboardContents(component.Contents, rootDashboardTitle, index)
  51. }
  52. }
  53. func walkEntityFieldset(fieldset *config.DashboardComponent, rootDashboardTitle, entityType string, index *dashboardTargetIndex) {
  54. for _, component := range fieldset.Contents {
  55. if component.Type == "directory" {
  56. walkEntityDirectory(component, entityType, index)
  57. continue
  58. }
  59. recordActionTargetForAllEntities(component, rootDashboardTitle, entityType, index)
  60. if len(component.Contents) > 0 {
  61. walkEntityFieldsetContents(component.Contents, rootDashboardTitle, entityType, index)
  62. }
  63. }
  64. }
  65. func walkEntityFieldsetContents(contents []*config.DashboardComponent, rootDashboardTitle, entityType string, index *dashboardTargetIndex) {
  66. for _, component := range contents {
  67. if component.Type == "directory" {
  68. walkEntityDirectory(component, entityType, index)
  69. continue
  70. }
  71. recordActionTargetForAllEntities(component, rootDashboardTitle, entityType, index)
  72. if len(component.Contents) > 0 {
  73. walkEntityFieldsetContents(component.Contents, rootDashboardTitle, entityType, index)
  74. }
  75. }
  76. }
  77. func walkEntityDirectory(directory *config.DashboardComponent, entityType string, index *dashboardTargetIndex) {
  78. for _, entity := range entities.GetEntityInstancesOrdered(entityType) {
  79. for _, component := range directory.Contents {
  80. recordActionTarget(component, directory.Title, entityType, entity.UniqueKey, index)
  81. }
  82. }
  83. }
  84. func recordActionTargetForAllEntities(component *config.DashboardComponent, rootDashboardTitle, entityType string, index *dashboardTargetIndex) {
  85. actionTitle := actionTitleFromComponent(component)
  86. if actionTitle == "" {
  87. return
  88. }
  89. target := dashboardNavigationTarget(rootDashboardTitle, "", "")
  90. for _, entity := range entities.GetEntityInstancesOrdered(entityType) {
  91. addEntityTarget(index, actionTitle, entity.UniqueKey, target)
  92. }
  93. }
  94. func recordActionTarget(component *config.DashboardComponent, dashboardTitle, entityType, entityKey string, index *dashboardTargetIndex) {
  95. actionTitle := actionTitleFromComponent(component)
  96. if actionTitle == "" {
  97. return
  98. }
  99. target := dashboardNavigationTarget(dashboardTitle, entityType, entityKey)
  100. if entityType != "" && entityKey != "" {
  101. addEntityTarget(index, actionTitle, entityKey, target)
  102. return
  103. }
  104. addTitleTarget(index, actionTitle, target)
  105. }
  106. func actionTitleFromComponent(component *config.DashboardComponent) string {
  107. if title := inlineActionTitle(component); title != "" {
  108. return title
  109. }
  110. if component.Type == "link" || component.Type == "" {
  111. return component.Title
  112. }
  113. return ""
  114. }
  115. func inlineActionTitle(component *config.DashboardComponent) string {
  116. if component.InlineAction == nil {
  117. return ""
  118. }
  119. if component.Title != "" {
  120. return component.Title
  121. }
  122. return component.InlineAction.Title
  123. }
  124. func dashboardNavigationTarget(title, entityType, entityKey string) DashboardNavigationTarget {
  125. return DashboardNavigationTarget{
  126. Title: title,
  127. EntityType: entityType,
  128. EntityKey: entityKey,
  129. Path: dashboardNavigationPath(title, entityType, entityKey),
  130. }
  131. }
  132. func dashboardNavigationPath(title, entityType, entityKey string) string {
  133. if title == "Actions" {
  134. return "/"
  135. }
  136. if entityType != "" && entityKey != "" {
  137. return fmt.Sprintf("/dashboards/%s/%s/%s", title, entityType, entityKey)
  138. }
  139. return fmt.Sprintf("/dashboards/%s", title)
  140. }
  141. func addTitleTarget(index *dashboardTargetIndex, actionTitle string, target DashboardNavigationTarget) {
  142. index.byTitle[actionTitle] = appendUniqueTarget(index.byTitle[actionTitle], target)
  143. }
  144. func addEntityTarget(index *dashboardTargetIndex, actionTitle, entityKey string, target DashboardNavigationTarget) {
  145. if index.byTitleEntityKey[actionTitle] == nil {
  146. index.byTitleEntityKey[actionTitle] = make(map[string][]DashboardNavigationTarget)
  147. }
  148. entityTargets := index.byTitleEntityKey[actionTitle][entityKey]
  149. index.byTitleEntityKey[actionTitle][entityKey] = appendUniqueTarget(entityTargets, target)
  150. }
  151. func appendUniqueTarget(targets []DashboardNavigationTarget, target DashboardNavigationTarget) []DashboardNavigationTarget {
  152. for _, existing := range targets {
  153. if existing.key() == target.key() {
  154. return targets
  155. }
  156. }
  157. return append(targets, target)
  158. }
  159. func (index *dashboardTargetIndex) targetsForAction(actionTitle string) []DashboardNavigationTarget {
  160. return dedupeTargets(index.byTitle[actionTitle])
  161. }
  162. func (index *dashboardTargetIndex) targetsForEntityAction(actionTitle, entityKey string) []DashboardNavigationTarget {
  163. targets := make([]DashboardNavigationTarget, 0)
  164. targets = append(targets, index.byTitle[actionTitle]...)
  165. if entityTargets, ok := index.byTitleEntityKey[actionTitle]; ok {
  166. targets = append(targets, entityTargets[entityKey]...)
  167. }
  168. return dedupeTargets(targets)
  169. }
  170. func dedupeTargets(targets []DashboardNavigationTarget) []DashboardNavigationTarget {
  171. if len(targets) == 0 {
  172. return nil
  173. }
  174. seen := make(map[string]bool, len(targets))
  175. result := make([]DashboardNavigationTarget, 0, len(targets))
  176. for _, target := range targets {
  177. key := target.key()
  178. if seen[key] {
  179. continue
  180. }
  181. seen[key] = true
  182. result = append(result, target)
  183. }
  184. return result
  185. }
  186. func resolveOnDashboards(index *dashboardTargetIndex, actionTitle, entityKey string) []DashboardNavigationTarget {
  187. var targets []DashboardNavigationTarget
  188. if entityKey == "" {
  189. targets = index.targetsForAction(actionTitle)
  190. } else {
  191. targets = index.targetsForEntityAction(actionTitle, entityKey)
  192. }
  193. if len(targets) == 0 {
  194. return []DashboardNavigationTarget{dashboardNavigationTarget("Actions", "", "")}
  195. }
  196. return targets
  197. }