Skip to content

Commit

Permalink
feat : implement code review comments
Browse files Browse the repository at this point in the history
  • Loading branch information
rajadilipkolli committed Dec 2, 2024
1 parent bd6fe79 commit c4468a5
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

// As JpaAuditing works based on Proxy we shouldn't create configuration as proxyBeans as false
/**
* Enables JPA auditing for automatic management of entity timestamps.
* Note: JPA Auditing works based on Proxy, so proxy beans must remain enabled.
*
* This configuration supports:
* - Automatic population of @CreatedDate in Auditable entities
* - Primarily used by Animal entity for tracking creation timestamps
*/
@Configuration
@EnableJpaAuditing
public class JpaAuditConfig {}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

import jakarta.validation.constraints.NotBlank;

/**
* Request object for animal operations containing essential animal information.
*
* @param name The name of the animal (required)
* @param type The type/species of the animal (required)
* @param habitat The natural environment where the animal lives (optional)
*/
public record AnimalRequest(
@NotBlank(message = "Name cannot be blank") String name,
@NotBlank(message = "Type cannot be blank") String type,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,13 @@

import java.time.LocalDateTime;

/**
* Response record representing an animal entity with its basic attributes and audit information.
*
* @param id Unique identifier of the animal
* @param name Name of the animal
* @param type Type/species of the animal
* @param habitat Natural environment where the animal lives
* @param created Timestamp when the animal record was created
*/
public record AnimalResponse(Long id, String name, String type, String habitat, LocalDateTime created) {}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@
import org.springframework.data.domain.Window;
import org.springframework.data.jpa.domain.Specification;

/**
* Custom repository interface for efficient keyset pagination of Animal entities.
*/
public interface CustomAnimalRepository {

/**
* Finds all animals matching the given specification using keyset pagination.
*
* @param spec The specification to filter animals
* @param pageRequest The pagination information
* @param scrollPosition The current position in the result set
* @return A window containing the paginated results
*/
Window<Animal> findAll(Specification<Animal> spec, PageRequest pageRequest, ScrollPosition scrollPosition);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
import jakarta.persistence.criteria.Predicate;
import java.util.List;
import java.util.Map;
import jdk.jfr.Registered;
import org.springframework.data.domain.KeysetScrollPosition;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.ScrollPosition;
import org.springframework.data.domain.Window;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Repository;

@Registered
@Repository
public class CustomAnimalRepositoryImpl implements CustomAnimalRepository {

private final EntityManager entityManager;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,14 @@ private Pageable createPageable(FindAnimalsQuery findAnimalsQuery) {
}

public Window<AnimalResponse> searchAnimals(String name, String type, int pageSize, Long scrollId) {
Specification<Animal> specification = null;
Specification<Animal> specification = Specification.where(null);

if (name != null && !name.isEmpty()) {
specification = Specification.where(AnimalSpecifications.hasName(name));
} else if (type != null && !type.isEmpty()) {
if (specification != null) {
specification = specification.and(AnimalSpecifications.hasType(type));
} else {
specification = Specification.where(AnimalSpecifications.hasType(type));
}
specification = specification.and(AnimalSpecifications.hasName(name));
}

if (type != null && !type.isEmpty()) {
specification = specification.and(AnimalSpecifications.hasType(type));
}

// Create initial ScrollPosition or continue from the given scrollId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@
import com.example.keysetpagination.model.response.PagedResult;
import com.example.keysetpagination.services.AnimalService;
import com.example.keysetpagination.utils.AppConstants;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import java.net.URI;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Window;
Expand Down Expand Up @@ -46,12 +52,22 @@ PagedResult<AnimalResponse> getAllAnimals(
return animalService.findAllAnimals(findAnimalsQuery);
}

@Operation(summary = "Search animals with keyset pagination support")
@ApiResponses(
value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved animals"),
@ApiResponse(responseCode = "400", description = "Invalid parameters supplied")
})
@GetMapping("/search")
public Window<AnimalResponse> searchAnimals(
@RequestParam(required = false) String name,
@RequestParam(required = false) String type,
@RequestParam(defaultValue = "10") int pageSize,
@RequestParam(required = false) Long scrollId) {
@Parameter(description = "Animal name to search for") @RequestParam(required = false) String name,
@Parameter(description = "Animal type to filter by") @RequestParam(required = false) String type,
@Parameter(description = "Number of items per page (max 100)")
@RequestParam(defaultValue = "10")
@Min(1)
@Max(100)
int pageSize,
@Parameter(description = "Scroll ID for pagination") @RequestParam(required = false) Long scrollId) {

return animalService.searchAnimals(name, type, pageSize, scrollId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,15 @@ void shouldSearchAnimals() throws Exception {
.andExpect(jsonPath("$.last", is(true)));
}

@Test
void shouldReturnEmptyResultForNonExistentType() throws Exception {
this.mockMvc
.perform(get("/api/animals/search").param("type", "NonExistentType"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.content", hasSize(0)))
.andExpect(jsonPath("$.last", is(true)));
}

@Test
void shouldFindAnimalById() throws Exception {
Animal animal = animalList.getFirst();
Expand All @@ -123,7 +132,9 @@ void shouldCreateNewAnimal() throws Exception {
.andExpect(status().isCreated())
.andExpect(header().exists(HttpHeaders.LOCATION))
.andExpect(jsonPath("$.id", notNullValue()))
.andExpect(jsonPath("$.name", is(animalRequest.name())));
.andExpect(jsonPath("$.name", is(animalRequest.name())))
.andExpect(jsonPath("$.type", is(animalRequest.type())))
.andExpect(jsonPath("$.habitat", is(animalRequest.habitat())));
}

@Test
Expand Down

0 comments on commit c4468a5

Please sign in to comment.