api_entities_list.go 4.8 KB

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