diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/product/ProductController.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/product/ProductController.java new file mode 100644 index 0000000000000000000000000000000000000000..2b44cec707cf6676ddc34ec650f7c6797db7437f --- /dev/null +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/controller/product/ProductController.java @@ -0,0 +1,121 @@ +package ntnu.idatt2016.v233.SmartMat.controller.product; + +import lombok.AllArgsConstructor; +import ntnu.idatt2016.v233.SmartMat.dto.request.ProductRequest; +import ntnu.idatt2016.v233.SmartMat.entity.product.Product; +import ntnu.idatt2016.v233.SmartMat.service.product.ProductService; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Optional; + +/** + * The product controller is responsible for handling requests related to products. + * It uses the product service to handle the requests. + * @version 1.0 + * @Author Birk + * @since 26.04.2023 + */ +@RestController +@AllArgsConstructor +@RequestMapping("/api/product") +public class ProductController { + + private final ProductService productService; + + /** + * Creates a product if it does not already exist. + * @param productRequest The product to be registered. + * @return The product that was registered. + */ + @PostMapping("/") + public ResponseEntity<Product> createProduct(@RequestBody ProductRequest productRequest) { + Product product = Product.builder() + .ean(productRequest.ean()) + .name(productRequest.name()) + .description(productRequest.description()) + .url(productRequest.image()) + .build(); + + if(productService.getProductById(productRequest.ean()).isPresent()) + return ResponseEntity.status(409).build(); + + + Optional<List<String>> volumeUnit = productService.getProductVolume(productRequest.ean()); + + if(volumeUnit.isPresent()){ + product.setUnit(volumeUnit.get().get(1)); + product.setAmount(Double.parseDouble(volumeUnit.get().get(0))); + } + + productService.saveProduct(product); + return ResponseEntity.ok(product); + } + + /** + * Returns a product with the given ean. + * @param ean The ean of the product to be returned. + * @return The product with the given ean. + */ + @GetMapping("ean/{ean}") + public ResponseEntity<Product> getProduct(@PathVariable long ean) { + return productService.getProductById(ean) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()); + } + + /** + * Returns all products in the database. + * @return All products in the database. + */ + @GetMapping("/") + public ResponseEntity<List<Product>> getAllProducts() { + return ResponseEntity.ok(productService.getAllProducts()); + } + + /** + * Deletes a product with the given ean. + * @param ean The ean of the product to be deleted. + * @return The product that was deleted. + */ + @DeleteMapping("ean/{ean}") + public ResponseEntity<Product> deleteProduct(@PathVariable long ean) { + Optional<Product> product = productService.getProductById(ean); + if(product.isPresent()) { + productService.deleteProductById(product.get().getEan()); + return ResponseEntity.ok(product.get()); + } + return ResponseEntity.notFound().build(); + } + + + /** + * Updates a product with the given ean. + * @param ean The ean of the product to be updated. + * @param productRequest The product to be updated. + * @return The product that was updated. + */ + @PutMapping("ean/{ean}") + public ResponseEntity<Product> updateProduct(@PathVariable long ean, @RequestBody ProductRequest productRequest) { + Optional<Product> product = productService.getProductById(ean); + if(product.isPresent()) { + product.get().setName(productRequest.name()); + product.get().setDescription(productRequest.description()); + product.get().setUrl(productRequest.image()); + Optional<List<String>> volumeUnit = productService.getProductVolume(productRequest.ean()); + + if(volumeUnit.isPresent()){ + product.get().setUnit(volumeUnit.get().get(1)); + product.get().setAmount(Double.parseDouble(volumeUnit.get().get(0))); + } + + + productService.updateProduct(product.get()); + return ResponseEntity.ok(product.get()); + } + return ResponseEntity.notFound().build(); + } + + +} diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/dto/request/ProductRequest.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/dto/request/ProductRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..109aa338e4320b8354bc1fbffba6765e8b3d4452 --- /dev/null +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/dto/request/ProductRequest.java @@ -0,0 +1,7 @@ +package ntnu.idatt2016.v233.SmartMat.dto.request; + +import lombok.Builder; + +@Builder +public record ProductRequest(long ean, String name, String description, String image, double price) { +} diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/product/Product.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/product/Product.java index 7c2f1bc6613d46d92c7da35fa185791267a6005d..ba2a2a1ebe3350f02a7532b6e8fb48f481d6f82a 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/product/Product.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/entity/product/Product.java @@ -35,6 +35,7 @@ public class Product{ @Column(name = "description") String description; + @ManyToOne @JoinColumn(name = "category_name") @JsonIgnoreProperties({"products"}) diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/service/product/ProductService.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/service/product/ProductService.java index e668812f2caab46bcd4e121be3c6cc979bf517a4..1e8c53a791c6f2a19dd19ca0ed993324b87042e2 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/service/product/ProductService.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/service/product/ProductService.java @@ -76,7 +76,7 @@ public class ProductService { * @param id The id of the product to get the volume from * @return The volume of the product, if it exists */ - public Optional<String> getProductVolume(long id) { + public Optional<List<String>> getProductVolume(long id) { if(productRepository.findById(id).isEmpty()) return Optional.empty(); diff --git a/src/main/java/ntnu/idatt2016/v233/SmartMat/util/ProductUtil.java b/src/main/java/ntnu/idatt2016/v233/SmartMat/util/ProductUtil.java index 6aa4a7062447f2d514ee41d811716d7ed8a7056a..c77016523dfd1161343e576eb1ff5493963f951e 100644 --- a/src/main/java/ntnu/idatt2016/v233/SmartMat/util/ProductUtil.java +++ b/src/main/java/ntnu/idatt2016/v233/SmartMat/util/ProductUtil.java @@ -5,6 +5,7 @@ import ntnu.idatt2016.v233.SmartMat.entity.product.Product; import java.util.Arrays; import java.util.List; import java.util.Optional; +import java.util.regex.Pattern; /** * Utility class for products @@ -26,7 +27,7 @@ public class ProductUtil { * @param product The product to get the volume from * @return The volume of the product, if it exists */ - public static Optional<String> getVolumeFromProduct(Product product) { + public static Optional<List<String>> getVolumeFromProduct(Product product) { for (String desc : Arrays.asList(product.getName(), product.getDescription())) { List<String> words = List.of(desc.split(" ")); if (words.size() > 1) { @@ -34,16 +35,25 @@ public class ProductUtil { for (String unit : VOLUME_UNITS) { int i = words.indexOf(unit); if (i != -1) { - return Optional.of(words.get(i - 1) + unit); + return Optional.of(List.of(words.get(i - 1), unit)); } } - volume = words.stream().filter(word -> Arrays.stream(VOLUME_UNITS).anyMatch(word::contains)) + volume = words.stream().map(word -> Arrays.stream(VOLUME_UNITS).map(unit -> { + int index = word.indexOf(unit); + if (index == -1) { + if (!Pattern.matches("[a-zA-Z]+", word) && ProductUtil.hasNumbers(word)) { + return word; + } + return ""; + } + return word.substring(0, index) + " " + word.substring(index); + }).findAny().orElse("")) .filter(ProductUtil::hasNumbers) .findAny() .orElse(""); if (!volume.equals("")){ - return Optional.of(volume); + return Optional.of(List.of(volume.split(" "))); } } } diff --git a/src/test/java/ntnu/idatt2016/v233/SmartMat/controller/product/ProductControllerTest.java b/src/test/java/ntnu/idatt2016/v233/SmartMat/controller/product/ProductControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ab69fc29600aa429ad1c69d340fe71d4388a4555 --- /dev/null +++ b/src/test/java/ntnu/idatt2016/v233/SmartMat/controller/product/ProductControllerTest.java @@ -0,0 +1,108 @@ +package ntnu.idatt2016.v233.SmartMat.controller.product; + +import ntnu.idatt2016.v233.SmartMat.dto.request.ProductRequest; +import ntnu.idatt2016.v233.SmartMat.entity.product.Product; +import ntnu.idatt2016.v233.SmartMat.service.product.ProductService; +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 org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import java.util.List; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class ProductControllerTest { + + @Mock + private ProductService productService; + + @InjectMocks + private ProductController productController; + + @Test + public void createProduct_productDoesNotExist_returnsCreatedProduct() { + // Arrange + ProductRequest productRequest = ProductRequest.builder() + .ean(123L) + .name("Test Product") + .description("A test product") + .image("http://test.com/image.jpg") + .build(); + + when(productService.getProductById(123L)).thenReturn(Optional.empty()); + when(productService.getProductVolume(123L)).thenReturn(Optional.of(List.of("1", "kg"))); + + // Act + ResponseEntity<Product> response = productController.createProduct(productRequest); + Product product = response.getBody(); + + // Assert + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(123L, product.getEan()); + assertEquals("Test Product", product.getName()); + assertEquals("A test product", product.getDescription()); + assertEquals("http://test.com/image.jpg", product.getUrl()); + assertEquals("kg", product.getUnit()); + assertEquals(1.0, product.getAmount()); + } + + @Test + public void createProduct_productExists_returnsConflict() { + // Arrange + ProductRequest productRequest = ProductRequest.builder() + .ean(123L) + .name("Test Product") + .description("A test product") + .image("http://test.com/image.jpg") + .build(); + + when(productService.getProductById(123L)).thenReturn(Optional.of(new Product())); + + // Act + ResponseEntity<Product> response = productController.createProduct(productRequest); + + // Assert + assertEquals(HttpStatus.CONFLICT, response.getStatusCode()); + assertFalse(response.hasBody()); + } + + @Test + public void getProduct_productExists_returnsProduct() { + // Arrange + Product product = new Product(); + product.setEan(123L); + product.setName("Test Product"); + + when(productService.getProductById(123L)).thenReturn(Optional.of(product)); + + // Act + ResponseEntity<Product> response = productController.getProduct(123L); + Product result = response.getBody(); + + // Assert + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertEquals(123L, result.getEan()); + assertEquals("Test Product", result.getName()); + } + + @Test + public void getProduct_productDoesNotExist_returnsNotFound() { + // Arrange + when(productService.getProductById(123L)).thenReturn(Optional.empty()); + + // Act + ResponseEntity<Product> response = productController.getProduct(123L); + + // Assert + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + assertFalse(response.hasBody()); + } +} \ No newline at end of file diff --git a/src/test/java/ntnu/idatt2016/v233/SmartMat/service/product/ProductServiceTest.java b/src/test/java/ntnu/idatt2016/v233/SmartMat/service/product/ProductServiceTest.java index e73b0826625ecf46065283f6f74c3a2840323261..c9a580871f06b0dc6a7f0d5c01340c3023d05292 100644 --- a/src/test/java/ntnu/idatt2016/v233/SmartMat/service/product/ProductServiceTest.java +++ b/src/test/java/ntnu/idatt2016/v233/SmartMat/service/product/ProductServiceTest.java @@ -133,9 +133,9 @@ public class ProductServiceTest { ProductService productService = new ProductService(mockRepository); // Verify that the service returns the correct volume - Optional<String> returnedVolume = productService.getProductVolume(productId); + Optional<List<String>> returnedVolume = productService.getProductVolume(productId); assertTrue(returnedVolume.isPresent()); - assertEquals("500ml", returnedVolume.get()); + assertEquals(List.of("500", "ml"), returnedVolume.get()); } @Test @@ -150,7 +150,7 @@ public class ProductServiceTest { ProductService productService = new ProductService(mockRepository); // Verify that the service returns an empty optional - Optional<String> returnedVolume = productService.getProductVolume(productId); + Optional<List<String>> returnedVolume = productService.getProductVolume(productId); assertTrue(returnedVolume.isEmpty()); } diff --git a/src/test/java/ntnu/idatt2016/v233/SmartMat/util/ProductUtilTest.java b/src/test/java/ntnu/idatt2016/v233/SmartMat/util/ProductUtilTest.java index 2eb3f4d6d5d323351c472703aa24d61351d9d214..0164ae29d869b4a85b47dab4cdd82d95c78d8b1e 100644 --- a/src/test/java/ntnu/idatt2016/v233/SmartMat/util/ProductUtilTest.java +++ b/src/test/java/ntnu/idatt2016/v233/SmartMat/util/ProductUtilTest.java @@ -4,6 +4,8 @@ import ntnu.idatt2016.v233.SmartMat.entity.product.Product; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.util.List; + import static org.junit.jupiter.api.Assertions.*; class ProductUtilTest { @@ -20,7 +22,7 @@ class ProductUtilTest { @Test void getVolumeFromProduct() { - assertEquals("24x500ml", ProductUtil.getVolumeFromProduct(product).get()); + assertEquals(List.of("24x500", "ml"), ProductUtil.getVolumeFromProduct(product).get()); this.product = Product.builder() .ean(123456789) @@ -28,7 +30,7 @@ class ProductUtilTest { .description("Pepsi Original 24x500 ml") .build(); - assertEquals("24x500ml", ProductUtil.getVolumeFromProduct(product).get()); + assertEquals(List.of("24x500", "ml"), ProductUtil.getVolumeFromProduct(product).get()); this.product = Product.builder() .ean(123456789) @@ -37,7 +39,7 @@ class ProductUtilTest { .build(); - assertEquals("24x500ml", ProductUtil.getVolumeFromProduct(product).get()); + assertEquals(List.of("24x500", "ml"), ProductUtil.getVolumeFromProduct(product).get()); this.product = Product.builder() .ean(123456789) @@ -46,6 +48,6 @@ class ProductUtilTest { .build(); - assertEquals("24x500ml", ProductUtil.getVolumeFromProduct(product).get()); + assertEquals(List.of("24x500", "ml"), ProductUtil.getVolumeFromProduct(product).get()); } } \ No newline at end of file