Implement topics sorting (backend) (#1498)
* [ISSUE-1383]Create parameters of request for sorting topics. (backend) * [ISSUE-1383]Create parameters of request for sorting topics. (backend) Co-authored-by: Roman Zabaluev <rzabaluev@provectus.com>
This commit is contained in:
parent
540b8eb79b
commit
7cdcacf5d5
4 changed files with 68 additions and 12 deletions
|
@ -5,6 +5,7 @@ import com.provectus.kafka.ui.model.PartitionsIncreaseDTO;
|
||||||
import com.provectus.kafka.ui.model.PartitionsIncreaseResponseDTO;
|
import com.provectus.kafka.ui.model.PartitionsIncreaseResponseDTO;
|
||||||
import com.provectus.kafka.ui.model.ReplicationFactorChangeDTO;
|
import com.provectus.kafka.ui.model.ReplicationFactorChangeDTO;
|
||||||
import com.provectus.kafka.ui.model.ReplicationFactorChangeResponseDTO;
|
import com.provectus.kafka.ui.model.ReplicationFactorChangeResponseDTO;
|
||||||
|
import com.provectus.kafka.ui.model.SortOrderDTO;
|
||||||
import com.provectus.kafka.ui.model.TopicColumnsToSortDTO;
|
import com.provectus.kafka.ui.model.TopicColumnsToSortDTO;
|
||||||
import com.provectus.kafka.ui.model.TopicConfigDTO;
|
import com.provectus.kafka.ui.model.TopicConfigDTO;
|
||||||
import com.provectus.kafka.ui.model.TopicCreationDTO;
|
import com.provectus.kafka.ui.model.TopicCreationDTO;
|
||||||
|
@ -66,6 +67,7 @@ public class TopicsController extends AbstractController implements TopicsApi {
|
||||||
@Valid Boolean showInternal,
|
@Valid Boolean showInternal,
|
||||||
@Valid String search,
|
@Valid String search,
|
||||||
@Valid TopicColumnsToSortDTO orderBy,
|
@Valid TopicColumnsToSortDTO orderBy,
|
||||||
|
@Valid SortOrderDTO sortOrder,
|
||||||
ServerWebExchange exchange) {
|
ServerWebExchange exchange) {
|
||||||
return topicsService
|
return topicsService
|
||||||
.getTopics(
|
.getTopics(
|
||||||
|
@ -74,7 +76,8 @@ public class TopicsController extends AbstractController implements TopicsApi {
|
||||||
Optional.ofNullable(perPage),
|
Optional.ofNullable(perPage),
|
||||||
Optional.ofNullable(showInternal),
|
Optional.ofNullable(showInternal),
|
||||||
Optional.ofNullable(search),
|
Optional.ofNullable(search),
|
||||||
Optional.ofNullable(orderBy)
|
Optional.ofNullable(orderBy),
|
||||||
|
Optional.ofNullable(sortOrder)
|
||||||
).map(ResponseEntity::ok);
|
).map(ResponseEntity::ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ import com.provectus.kafka.ui.model.PartitionsIncreaseDTO;
|
||||||
import com.provectus.kafka.ui.model.PartitionsIncreaseResponseDTO;
|
import com.provectus.kafka.ui.model.PartitionsIncreaseResponseDTO;
|
||||||
import com.provectus.kafka.ui.model.ReplicationFactorChangeDTO;
|
import com.provectus.kafka.ui.model.ReplicationFactorChangeDTO;
|
||||||
import com.provectus.kafka.ui.model.ReplicationFactorChangeResponseDTO;
|
import com.provectus.kafka.ui.model.ReplicationFactorChangeResponseDTO;
|
||||||
|
import com.provectus.kafka.ui.model.SortOrderDTO;
|
||||||
import com.provectus.kafka.ui.model.TopicColumnsToSortDTO;
|
import com.provectus.kafka.ui.model.TopicColumnsToSortDTO;
|
||||||
import com.provectus.kafka.ui.model.TopicConfigDTO;
|
import com.provectus.kafka.ui.model.TopicConfigDTO;
|
||||||
import com.provectus.kafka.ui.model.TopicCreationDTO;
|
import com.provectus.kafka.ui.model.TopicCreationDTO;
|
||||||
|
@ -67,10 +68,11 @@ public class TopicsService {
|
||||||
Optional<Integer> nullablePerPage,
|
Optional<Integer> nullablePerPage,
|
||||||
Optional<Boolean> showInternal,
|
Optional<Boolean> showInternal,
|
||||||
Optional<String> search,
|
Optional<String> search,
|
||||||
Optional<TopicColumnsToSortDTO> sortBy) {
|
Optional<TopicColumnsToSortDTO> sortBy,
|
||||||
|
Optional<SortOrderDTO> sortOrder) {
|
||||||
return adminClientService.get(cluster).flatMap(ac ->
|
return adminClientService.get(cluster).flatMap(ac ->
|
||||||
new Pagination(ac, metricsCache.get(cluster))
|
new Pagination(ac, metricsCache.get(cluster))
|
||||||
.getPage(pageNum, nullablePerPage, showInternal, search, sortBy)
|
.getPage(pageNum, nullablePerPage, showInternal, search, sortBy, sortOrder)
|
||||||
.flatMap(page ->
|
.flatMap(page ->
|
||||||
loadTopics(cluster, page.getTopics())
|
loadTopics(cluster, page.getTopics())
|
||||||
.map(topics ->
|
.map(topics ->
|
||||||
|
@ -409,12 +411,15 @@ public class TopicsService {
|
||||||
Optional<Integer> nullablePerPage,
|
Optional<Integer> nullablePerPage,
|
||||||
Optional<Boolean> showInternal,
|
Optional<Boolean> showInternal,
|
||||||
Optional<String> search,
|
Optional<String> search,
|
||||||
Optional<TopicColumnsToSortDTO> sortBy) {
|
Optional<TopicColumnsToSortDTO> sortBy,
|
||||||
|
Optional<SortOrderDTO> sortOrder) {
|
||||||
return geTopicsForPagination()
|
return geTopicsForPagination()
|
||||||
.map(paginatingTopics -> {
|
.map(paginatingTopics -> {
|
||||||
Predicate<Integer> positiveInt = i -> i > 0;
|
Predicate<Integer> positiveInt = i -> i > 0;
|
||||||
int perPage = nullablePerPage.filter(positiveInt).orElse(DEFAULT_PAGE_SIZE);
|
int perPage = nullablePerPage.filter(positiveInt).orElse(DEFAULT_PAGE_SIZE);
|
||||||
var topicsToSkip = (pageNum.filter(positiveInt).orElse(1) - 1) * perPage;
|
var topicsToSkip = (pageNum.filter(positiveInt).orElse(1) - 1) * perPage;
|
||||||
|
var comparator = sortOrder.isEmpty() || !sortOrder.get().equals(SortOrderDTO.DESC)
|
||||||
|
? getComparatorForTopic(sortBy) : getComparatorForTopic(sortBy).reversed();
|
||||||
List<InternalTopic> topics = paginatingTopics.stream()
|
List<InternalTopic> topics = paginatingTopics.stream()
|
||||||
.filter(topic -> !topic.isInternal()
|
.filter(topic -> !topic.isInternal()
|
||||||
|| showInternal.map(i -> topic.isInternal() == i).orElse(true))
|
|| showInternal.map(i -> topic.isInternal() == i).orElse(true))
|
||||||
|
@ -422,7 +427,7 @@ public class TopicsService {
|
||||||
search
|
search
|
||||||
.map(s -> StringUtils.containsIgnoreCase(topic.getName(), s))
|
.map(s -> StringUtils.containsIgnoreCase(topic.getName(), s))
|
||||||
.orElse(true))
|
.orElse(true))
|
||||||
.sorted(getComparatorForTopic(sortBy))
|
.sorted(comparator)
|
||||||
.collect(toList());
|
.collect(toList());
|
||||||
var totalPages = (topics.size() / perPage)
|
var totalPages = (topics.size() / perPage)
|
||||||
+ (topics.size() % perPage == 0 ? 0 : 1);
|
+ (topics.size() % perPage == 0 ? 0 : 1);
|
||||||
|
|
|
@ -4,8 +4,10 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import com.provectus.kafka.ui.model.SortOrderDTO;
|
||||||
import com.provectus.kafka.ui.model.TopicColumnsToSortDTO;
|
import com.provectus.kafka.ui.model.TopicColumnsToSortDTO;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -46,12 +48,35 @@ class TopicsServicePaginationTest {
|
||||||
|
|
||||||
var topics = pagination.getPage(
|
var topics = pagination.getPage(
|
||||||
Optional.empty(), Optional.empty(), Optional.empty(),
|
Optional.empty(), Optional.empty(), Optional.empty(),
|
||||||
Optional.empty(), Optional.empty()).block();
|
Optional.empty(), Optional.empty(), Optional.empty()).block();
|
||||||
assertThat(topics.getTotalPages()).isEqualTo(4);
|
assertThat(topics.getTotalPages()).isEqualTo(4);
|
||||||
assertThat(topics.getTopics()).hasSize(25);
|
assertThat(topics.getTopics()).hasSize(25);
|
||||||
assertThat(topics.getTopics()).isSorted();
|
assertThat(topics.getTopics()).isSorted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldListFirst25TopicsSortedByNameDescendingOrder() {
|
||||||
|
var topicDescriptions = IntStream.rangeClosed(1, 100).boxed()
|
||||||
|
.map(Objects::toString)
|
||||||
|
.map(name -> new TopicDescription(name, false, List.of()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
init(topicDescriptions);
|
||||||
|
|
||||||
|
var topics = pagination.getPage(
|
||||||
|
Optional.empty(), Optional.empty(), Optional.empty(),
|
||||||
|
Optional.empty(), Optional.of(TopicColumnsToSortDTO.NAME), Optional.of(SortOrderDTO.DESC)).block();
|
||||||
|
assertThat(topics.getTotalPages()).isEqualTo(4);
|
||||||
|
assertThat(topics.getTopics()).hasSize(25);
|
||||||
|
assertThat(topics.getTopics()).isSortedAccordingTo(Comparator.reverseOrder());
|
||||||
|
assertThat(topics.getTopics()).containsExactlyElementsOf(
|
||||||
|
topicDescriptions.stream()
|
||||||
|
.map(TopicDescription::name)
|
||||||
|
.sorted(Comparator.reverseOrder())
|
||||||
|
.limit(25)
|
||||||
|
.collect(Collectors.toList())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldCalculateCorrectPageCountForNonDivisiblePageSize() {
|
public void shouldCalculateCorrectPageCountForNonDivisiblePageSize() {
|
||||||
init(
|
init(
|
||||||
|
@ -62,7 +87,7 @@ class TopicsServicePaginationTest {
|
||||||
);
|
);
|
||||||
|
|
||||||
var topics = pagination.getPage(Optional.of(4), Optional.of(33),
|
var topics = pagination.getPage(Optional.of(4), Optional.of(33),
|
||||||
Optional.empty(), Optional.empty(), Optional.empty()).block();
|
Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty()).block();
|
||||||
assertThat(topics.getTotalPages()).isEqualTo(4);
|
assertThat(topics.getTotalPages()).isEqualTo(4);
|
||||||
assertThat(topics.getTopics()).hasSize(1)
|
assertThat(topics.getTopics()).hasSize(1)
|
||||||
.first().isEqualTo("99");
|
.first().isEqualTo("99");
|
||||||
|
@ -78,7 +103,7 @@ class TopicsServicePaginationTest {
|
||||||
);
|
);
|
||||||
|
|
||||||
var topics = pagination.getPage(Optional.of(0), Optional.of(-1),
|
var topics = pagination.getPage(Optional.of(0), Optional.of(-1),
|
||||||
Optional.empty(), Optional.empty(), Optional.empty()).block();
|
Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty()).block();
|
||||||
assertThat(topics.getTotalPages()).isEqualTo(4);
|
assertThat(topics.getTotalPages()).isEqualTo(4);
|
||||||
assertThat(topics.getTopics()).hasSize(25);
|
assertThat(topics.getTopics()).hasSize(25);
|
||||||
assertThat(topics.getTopics()).isSorted();
|
assertThat(topics.getTopics()).isSorted();
|
||||||
|
@ -95,7 +120,7 @@ class TopicsServicePaginationTest {
|
||||||
|
|
||||||
var topics = pagination.getPage(
|
var topics = pagination.getPage(
|
||||||
Optional.empty(), Optional.empty(), Optional.of(true),
|
Optional.empty(), Optional.empty(), Optional.of(true),
|
||||||
Optional.empty(), Optional.empty()).block();
|
Optional.empty(), Optional.empty(), Optional.empty()).block();
|
||||||
assertThat(topics.getTotalPages()).isEqualTo(4);
|
assertThat(topics.getTotalPages()).isEqualTo(4);
|
||||||
assertThat(topics.getTopics()).hasSize(25);
|
assertThat(topics.getTopics()).hasSize(25);
|
||||||
assertThat(topics.getTopics()).isSorted();
|
assertThat(topics.getTopics()).isSorted();
|
||||||
|
@ -113,7 +138,7 @@ class TopicsServicePaginationTest {
|
||||||
|
|
||||||
var topics = pagination.getPage(
|
var topics = pagination.getPage(
|
||||||
Optional.empty(), Optional.empty(), Optional.of(true),
|
Optional.empty(), Optional.empty(), Optional.of(true),
|
||||||
Optional.empty(), Optional.empty()).block();
|
Optional.empty(), Optional.empty(), Optional.empty()).block();
|
||||||
assertThat(topics.getTotalPages()).isEqualTo(4);
|
assertThat(topics.getTotalPages()).isEqualTo(4);
|
||||||
assertThat(topics.getTopics()).hasSize(25);
|
assertThat(topics.getTopics()).hasSize(25);
|
||||||
assertThat(topics.getTopics()).isSorted();
|
assertThat(topics.getTopics()).isSorted();
|
||||||
|
@ -131,7 +156,7 @@ class TopicsServicePaginationTest {
|
||||||
|
|
||||||
var topics = pagination.getPage(
|
var topics = pagination.getPage(
|
||||||
Optional.empty(), Optional.empty(), Optional.empty(),
|
Optional.empty(), Optional.empty(), Optional.empty(),
|
||||||
Optional.of("1"), Optional.empty()).block();
|
Optional.of("1"), Optional.empty(), Optional.empty()).block();
|
||||||
assertThat(topics.getTotalPages()).isEqualTo(1);
|
assertThat(topics.getTotalPages()).isEqualTo(1);
|
||||||
assertThat(topics.getTopics()).hasSize(20);
|
assertThat(topics.getTopics()).hasSize(20);
|
||||||
assertThat(topics.getTopics()).isSorted();
|
assertThat(topics.getTopics()).isSorted();
|
||||||
|
@ -151,7 +176,7 @@ class TopicsServicePaginationTest {
|
||||||
|
|
||||||
var topics = pagination.getPage(
|
var topics = pagination.getPage(
|
||||||
Optional.empty(), Optional.empty(), Optional.empty(),
|
Optional.empty(), Optional.empty(), Optional.empty(),
|
||||||
Optional.empty(), Optional.of(TopicColumnsToSortDTO.TOTAL_PARTITIONS)).block();
|
Optional.empty(), Optional.of(TopicColumnsToSortDTO.TOTAL_PARTITIONS), Optional.empty()).block();
|
||||||
assertThat(topics.getTotalPages()).isEqualTo(4);
|
assertThat(topics.getTotalPages()).isEqualTo(4);
|
||||||
assertThat(topics.getTopics()).hasSize(25);
|
assertThat(topics.getTopics()).hasSize(25);
|
||||||
assertThat(topics.getTopics()).containsExactlyElementsOf(
|
assertThat(topics.getTopics()).containsExactlyElementsOf(
|
||||||
|
@ -159,6 +184,18 @@ class TopicsServicePaginationTest {
|
||||||
.map(TopicDescription::name)
|
.map(TopicDescription::name)
|
||||||
.limit(25)
|
.limit(25)
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
|
|
||||||
|
var topicsSortedDesc = pagination.getPage(
|
||||||
|
Optional.empty(), Optional.empty(), Optional.empty(),
|
||||||
|
Optional.empty(), Optional.of(TopicColumnsToSortDTO.TOTAL_PARTITIONS), Optional.of(SortOrderDTO.DESC)).block();
|
||||||
|
assertThat(topicsSortedDesc.getTotalPages()).isEqualTo(4);
|
||||||
|
assertThat(topicsSortedDesc.getTopics()).hasSize(25);
|
||||||
|
assertThat(topicsSortedDesc.getTopics()).containsExactlyElementsOf(
|
||||||
|
topicDescriptions.stream()
|
||||||
|
.sorted((a, b) -> b.partitions().size() - a.partitions().size())
|
||||||
|
.map(TopicDescription::name)
|
||||||
|
.limit(25)
|
||||||
|
.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -295,6 +295,11 @@ paths:
|
||||||
required: false
|
required: false
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/TopicColumnsToSort'
|
$ref: '#/components/schemas/TopicColumnsToSort'
|
||||||
|
- name: sortOrder
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/SortOrder'
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: OK
|
description: OK
|
||||||
|
@ -1729,6 +1734,12 @@ components:
|
||||||
- TOTAL_PARTITIONS
|
- TOTAL_PARTITIONS
|
||||||
- REPLICATION_FACTOR
|
- REPLICATION_FACTOR
|
||||||
|
|
||||||
|
SortOrder:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- ASC
|
||||||
|
- DESC
|
||||||
|
|
||||||
Topic:
|
Topic:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
|
Loading…
Add table
Reference in a new issue