Skip to content

Commit

Permalink
Merge pull request #650 from hngprojects/feature/dashboard
Browse files Browse the repository at this point in the history
feat: implement dashboard for products
  • Loading branch information
Am0du authored Aug 25, 2024
2 parents 50674db + f580dc4 commit 4fa36df
Show file tree
Hide file tree
Showing 11 changed files with 237 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package hng_java_boilerplate.dashboard.controller;

import hng_java_boilerplate.dashboard.service.DashboardService;
import hng_java_boilerplate.product.dto.GetProductsDTO;
import hng_java_boilerplate.product.dto.ProductCountDto;
import hng_java_boilerplate.product.dto.ProductDTO;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
@PreAuthorize("hasRole('SUPER_ADMIN')")
@RequestMapping("/api/v1/dashboards")
@Tag(name = "dashboard", description = "admin dashboard")
public class DashboardController {
private final DashboardService dashboardService;

@GetMapping("/products")
@Tag(name = "Products", description = "get all the products in the database")
public ResponseEntity<GetProductsDTO> getProducts() {
return ResponseEntity.ok(dashboardService.getAllProducts());
}

@GetMapping("/products/count")
@Tag(name = "Product count", description = "get the total counts of products")
public ResponseEntity<ProductCountDto> getProductCount() {
return ResponseEntity.ok(dashboardService.getProductCount());
}

@GetMapping("/products/{productId}")
@Tag(name = "product", description = "get a product by the product id")
public ResponseEntity<ProductDTO> getProductById(@PathVariable String productId) {
return ResponseEntity.ok(dashboardService.getProductById(productId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package hng_java_boilerplate.dashboard.service;

import hng_java_boilerplate.product.dto.GetProductsDTO;
import hng_java_boilerplate.product.dto.ProductCountDto;
import hng_java_boilerplate.product.dto.ProductDTO;
import hng_java_boilerplate.product.service.ProductService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class DashboardService {
private final ProductService productService;

public ProductCountDto getProductCount() {
return productService.getProductsCount();
}

public GetProductsDTO getAllProducts() {
return productService.getProducts();
}

public ProductDTO getProductById(String productId) {
return productService.getProductById(productId);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package hng_java_boilerplate.product.controller;

import hng_java_boilerplate.product.dto.ProductDTO;
import hng_java_boilerplate.product.dto.ProductSearchDTO;
import hng_java_boilerplate.product.entity.Product;
import hng_java_boilerplate.product.product_mapper.ProductMapper;
Expand All @@ -15,10 +16,7 @@
import org.springframework.data.domain.PageRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

import java.util.List;

Expand Down Expand Up @@ -69,4 +67,9 @@ public ResponseEntity<ProductSearchDTO> searchProducts(
productSearchDTO.setSuccess(true);
return new ResponseEntity<>(productSearchDTO, HttpStatus.OK);
}

@GetMapping("/{productId}")
public ResponseEntity<ProductDTO> getProductById(@PathVariable String productId) {
return ResponseEntity.ok(productService.getProductById(productId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package hng_java_boilerplate.product.dto;

import lombok.Builder;

import java.util.List;

@Builder
public record GetProductsDTO(int status_code, String status, List<ProductDTO> data) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package hng_java_boilerplate.product.dto;

import lombok.Builder;

@Builder
public record ProductCountDto(int status_code, String status, CountData data) {

@Builder
public record CountData(int count) {}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
package hng_java_boilerplate.product.dto;

import jakarta.persistence.Column;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class ProductDTO {
private String id;
private String name;
private String description;
private String category;
private double price;
private String imageUrl;
private String image_url;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public interface ProductMapper {
@Mapping(source = "name", target = "name")
@Mapping(source = "category", target = "category")
@Mapping(source = "description", target = "description")
@Mapping(source = "imageUrl", target = "imageUrl")
@Mapping(source = "imageUrl", target = "image_url")
ProductDTO toDTO(Product product);

default Page<ProductDTO> toDTOList(Page<Product> products) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package hng_java_boilerplate.product.service;

import hng_java_boilerplate.product.dto.GetProductsDTO;
import hng_java_boilerplate.product.dto.ProductCountDto;
import hng_java_boilerplate.product.dto.ProductDTO;
import hng_java_boilerplate.product.entity.Product;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
Expand All @@ -10,4 +13,7 @@ public interface ProductService {

//Method to Search for products with certain criteria, returns a list of products
Page<Product> productsSearch(String name, String category, Double minPrice, Double maxPrice, Pageable pageable);
ProductCountDto getProductsCount();
GetProductsDTO getProducts();
ProductDTO getProductById(String productId);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
package hng_java_boilerplate.product.service;
import hng_java_boilerplate.exception.NotFoundException;
import hng_java_boilerplate.product.dto.GetProductsDTO;
import hng_java_boilerplate.product.dto.ProductCountDto;
import hng_java_boilerplate.product.dto.ProductDTO;
import hng_java_boilerplate.product.entity.Product;
import hng_java_boilerplate.product.repository.ProductRepository;
import org.springframework.data.domain.Page;
Expand All @@ -7,10 +11,9 @@
import java.util.List;

@Service
public class ProductServiceImpl implements ProductService {

public class ProductServiceImpl implements ProductService{

private ProductRepository productRepository;
private final ProductRepository productRepository;

public ProductServiceImpl(ProductRepository productRepository) {
this.productRepository = productRepository;
Expand All @@ -20,4 +23,53 @@ public ProductServiceImpl(ProductRepository productRepository) {
public Page<Product> productsSearch(String name, String category, Double minPrice, Double maxPrice, Pageable pageable) {
return productRepository.searchProducts(name, category, minPrice, maxPrice, pageable);
}

@Override
public ProductCountDto getProductsCount() {
List<Product> products = productRepository.findAll();
int productsCount = products.size();

ProductCountDto.CountData data = new ProductCountDto
.CountData(productsCount);

return ProductCountDto
.builder()
.status_code(200)
.status("success")
.data(data)
.build();
}

@Override
public GetProductsDTO getProducts() {
List<Product> products = productRepository.findAll();

List<ProductDTO> productDTOS = products.stream()
.map(this::mapToProductDto)
.toList();

return GetProductsDTO.builder()
.status_code(200)
.status("success")
.data(productDTOS)
.build();
}

@Override
public ProductDTO getProductById(String productId) {
Product product = productRepository.findById(productId)
.orElseThrow(() -> new NotFoundException("product not found with id: " + productId));
return mapToProductDto(product);
}

private ProductDTO mapToProductDto(Product product) {
return ProductDTO.builder()
.id(product.getId())
.name(product.getName())
.price(product.getPrice())
.description(product.getDescription())
.category(product.getCategory())
.image_url(product.getImageUrl())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ private GetUserDto convertUserToGetUserDto(User user) {
.build();
}

// Save method for User entity
// Save method for User controller
@Override
public User save(User user) {
return userRepository.save(user);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package hng_java_boilerplate.product.service;

import hng_java_boilerplate.product.dto.GetProductsDTO;
import hng_java_boilerplate.product.dto.ProductCountDto;
import hng_java_boilerplate.product.dto.ProductDTO;
import hng_java_boilerplate.product.entity.Product;
import hng_java_boilerplate.product.repository.ProductRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

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

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class ProductServiceImplTest {
@Mock
private ProductRepository productRepository;
@InjectMocks
private ProductServiceImpl underTest;

private Product product;

@BeforeEach
void setUp() {
product = new Product();
product.setId("prod-id");
product.setName("product");
product.setDescription("prod-desc");
product.setCategory("prod-cat");
}


@Test
void shouldGetProductsCount() {
when(productRepository.findAll()).thenReturn(List.of(product));

ProductCountDto response = underTest.getProductsCount();

assertThat(response.status_code()).isEqualTo(200);
assertThat(response.status()).isEqualTo("success");
assertThat(response.data()).hasFieldOrPropertyWithValue("count", 1);

verify(productRepository).findAll();
}

@Test
void getProducts() {
when(productRepository.findAll()).thenReturn(List.of(product));
GetProductsDTO products = underTest.getProducts();

assertThat(products.status_code()).isEqualTo(200);
assertThat(products.status()).isEqualTo("success");
assertThat(products.data()).hasSize(1);
assertThat(products.data().get(0)).hasFieldOrPropertyWithValue("id", product.getId());
assertThat(products.data().get(0)).hasFieldOrPropertyWithValue("name", product.getName());

verify(productRepository).findAll();
}

@Test
void getProductById() {
when(productRepository.findById("prod-id")).thenReturn(Optional.of(product));

ProductDTO response = underTest.getProductById("prod-id");

assertThat(response.getId()).isEqualTo(product.getId());
assertThat(response.getName()).isEqualTo(product.getName());
assertThat(response.getDescription()).isEqualTo(product.getDescription());

verify(productRepository).findById("prod-id");
}
}

0 comments on commit 4fa36df

Please sign in to comment.