diff --git a/api-gateway/src/main/resources/static/index.html b/api-gateway/src/main/resources/static/index.html
index 34122ec4c2..046bfcb15d 100755
--- a/api-gateway/src/main/resources/static/index.html
+++ b/api-gateway/src/main/resources/static/index.html
@@ -150,6 +150,12 @@
+
+
+
+
+
+
diff --git a/api-gateway/src/main/resources/static/scripts/app.js b/api-gateway/src/main/resources/static/scripts/app.js
index e98be79bda..19b24d4a77 100644
--- a/api-gateway/src/main/resources/static/scripts/app.js
+++ b/api-gateway/src/main/resources/static/scripts/app.js
@@ -10,9 +10,9 @@ const whiteList = new Set([
/* App Module */
const petClinicApp = angular.module('petClinicApp', [
'ui.router', 'layoutNav', 'layoutFooter', 'layoutWelcome', 'ownerList', 'ownerDetails', 'ownerForm', 'ownerRegister', 'petRegister', 'petForm'
- , 'visits', 'vetList','vetForm','vetDetails', 'visitList', 'billForm', 'billUpdateForm', 'loginForm', 'rolesDetails', 'signupForm',
- 'billDetails', 'billsByOwnerId', 'billHistory','billsByVetId','inventoryList', 'inventoryForm', 'productForm','inventoryProductList', 'inventoryUpdateForm', 'productUpdateForm'
- , 'verification' , 'adminPanel','resetPwdForm','forgotPwdForm','petTypeList']);
+ , 'visits', 'vetList','vetForm','vetDetails', 'visitList', 'billForm', 'billUpdateForm', 'loginForm', 'rolesDetails', 'signupForm', 'productDetailsInfo',
+ 'billDetails', 'billsByOwnerId', 'billHistory','billsByVetId','inventoryList', 'inventoryForm', 'productForm','inventoryProductList', 'inventoryUpdateForm', 'productUpdateForm',
+ 'verification' , 'adminPanel','resetPwdForm','forgotPwdForm','petTypeList']);
diff --git a/api-gateway/src/main/resources/static/scripts/inventory-list/inventory-list.template.html b/api-gateway/src/main/resources/static/scripts/inventory-list/inventory-list.template.html
index 8fbe712eb3..7434da746e 100644
--- a/api-gateway/src/main/resources/static/scripts/inventory-list/inventory-list.template.html
+++ b/api-gateway/src/main/resources/static/scripts/inventory-list/inventory-list.template.html
@@ -14,7 +14,7 @@
Inventory
Name |
Type |
Description |
- Update |
+
diff --git a/api-gateway/src/main/resources/static/scripts/inventory-product-list/inventory-product-list.controller.js b/api-gateway/src/main/resources/static/scripts/inventory-product-list/inventory-product-list.controller.js
index da64adf7ce..837df5dc3f 100644
--- a/api-gateway/src/main/resources/static/scripts/inventory-product-list/inventory-product-list.controller.js
+++ b/api-gateway/src/main/resources/static/scripts/inventory-product-list/inventory-product-list.controller.js
@@ -13,6 +13,8 @@ angular.module('inventoryProductList')
// Handle if inventory is empty
console.log("The inventory is empty!");
}
+
+
}).catch(function (error) {
if (error.status === 404) {
console.clear()
@@ -24,7 +26,7 @@ angular.module('inventoryProductList')
});
- $scope.deleteProduct = function (product) {
+ $scope.deleteProduct = function (product) {
let varIsConf = confirm('Are you sure you want to remove this product?');
if (varIsConf) {
diff --git a/api-gateway/src/main/resources/static/scripts/inventory-product-list/inventory-product-list.template.html b/api-gateway/src/main/resources/static/scripts/inventory-product-list/inventory-product-list.template.html
index 5c2bde164c..a7aa4e00f6 100644
--- a/api-gateway/src/main/resources/static/scripts/inventory-product-list/inventory-product-list.template.html
+++ b/api-gateway/src/main/resources/static/scripts/inventory-product-list/inventory-product-list.template.html
@@ -52,14 +52,27 @@ Inventory Products
|
{{product.productName}} |
- {{product.productId}} |
+ {{product.productId}} |
{{product.productQuantity}} |
{{product.productPrice}} |
{{product.productDescription}} |
{{product.productSalePrice}} |
+
+
+
+ Get Product Details
+
+
+ |
+
+
-
+
|
+
+
+
+
Delete |
|
diff --git a/api-gateway/src/main/resources/static/scripts/product-details-info/product-details-info.component.js b/api-gateway/src/main/resources/static/scripts/product-details-info/product-details-info.component.js
new file mode 100644
index 0000000000..462a4e59a6
--- /dev/null
+++ b/api-gateway/src/main/resources/static/scripts/product-details-info/product-details-info.component.js
@@ -0,0 +1,7 @@
+'use strict';
+
+angular.module('productDetailsInfo')
+ .component('productDetailsInfo', {
+ templateUrl: 'scripts/product-details-info/product-details-info.template.html',
+ controller: 'ProductDetailsInfoController'
+ });
diff --git a/api-gateway/src/main/resources/static/scripts/product-details-info/product-details-info.controller.js b/api-gateway/src/main/resources/static/scripts/product-details-info/product-details-info.controller.js
new file mode 100644
index 0000000000..a08c546281
--- /dev/null
+++ b/api-gateway/src/main/resources/static/scripts/product-details-info/product-details-info.controller.js
@@ -0,0 +1,20 @@
+angular.module('productDetailsInfo')
+ .controller('ProductDetailsInfoController', ["$http", '$state', '$stateParams', '$scope', 'InventoryService', function ($http, $state, $stateParams, $scope, InventoryService) {
+ var self = this;
+ self.product = {}; // Initialize self.product
+ var inventoryId = InventoryService.getInventoryId();
+ var productId = $stateParams.productId;
+
+ $http.get('/api/gateway/inventory/' + inventoryId + '/products/' + productId)
+ .then(function (resp) {
+ // Handle the response data for the specific product
+ var product = resp.data;
+ console.log("Product found:", product);
+ self.product = product; // Update the product data in your controller
+ })
+ .catch(function (error) {
+ // Handle errors if the product is not found or other issues
+ console.error("Error fetching product:", error);
+ });
+
+ }]);
\ No newline at end of file
diff --git a/api-gateway/src/main/resources/static/scripts/product-details-info/product-details-info.js b/api-gateway/src/main/resources/static/scripts/product-details-info/product-details-info.js
new file mode 100644
index 0000000000..8610df5c6d
--- /dev/null
+++ b/api-gateway/src/main/resources/static/scripts/product-details-info/product-details-info.js
@@ -0,0 +1,17 @@
+'use strict';
+
+angular.module('productDetailsInfo', ['ui.router'])
+ .config(['$stateProvider', function ($stateProvider) {
+ $stateProvider
+ .state('productDetails', {
+ parent: 'app',
+ url: '/inventory/:inventoryId/products/:productId',
+ template: ''
+ })
+
+ .state('products', {
+ parent: 'app',
+ url: '/inventory/:inventoryId/products',
+ template: ''
+ })
+ }]);
\ No newline at end of file
diff --git a/api-gateway/src/main/resources/static/scripts/product-details-info/product-details-info.template.html b/api-gateway/src/main/resources/static/scripts/product-details-info/product-details-info.template.html
new file mode 100644
index 0000000000..bae3b013ef
--- /dev/null
+++ b/api-gateway/src/main/resources/static/scripts/product-details-info/product-details-info.template.html
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
Inventory Id:
+
+
+
{{$ctrl.product.inventoryId}}
+
+
+
+
+
+
Product Id:
+
+
+
{{$ctrl.product.productId}}
+
+
+
+
+
+
Product Description:
+
+
+
{{$ctrl.product.productDescription}}
+
+
+
+
+
+
Product Price:
+
+
+
{{$ctrl.product.productPrice}}
+
+
+
+
+
+
+
+
+
Product SalePrice:
+
+
+
{{$ctrl.product.productSalePrice}}
+
+
+
+
+
+
+
+
Product Quantity:
+
+
+
{{$ctrl.product.productQuantity}}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/api-gateway/src/main/resources/static/scripts/product-details/product-details.component.js b/api-gateway/src/main/resources/static/scripts/product-details/product-details.component.js
new file mode 100644
index 0000000000..f9e7ae5e7a
--- /dev/null
+++ b/api-gateway/src/main/resources/static/scripts/product-details/product-details.component.js
@@ -0,0 +1,7 @@
+'use strict';
+
+angular.module('productDetails')
+ .component('productDetails', {
+ templateUrl: 'scripts/product-details/product-details.template.html',
+ controller: 'ProductDetailsController'
+ });
diff --git a/api-gateway/src/main/resources/static/scripts/product-details/product-details.controller.js b/api-gateway/src/main/resources/static/scripts/product-details/product-details.controller.js
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/api-gateway/src/main/resources/static/scripts/product-details/product-details.js b/api-gateway/src/main/resources/static/scripts/product-details/product-details.js
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/api-gateway/src/main/resources/static/scripts/product-details/product-details.template.html b/api-gateway/src/main/resources/static/scripts/product-details/product-details.template.html
new file mode 100644
index 0000000000..e1d6db055f
--- /dev/null
+++ b/api-gateway/src/main/resources/static/scripts/product-details/product-details.template.html
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
Inventory Id:
+
+
+
{{$ctrl.product.inventoryId}}
+
+
+
+
+
+
Product Id:
+
+
+
{{$ctrl.product.productId}}
+
+
+
+
+
+
Product Description:
+
+
+
{{$ctrl.product.productDescription}}
+
+
+
+
+
+
Product Price:
+
+
+
{{$ctrl.product.productPrice}}
+
+
+
+
+
+
+
+
+
Product SalePrice:
+
+
+
{{$ctrl.product.productSalePrice}}
+
+
+
+
+
+
+
+
Product Quantity:
+
+
+
{{$ctrl.product.productQuantity}}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/api-gateway/src/test/java/com/petclinic/bffapigateway/presentationlayer/ApiGatewayControllerTest.java b/api-gateway/src/test/java/com/petclinic/bffapigateway/presentationlayer/ApiGatewayControllerTest.java
index 4613249181..9596947798 100755
--- a/api-gateway/src/test/java/com/petclinic/bffapigateway/presentationlayer/ApiGatewayControllerTest.java
+++ b/api-gateway/src/test/java/com/petclinic/bffapigateway/presentationlayer/ApiGatewayControllerTest.java
@@ -2769,8 +2769,36 @@ void updateInventory_withValidValue_shouldSucceed() {
@Test
- void getInventoryByInventoryId_ValidId_shouldSucceed() {
+ void GetProductByInventoryIdAndProductId_InsideInventory() {
+ ProductResponseDTO productResponseDTO = buildProductDTO();
+ when(inventoryServiceClient.getProductByProductIdInInventory(productResponseDTO.getInventoryId(), productResponseDTO.getProductId()))
+ .thenReturn(Mono.just(productResponseDTO));
+ client.get()
+ .uri("/api/gateway/inventory/{inventoryId}/products/{productId}", productResponseDTO.getInventoryId(), productResponseDTO.getProductId())
+ .accept(MediaType.APPLICATION_JSON)
+ .exchange()
+ .expectStatus().isOk()
+ .expectBody(ProductResponseDTO.class)
+ .value(dto -> {
+ assertNotNull(dto);
+ assertEquals(productResponseDTO.getInventoryId(), dto.getInventoryId());
+ assertEquals(productResponseDTO.getProductId(), dto.getProductId());
+ assertEquals(productResponseDTO.getProductName(), dto.getProductName());
+ assertEquals(productResponseDTO.getProductDescription(), dto.getProductDescription());
+ assertEquals(productResponseDTO.getProductPrice(), dto.getProductPrice());
+ assertEquals(productResponseDTO.getProductQuantity(), dto.getProductQuantity());
+ assertEquals(productResponseDTO.getProductSalePrice(), dto.getProductSalePrice());
+
+ });
+
+ verify(inventoryServiceClient, times(1))
+ .getProductByProductIdInInventory(productResponseDTO.getInventoryId(), productResponseDTO.getProductId());
+ }
+
+
+ @Test
+ void getInventoryByInventoryId_ValidId_shouldSucceed() {
String validInventoryId = "inventoryId_1";
InventoryResponseDTO inventoryResponseDTO = InventoryResponseDTO.builder()
.inventoryId(validInventoryId)
@@ -2987,6 +3015,9 @@ void testAddProductToInventory_InvalidInventoryId_ShouldReturnNotFoundException(
.addProductToInventory(eq(requestDTO), eq("invalidInventoryId"));
}
+
+
+
private ProductResponseDTO buildProductDTO(){
return ProductResponseDTO.builder()
.id("1")
diff --git a/inventory-service/src/main/java/com/petclinic/inventoryservice/businesslayer/ProductInventoryServiceImpl.java b/inventory-service/src/main/java/com/petclinic/inventoryservice/businesslayer/ProductInventoryServiceImpl.java
index 3ea908b267..4acfdf8675 100644
--- a/inventory-service/src/main/java/com/petclinic/inventoryservice/businesslayer/ProductInventoryServiceImpl.java
+++ b/inventory-service/src/main/java/com/petclinic/inventoryservice/businesslayer/ProductInventoryServiceImpl.java
@@ -313,21 +313,10 @@ public Mono deleteInventoryByInventoryId(String inventoryId) {
@Override
public Mono getProductByProductIdInInventory(String inventoryId, String productId) {
- if(inventoryId == null ){
- return Mono.error(new NotFoundException("Inventory id not found:" + inventoryId ));
-
- }
- else if (productId == null) {
- return Mono.error(new NotFoundException("product id not found:" + productId ));
- }
-
-
return productRepository
.findProductByInventoryIdAndProductId(inventoryId, productId)
.map(EntityDTOUtil::toProductResponseDTO)
.switchIfEmpty(Mono.error(new NotFoundException("Inventory id:" + inventoryId + "and product:" + productId + "are not found")));
-
-
}
//delete all products and delete all inventory
diff --git a/inventory-service/src/test/java/com/petclinic/inventoryservice/businesslayer/ProductInventoryServiceUnitTest.java b/inventory-service/src/test/java/com/petclinic/inventoryservice/businesslayer/ProductInventoryServiceUnitTest.java
index 2ff91d77b4..8f7a13bcb8 100644
--- a/inventory-service/src/test/java/com/petclinic/inventoryservice/businesslayer/ProductInventoryServiceUnitTest.java
+++ b/inventory-service/src/test/java/com/petclinic/inventoryservice/businesslayer/ProductInventoryServiceUnitTest.java
@@ -226,6 +226,44 @@ public void deleteProduct_validProductAndInventory_ShouldSucceed() {
+ @Test
+ void getProductsByInventoryId_andProductId_withValidFields_shouldSucceed() {
+ String inventoryId = "1";
+ String productId = "12345";
+
+ when(productRepository
+ .findProductByInventoryIdAndProductId(inventoryId, productId))
+ .thenReturn(Mono.just(product));
+
+ Mono productResponseDTOMono = productInventoryService
+ .getProductByProductIdInInventory(inventoryId, productId);
+
+ StepVerifier
+ .create(productResponseDTOMono)
+ .expectNextCount(1)
+ .verifyComplete();
+ }
+
+
+ @Test
+ void getProductsByInventoryId_andProductId_withInvalidFields_shouldReturnEmptyFlux() {
+ String invalidInventoryId = "999"; // Invalid inventory ID
+ String invalidProductId = "invalid123"; // Invalid product ID
+
+ when(productRepository
+ .findProductByInventoryIdAndProductId(invalidInventoryId, invalidProductId))
+ .thenReturn(Mono.empty());
+
+ Mono productResponseDTOMono = productInventoryService
+ .getProductByProductIdInInventory(invalidInventoryId, invalidProductId);
+
+
+ StepVerifier.create(productResponseDTOMono)
+ .expectError(NotFoundException.class)
+ .verify();
+ }
+
+
@Test
void getInventoryByInventoryId_ValidId_shouldSucceed(){
String inventoryId ="1";
@@ -255,6 +293,7 @@ void getInventoryByInventoryId_ValidId_shouldSucceed(){
+
@Test
void GetInventoryByInvalid_InventoryId_throwNotFound() {
diff --git a/inventory-service/src/test/java/com/petclinic/inventoryservice/datalayer/Product/ProductRepositoryTest.java b/inventory-service/src/test/java/com/petclinic/inventoryservice/datalayer/Product/ProductRepositoryTest.java
index d97e108d1d..7db3e80114 100644
--- a/inventory-service/src/test/java/com/petclinic/inventoryservice/datalayer/Product/ProductRepositoryTest.java
+++ b/inventory-service/src/test/java/com/petclinic/inventoryservice/datalayer/Product/ProductRepositoryTest.java
@@ -106,6 +106,21 @@ public void shouldGetTwoProductsByInventoryIdAndProductName(){
.expectNextCount(2)
.verifyComplete();
}
+
+
+ @Test
+ public void shouldGetSingleProductByProductIdAndInventoryId(){
+ StepVerifier
+ .create(productRepository.findProductByInventoryIdAndProductId(
+ product1.getInventoryId(),
+ product1.getProductId()
+ ))
+ .expectNextCount(1)
+ .verifyComplete();
+ }
+
+
+
@Test
public void ShouldDeleteAllProducts() {
// Arrange
diff --git a/inventory-service/src/test/java/com/petclinic/inventoryservice/presentationlayer/InventoryControllerIntegrationTest.java b/inventory-service/src/test/java/com/petclinic/inventoryservice/presentationlayer/InventoryControllerIntegrationTest.java
index 4df45317b0..ed53e71422 100644
--- a/inventory-service/src/test/java/com/petclinic/inventoryservice/presentationlayer/InventoryControllerIntegrationTest.java
+++ b/inventory-service/src/test/java/com/petclinic/inventoryservice/presentationlayer/InventoryControllerIntegrationTest.java
@@ -52,10 +52,13 @@ class InventoryControllerIntegrationTest {
.type("Sales")
.build();
+
+
Inventory inventory1 = buildInventory("inventoryId_3", "internal", inventoryType1.getType(),"inventoryDescription_3");
Inventory inventory2 = buildInventory("inventoryId_4", "sales", inventoryType2.getType() ,"inventoryDescription_4");
+ Product product1 = buildProduct("productId1", "inventoryId_3" , "drug" , "drug", 18.00, 30.00, 3);
@BeforeEach
public void dbSetup() {
@@ -461,6 +464,43 @@ public void updateInventory_withValidId_ShouldSucceed() {
});
}
+
+
+ @Test
+ void getProductInInventory_withInvalidInventoryId_invalidProductId_throwsNotFoundException() {
+ String invalidInventoryId = "123";
+ String invalidProductId = "897";
+
+ webTestClient.get()
+ .uri("/inventory/{inventoryId}/products/{productId}",
+ invalidInventoryId, invalidProductId)
+ .accept(MediaType.APPLICATION_JSON)
+ .exchange()
+ .expectStatus().isNotFound()
+ .expectHeader().contentType(MediaType.APPLICATION_JSON)
+ .expectBody()
+ .jsonPath("$.message").isEqualTo("Inventory id:" + invalidInventoryId + "and product:" + invalidProductId + "are not found");
+ }
+
+
+
+ @Test
+ public void getProductInInventory_byProductId_ShouldSucceed() {
+
+
+ webTestClient.get()
+ .uri("/inventory/{inventoryId}/products/{productId}", "1", "123F567C9")
+ .accept(MediaType.APPLICATION_JSON)
+ .exchange()
+ .expectStatus().isOk()
+ .expectHeader().contentType(MediaType.APPLICATION_JSON)
+ .expectBody()
+ .jsonPath("$.productId").isEqualTo("123F567C9");
+ }
+
+
+
+
@Test
public void updateInventory_withInvalidInventoryId() {
String InvalidInventoryId = "inventoryId_234";
@@ -538,13 +578,14 @@ private Inventory buildInventory(String inventoryId, String name, String invento
.build();
}
- private Product buildProduct(String productId, String inventoryId, String productName, String productDescription, Double productPrice, Integer productQuantity) {
+ private Product buildProduct(String productId, String inventoryId, String productName, String productDescription, Double productPrice, Double SalePrice, Integer productQuantity) {
return Product.builder()
.productId(productId)
.inventoryId(inventoryId)
.productName(productName)
.productDescription(productDescription)
.productPrice(productPrice)
+ .productSalePrice(SalePrice)
.productQuantity(productQuantity)
.build();
}
diff --git a/inventory-service/src/test/java/com/petclinic/inventoryservice/presentationlayer/InventoryControllerUnitTest.java b/inventory-service/src/test/java/com/petclinic/inventoryservice/presentationlayer/InventoryControllerUnitTest.java
index d0153f1051..787d312547 100644
--- a/inventory-service/src/test/java/com/petclinic/inventoryservice/presentationlayer/InventoryControllerUnitTest.java
+++ b/inventory-service/src/test/java/com/petclinic/inventoryservice/presentationlayer/InventoryControllerUnitTest.java
@@ -14,6 +14,7 @@
import org.springframework.test.web.reactive.server.WebTestClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
import java.util.Arrays;
import java.util.List;
@@ -303,6 +304,10 @@ void getProductsInInventory_withValidId_andValidProductPrice_shouldSucceed() {
});
}
+
+
+
+
@Test
void getInventoryByInventoryId_ValidIdShouldSucceed(){
//arrange
@@ -339,6 +344,74 @@ void getInventoryByInventoryId_ValidIdShouldSucceed(){
+
+
+
+ @Test
+ void getProductsInInventoryByInventoryIdAndProductId_ValidRequest_ShouldSucceed() {
+ // Arrange
+ String inventoryId = "1";
+ String productId = "123F567C9";
+ ProductResponseDTO productResponseDTO = ProductResponseDTO.builder()
+ .id("1")
+ .inventoryId(inventoryId)
+ .productId(productId)
+ .productName("Benzodiazepines")
+ .productDescription("Sedative Medication")
+ .productPrice(100.00)
+ .productQuantity(10)
+ .productSalePrice(15.99)
+ .build();
+
+ when(productInventoryService.getProductByProductIdInInventory(eq(inventoryId), eq(productId)))
+ .thenReturn(Mono.just(productResponseDTO));
+
+ // Act
+ Mono productResponseDTOMono = productInventoryService.getProductByProductIdInInventory(inventoryId, productId);
+
+ // Assert
+ StepVerifier
+ .create(productResponseDTOMono)
+ .expectNextMatches(response -> {
+ assertNotNull(response);
+ assertEquals(productResponseDTO.getId(), response.getId());
+ assertEquals(productResponseDTO.getInventoryId(), response.getInventoryId());
+ assertEquals(productResponseDTO.getProductId(), response.getProductId());
+ assertEquals(productResponseDTO.getProductName(), response.getProductName());
+ assertEquals(productResponseDTO.getProductDescription(), response.getProductDescription());
+ assertEquals(productResponseDTO.getProductPrice(), response.getProductPrice());
+ assertEquals(productResponseDTO.getProductQuantity(), response.getProductQuantity());
+ assertEquals(productResponseDTO.getProductSalePrice(), response.getProductSalePrice());
+ return true;
+ })
+ .verifyComplete();
+
+ verify(productInventoryService, times(1))
+ .getProductByProductIdInInventory(eq(inventoryId), eq(productId));
+ }
+
+
+ @Test
+ void getProductsInInventoryByInventoryIdAndProductId_InvalidRequest_ShouldFail() {
+ // Arrange
+ String invalidInventoryId = "invalidInventoryId";
+ String invalidProductId = "invalidProductId";
+
+ when(productInventoryService.getProductByProductIdInInventory(eq(invalidInventoryId), eq(invalidProductId)))
+ .thenReturn(Mono.empty());
+
+ // Act
+ Mono productResponseDTOMono = productInventoryService.getProductByProductIdInInventory(invalidInventoryId, invalidProductId);
+
+ // Assert
+ StepVerifier
+ .create(productResponseDTOMono)
+ .expectComplete()
+ .verify();
+
+ verify(productInventoryService, times(1))
+ .getProductByProductIdInInventory(eq(invalidInventoryId), eq(invalidProductId));
+ }
@Test
void getInventoryByInvalidInventoryId_ReturnNotFound() {