diff --git a/src/main/java/eu/europa/ec/simpl/usersroles/services/impl/RoleServiceImpl.java b/src/main/java/eu/europa/ec/simpl/usersroles/services/impl/RoleServiceImpl.java index 05c95fd75326053ceeafe287ef69a1873fe0dac1..41b7fbc9f7ba38358d2fd555f279689d93fb09f2 100644 --- a/src/main/java/eu/europa/ec/simpl/usersroles/services/impl/RoleServiceImpl.java +++ b/src/main/java/eu/europa/ec/simpl/usersroles/services/impl/RoleServiceImpl.java @@ -14,17 +14,25 @@ import eu.europa.ec.simpl.usersroles.services.KeycloakUserService; import eu.europa.ec.simpl.usersroles.services.RoleService; import jakarta.ws.rs.ClientErrorException; import jakarta.ws.rs.NotFoundException; +import java.beans.PropertyDescriptor; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.UUID; import java.util.stream.Collectors; +import java.util.stream.Stream; +import lombok.SneakyThrows; import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.StringUtils; import org.keycloak.representations.idm.RoleRepresentation; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.BeansException; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -203,6 +211,7 @@ public class RoleServiceImpl implements RoleService { var filteredRoles = roleList.stream() .map(role -> roleMapper.toDto(role, getEnabledAttrs(role))) .filter(roleDTO -> shouldMatchFilter(roleDTO, request)) + .sorted(toComparator(pageable.getSort())) .skip(pageable.getOffset()) .limit(pageable.getPageSize()) .toList(); @@ -215,6 +224,53 @@ public class RoleServiceImpl implements RoleService { return new PageImpl<>(filteredRoles, pageable, totalElements); } + private Comparator<? super RoleDTO> toComparator(Sort sort) { + return sort.stream() + .flatMap(this::toComparator) + .reduce(Comparator::thenComparing) + .orElse(unsortedComparator()); + } + + private Stream<Comparator<RoleDTO>> toComparator(Sort.Order order) { + try { + return Optional.ofNullable(getPropertyDescriptor(order)) + .flatMap(property -> toComparator(property, order.getDirection())) + .stream(); + } catch (BeansException e) { + return Stream.empty(); + } + } + + private static PropertyDescriptor getPropertyDescriptor(Sort.Order order) { + return BeanUtils.getPropertyDescriptor(RoleDTO.class, order.getProperty()); + } + + private static Optional<Comparator<RoleDTO>> toComparator(PropertyDescriptor property, Sort.Direction direction) { + if (Comparable.class.isAssignableFrom(property.getPropertyType())) { + return Optional.of(buildComparator(property, direction)); + } else { + return Optional.empty(); + } + } + + private static Comparator<RoleDTO> buildComparator(PropertyDescriptor property, Sort.Direction direction) { + var comparator = Comparator.<RoleDTO, Comparable<Object>>comparing(o -> get(o, property)); + if (direction == Sort.Direction.DESC) { + comparator = comparator.reversed(); + } + return comparator; + } + + private static Comparator<RoleDTO> unsortedComparator() { + return (o1, o2) -> 0; + } + + @SneakyThrows + @SuppressWarnings("unchecked") + private static Comparable<Object> get(RoleDTO o, PropertyDescriptor property) { + return (Comparable<Object>) property.getReadMethod().invoke(o); + } + @Override public void importRoles(List<KeycloakRoleDTO> roles) { keycloakService.importRoles(roles);