Skip to content

Commit

Permalink
Merge pull request #178 from FEBFES/feature/46/add-sorting
Browse files Browse the repository at this point in the history
46 - add sorting
  • Loading branch information
IvanShish authored Sep 23, 2023
2 parents d8e0ef5 + 89d753d commit 0cb7b6a
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 18 deletions.
17 changes: 17 additions & 0 deletions src/main/java/com/febfes/fftmback/annotation/SortParam.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.febfes.fftmback.annotation;

import io.swagger.v3.oas.annotations.Parameter;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
@Parameter(
name = "sort",
description = " Sort parameter. ASCENDING = \"+\"; DESCENDING = \"-\". For example: sort=-id&sort=+name"
)
public @interface SortParam {
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

import java.util.List;

import static com.febfes.fftmback.util.SortUtils.getOrderFromParams;

@RestController
@RequestMapping("v1/projects")
@RequiredArgsConstructor
Expand All @@ -41,8 +43,11 @@ public class ProjectController {

@Operation(summary = "Get all projects for authenticated user")
@ApiGet
public List<ProjectDto> getProjectsForUser(@AuthenticationPrincipal UserEntity user) {
return projectService.getProjectsForUser(user.getId());
public List<ProjectDto> getProjectsForUser(
@SortParam @RequestParam(defaultValue = "-createDate") String[] sort,
@AuthenticationPrincipal UserEntity user
) {
return projectService.getProjectsForUser(user.getId(), getOrderFromParams(sort));
}

@Operation(summary = "Create new project")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.febfes.fftmback.domain.dao.ProjectEntity;
import com.febfes.fftmback.domain.projection.ProjectProjection;
import com.febfes.fftmback.domain.projection.ProjectWithMembersProjection;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
Expand Down Expand Up @@ -61,7 +62,8 @@ public interface ProjectRepository extends JpaRepository<ProjectEntity, Long> {
FROM project P
LEFT JOIN favourite_project FP ON P.id = FP.project_id AND FP.user_id = :userId
INNER JOIN user_project UP ON UP.project_id = P.id AND UP.user_id = :userId
ORDER BY "isFavourite" desc, ?#{#sort}
"""
)
List<ProjectProjection> getUserProjects(Long userId);
List<ProjectProjection> getUserProjects(Long userId, Sort sort);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
import com.febfes.fftmback.domain.common.specification.TaskSpec;
import com.febfes.fftmback.domain.dao.ProjectEntity;
import com.febfes.fftmback.dto.*;
import org.springframework.data.domain.Sort;

import java.util.List;

public interface ProjectService {

ProjectEntity createProject(ProjectEntity project, Long userId);

List<ProjectDto> getProjectsForUser(Long userId);
List<ProjectDto> getProjectsForUser(Long userId, List<Sort.Order> sort);

ProjectEntity getProject(Long id);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,15 @@
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Sort;
import org.springframework.data.util.ReflectionUtils;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.util.List;

import static com.febfes.fftmback.util.CaseUtils.camelToSnake;

@Slf4j
@Service
@Transactional
Expand Down Expand Up @@ -66,8 +69,12 @@ public ProjectEntity createProject(
}

@Override
public List<ProjectDto> getProjectsForUser(Long userId) {
List<ProjectProjection> userProjects = projectRepository.getUserProjects(userId);
public List<ProjectDto> getProjectsForUser(Long userId, List<Sort.Order> sort) {
List<Sort.Order> snakeCaseSort = sort.stream()
.map(s -> new Sort.Order(s.getDirection(), camelToSnake(s.getProperty())))
.toList();
List<ProjectProjection> userProjects = projectRepository.getUserProjects(
userId, Sort.by(snakeCaseSort));
log.info("Received {} projects for user with id={}", userProjects.size(), userId);
return userProjects.stream()
.map(ProjectMapper.INSTANCE::projectProjectionToProjectDto)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@
public class TaskServiceImpl implements TaskService {

private static final String RECEIVED_TASKS_SIZE_LOG = "Received tasks size: {}";
private static final int DEFAULT_PAGE = 0;
private static final int DEFAULT_LIMIT = 1000;

private final TaskRepository taskRepository;
private final TaskViewRepository taskViewRepository;
Expand All @@ -62,9 +60,7 @@ public List<TaskView> getTasks(
Long columnId,
TaskSpec taskSpec
) {
Pageable pageableRequest = PageRequest.of(DEFAULT_PAGE, DEFAULT_LIMIT, Sort.by(ORDER_FIELD_NAME));
List<TaskView> tasks = taskViewRepository.findAll(taskSpec.and(byColumnId(columnId)), pageableRequest)
.getContent();
List<TaskView> tasks = taskViewRepository.findAll(taskSpec.and(byColumnId(columnId)), Sort.by(ORDER_FIELD_NAME));
log.info(RECEIVED_TASKS_SIZE_LOG, tasks.size());
return tasks;
}
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/com/febfes/fftmback/util/CaseUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.febfes.fftmback.util;

import lombok.experimental.UtilityClass;

@UtilityClass
public class CaseUtils {

public static String camelToSnake(final String camelStr) {
String ret = camelStr.replaceAll("([a-z])([A-Z]+)", "$1_$2");
return ret.toLowerCase();
}
}
37 changes: 37 additions & 0 deletions src/main/java/com/febfes/fftmback/util/SortUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.febfes.fftmback.util;

import lombok.experimental.UtilityClass;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@UtilityClass
public class SortUtils {

public static Order getOrderFromParam(String sortParam) {
Pattern pattern = Pattern.compile("([\\-+]?)(\\w+)");
Matcher matcher = pattern.matcher(sortParam);
if (!matcher.find()) {
throw new IllegalArgumentException("Sort parameter doesn't match pattern");
}
String direction = matcher.group(1);
String property = matcher.group(2);
return new Order(getDirection(direction), property);
}

public static List<Order> getOrderFromParams(String[] sortParams) {
List<Order> sortRows = new ArrayList<>();
for (String sortParam : sortParams) {
sortRows.add(getOrderFromParam(sortParam));
}
return sortRows;
}

private static Sort.Direction getDirection(String direction) {
return direction.equals("+") ? Sort.Direction.ASC : Sort.Direction.DESC;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

Expand Down Expand Up @@ -196,7 +197,7 @@ void successfulSetProjectFavouriteTest() {
OneProjectDto updatedProject = projectService.getProjectForUser(createdProjectId, createdUserId);
Assertions.assertThat(updatedProject.isFavourite())
.isTrue();
List<ProjectDto> userProjects = projectService.getProjectsForUser(createdUserId);
List<ProjectDto> userProjects = projectService.getProjectsForUser(createdUserId, new ArrayList<>());
Optional<ProjectDto> userProject = userProjects.stream().findFirst();
Assertions.assertThat(userProject)
.isNotEmpty();
Expand All @@ -219,7 +220,7 @@ void failedSetProjectFavouriteTest() {
OneProjectDto updatedProject = projectService.getProjectForUser(createdProjectId, createdUserId);
Assertions.assertThat(updatedProject.isFavourite())
.isFalse();
List<ProjectDto> userProjects = projectService.getProjectsForUser(createdUserId);
List<ProjectDto> userProjects = projectService.getProjectsForUser(createdUserId, new ArrayList<>());
Optional<ProjectDto> userProject = userProjects.stream().findFirst();
Assertions.assertThat(userProject)
.isNotEmpty();
Expand Down Expand Up @@ -267,9 +268,11 @@ protected void doInTransactionWithoutResult(@NonNull TransactionStatus status) {
List<MemberDto> members = userService.getProjectMembersWithRole(createdProjectId);
// as owner is also a member
Assertions.assertThat(members).hasSize(3);
List<ProjectDto> secondUserProjects = projectService.getProjectsForUser(secondCreatedUserId);
List<ProjectDto> secondUserProjects =
projectService.getProjectsForUser(secondCreatedUserId, new ArrayList<>());
Assertions.assertThat(secondUserProjects).hasSize(1);
List<ProjectDto> thirdUserProjects = projectService.getProjectsForUser(thirdCreatedUserId);
List<ProjectDto> thirdUserProjects =
projectService.getProjectsForUser(thirdCreatedUserId, new ArrayList<>());
Assertions.assertThat(thirdUserProjects).hasSize(1);
}
});
Expand All @@ -279,7 +282,7 @@ protected void doInTransactionWithoutResult(@NonNull TransactionStatus status) {
void successfulRemoveMemberTest() {
successfulAddNewMembersTest();
Long secondCreatedUserId = userService.getUserIdByUsername(USER_USERNAME + "1");
Optional<ProjectDto> userProject = projectService.getProjectsForUser(createdUserId).stream().findFirst();
Optional<ProjectDto> userProject = projectService.getProjectsForUser(createdUserId, new ArrayList<>()).stream().findFirst();
Assertions.assertThat(userProject)
.isNotEmpty();
Long createdProjectId = userProject.get().id();
Expand All @@ -292,14 +295,14 @@ void successfulRemoveMemberTest() {

List<MemberDto> members = userService.getProjectMembersWithRole(createdProjectId);
Assertions.assertThat(members).hasSize(2);
List<ProjectDto> secondMemberProjects = projectService.getProjectsForUser(secondCreatedUserId);
List<ProjectDto> secondMemberProjects = projectService.getProjectsForUser(secondCreatedUserId, new ArrayList<>());
Assertions.assertThat(secondMemberProjects).isEmpty();
}

@Test
void successfulGetMembersTest() {
successfulAddNewMembersTest();
Long createdProjectId = projectService.getProjectsForUser(createdUserId).get(0).id();
Long createdProjectId = projectService.getProjectsForUser(createdUserId, new ArrayList<>()).get(0).id();
List<MemberDto> projectMembers = requestWithBearerToken()
.contentType(ContentType.JSON)
.when()
Expand Down Expand Up @@ -359,6 +362,36 @@ void successfulGetOneProjectTest() {
.isEqualTo(RoleName.OWNER);
}

@Test
void successfulGetProjectsWithSort() {
projectService.createProject(
ProjectEntity.builder().name(PROJECT_NAME + "1").build(),
createdUserId
);
projectService.createProject(
ProjectEntity.builder().name(PROJECT_NAME + "2").build(),
createdUserId
);

List<ProjectDto> projects = requestWithBearerToken()
.contentType(ContentType.JSON)
.when()
.get(PATH_TO_PROJECTS_API + "?sort=-id&sort=+name")
.then()
.statusCode(HttpStatus.SC_OK)
.extract()
.response()
.as(new TypeRef<>() {
});

Assertions.assertThat(projects)
.hasSize(2);
Assertions.assertThat(projects.get(0).name())
.isEqualTo(PROJECT_NAME + "2");
Assertions.assertThat(projects.get(1).name())
.isEqualTo(PROJECT_NAME + "1");
}

private Long createNewProject(String projectName) {
ProjectDto createProjectDto = DtoBuilders.createProjectDto(projectName);
Response createResponse = createNewProject(createProjectDto);
Expand Down

0 comments on commit 0cb7b6a

Please sign in to comment.