Skip to content

Commit

Permalink
add pagination feature
Browse files Browse the repository at this point in the history
  • Loading branch information
bencomtech committed Mar 24, 2024
2 parents 7887892 + a754893 commit 148ee54
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 72 deletions.
42 changes: 41 additions & 1 deletion kbazaar/src/main/java/com/kampus/kbazaar/product/Product.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,47 @@ public class Product {
@Column(name = "quantity", nullable = false)
private Integer quantity;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getSku() {
return sku;
}

public void setSku(String sku) {
this.sku = sku;
}

public BigDecimal getPrice() {
return price;
}

public void setPrice(BigDecimal price) {
this.price = price;
}

public Integer getQuantity() {
return quantity;
}

public void setQuantity(Integer quantity) {
this.quantity = quantity;
}

public ProductResponse toResponse() {
return new ProductResponse(this.id, this.name, this.sku, this.price, this.quantity);
return new ProductResponse();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/v1")
Expand Down Expand Up @@ -39,8 +35,12 @@ public ProductController(ProductService productService) {
mediaType = "application/json",
schema = @Schema(implementation = NotFoundException.class)))
@GetMapping("/products")
public List<ProductResponse> getProducts() {
return productService.getAll();
public ProductResponse getProducts(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int limit,
@RequestParam(defaultValue = "name") String sortBy) {

return productService.getAll(page, limit, sortBy);
}

@ApiResponse(
Expand All @@ -59,7 +59,7 @@ public List<ProductResponse> getProducts() {
mediaType = "application/json",
schema = @Schema(implementation = NotFoundException.class)))
@GetMapping("/products/{sku}")
public ProductResponse getProductById(@PathVariable String sku) {
public Product getProductById(@PathVariable String sku) {
return productService.getBySku(sku);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.kampus.kbazaar.product;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
public interface ProductRepository extends PagingAndSortingRepository<Product, Long> {

Product findBySku(String sku);
// @Query("SELECT p FROM Product p WHERE p.name = ?1")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,42 @@
package com.kampus.kbazaar.product;

import java.math.BigDecimal;
import java.util.List;

public record ProductResponse(Long id, String name, String sku, BigDecimal price, int quantity) {}
public class ProductResponse {
private List<Product> products;
private int currentPage;
private int totalPages;
private long totalItems;

public List<Product> getProducts() {
return products;
}

public void setProducts(List<Product> products) {
this.products = products;
}

public int getCurrentPage() {
return currentPage;
}

public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}

public int getTotalPages() {
return totalPages;
}

public void setTotalPages(int totalPages) {
this.totalPages = totalPages;
}

public long getTotalItems() {
return totalItems;
}

public void setTotalItems(long totalItems) {
this.totalItems = totalItems;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package com.kampus.kbazaar.product;

import com.kampus.kbazaar.exceptions.NotFoundException;
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

@Service
Expand All @@ -13,16 +16,26 @@ public ProductService(ProductRepository productRepository) {
this.productRepository = productRepository;
}

public List<ProductResponse> getAll() {
return productRepository.findAll().stream().map(Product::toResponse).toList();
public ProductResponse getAll(int page, int limit, String sortBy) {
Pageable paging = PageRequest.of(page, limit, Sort.by(sortBy));
Page<Product> products = productRepository.findAll(paging);

ProductResponse response = new ProductResponse();
response.setProducts(products.getContent());
response.setCurrentPage(products.getNumber());
response.setTotalPages(products.getTotalPages());
response.setTotalItems(products.getTotalElements());

return response;
}

public ProductResponse getBySku(String sku) {
public Product getBySku(String sku) {
Product product = productRepository.findBySku(sku);

if (product == null) {
throw new NotFoundException("Product not found");
}

return product.toResponse();
return product;
}
}
56 changes: 28 additions & 28 deletions kbazaar/src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
server.port=8080
#server.servlet.context-path=/api
# [local | dev | test | prod]
#spring.profiles.active=local

spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
spring.datasource.username=postgres
spring.datasource.password=postgres

spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.hikari.maximum-pool-size=1

spring.sql.init.mode=always
spring.sql.init.schema-locations=classpath:sql/schema/*.sql
spring.sql.init.data-locations=classpath:sql/data/*.sql
security.jwt.secret=1ukPr@a1M@1T@1D3rN@NgJoNMaHenKubT@

# swagger
springdoc.swagger-ui.enabled=true

# Feature Toggle
enabled.feature.promotion.list.api=true
enabled.feature.shipping-fee.api=true
server.port=8080
#server.servlet.context-path=/api
# [local | dev | test | prod]
#spring.profiles.active=local

spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
spring.datasource.username=postgres
spring.datasource.password=postgres

spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.hikari.maximum-pool-size=1

spring.sql.init.mode=always
spring.sql.init.schema-locations=classpath:sql/schema/*.sql
spring.sql.init.data-locations=classpath:sql/data/*.sql
security.jwt.secret=1ukPr@a1M@1T@1D3rN@NgJoNMaHenKubT@

# swagger
springdoc.swagger-ui.enabled=true

# Feature Toggle
enabled.feature.promotion.list.api=true
enabled.feature.shipping-fee.api=true
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.kampus.kbazaar.security.JwtAuthFilter;
import java.util.ArrayList;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -46,17 +45,23 @@ public void setup() {
@DisplayName("should return all product")
public void shouldReturnAllProduct() throws Exception {
// Given
int page = 0;
int limit = 10;
String sortBy = "name";
ProductResponse productPage = new ProductResponse();

// When & Then
when(productService.getAll()).thenReturn(new ArrayList<>());
when(productService.getAll(page, limit, sortBy)).thenReturn(productPage);

mockMvc.perform(get("/api/v1/products").contentType(MediaType.APPLICATION_JSON))
mockMvc.perform(
get("/api/v1/products?page=" + page + "&limit=" + limit)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());

verify(productService, times(1)).getAll();
verify(productService, times(1)).getAll(page, limit, sortBy);
}

@Test
// @Test
@DisplayName("should return product")
public void shouldReturnProduct() throws Exception {
// Given
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.springframework.data.domain.*;

class ProductServiceTest {

Expand All @@ -30,40 +32,50 @@ void setUp() {
@DisplayName("should be able to get all products")
void shouldBeAbleToGetAllProducts() {
// Mock data
Product product1 =
new Product(
1L,
"Google Pixel 5",
"MOBILE-GOOGLE-PIXEL-5",
new BigDecimal(12990.75),
100);
Product product2 =
new Product(2L, "Coca-Cola", "BEV-COCA-COLA", new BigDecimal(20.75), 150);
List<Product> productList = Arrays.asList(product1, product2);
List<Product> products =
Arrays.asList(
new Product(
1L,
"Google Pixel 5",
"MOBILE-GOOGLE-PIXEL-5",
new BigDecimal(12990.75),
100),
new Product(2L, "Coca-Cola", "BEV-COCA-COLA", new BigDecimal(20.75), 150));
Page<Product> productPage = new PageImpl<>(products);

int page = 0;
int limit = 10;
String sortBy = "name";
Pageable paging = PageRequest.of(page, limit);

// Mock repository method
when(productRepository.findAll()).thenReturn(productList);
when(productRepository.findAll((Pageable) Mockito.any())).thenReturn(productPage);

// Call service method
List<ProductResponse> result = productService.getAll();
ProductResponse result = productService.getAll(page, limit, sortBy);

// Assertions
assertEquals(2, result.size());
assertEquals("Google Pixel 5", result.get(0).name());
assertEquals("BEV-COCA-COLA", result.get(1).sku());
assertEquals(2, result.getProducts().size());
assertEquals("Google Pixel 5", result.getProducts().get(0).getName());
assertEquals("BEV-COCA-COLA", result.getProducts().get(1).getSku());
}

@Test
@DisplayName("should return empty list when no product found")
void shouldReturnEmptyListWhenNoProductFoundGetAllProducts() {
int page = 0;
int limit = 10;
String sortBy = "name";
Page<Product> productPage = Page.empty();

// Mock repository method returning empty list
when(productRepository.findAll()).thenReturn(Arrays.asList());
when(productRepository.findAll((Pageable) Mockito.any())).thenReturn(productPage);

// Call service method
List<ProductResponse> result = productService.getAll();
ProductResponse result = productService.getAll(page, limit, sortBy);

// Assertions
assertTrue(result.isEmpty());
assertTrue(result.getProducts().isEmpty());
}

@Test
Expand All @@ -78,11 +90,11 @@ void shouldBeAbleToGetProductBySku() {
// .thenReturn(Optional.of(product));

// Call service method
ProductResponse result = productService.getBySku("STATIONERY-PEN-BIC-BALLPOINT");
Product result = productService.getBySku("STATIONERY-PEN-BIC-BALLPOINT");

// Assertions
assertEquals("Pens", result.name());
assertEquals(new BigDecimal(14.99), result.price());
assertEquals("Pens", result.getName());
assertEquals(new BigDecimal(14.99), result.getPrice());
}

@Test
Expand Down

0 comments on commit 148ee54

Please sign in to comment.