api_entity_related_actions.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. package api
  2. import (
  3. "sort"
  4. apiv1 "github.com/OliveTin/OliveTin/gen/olivetin/api/v1"
  5. authpublic "github.com/OliveTin/OliveTin/internal/auth/authpublic"
  6. config "github.com/OliveTin/OliveTin/internal/config"
  7. "github.com/OliveTin/OliveTin/internal/entities"
  8. "github.com/OliveTin/OliveTin/internal/executor"
  9. "github.com/OliveTin/OliveTin/internal/tpl"
  10. )
  11. type relatedActionCandidate struct {
  12. binding *executor.ActionBinding
  13. prefilled map[string]string
  14. }
  15. func (api *oliveTinAPI) relatedActionsForEntity(user *authpublic.AuthenticatedUser, entityType string, entity *entities.Entity) []*apiv1.EntityRelatedAction {
  16. renderRequest := api.createDashboardRenderRequest(user, entityType, entity.UniqueKey)
  17. populateActiveBindingStates(renderRequest)
  18. candidates := collectRelatedActionCandidates(api, user, entityType, entity)
  19. sortRelatedActionCandidates(candidates)
  20. return buildEntityRelatedActions(candidates, renderRequest)
  21. }
  22. func collectRelatedActionCandidates(api *oliveTinAPI, user *authpublic.AuthenticatedUser, entityType string, entity *entities.Entity) []relatedActionCandidate {
  23. seen := make(map[string]bool)
  24. candidates := make([]relatedActionCandidate, 0)
  25. api.executor.MapActionBindingsLock.RLock()
  26. defer api.executor.MapActionBindingsLock.RUnlock()
  27. for _, binding := range api.executor.MapActionBindings {
  28. tryAppendRelatedCandidate(&candidates, seen, api, user, entityType, entity, binding)
  29. }
  30. return candidates
  31. }
  32. func tryAppendRelatedCandidate(candidates *[]relatedActionCandidate, seen map[string]bool, api *oliveTinAPI, user *authpublic.AuthenticatedUser, entityType string, entity *entities.Entity, binding *executor.ActionBinding) {
  33. prefilled, ok := relatedPrefillForBinding(binding, entityType, entity)
  34. if !ok || !bindingViewableForRelated(seen, api, user, binding) {
  35. return
  36. }
  37. seen[binding.ID] = true
  38. *candidates = append(*candidates, relatedActionCandidate{
  39. binding: binding,
  40. prefilled: prefilled,
  41. })
  42. }
  43. func bindingViewableForRelated(seen map[string]bool, api *oliveTinAPI, user *authpublic.AuthenticatedUser, binding *executor.ActionBinding) bool {
  44. if binding == nil || binding.Action == nil || seen[binding.ID] || binding.Action.Hidden {
  45. return false
  46. }
  47. return api.userCanViewAction(user, binding.Action)
  48. }
  49. func relatedPrefillForBinding(binding *executor.ActionBinding, entityType string, entity *entities.Entity) (map[string]string, bool) {
  50. if isEntityBoundBindingFor(binding, entityType, entity) {
  51. return nil, true
  52. }
  53. return argumentEntityPrefill(binding, entityType, entity)
  54. }
  55. func argumentEntityPrefill(binding *executor.ActionBinding, entityType string, entity *entities.Entity) (map[string]string, bool) {
  56. if binding == nil || binding.Entity != nil || binding.Action == nil {
  57. return nil, false
  58. }
  59. prefilled := buildPrefilledArgumentsForEntity(binding.Action, entityType, entity)
  60. return prefilled, len(prefilled) > 0
  61. }
  62. func isEntityBoundBindingFor(binding *executor.ActionBinding, entityType string, entity *entities.Entity) bool {
  63. if entity == nil || !bindingHasEntity(binding) {
  64. return false
  65. }
  66. return binding.Action.Entity == entityType && binding.Entity.UniqueKey == entity.UniqueKey
  67. }
  68. func bindingHasEntity(binding *executor.ActionBinding) bool {
  69. return binding != nil && binding.Entity != nil && binding.Action != nil
  70. }
  71. func buildPrefilledArgumentsForEntity(action *config.Action, entityType string, entity *entities.Entity) map[string]string {
  72. prefilled := make(map[string]string)
  73. for i := range action.Arguments {
  74. arg := &action.Arguments[i]
  75. if arg.Entity != entityType || len(arg.Choices) != 1 {
  76. continue
  77. }
  78. prefilled[arg.Name] = tpl.ParseTemplateOfActionBeforeExec(arg.Choices[0].Value, entity)
  79. }
  80. return prefilled
  81. }
  82. func sortRelatedActionCandidates(candidates []relatedActionCandidate) {
  83. sort.SliceStable(candidates, func(i, j int) bool {
  84. if candidates[i].binding.ConfigOrder != candidates[j].binding.ConfigOrder {
  85. return candidates[i].binding.ConfigOrder < candidates[j].binding.ConfigOrder
  86. }
  87. return candidates[i].binding.ID < candidates[j].binding.ID
  88. })
  89. }
  90. func buildEntityRelatedActions(candidates []relatedActionCandidate, rr *DashboardRenderRequest) []*apiv1.EntityRelatedAction {
  91. result := make([]*apiv1.EntityRelatedAction, 0, len(candidates))
  92. for _, candidate := range candidates {
  93. action := buildAction(candidate.binding, rr)
  94. if action == nil {
  95. continue
  96. }
  97. result = append(result, &apiv1.EntityRelatedAction{
  98. Action: action,
  99. PrefilledArguments: candidate.prefilled,
  100. })
  101. }
  102. return result
  103. }