api_entities_list.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. package api
  2. import (
  3. "sort"
  4. "strings"
  5. apiv1 "github.com/OliveTin/OliveTin/gen/olivetin/api/v1"
  6. config "github.com/OliveTin/OliveTin/internal/config"
  7. "github.com/OliveTin/OliveTin/internal/entities"
  8. )
  9. const (
  10. defaultEntityInstancesPageSize = 10
  11. maxEntityInstancesPageSize = 100
  12. )
  13. func (api *oliveTinAPI) buildEntityDefinitionsResponse(req *apiv1.GetEntitiesRequest, entityMap entities.EntitiesByClass) []*apiv1.EntityDefinition {
  14. if req != nil && req.EntityType != "" {
  15. return api.buildFilteredEntityDefinitions(req, entityMap)
  16. }
  17. return api.buildAllEntityDefinitions(entityMap)
  18. }
  19. func (api *oliveTinAPI) buildAllEntityDefinitions(entityMap entities.EntitiesByClass) []*apiv1.EntityDefinition {
  20. entityNames := sortedEntityTypeNames(entityMap)
  21. entityDefinitions := make([]*apiv1.EntityDefinition, 0, len(entityNames))
  22. for _, name := range entityNames {
  23. entityFile := entityFileForType(api.cfg, name)
  24. properties := entityPropertiesFromFile(entityFile)
  25. instances := buildSortedEntityInstances(name, entityMap[name], properties)
  26. def := &apiv1.EntityDefinition{
  27. Title: name,
  28. UsedOnDashboards: findDashboardsForEntity(name, api.cfg.Dashboards),
  29. Icon: entityTypeIcon(api.cfg, name),
  30. Properties: entityDefinitionProperties(properties),
  31. TotalInstances: int32(len(instances)),
  32. }
  33. if len(properties) == 0 {
  34. def.Instances = instances
  35. }
  36. entityDefinitions = append(entityDefinitions, def)
  37. }
  38. return entityDefinitions
  39. }
  40. func (api *oliveTinAPI) buildFilteredEntityDefinitions(req *apiv1.GetEntitiesRequest, entityMap entities.EntitiesByClass) []*apiv1.EntityDefinition {
  41. entityInstances, ok := entityMap[req.EntityType]
  42. if !ok || len(entityInstances) == 0 {
  43. return nil
  44. }
  45. entityFile := entityFileForType(api.cfg, req.EntityType)
  46. properties := entityPropertiesFromFile(entityFile)
  47. instances := buildSortedEntityInstances(req.EntityType, entityInstances, properties)
  48. filtered := filterEntityInstances(instances, req.Filter)
  49. pageSize := normalizeEntityInstancesPageSize(req.PageSize)
  50. page := normalizeEntityInstancesPage(req.Page)
  51. def := &apiv1.EntityDefinition{
  52. Title: req.EntityType,
  53. UsedOnDashboards: findDashboardsForEntity(req.EntityType, api.cfg.Dashboards),
  54. Icon: entityTypeIcon(api.cfg, req.EntityType),
  55. Properties: entityDefinitionProperties(properties),
  56. TotalInstances: int32(len(filtered)),
  57. Instances: paginateEntityInstances(filtered, page, pageSize),
  58. }
  59. return []*apiv1.EntityDefinition{def}
  60. }
  61. func sortedEntityTypeNames(entityMap entities.EntitiesByClass) []string {
  62. entityNames := make([]string, 0, len(entityMap))
  63. for name := range entityMap {
  64. entityNames = append(entityNames, name)
  65. }
  66. sort.Strings(entityNames)
  67. return entityNames
  68. }
  69. func normalizeEntityInstancesPage(page int32) int32 {
  70. if page < 1 {
  71. return 1
  72. }
  73. return page
  74. }
  75. func normalizeEntityInstancesPageSize(pageSize int32) int32 {
  76. if pageSize < 1 {
  77. return defaultEntityInstancesPageSize
  78. }
  79. if pageSize > maxEntityInstancesPageSize {
  80. return maxEntityInstancesPageSize
  81. }
  82. return pageSize
  83. }
  84. func filterEntityInstances(instances []*apiv1.Entity, filter string) []*apiv1.Entity {
  85. filter = strings.TrimSpace(strings.ToLower(filter))
  86. if filter == "" {
  87. return instances
  88. }
  89. filtered := make([]*apiv1.Entity, 0, len(instances))
  90. for _, instance := range instances {
  91. if entityInstanceMatchesFilter(instance, filter) {
  92. filtered = append(filtered, instance)
  93. }
  94. }
  95. return filtered
  96. }
  97. func entityInstanceMatchesFilter(instance *apiv1.Entity, filter string) bool {
  98. if strings.Contains(strings.ToLower(instance.Title), filter) {
  99. return true
  100. }
  101. for _, value := range instance.Fields {
  102. if strings.Contains(strings.ToLower(value), filter) {
  103. return true
  104. }
  105. }
  106. return false
  107. }
  108. func paginateEntityInstances(instances []*apiv1.Entity, page, pageSize int32) []*apiv1.Entity {
  109. count := int64(len(instances))
  110. start := int64(page-1) * int64(pageSize)
  111. if start >= count {
  112. return []*apiv1.Entity{}
  113. }
  114. end := start + int64(pageSize)
  115. if end > count {
  116. end = count
  117. }
  118. return instances[int(start):int(end)]
  119. }
  120. func entityFieldsForResponse(data any, properties []config.EntityProperty) map[string]string {
  121. if len(properties) > 0 {
  122. return entityListFields(data, properties)
  123. }
  124. return serializeEntityFields(data)
  125. }