autocompletequeryservice.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. package allocation
  2. import (
  3. "fmt"
  4. "strings"
  5. "github.com/opencost/opencost/core/pkg/autocomplete"
  6. coreallocation "github.com/opencost/opencost/core/pkg/autocomplete/allocation"
  7. "github.com/opencost/opencost/core/pkg/opencost"
  8. )
  9. func QueryAllocationAutocompleteFromSetRange(asr *opencost.AllocationSetRange, req autocomplete.Request) (*autocomplete.Response, error) {
  10. field, err := autocomplete.NormalizeRequest(&req, coreallocation.ValidateField, autocomplete.NormalizeOptions{
  11. EnsureLabelConfig: true,
  12. })
  13. if err != nil {
  14. return nil, err
  15. }
  16. if route, _, _ := coreallocation.RouteField(field); route == coreallocation.RouteAlias {
  17. if key, ok := coreallocation.ResolveAliasLabelKey(field, req.LabelConfig); ok {
  18. field = "label:" + key
  19. }
  20. }
  21. var matcher opencost.AllocationMatcher
  22. if autocomplete.HasFilter(req.Filter) {
  23. compiler := opencost.NewAllocationMatchCompiler(req.LabelConfig)
  24. matcher, err = compiler.Compile(req.Filter)
  25. if err != nil {
  26. return nil, fmt.Errorf("%w: failed to compile filter: %w", autocomplete.ErrBadRequest, err)
  27. }
  28. }
  29. search := strings.ToLower(req.Search)
  30. results := map[string]struct{}{}
  31. for _, as := range asr.Allocations {
  32. if as == nil {
  33. continue
  34. }
  35. for _, alloc := range as.Allocations {
  36. if alloc == nil || alloc.Properties == nil {
  37. continue
  38. }
  39. if matcher != nil && !matcher.Matches(alloc) {
  40. continue
  41. }
  42. values := allocationAutocompleteValues(alloc.Properties, field)
  43. for _, value := range values {
  44. if value == "" {
  45. continue
  46. }
  47. if search != "" && !strings.Contains(strings.ToLower(value), search) {
  48. continue
  49. }
  50. results[value] = struct{}{}
  51. }
  52. }
  53. }
  54. return &autocomplete.Response{Data: autocomplete.UniqueSortedLimited(results, req.Limit)}, nil
  55. }
  56. func allocationAutocompleteValues(props *opencost.AllocationProperties, field string) []string {
  57. switch {
  58. case field == "account":
  59. return nil
  60. case field == "cluster":
  61. return []string{props.Cluster}
  62. case field == "namespace":
  63. return []string{props.Namespace}
  64. case field == "node":
  65. return []string{props.Node}
  66. case field == "controllerkind":
  67. return []string{props.ControllerKind}
  68. case field == "controllername":
  69. return []string{props.Controller}
  70. case field == "pod":
  71. return []string{props.Pod}
  72. case field == "container":
  73. return []string{props.Container}
  74. case field == "label":
  75. return mapKeys(props.Labels)
  76. case strings.HasPrefix(field, "label:"):
  77. label := strings.TrimPrefix(field, "label:")
  78. if v, ok := autocomplete.MapValueFold(props.Labels, label); ok {
  79. return []string{v}
  80. }
  81. case field == "namespacelabel":
  82. return mapKeys(props.NamespaceLabels)
  83. case strings.HasPrefix(field, "namespacelabel:"):
  84. label := strings.TrimPrefix(field, "namespacelabel:")
  85. if v, ok := autocomplete.MapValueFold(props.NamespaceLabels, label); ok {
  86. return []string{v}
  87. }
  88. }
  89. return nil
  90. }
  91. func mapKeys(values map[string]string) []string {
  92. result := make([]string, 0, len(values))
  93. for k := range values {
  94. result = append(result, k)
  95. }
  96. return result
  97. }