dashboards.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. package api
  2. import (
  3. "sort"
  4. apiv1 "github.com/OliveTin/OliveTin/gen/olivetin/api/v1"
  5. config "github.com/OliveTin/OliveTin/internal/config"
  6. log "github.com/sirupsen/logrus"
  7. "golang.org/x/exp/slices"
  8. )
  9. func renderDashboard(rr *DashboardRenderRequest, dashboardTitle string) *apiv1.Dashboard {
  10. if dashboardTitle == "default" {
  11. return buildDefaultDashboard(rr)
  12. }
  13. return findAndRenderDashboard(rr, dashboardTitle)
  14. }
  15. func findAndRenderDashboard(rr *DashboardRenderRequest, dashboardTitle string) *apiv1.Dashboard {
  16. for _, dashboard := range rr.cfg.Dashboards {
  17. if dashboard.Title != dashboardTitle {
  18. continue
  19. }
  20. if len(dashboard.Contents) == 0 {
  21. logEmptyDashboard(dashboard.Title, rr.AuthenticatedUser.Username)
  22. return nil
  23. }
  24. return buildDashboardFromConfig(dashboard, rr)
  25. }
  26. return nil
  27. }
  28. func logEmptyDashboard(dashboardTitle, username string) {
  29. log.WithFields(log.Fields{
  30. "dashboard": dashboardTitle,
  31. "username": username,
  32. }).Debugf("Dashboard has no readable contents, so it will not be visible in the web ui")
  33. }
  34. func buildDashboardFromConfig(dashboard *config.DashboardComponent, rr *DashboardRenderRequest) *apiv1.Dashboard {
  35. return &apiv1.Dashboard{
  36. Title: dashboard.Title,
  37. Contents: sortActions(removeNulls(getDashboardComponentContents(dashboard, rr))),
  38. }
  39. }
  40. //gocyclo:ignore
  41. func buildDefaultDashboard(rr *DashboardRenderRequest) *apiv1.Dashboard {
  42. db := &apiv1.Dashboard{
  43. Title: "Actions",
  44. Contents: make([]*apiv1.DashboardComponent, 0),
  45. }
  46. fieldset := &apiv1.DashboardComponent{
  47. Type: "fieldset",
  48. Title: "Actions",
  49. Contents: make([]*apiv1.DashboardComponent, 0),
  50. }
  51. for _, binding := range rr.ex.MapActionIdToBinding {
  52. if binding.Action.Hidden {
  53. continue
  54. }
  55. if binding.IsOnDashboard {
  56. continue
  57. }
  58. action := buildAction(binding, rr)
  59. fieldset.Contents = append(fieldset.Contents, &apiv1.DashboardComponent{
  60. Type: "link",
  61. Title: action.Title,
  62. Icon: action.Icon,
  63. Action: action,
  64. })
  65. }
  66. if len(fieldset.Contents) > 0 {
  67. fieldset.Contents = sortActions(fieldset.Contents)
  68. db.Contents = append(db.Contents, fieldset)
  69. }
  70. return db
  71. }
  72. func sortActions(components []*apiv1.DashboardComponent) []*apiv1.DashboardComponent {
  73. sort.Slice(components, func(i, j int) bool {
  74. if components[i].Action == nil || components[j].Action == nil {
  75. return components[i].Title < components[j].Title
  76. }
  77. if components[i].Action.Order == components[j].Action.Order {
  78. return components[i].Action.Title < components[j].Action.Title
  79. } else {
  80. return components[i].Action.Order < components[j].Action.Order
  81. }
  82. })
  83. return components
  84. }
  85. func removeNulls(components []*apiv1.DashboardComponent) []*apiv1.DashboardComponent {
  86. ret := make([]*apiv1.DashboardComponent, 0)
  87. for _, component := range components {
  88. if component == nil {
  89. continue
  90. }
  91. ret = append(ret, component)
  92. }
  93. return ret
  94. }
  95. func getDashboardComponentContents(dashboard *config.DashboardComponent, rr *DashboardRenderRequest) []*apiv1.DashboardComponent {
  96. ret := make([]*apiv1.DashboardComponent, 0)
  97. rootFieldset := createRootFieldset()
  98. for _, subitem := range dashboard.Contents {
  99. processDashboardSubitem(subitem, rr, &ret, rootFieldset)
  100. }
  101. return appendRootFieldsetIfNeeded(ret, rootFieldset)
  102. }
  103. func createRootFieldset() *apiv1.DashboardComponent {
  104. return &apiv1.DashboardComponent{
  105. Type: "fieldset",
  106. Title: "Actions",
  107. Contents: make([]*apiv1.DashboardComponent, 0),
  108. }
  109. }
  110. func processDashboardSubitem(subitem *config.DashboardComponent, rr *DashboardRenderRequest, ret *[]*apiv1.DashboardComponent, rootFieldset *apiv1.DashboardComponent) {
  111. if subitem.Type != "fieldset" {
  112. rootFieldset.Contents = append(rootFieldset.Contents, buildDashboardComponentSimple(subitem, rr))
  113. return
  114. }
  115. if subitem.Entity != "" {
  116. *ret = append(*ret, buildEntityFieldsets(subitem.Entity, subitem, rr)...)
  117. } else {
  118. *ret = append(*ret, buildDashboardComponentSimple(subitem, rr))
  119. }
  120. }
  121. func appendRootFieldsetIfNeeded(ret []*apiv1.DashboardComponent, rootFieldset *apiv1.DashboardComponent) []*apiv1.DashboardComponent {
  122. if len(rootFieldset.Contents) > 0 {
  123. ret = append(ret, rootFieldset)
  124. }
  125. return ret
  126. }
  127. func buildDashboardComponentSimple(subitem *config.DashboardComponent, rr *DashboardRenderRequest) *apiv1.DashboardComponent {
  128. newitem := &apiv1.DashboardComponent{
  129. Title: subitem.Title,
  130. Type: getDashboardComponentType(subitem),
  131. Contents: getDashboardComponentContents(subitem, rr),
  132. Icon: getDashboardComponentIcon(subitem, rr.cfg),
  133. CssClass: subitem.CssClass,
  134. Action: rr.findAction(subitem.Title),
  135. }
  136. return newitem
  137. }
  138. func getDashboardComponentIcon(item *config.DashboardComponent, cfg *config.Config) string {
  139. if item.Icon == "" {
  140. return cfg.DefaultIconForDirectories
  141. }
  142. return item.Icon
  143. }
  144. func getDashboardComponentType(item *config.DashboardComponent) string {
  145. allowedTypes := []string{
  146. "stdout-most-recent-execution",
  147. "display",
  148. }
  149. if len(item.Contents) > 0 {
  150. if item.Type != "fieldset" {
  151. return "directory"
  152. }
  153. return "fieldset"
  154. } else if slices.Contains(allowedTypes, item.Type) {
  155. return item.Type
  156. }
  157. return "link"
  158. }