From add5ffdd71407d98ddfe16fbd045c59754b3d15b Mon Sep 17 00:00:00 2001
From: manirDev <mhtnourmhtmjr@gmail.com>
Date: Fri, 11 Nov 2022 17:09:50 +0300
Subject: [PATCH 01/16] Shopping cart response and code refactor is done
 successfully :)

---
 .../controller/ShoppingCartController.java    | 23 ++---
 .../dto/CartItemDto.java                      |  4 +
 .../service/CommonService.java                |  5 ++
 .../service/Impl/CommonServiceImpl.java       | 16 ++++
 .../service/Impl/ShoppingCartServiceImpl.java | 87 ++++++++++---------
 .../service/ShoppingCartService.java          | 10 +--
 6 files changed, 87 insertions(+), 58 deletions(-)

diff --git a/src/main/java/com/manir/springbootecommercerestapi/controller/ShoppingCartController.java b/src/main/java/com/manir/springbootecommercerestapi/controller/ShoppingCartController.java
index 73e8ada..1880715 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/controller/ShoppingCartController.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/controller/ShoppingCartController.java
@@ -1,6 +1,7 @@
 package com.manir.springbootecommercerestapi.controller;
 
 import com.manir.springbootecommercerestapi.dto.CartItemDto;
+import com.manir.springbootecommercerestapi.response.CartItemResponse;
 import com.manir.springbootecommercerestapi.service.ShoppingCartService;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
@@ -18,29 +19,29 @@ public class ShoppingCartController {
 
     //find by customer api
     @GetMapping("/findByCustomer/{customerId}")
-    public List<CartItemDto> findByCustomerId(@PathVariable Long customerId){
-        List<CartItemDto> responseCartItems = shoppingCartService.findByCustomerId(customerId);
+    public CartItemResponse findByCustomerId(@PathVariable Long customerId){
+        CartItemResponse responseCartItems = shoppingCartService.findByCustomerId(customerId);
 
         return responseCartItems;
     }
 
     //add item to the cart api
     @PostMapping("/addItem/{customerId}/{productId}/{quantity}")
-    public ResponseEntity<CartItemDto> addCartItem(@PathVariable Long customerId,
-                                                   @PathVariable Long productId,
-                                                   @PathVariable Integer quantity){
-        CartItemDto responseCartItem = shoppingCartService.addCartItem(customerId, productId, quantity);
+    public ResponseEntity<CartItemResponse> addCartItem(@PathVariable Long customerId,
+                                                        @PathVariable Long productId,
+                                                        @PathVariable Integer quantity){
+        CartItemResponse responseCartItem = shoppingCartService.addCartItem(customerId, productId, quantity);
 
         return new ResponseEntity<>(responseCartItem, HttpStatus.CREATED);
     }
 
     //update item quantity api
     @PutMapping("/updateItemQuantity/{customerId}/{productId}/{quantity}")
-    public ResponseEntity<CartItemDto> updateItemQuantity(@PathVariable Long customerId,
-                                                          @PathVariable Long productId,
-                                                          @PathVariable Integer quantity){
+    public ResponseEntity<CartItemResponse> updateItemQuantity(@PathVariable Long customerId,
+                                                               @PathVariable Long productId,
+                                                               @PathVariable Integer quantity){
 
-        CartItemDto responseCartItem = shoppingCartService.updateItemQuantity(customerId, productId, quantity);
+        CartItemResponse responseCartItem = shoppingCartService.updateItemQuantity(customerId, productId, quantity);
 
         return  new ResponseEntity<>(responseCartItem, HttpStatus.OK);
     }
@@ -49,6 +50,6 @@ public ResponseEntity<CartItemDto> updateItemQuantity(@PathVariable Long custome
     @DeleteMapping("/deleteItemProduct/{customerId}/{productId}")
     public ResponseEntity<String> deleteItemProduct(@PathVariable Long customerId, @PathVariable Long productId){
         shoppingCartService.deleteItemProduct(customerId, productId);
-        return ResponseEntity.ok("Product deleted successfully from cart item");
+        return ResponseEntity.ok("Product with id = " + productId +" is deleted successfully from your shopping cart");
     }
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/dto/CartItemDto.java b/src/main/java/com/manir/springbootecommercerestapi/dto/CartItemDto.java
index 62776fd..b5baf24 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/dto/CartItemDto.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/dto/CartItemDto.java
@@ -1,5 +1,7 @@
 package com.manir.springbootecommercerestapi.dto;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.manir.springbootecommercerestapi.model.Product;
 import lombok.Data;
 
 @Data
@@ -7,4 +9,6 @@ public class CartItemDto {
     private Long id;
     private Integer quantity;
     private String status;
+    @JsonIgnore
+    private Product product;
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/CommonService.java b/src/main/java/com/manir/springbootecommercerestapi/service/CommonService.java
index 08f4c32..1dfce02 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/CommonService.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/CommonService.java
@@ -1,5 +1,7 @@
 package com.manir.springbootecommercerestapi.service;
 
+import com.manir.springbootecommercerestapi.dto.CartItemDto;
+import com.manir.springbootecommercerestapi.response.CartItemResponse;
 import com.manir.springbootecommercerestapi.response.CommonResponse;
 import org.springframework.data.domain.Page;
 
@@ -9,4 +11,7 @@ public interface CommonService<T> {
 
     //CommonResponse getAllCategoryOrProduct(int pageNo, int pageSize, String sortBy, String sortDir);
     CommonResponse getResponseContent(Page<T> page, List<T> dtoList);
+
+    //cart iem response handler
+    CartItemResponse getResponse(CartItemDto cartItemDto);
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/CommonServiceImpl.java b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/CommonServiceImpl.java
index 35f6107..de3fec7 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/CommonServiceImpl.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/CommonServiceImpl.java
@@ -1,5 +1,7 @@
 package com.manir.springbootecommercerestapi.service.Impl;
 
+import com.manir.springbootecommercerestapi.dto.CartItemDto;
+import com.manir.springbootecommercerestapi.response.CartItemResponse;
 import com.manir.springbootecommercerestapi.response.CommonResponse;
 import com.manir.springbootecommercerestapi.service.CommonService;
 import lombok.extern.slf4j.Slf4j;
@@ -8,6 +10,7 @@
 import org.springframework.data.domain.Page;
 import org.springframework.stereotype.Service;
 
+import java.util.ArrayList;
 import java.util.List;
 
 @Service
@@ -29,4 +32,17 @@ public CommonResponse getResponseContent(Page page, List dtoList) {
 
         return commonResponse;
     }
+
+    @Override
+    public CartItemResponse getResponse(CartItemDto cartItemDto) {
+
+        CartItemResponse cartItemResponse = new CartItemResponse();
+
+        double totalPrice = cartItemDto.getProduct().getPrice() * cartItemDto.getQuantity();
+        List<CartItemDto> cartItemDtoList = new ArrayList<>();
+        cartItemDtoList.add(cartItemDto);
+        cartItemResponse.setContent(cartItemDtoList);
+        cartItemResponse.setTotalCost(totalPrice);
+        return cartItemResponse;
+    }
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/ShoppingCartServiceImpl.java b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/ShoppingCartServiceImpl.java
index 165d37b..9dbe839 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/ShoppingCartServiceImpl.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/ShoppingCartServiceImpl.java
@@ -9,6 +9,8 @@
 import com.manir.springbootecommercerestapi.repository.CartItemRepository;
 import com.manir.springbootecommercerestapi.repository.CustomerRepository;
 import com.manir.springbootecommercerestapi.repository.ProductRepository;
+import com.manir.springbootecommercerestapi.response.CartItemResponse;
+import com.manir.springbootecommercerestapi.service.CommonService;
 import com.manir.springbootecommercerestapi.service.ShoppingCartService;
 import lombok.extern.slf4j.Slf4j;
 import org.modelmapper.ModelMapper;
@@ -17,12 +19,13 @@
 
 import javax.annotation.Resource;
 import javax.transaction.Transactional;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
+import java.util.stream.DoubleStream;
 
 @Service
 @Slf4j
-
 public class ShoppingCartServiceImpl implements ShoppingCartService {
 
     @Resource
@@ -33,9 +36,10 @@ public class ShoppingCartServiceImpl implements ShoppingCartService {
     private ProductRepository productRepository;
     @Resource
     private CustomerRepository customerRepository;
-
+    @Resource
+    private CommonService commonService;
     @Override
-    public List<CartItemDto> findByCustomerId(Long customerId) {
+    public CartItemResponse findByCustomerId(Long customerId) {
 
         List<CartItem> cartItems = cartItemRepository.findByCustomerId(customerId);
 
@@ -46,21 +50,19 @@ public List<CartItemDto> findByCustomerId(Long customerId) {
         List<CartItemDto> cartItemDtoList = cartItems.stream()
                                                      .map(cartItem -> mapToDto(cartItem))
                                                      .collect(Collectors.toList());
-
-        return cartItemDtoList;
+        DoubleStream totalPrice = cartItemDtoList.stream()
+                                                 .mapToDouble(cartItemDto -> cartItemDto.getProduct().getPrice() * cartItemDto.getQuantity());
+        CartItemResponse cartItemResponse = new CartItemResponse();
+        cartItemResponse.setContent(cartItemDtoList);
+        cartItemResponse.setTotalCost(totalPrice.sum());
+        return cartItemResponse;
     }
 
     @Override
-    public CartItemDto addCartItem(Long customerId, Long productId, Integer quantity) {
+    public CartItemResponse addCartItem(Long customerId, Long productId, Integer quantity) {
         Integer addedQuantity = quantity;
-        Customer customer = customerRepository.findById(customerId)
-                                              .orElseThrow(
-                                                    () -> new ResourceNotFoundException("Customer", customerId)
-                                              );
-        Product product = productRepository.findById(productId)
-                                           .orElseThrow(
-                                                    () -> new ResourceNotFoundException("Product", productId)
-                                           );
+        Customer customer = findCustomerById(customerId);
+        Product product = findProductById(productId);
 
         CartItem cartItem = cartItemRepository.findByCustomerIdAndProductId(customerId, productId);
         if(cartItem != null){
@@ -74,49 +76,36 @@ public CartItemDto addCartItem(Long customerId, Long productId, Integer quantity
         }
 
         CartItem addedCartItem = cartItemRepository.save(cartItem);
+        CartItemDto cartItemDto = mapToDto(addedCartItem);
 
-        return mapToDto(addedCartItem);
+        CartItemResponse cartItemResponse = commonService.getResponse(cartItemDto);
+        return cartItemResponse;
     }
 
     @Override
-    public CartItemDto updateItemQuantity(Long customerId, Long productId, Integer quantity) {
-
-        Customer customer = customerRepository.findById(customerId)
-                .orElseThrow(
-                        () -> new ResourceNotFoundException("Customer", customerId)
-                );
-        Product product = productRepository.findById(productId)
-                .orElseThrow(
-                        () -> new ResourceNotFoundException("Product", productId)
-                );
+    public CartItemResponse updateItemQuantity(Long customerId, Long productId, Integer quantity) {
 
         CartItem cartItem = cartItemRepository.findByCustomerIdAndProductId(customerId, productId);
-        if (!cartItem.getCustomer().getId().equals(customer.getId()) || !cartItem.getProduct().getId().equals(product.getId())){
-            throw new EcommerceApiException(" this cart item does not belong to Customer or Product ", HttpStatus.BAD_REQUEST);
+        if (cartItem == null){
+            throw new EcommerceApiException("Product is not in the cart item", HttpStatus.BAD_REQUEST);
         }
         cartItem.setQuantity(quantity);
         CartItem updatedItemQuantity = cartItemRepository.save(cartItem);
-        return mapToDto(updatedItemQuantity);
+        CartItemDto cartItemDto = mapToDto(updatedItemQuantity);
+
+        CartItemResponse cartItemResponse = commonService.getResponse(cartItemDto);
+        return cartItemResponse;
     }
 
     @Override
     @Transactional
     public void deleteItemProduct(Long customerId, Long productId) {
-        Customer customer = customerRepository.findById(customerId)
-                .orElseThrow(
-                        () -> new ResourceNotFoundException("Customer", customerId)
-                );
-        Product product = productRepository.findById(productId)
-                .orElseThrow(
-                        () -> new ResourceNotFoundException("Product", productId)
-                );
+
         CartItem cartItem = cartItemRepository.findByCustomerIdAndProductId(customerId, productId);
-        if (cartItem != null){
-            cartItemRepository.deleteByCustomerIdAndProductId(customerId, productId);
-        }
-        if (!cartItem.getCustomer().getId().equals(customer.getId()) || !cartItem.getProduct().getId().equals(product.getId())){
-            throw new EcommerceApiException(" this cart item does not belong to Customer or Product ", HttpStatus.BAD_REQUEST);
+        if (cartItem == null){
+            throw new EcommerceApiException("Product is not in the cart item", HttpStatus.BAD_REQUEST);
         }
+        cartItemRepository.deleteByCustomerIdAndProductId(customerId, productId);
     }
 
     //map to dto
@@ -130,4 +119,20 @@ private CartItem mapToEntity(CartItemDto cartItemDto){
         CartItem cartItem = modelMapper.map(cartItemDto, CartItem.class);
         return cartItem;
     }
+
+    private Customer findCustomerById(Long customerId){
+        Customer customer = customerRepository.findById(customerId)
+                .orElseThrow(
+                        () -> new ResourceNotFoundException("Customer", customerId)
+                );
+        return customer;
+    }
+
+    private Product findProductById(Long productId){
+        Product product = productRepository.findById(productId)
+                .orElseThrow(
+                        () -> new ResourceNotFoundException("Product", productId)
+                );
+        return product;
+    }
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/ShoppingCartService.java b/src/main/java/com/manir/springbootecommercerestapi/service/ShoppingCartService.java
index 8f6b5d9..7520bbf 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/ShoppingCartService.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/ShoppingCartService.java
@@ -1,16 +1,14 @@
 package com.manir.springbootecommercerestapi.service;
 
-import com.manir.springbootecommercerestapi.dto.CartItemDto;
-
-import java.util.List;
+import com.manir.springbootecommercerestapi.response.CartItemResponse;
 
 public interface ShoppingCartService {
 
-    List<CartItemDto> findByCustomerId(Long customerId);
+    CartItemResponse findByCustomerId(Long customerId);
 
-    CartItemDto addCartItem(Long customerId, Long productId, Integer quantity);
+    CartItemResponse addCartItem(Long customerId, Long productId, Integer quantity);
 
-    CartItemDto updateItemQuantity(Long customerId, Long productId, Integer quantity);
+    CartItemResponse updateItemQuantity(Long customerId, Long productId, Integer quantity);
 
     void deleteItemProduct(Long customerId, Long productId);
 }

From ef48c9322fca4662d7e6c5fb67596a894c3c82b5 Mon Sep 17 00:00:00 2001
From: Mahamat Nour Mahamat Abdraman
 <42040735+manirDev@users.noreply.github.com>
Date: Fri, 11 Nov 2022 17:11:51 +0300
Subject: [PATCH 02/16] Create README.md

---
 README.md | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644 README.md

diff --git a/README.md b/README.md
new file mode 100644
index 0000000..70a9e4a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,2 @@
+# Spring-boot-e-commerce-rest-api
+Spring Boot E-commerce Rest Api

From 0de93eeda6aa9ff9bed4593451feb08491a7c7b6 Mon Sep 17 00:00:00 2001
From: manirDev <mhtnourmhtmjr@gmail.com>
Date: Mon, 14 Nov 2022 11:06:30 +0300
Subject: [PATCH 03/16] Spring Boot Security, In Memory auth and end Points
 security is done.

---
 pom.xml                                       |  5 +-
 .../config/SecurityConfig.java                | 54 +++++++++++++++++++
 .../controller/CategoryController.java        |  4 ++
 .../controller/ProductController.java         |  5 ++
 .../service/Impl/ShoppingCartServiceImpl.java |  2 +
 src/main/resources/application.properties     |  7 ++-
 6 files changed, 75 insertions(+), 2 deletions(-)
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/config/SecurityConfig.java

diff --git a/pom.xml b/pom.xml
index 7881907..1cd8705 100644
--- a/pom.xml
+++ b/pom.xml
@@ -48,7 +48,10 @@
 			<artifactId>modelmapper</artifactId>
 			<version>3.1.0</version>
 		</dependency>
-
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-security</artifactId>
+		</dependency>
 		<dependency>
 			<groupId>org.springframework.boot</groupId>
 			<artifactId>spring-boot-starter-test</artifactId>
diff --git a/src/main/java/com/manir/springbootecommercerestapi/config/SecurityConfig.java b/src/main/java/com/manir/springbootecommercerestapi/config/SecurityConfig.java
new file mode 100644
index 0000000..08e6a9f
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/config/SecurityConfig.java
@@ -0,0 +1,54 @@
+package com.manir.springbootecommercerestapi.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.provisioning.InMemoryUserDetailsManager;
+
+@Configuration
+@EnableWebSecurity
+/***
+    global security is used for enable security at method level for example permitting get methods
+    Ex: PreAuthorize("hasRole('ADMIN')")
+ ***/
+@EnableGlobalMethodSecurity(prePostEnabled = true)
+public class SecurityConfig extends WebSecurityConfigurerAdapter {
+
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+        http
+                .csrf().disable()
+                .authorizeRequests()
+                //to permit all get request and secure post put and delete methods
+                .antMatchers(HttpMethod.GET, "/api/**").permitAll()
+                .anyRequest()
+                .authenticated()
+                .and()
+                .httpBasic();
+
+    }
+
+    //In memory Auth
+    @Override
+    @Bean
+    protected UserDetailsService userDetailsService() {
+        UserDetails user =  User.builder().username("user").password(passwordEncoder().encode("user")).roles("USER").build();
+        UserDetails admin =  User.builder().username("admin").password(passwordEncoder().encode("admin")).roles("ADMIN").build();
+
+        return new InMemoryUserDetailsManager(user, admin);
+    }
+
+    @Bean
+    PasswordEncoder passwordEncoder(){
+        return new BCryptPasswordEncoder();
+    }
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/controller/CategoryController.java b/src/main/java/com/manir/springbootecommercerestapi/controller/CategoryController.java
index 1bcef5a..23544d0 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/controller/CategoryController.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/controller/CategoryController.java
@@ -7,6 +7,7 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
 
@@ -18,6 +19,7 @@ public class CategoryController {
     private CategoryService categoryService;
 
     //create category api
+    @PreAuthorize("hasRole('ADMIN')")
     @PostMapping("/createCategory")
     public ResponseEntity<CategoryDto> createCategory(@RequestBody CategoryDto categoryDto){
         CategoryDto responseCategory = categoryService.createCategory(categoryDto);
@@ -42,6 +44,7 @@ public ResponseEntity<CategoryDto> getCatecoryById(@PathVariable Long categoryId
     }
 
     //update category api
+    @PreAuthorize("hasRole('ADMIN')")
     @PutMapping("/updateCategory/{categoryId}")
     public ResponseEntity<CategoryDto> updateCategory(@RequestBody CategoryDto categoryDto,
                                                       @PathVariable Long categoryId){
@@ -50,6 +53,7 @@ public ResponseEntity<CategoryDto> updateCategory(@RequestBody CategoryDto categ
     }
 
     //delete category api
+    @PreAuthorize("hasRole('ADMIN')")
     @DeleteMapping("/deleteCategory/{categoryId}")
     public ResponseEntity<String> deleteCategory(@PathVariable Long categoryId){
         categoryService.deleteCategory(categoryId);
diff --git a/src/main/java/com/manir/springbootecommercerestapi/controller/ProductController.java b/src/main/java/com/manir/springbootecommercerestapi/controller/ProductController.java
index d7b56b5..d5e2605 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/controller/ProductController.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/controller/ProductController.java
@@ -9,6 +9,7 @@
 import org.springframework.http.HttpStatus;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
@@ -22,6 +23,7 @@ public class ProductController {
     private ProductService productService;
 
     //product create api
+    @PreAuthorize("hasRole('ADMIN')")
     @RequestMapping(value = "/createProduct", method = RequestMethod.POST, consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.MULTIPART_FORM_DATA_VALUE})
     public ResponseEntity<ProductDto> createProduct(@RequestPart("productDto") ProductDto productDto,
                                                     @RequestPart("file") MultipartFile file){
@@ -30,6 +32,7 @@ public ResponseEntity<ProductDto> createProduct(@RequestPart("productDto") Produ
     }
 
     //create product with category
+    @PreAuthorize("hasRole('ADMIN')")
     @PostMapping("/{categoryId}/saveProductByCategoryId")
     public ResponseEntity<ProductDto> saveProductByCategoryId(@PathVariable Long categoryId,
                                                               @RequestBody ProductDto productDto){
@@ -57,6 +60,7 @@ public ResponseEntity<ProductDto> getProductById(@PathVariable Long productId){
     }
 
     //update product api
+    @PreAuthorize("hasRole('ADMIN')")
     @PutMapping("/{categoryId}/updateProduct/{productId}")
     public ResponseEntity<ProductDto> updateProduct(@PathVariable Long categoryId,
                                                     @RequestBody ProductDto productDto,
@@ -66,6 +70,7 @@ public ResponseEntity<ProductDto> updateProduct(@PathVariable Long categoryId,
     }
 
     //delete product api
+    @PreAuthorize("hasRole('ADMIN')")
     @DeleteMapping("/deleteProduct/{productId}")
     public  ResponseEntity<String> deleteProduct(@PathVariable Long productId){
         productService.deleteProduct(productId);
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/ShoppingCartServiceImpl.java b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/ShoppingCartServiceImpl.java
index 9dbe839..e695637 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/ShoppingCartServiceImpl.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/ShoppingCartServiceImpl.java
@@ -135,4 +135,6 @@ private Product findProductById(Long productId){
                 );
         return product;
     }
+
+
 }
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index f4d8fbb..87078d1 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -6,5 +6,10 @@ spring.datasource.username=root
 spring.datasource.url=jdbc:mysql://localhost:3306/springBootEcommerceRestApi?serverTimezone=UTC
 spring.jpa.hibernate.ddl-auto=update
 
-#logging.level.org.springframework.security=DEBUG
+#TO see security is working on the console
+logging.level.org.springframework.security=DEBUG
+#Spring security default auth credential
+#spring.security.user.password= user
+#spring.security.user.name= user
+#spring.security.user.roles= ADMIN
 

From 425ced43bf093264d3a1272fdb79f25694289f39 Mon Sep 17 00:00:00 2001
From: manirDev <mhtnourmhtmjr@gmail.com>
Date: Mon, 14 Nov 2022 13:26:52 +0300
Subject: [PATCH 04/16] Spring Boot Security,DB Auth and Custom UserService.

---
 .../config/SecurityConfig.java                | 28 +++++++++---
 .../controller/ShoppingCartController.java    |  6 +++
 .../model/CartItem.java                       |  6 +--
 .../model/Role.java                           | 17 ++++++++
 .../model/{Customer.java => User.java}        | 14 ++++--
 .../repository/CartItemRepository.java        |  2 +-
 .../repository/CustomerRepository.java        |  8 ----
 .../repository/RoleRepository.java            | 10 +++++
 .../repository/UserRepository.java            | 16 +++++++
 .../security/CustomUserDetailsService.java    | 43 +++++++++++++++++++
 .../service/Impl/ShoppingCartServiceImpl.java | 21 +++++----
 .../utils/PasswordEncoderGenerator.java       | 12 ++++++
 .../utils/isAuthenticatedAsAdminOrUser.java   | 11 +++++
 src/main/resources/application.properties     |  6 +--
 14 files changed, 163 insertions(+), 37 deletions(-)
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/model/Role.java
 rename src/main/java/com/manir/springbootecommercerestapi/model/{Customer.java => User.java} (56%)
 delete mode 100644 src/main/java/com/manir/springbootecommercerestapi/repository/CustomerRepository.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/repository/RoleRepository.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/repository/UserRepository.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/security/CustomUserDetailsService.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/utils/PasswordEncoderGenerator.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/utils/isAuthenticatedAsAdminOrUser.java

diff --git a/src/main/java/com/manir/springbootecommercerestapi/config/SecurityConfig.java b/src/main/java/com/manir/springbootecommercerestapi/config/SecurityConfig.java
index 08e6a9f..81e3fd8 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/config/SecurityConfig.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/config/SecurityConfig.java
@@ -1,8 +1,11 @@
 package com.manir.springbootecommercerestapi.config;
 
+import com.manir.springbootecommercerestapi.security.CustomUserDetailsService;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.http.HttpMethod;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
 import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@@ -14,6 +17,8 @@
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.security.provisioning.InMemoryUserDetailsManager;
 
+import javax.annotation.Resource;
+
 @Configuration
 @EnableWebSecurity
 /***
@@ -23,6 +28,9 @@
 @EnableGlobalMethodSecurity(prePostEnabled = true)
 public class SecurityConfig extends WebSecurityConfigurerAdapter {
 
+    @Autowired
+    private CustomUserDetailsService customUserDetailsService;
+
     @Override
     protected void configure(HttpSecurity http) throws Exception {
         http
@@ -38,13 +46,21 @@ protected void configure(HttpSecurity http) throws Exception {
     }
 
     //In memory Auth
-    @Override
-    @Bean
-    protected UserDetailsService userDetailsService() {
-        UserDetails user =  User.builder().username("user").password(passwordEncoder().encode("user")).roles("USER").build();
-        UserDetails admin =  User.builder().username("admin").password(passwordEncoder().encode("admin")).roles("ADMIN").build();
+    /**
+        @Override
+        @Bean
+        protected UserDetailsService userDetailsService() {
+            UserDetails user =  User.builder().username("customer").password(passwordEncoder().encode("customer")).roles("USER").build();
+            UserDetails admin =  User.builder().username("admin").password(passwordEncoder().encode("admin")).roles("ADMIN").build();
+
+            return new InMemoryUserDetailsManager(user, admin);
+        }
+    **/
 
-        return new InMemoryUserDetailsManager(user, admin);
+    //DB Auth
+    @Override
+    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+        auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
     }
 
     @Bean
diff --git a/src/main/java/com/manir/springbootecommercerestapi/controller/ShoppingCartController.java b/src/main/java/com/manir/springbootecommercerestapi/controller/ShoppingCartController.java
index 1880715..4859845 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/controller/ShoppingCartController.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/controller/ShoppingCartController.java
@@ -3,8 +3,10 @@
 import com.manir.springbootecommercerestapi.dto.CartItemDto;
 import com.manir.springbootecommercerestapi.response.CartItemResponse;
 import com.manir.springbootecommercerestapi.service.ShoppingCartService;
+import com.manir.springbootecommercerestapi.utils.isAuthenticatedAsAdminOrUser;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
@@ -18,6 +20,7 @@ public class ShoppingCartController {
     private ShoppingCartService shoppingCartService;
 
     //find by customer api
+    @isAuthenticatedAsAdminOrUser
     @GetMapping("/findByCustomer/{customerId}")
     public CartItemResponse findByCustomerId(@PathVariable Long customerId){
         CartItemResponse responseCartItems = shoppingCartService.findByCustomerId(customerId);
@@ -26,6 +29,7 @@ public CartItemResponse findByCustomerId(@PathVariable Long customerId){
     }
 
     //add item to the cart api
+    @isAuthenticatedAsAdminOrUser
     @PostMapping("/addItem/{customerId}/{productId}/{quantity}")
     public ResponseEntity<CartItemResponse> addCartItem(@PathVariable Long customerId,
                                                         @PathVariable Long productId,
@@ -36,6 +40,7 @@ public ResponseEntity<CartItemResponse> addCartItem(@PathVariable Long customerI
     }
 
     //update item quantity api
+    @isAuthenticatedAsAdminOrUser
     @PutMapping("/updateItemQuantity/{customerId}/{productId}/{quantity}")
     public ResponseEntity<CartItemResponse> updateItemQuantity(@PathVariable Long customerId,
                                                                @PathVariable Long productId,
@@ -47,6 +52,7 @@ public ResponseEntity<CartItemResponse> updateItemQuantity(@PathVariable Long cu
     }
 
     //delete item product api
+    @isAuthenticatedAsAdminOrUser
     @DeleteMapping("/deleteItemProduct/{customerId}/{productId}")
     public ResponseEntity<String> deleteItemProduct(@PathVariable Long customerId, @PathVariable Long productId){
         shoppingCartService.deleteItemProduct(customerId, productId);
diff --git a/src/main/java/com/manir/springbootecommercerestapi/model/CartItem.java b/src/main/java/com/manir/springbootecommercerestapi/model/CartItem.java
index 0dfa585..70155da 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/model/CartItem.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/model/CartItem.java
@@ -1,12 +1,10 @@
 package com.manir.springbootecommercerestapi.model;
 
-import com.fasterxml.jackson.annotation.JsonIgnore;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
 import javax.persistence.*;
-import java.util.Set;
 
 @AllArgsConstructor
 @NoArgsConstructor
@@ -27,9 +25,9 @@ public class CartItem {
     @JoinColumn(name = "product_id")
     private Product product;
 
-    //relation with user
+    //relation with customer
     @ManyToOne()
     @JoinColumn(name = "customer_id")
-    private Customer customer;
+    private User customer;
 
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/model/Role.java b/src/main/java/com/manir/springbootecommercerestapi/model/Role.java
new file mode 100644
index 0000000..3ad1a0e
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/model/Role.java
@@ -0,0 +1,17 @@
+package com.manir.springbootecommercerestapi.model;
+
+import lombok.Data;
+
+import javax.persistence.*;
+
+@Data
+@Entity
+@Table(name = "roles")
+public class Role {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    @Column(length = 60)
+    private String name;
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/model/Customer.java b/src/main/java/com/manir/springbootecommercerestapi/model/User.java
similarity index 56%
rename from src/main/java/com/manir/springbootecommercerestapi/model/Customer.java
rename to src/main/java/com/manir/springbootecommercerestapi/model/User.java
index b2d3d99..5f92a22 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/model/Customer.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/model/User.java
@@ -1,6 +1,5 @@
 package com.manir.springbootecommercerestapi.model;
 
-import com.fasterxml.jackson.annotation.JsonIgnore;
 import lombok.Data;
 
 import javax.persistence.*;
@@ -8,10 +7,10 @@
 
 @Data
 @Entity
-@Table(name = "customers", uniqueConstraints = {@UniqueConstraint(columnNames = {"userName"}),
+@Table(name = "users", uniqueConstraints = {@UniqueConstraint(columnNames = {"userName"}),
         @UniqueConstraint(columnNames = {"email"})
 })
-public class Customer {
+public class User {
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
@@ -25,4 +24,11 @@ public class Customer {
             fetch = FetchType.LAZY, orphanRemoval = true,
             mappedBy = "customer")
     private Set<CartItem> cartItems;
-}
+
+    //relation with role
+    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
+    @JoinTable(name = "user_roles",
+               joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
+               inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
+    private Set<Role> roles;
+ }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/repository/CartItemRepository.java b/src/main/java/com/manir/springbootecommercerestapi/repository/CartItemRepository.java
index 4d4482b..d315263 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/repository/CartItemRepository.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/repository/CartItemRepository.java
@@ -11,7 +11,7 @@ public interface CartItemRepository extends JpaRepository<CartItem, Long> {
 
     List<CartItem> findByCustomerId(Long customerId);
 
-    //CartItem findByCustomerAndProduct(Customer customer, Product product);
+    //CartItem findByCustomerAndProduct(User customer, Product product);
     CartItem findByCustomerIdAndProductId(Long customerId, Long productId);
 
     @Query("UPDATE CartItem c SET c.quantity = ?3 WHERE c.product.id = ?2 AND c.customer.id = ?1")
diff --git a/src/main/java/com/manir/springbootecommercerestapi/repository/CustomerRepository.java b/src/main/java/com/manir/springbootecommercerestapi/repository/CustomerRepository.java
deleted file mode 100644
index ae617c2..0000000
--- a/src/main/java/com/manir/springbootecommercerestapi/repository/CustomerRepository.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.manir.springbootecommercerestapi.repository;
-
-import com.manir.springbootecommercerestapi.model.Customer;
-import org.springframework.data.jpa.repository.JpaRepository;
-
-public interface CustomerRepository extends JpaRepository<Customer, Long>{
-
-}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/repository/RoleRepository.java b/src/main/java/com/manir/springbootecommercerestapi/repository/RoleRepository.java
new file mode 100644
index 0000000..1681e21
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/repository/RoleRepository.java
@@ -0,0 +1,10 @@
+package com.manir.springbootecommercerestapi.repository;
+
+import com.manir.springbootecommercerestapi.model.Role;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.Optional;
+
+public interface RoleRepository extends JpaRepository<Role, Long> {
+    Optional<Role> findByName(String name);
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/repository/UserRepository.java b/src/main/java/com/manir/springbootecommercerestapi/repository/UserRepository.java
new file mode 100644
index 0000000..9b9bc19
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/repository/UserRepository.java
@@ -0,0 +1,16 @@
+package com.manir.springbootecommercerestapi.repository;
+
+import com.manir.springbootecommercerestapi.model.User;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.Optional;
+
+public interface UserRepository extends JpaRepository<User, Long>{
+
+    Optional<User> findByEmail(String email);
+    Optional<User> findByUserNameOrEmail(String username, String email);
+    Optional<User> findByUserName(String username);
+    Boolean existsByUserName(String username);
+    Boolean existsByEmail(String email);
+
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/security/CustomUserDetailsService.java b/src/main/java/com/manir/springbootecommercerestapi/security/CustomUserDetailsService.java
new file mode 100644
index 0000000..a21131b
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/security/CustomUserDetailsService.java
@@ -0,0 +1,43 @@
+package com.manir.springbootecommercerestapi.security;
+
+import com.manir.springbootecommercerestapi.model.Role;
+import com.manir.springbootecommercerestapi.model.User;
+import com.manir.springbootecommercerestapi.repository.UserRepository;
+import lombok.AllArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.Collection;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Service
+@AllArgsConstructor
+public class CustomUserDetailsService implements UserDetailsService {
+    private final UserRepository userRepository;
+
+    @Override
+    public UserDetails loadUserByUsername(String userNameOrEmail) throws UsernameNotFoundException {
+        User user =  userRepository.findByUserNameOrEmail(userNameOrEmail, userNameOrEmail)
+                                   .orElseThrow(
+                                           () -> new UsernameNotFoundException("User not found with username or email: " + userNameOrEmail)
+                                   );
+        return new org.springframework.security.core.userdetails.User(
+                                    user.getEmail(),
+                                    user.getPassword(),
+                                    mapRolesToAuthorities(user.getRoles())
+                            );
+    }
+
+    private Collection<? extends GrantedAuthority> mapRolesToAuthorities(Set<Role> roles){
+        return roles.stream()
+                            .map(role -> new SimpleGrantedAuthority(role.getName()))
+                            .collect(Collectors.toList());
+    }
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/ShoppingCartServiceImpl.java b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/ShoppingCartServiceImpl.java
index e695637..a35796b 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/ShoppingCartServiceImpl.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/ShoppingCartServiceImpl.java
@@ -4,10 +4,10 @@
 import com.manir.springbootecommercerestapi.exception.EcommerceApiException;
 import com.manir.springbootecommercerestapi.exception.ResourceNotFoundException;
 import com.manir.springbootecommercerestapi.model.CartItem;
-import com.manir.springbootecommercerestapi.model.Customer;
+import com.manir.springbootecommercerestapi.model.User;
 import com.manir.springbootecommercerestapi.model.Product;
 import com.manir.springbootecommercerestapi.repository.CartItemRepository;
-import com.manir.springbootecommercerestapi.repository.CustomerRepository;
+import com.manir.springbootecommercerestapi.repository.UserRepository;
 import com.manir.springbootecommercerestapi.repository.ProductRepository;
 import com.manir.springbootecommercerestapi.response.CartItemResponse;
 import com.manir.springbootecommercerestapi.service.CommonService;
@@ -19,7 +19,6 @@
 
 import javax.annotation.Resource;
 import javax.transaction.Transactional;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.DoubleStream;
@@ -35,7 +34,7 @@ public class ShoppingCartServiceImpl implements ShoppingCartService {
     @Resource
     private ProductRepository productRepository;
     @Resource
-    private CustomerRepository customerRepository;
+    private UserRepository userRepository;
     @Resource
     private CommonService commonService;
     @Override
@@ -44,7 +43,7 @@ public CartItemResponse findByCustomerId(Long customerId) {
         List<CartItem> cartItems = cartItemRepository.findByCustomerId(customerId);
 
         if (cartItems.size() == 0){
-            throw new EcommerceApiException("Customer has no product in cart item", HttpStatus.BAD_REQUEST);
+            throw new EcommerceApiException("User has no product in cart item", HttpStatus.BAD_REQUEST);
         }
 
         List<CartItemDto> cartItemDtoList = cartItems.stream()
@@ -61,7 +60,7 @@ public CartItemResponse findByCustomerId(Long customerId) {
     @Override
     public CartItemResponse addCartItem(Long customerId, Long productId, Integer quantity) {
         Integer addedQuantity = quantity;
-        Customer customer = findCustomerById(customerId);
+        User user = findCustomerById(customerId);
         Product product = findProductById(productId);
 
         CartItem cartItem = cartItemRepository.findByCustomerIdAndProductId(customerId, productId);
@@ -70,7 +69,7 @@ public CartItemResponse addCartItem(Long customerId, Long productId, Integer qua
             cartItem.setQuantity(addedQuantity);
         }else {
             cartItem = new CartItem();
-            cartItem.setCustomer(customer);
+            cartItem.setCustomer(user);
             cartItem.setProduct(product);
             cartItem.setQuantity(quantity);
         }
@@ -120,12 +119,12 @@ private CartItem mapToEntity(CartItemDto cartItemDto){
         return cartItem;
     }
 
-    private Customer findCustomerById(Long customerId){
-        Customer customer = customerRepository.findById(customerId)
+    private User findCustomerById(Long customerId){
+        User user = userRepository.findById(customerId)
                 .orElseThrow(
-                        () -> new ResourceNotFoundException("Customer", customerId)
+                        () -> new ResourceNotFoundException("User", customerId)
                 );
-        return customer;
+        return user;
     }
 
     private Product findProductById(Long productId){
diff --git a/src/main/java/com/manir/springbootecommercerestapi/utils/PasswordEncoderGenerator.java b/src/main/java/com/manir/springbootecommercerestapi/utils/PasswordEncoderGenerator.java
new file mode 100644
index 0000000..e466684
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/utils/PasswordEncoderGenerator.java
@@ -0,0 +1,12 @@
+package com.manir.springbootecommercerestapi.utils;
+
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+public class PasswordEncoderGenerator {
+    public static void main(String[] args) {
+        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
+        System.out.println("ADMIN: " + passwordEncoder.encode("admin"));
+        System.out.println("USER: " + passwordEncoder.encode("user"));
+    }
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/utils/isAuthenticatedAsAdminOrUser.java b/src/main/java/com/manir/springbootecommercerestapi/utils/isAuthenticatedAsAdminOrUser.java
new file mode 100644
index 0000000..83c6333
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/utils/isAuthenticatedAsAdminOrUser.java
@@ -0,0 +1,11 @@
+package com.manir.springbootecommercerestapi.utils;
+
+import org.springframework.security.access.prepost.PreAuthorize;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+@PreAuthorize("hasRole('ADMIN') || hasRole('USER')")
+public @interface isAuthenticatedAsAdminOrUser {
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 87078d1..8c3a84b 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -9,7 +9,7 @@ spring.jpa.hibernate.ddl-auto=update
 #TO see security is working on the console
 logging.level.org.springframework.security=DEBUG
 #Spring security default auth credential
-#spring.security.user.password= user
-#spring.security.user.name= user
-#spring.security.user.roles= ADMIN
+#spring.security.customer.password= customer
+#spring.security.customer.name= customer
+#spring.security.customer.roles= ADMIN
 

From e9d33c26dbe99df04258ec5c906657d73bacafc5 Mon Sep 17 00:00:00 2001
From: manirDev <mhtnourmhtmjr@gmail.com>
Date: Mon, 14 Nov 2022 14:57:52 +0300
Subject: [PATCH 05/16] Sign-In and Sign-Up is successfully done.

---
 .../config/SecurityConfig.java                | 10 +++
 .../controller/AuthController.java            | 61 +++++++++++++++++++
 .../dto/LoginDto.java                         |  9 +++
 .../dto/SignUpDto.java                        | 11 ++++
 .../service/Impl/UserRegisterServiceImpl.java | 57 +++++++++++++++++
 .../service/UserRegisterService.java          |  7 +++
 6 files changed, 155 insertions(+)
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/controller/AuthController.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/dto/LoginDto.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/dto/SignUpDto.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/service/Impl/UserRegisterServiceImpl.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/service/UserRegisterService.java

diff --git a/src/main/java/com/manir/springbootecommercerestapi/config/SecurityConfig.java b/src/main/java/com/manir/springbootecommercerestapi/config/SecurityConfig.java
index 81e3fd8..baf3107 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/config/SecurityConfig.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/config/SecurityConfig.java
@@ -5,6 +5,7 @@
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.http.HttpMethod;
+import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
 import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -38,6 +39,8 @@ protected void configure(HttpSecurity http) throws Exception {
                 .authorizeRequests()
                 //to permit all get request and secure post put and delete methods
                 .antMatchers(HttpMethod.GET, "/api/**").permitAll()
+                //authorize singIn and signUp
+                .antMatchers("/api/v1/auth/**").permitAll()
                 .anyRequest()
                 .authenticated()
                 .and()
@@ -67,4 +70,11 @@ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
     PasswordEncoder passwordEncoder(){
         return new BCryptPasswordEncoder();
     }
+
+    //User authentication manager bean
+    @Override
+    @Bean
+    public AuthenticationManager authenticationManagerBean() throws Exception {
+        return super.authenticationManagerBean();
+    }
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/controller/AuthController.java b/src/main/java/com/manir/springbootecommercerestapi/controller/AuthController.java
new file mode 100644
index 0000000..82257aa
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/controller/AuthController.java
@@ -0,0 +1,61 @@
+package com.manir.springbootecommercerestapi.controller;
+
+import com.manir.springbootecommercerestapi.dto.LoginDto;
+import com.manir.springbootecommercerestapi.dto.SignUpDto;
+import com.manir.springbootecommercerestapi.repository.UserRepository;
+import com.manir.springbootecommercerestapi.service.UserRegisterService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+
+
+@RestController
+@RequestMapping(value = "api/v1/auth")
+public class AuthController {
+
+    @Autowired
+    private AuthenticationManager authenticationManager;
+    @Autowired
+    private UserRepository userRepository;
+    @Autowired
+    private UserRegisterService userRegisterService;
+
+    //login api
+    @PostMapping("/login")
+    public ResponseEntity<String> authenticateUser(@RequestBody LoginDto loginDto){
+
+        Authentication authentication = authenticationManager.authenticate(
+                                    new UsernamePasswordAuthenticationToken(
+                                            loginDto.getUserNameOrEmail(),
+                                            loginDto.getPassword()
+                                    )
+                                );
+        SecurityContextHolder.getContext().setAuthentication(authentication);
+        return new ResponseEntity<>("User sign-In successfully", HttpStatus.OK);
+    }
+
+    //register api
+    @PostMapping("/register")
+    public ResponseEntity<?> registerUser(@RequestBody SignUpDto signUpDto){
+
+        //check for username exists in DB
+        if (userRepository.existsByUserName(signUpDto.getUsername())){
+            return new ResponseEntity<>("Username already exists", HttpStatus.BAD_REQUEST);
+        }
+        if (userRepository.existsByEmail(signUpDto.getEmail())){
+            return new ResponseEntity<>("Email already exists", HttpStatus.BAD_REQUEST);
+        }
+        SignUpDto registeredUser = userRegisterService.registerUser(signUpDto);
+        return new ResponseEntity<>("User is successfully registered", HttpStatus.OK);
+    }
+
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/dto/LoginDto.java b/src/main/java/com/manir/springbootecommercerestapi/dto/LoginDto.java
new file mode 100644
index 0000000..1c3c8a1
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/dto/LoginDto.java
@@ -0,0 +1,9 @@
+package com.manir.springbootecommercerestapi.dto;
+
+import lombok.Data;
+
+@Data
+public class LoginDto {
+    private String userNameOrEmail;
+    private String password;
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/dto/SignUpDto.java b/src/main/java/com/manir/springbootecommercerestapi/dto/SignUpDto.java
new file mode 100644
index 0000000..a2e2eaa
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/dto/SignUpDto.java
@@ -0,0 +1,11 @@
+package com.manir.springbootecommercerestapi.dto;
+
+import lombok.Data;
+
+@Data
+public class SignUpDto {
+    private String name;
+    private String username;
+    private String email;
+    private String password;
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/UserRegisterServiceImpl.java b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/UserRegisterServiceImpl.java
new file mode 100644
index 0000000..7458041
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/UserRegisterServiceImpl.java
@@ -0,0 +1,57 @@
+package com.manir.springbootecommercerestapi.service.Impl;
+
+import com.manir.springbootecommercerestapi.dto.SignUpDto;
+import com.manir.springbootecommercerestapi.model.Role;
+import com.manir.springbootecommercerestapi.model.User;
+import com.manir.springbootecommercerestapi.repository.RoleRepository;
+import com.manir.springbootecommercerestapi.repository.UserRepository;
+import com.manir.springbootecommercerestapi.service.UserRegisterService;
+import org.modelmapper.ModelMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+
+import java.util.Collections;
+
+@Service
+public class UserRegisterServiceImpl implements UserRegisterService {
+
+    @Autowired
+    private UserRepository userRepository;
+    @Autowired
+    private RoleRepository roleRepository;
+    @Autowired
+    private PasswordEncoder passwordEncoder;
+    @Autowired
+    private ModelMapper modelMapper;
+
+    @Override
+    public SignUpDto registerUser(SignUpDto signUpDto) {
+
+        //convert dto to entity
+        User user = mapToEntity(signUpDto);
+        //save user to db
+        User registeredUser = userRepository.save(user);
+        return  mapToDto(registeredUser);
+    }
+
+    //map to dto
+    private SignUpDto mapToDto(User user){
+        SignUpDto signUpDto = modelMapper.map(user, SignUpDto.class);
+        return signUpDto;
+    }
+
+    //map to entity
+    private User mapToEntity(SignUpDto signUpDto){
+        User user = new User();
+        user.setName(signUpDto.getName());
+        user.setUserName(signUpDto.getUsername());
+        user.setEmail(signUpDto.getEmail());
+        user.setPassword(passwordEncoder.encode(signUpDto.getPassword()));
+
+        //add role to the user
+        Role role = roleRepository.findByName("ROLE_USER").get();
+        user.setRoles(Collections.singleton(role));
+        return user;
+    }
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/UserRegisterService.java b/src/main/java/com/manir/springbootecommercerestapi/service/UserRegisterService.java
new file mode 100644
index 0000000..9096440
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/UserRegisterService.java
@@ -0,0 +1,7 @@
+package com.manir.springbootecommercerestapi.service;
+
+import com.manir.springbootecommercerestapi.dto.SignUpDto;
+
+public interface UserRegisterService {
+    SignUpDto registerUser(SignUpDto signUpDto);
+}

From 67233d517945423ade8c0a31ed3aab6e0e70aa46 Mon Sep 17 00:00:00 2001
From: manirDev <mhtnourmhtmjr@gmail.com>
Date: Tue, 15 Nov 2022 11:20:33 +0300
Subject: [PATCH 06/16] ShoppingCart changed with customer authentication

---
 .../controller/AuthController.java            |  2 +-
 .../controller/ShoppingCartController.java    | 82 ++++++++++++++-----
 .../dto/OrderDto.java                         | 15 ++++
 .../model/Order.java                          | 37 +++++++++
 .../model/OrderProducts.java                  | 38 +++++++++
 .../model/Product.java                        |  6 ++
 .../model/User.java                           | 12 +++
 .../repository/CartItemRepository.java        |  8 +-
 .../security/CustomUserDetails.java           | 53 ++++++++++++
 .../service/Impl/ShoppingCartServiceImpl.java | 25 +++---
 .../service/ShoppingCartService.java          |  9 +-
 11 files changed, 244 insertions(+), 43 deletions(-)
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/dto/OrderDto.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/model/Order.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/model/OrderProducts.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/security/CustomUserDetails.java

diff --git a/src/main/java/com/manir/springbootecommercerestapi/controller/AuthController.java b/src/main/java/com/manir/springbootecommercerestapi/controller/AuthController.java
index 82257aa..2bf09f2 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/controller/AuthController.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/controller/AuthController.java
@@ -54,7 +54,7 @@ public ResponseEntity<?> registerUser(@RequestBody SignUpDto signUpDto){
         if (userRepository.existsByEmail(signUpDto.getEmail())){
             return new ResponseEntity<>("Email already exists", HttpStatus.BAD_REQUEST);
         }
-        SignUpDto registeredUser = userRegisterService.registerUser(signUpDto);
+        userRegisterService.registerUser(signUpDto);
         return new ResponseEntity<>("User is successfully registered", HttpStatus.OK);
     }
 
diff --git a/src/main/java/com/manir/springbootecommercerestapi/controller/ShoppingCartController.java b/src/main/java/com/manir/springbootecommercerestapi/controller/ShoppingCartController.java
index 4859845..593ffe0 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/controller/ShoppingCartController.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/controller/ShoppingCartController.java
@@ -1,16 +1,22 @@
 package com.manir.springbootecommercerestapi.controller;
 
-import com.manir.springbootecommercerestapi.dto.CartItemDto;
+import com.manir.springbootecommercerestapi.exception.EcommerceApiException;
+import com.manir.springbootecommercerestapi.model.User;
+import com.manir.springbootecommercerestapi.repository.UserRepository;
 import com.manir.springbootecommercerestapi.response.CartItemResponse;
 import com.manir.springbootecommercerestapi.service.ShoppingCartService;
 import com.manir.springbootecommercerestapi.utils.isAuthenticatedAsAdminOrUser;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
 import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
-import java.util.List;
 
 @RestController
 @RequestMapping("api/v1/cart")
@@ -19,43 +25,75 @@ public class ShoppingCartController {
     @Resource
     private ShoppingCartService shoppingCartService;
 
+    @Autowired
+    private UserRepository userRepository;
+
+
     //find by customer api
     @isAuthenticatedAsAdminOrUser
-    @GetMapping("/findByCustomer/{customerId}")
-    public CartItemResponse findByCustomerId(@PathVariable Long customerId){
-        CartItemResponse responseCartItems = shoppingCartService.findByCustomerId(customerId);
+    @GetMapping("/findByCustomer")
+    public CartItemResponse findByCustomerId(@AuthenticationPrincipal Authentication authentication){
+        authentication = SecurityContextHolder.getContext().getAuthentication();
+        if (!(authentication instanceof AnonymousAuthenticationToken)) {
+            String currentUserEmail = authentication.getName();
+            //System.out.println("Name:" + currentUserEmail);
+            User customer = userRepository.findByEmail(currentUserEmail).orElseThrow(()-> new UsernameNotFoundException("Customer not found"));
+            CartItemResponse responseCartItems = shoppingCartService.findByCustomer(customer);
+            return responseCartItems;
+
+        }else{
+            throw new EcommerceApiException("User not authenticated", HttpStatus.BAD_REQUEST);
+        }
 
-        return responseCartItems;
     }
 
     //add item to the cart api
     @isAuthenticatedAsAdminOrUser
-    @PostMapping("/addItem/{customerId}/{productId}/{quantity}")
-    public ResponseEntity<CartItemResponse> addCartItem(@PathVariable Long customerId,
+    @PostMapping("/addItem/{productId}/{quantity}")
+    public ResponseEntity<CartItemResponse> addCartItem(@AuthenticationPrincipal Authentication authentication,
                                                         @PathVariable Long productId,
                                                         @PathVariable Integer quantity){
-        CartItemResponse responseCartItem = shoppingCartService.addCartItem(customerId, productId, quantity);
-
-        return new ResponseEntity<>(responseCartItem, HttpStatus.CREATED);
+        authentication = SecurityContextHolder.getContext().getAuthentication();
+        if (!(authentication instanceof AnonymousAuthenticationToken)){
+            String currentUserEmail = authentication.getName();
+            User customer = userRepository.findByEmail(currentUserEmail).orElseThrow(() -> new UsernameNotFoundException("Customer not found"));
+            CartItemResponse responseCartItem = shoppingCartService.addCartItem(customer, productId, quantity);
+            return new ResponseEntity<>(responseCartItem, HttpStatus.CREATED);
+        }else {
+            throw new EcommerceApiException("User not authenticated", HttpStatus.BAD_REQUEST);
+        }
     }
 
     //update item quantity api
     @isAuthenticatedAsAdminOrUser
-    @PutMapping("/updateItemQuantity/{customerId}/{productId}/{quantity}")
-    public ResponseEntity<CartItemResponse> updateItemQuantity(@PathVariable Long customerId,
+    @PutMapping("/updateItemQuantity/{productId}/{quantity}")
+    public ResponseEntity<CartItemResponse> updateItemQuantity(@AuthenticationPrincipal Authentication authentication,
                                                                @PathVariable Long productId,
                                                                @PathVariable Integer quantity){
-
-        CartItemResponse responseCartItem = shoppingCartService.updateItemQuantity(customerId, productId, quantity);
-
-        return  new ResponseEntity<>(responseCartItem, HttpStatus.OK);
+        authentication = SecurityContextHolder.getContext().getAuthentication();
+        if (!(authentication instanceof AnonymousAuthenticationToken)){
+            String currentUserEmail = authentication.getName();
+            User customer = userRepository.findByEmail(currentUserEmail).orElseThrow(() -> new UsernameNotFoundException("Customer Not found"));
+            CartItemResponse responseCartItem = shoppingCartService.updateItemQuantity(customer, productId, quantity);
+            return  new ResponseEntity<>(responseCartItem, HttpStatus.OK);
+        }else{
+            throw new EcommerceApiException("User not authenticated", HttpStatus.BAD_REQUEST);
+        }
     }
 
     //delete item product api
     @isAuthenticatedAsAdminOrUser
-    @DeleteMapping("/deleteItemProduct/{customerId}/{productId}")
-    public ResponseEntity<String> deleteItemProduct(@PathVariable Long customerId, @PathVariable Long productId){
-        shoppingCartService.deleteItemProduct(customerId, productId);
-        return ResponseEntity.ok("Product with id = " + productId +" is deleted successfully from your shopping cart");
+    @DeleteMapping("/deleteItemProduct/{productId}")
+    public ResponseEntity<String> deleteItemProduct(@AuthenticationPrincipal Authentication authentication,
+                                                    @PathVariable Long productId){
+        authentication = SecurityContextHolder.getContext().getAuthentication();
+        if (!(authentication instanceof AnonymousAuthenticationToken)){
+            String currentUserEmail = authentication.getName();
+            User customer = userRepository.findByEmail(currentUserEmail).orElseThrow(() -> new UsernameNotFoundException("Customer Not found"));
+            shoppingCartService.deleteItemProduct(customer, productId);
+            return ResponseEntity.ok("Product with id = " + productId +" is deleted successfully from your shopping cart");
+        }else{
+            throw new EcommerceApiException("User not authenticated", HttpStatus.BAD_REQUEST);
+        }
     }
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/dto/OrderDto.java b/src/main/java/com/manir/springbootecommercerestapi/dto/OrderDto.java
new file mode 100644
index 0000000..7f2cee5
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/dto/OrderDto.java
@@ -0,0 +1,15 @@
+package com.manir.springbootecommercerestapi.dto;
+
+import lombok.Data;
+
+@Data
+public class OrderDto {
+    private Long id;
+    private String name;
+    private String email;
+    private String phone;
+    private String address;
+    private double totalPrice;
+    private String note;
+    private  String status;
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/model/Order.java b/src/main/java/com/manir/springbootecommercerestapi/model/Order.java
new file mode 100644
index 0000000..56054d5
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/model/Order.java
@@ -0,0 +1,37 @@
+package com.manir.springbootecommercerestapi.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.*;
+import java.util.Set;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+@Entity
+@Table(name = "orders")
+public class Order {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String name;
+    private String email;
+    private String phone;
+    private String address;
+    private double totalPrice;
+    private String note;
+    private  String status;
+
+    //relation with user
+    @ManyToOne()
+    @JoinColumn(name = "customer_id")
+    private User customer;
+
+    //relation with order_products
+    @OneToMany(cascade = CascadeType.ALL,
+            fetch = FetchType.LAZY, orphanRemoval = true,
+            mappedBy = "order")
+    private Set<OrderProducts> orderProducts;
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/model/OrderProducts.java b/src/main/java/com/manir/springbootecommercerestapi/model/OrderProducts.java
new file mode 100644
index 0000000..b9362c5
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/model/OrderProducts.java
@@ -0,0 +1,38 @@
+package com.manir.springbootecommercerestapi.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.*;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+@Entity
+@Table(name = "order_products")
+public class OrderProducts {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private double productPrice;
+    private Integer productQuantity;
+    private double totalPrice;
+    private String note;
+    private String status;
+
+    //relation with user
+    @ManyToOne()
+    @JoinColumn(name = "customer_id")
+    private User customer;
+
+    //relation with product
+    @ManyToOne()
+    @JoinColumn(name = "product_id")
+    private Product product;
+
+    //relation with order
+    @ManyToOne()
+    @JoinColumn(name = "order_id")
+    private Order order;
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/model/Product.java b/src/main/java/com/manir/springbootecommercerestapi/model/Product.java
index 77bc06e..78c7f3b 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/model/Product.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/model/Product.java
@@ -54,4 +54,10 @@ public class Product {
     //relation to cart item
     @OneToMany(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true)
     private Set<CartItem> cartItems;
+
+    //relation with order_products
+    @OneToMany(cascade = CascadeType.ALL,
+            fetch = FetchType.LAZY, orphanRemoval = true,
+            mappedBy = "product")
+    private Set<OrderProducts> orderProducts;
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/model/User.java b/src/main/java/com/manir/springbootecommercerestapi/model/User.java
index 5f92a22..7489091 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/model/User.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/model/User.java
@@ -31,4 +31,16 @@ public class User {
                joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
                inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
     private Set<Role> roles;
+
+    //relation with order
+    @OneToMany(cascade = CascadeType.ALL,
+            fetch = FetchType.LAZY, orphanRemoval = true,
+            mappedBy = "customer")
+    private Set<Order> orders;
+
+    //relation with order_product
+    @OneToMany(cascade = CascadeType.ALL,
+            fetch = FetchType.LAZY, orphanRemoval = true,
+            mappedBy = "customer")
+    private Set<OrderProducts> orderProducts;
  }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/repository/CartItemRepository.java b/src/main/java/com/manir/springbootecommercerestapi/repository/CartItemRepository.java
index d315263..0f1c119 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/repository/CartItemRepository.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/repository/CartItemRepository.java
@@ -1,6 +1,8 @@
 package com.manir.springbootecommercerestapi.repository;
 
 import com.manir.springbootecommercerestapi.model.CartItem;
+import com.manir.springbootecommercerestapi.model.Product;
+import com.manir.springbootecommercerestapi.model.User;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.Modifying;
 import org.springframework.data.jpa.repository.Query;
@@ -9,16 +11,16 @@
 
 public interface CartItemRepository extends JpaRepository<CartItem, Long> {
 
-    List<CartItem> findByCustomerId(Long customerId);
+    List<CartItem> findByCustomer(User customer);
 
     //CartItem findByCustomerAndProduct(User customer, Product product);
-    CartItem findByCustomerIdAndProductId(Long customerId, Long productId);
+    CartItem findByCustomerAndProduct(User customer, Product product);
 
     @Query("UPDATE CartItem c SET c.quantity = ?3 WHERE c.product.id = ?2 AND c.customer.id = ?1")
     void updateItemQuantity(Long customerId, Long productId, Integer quantity);
 
     @Query("DELETE FROM CartItem c WHERE c.customer.id = ?1 AND c.product.id = ?2")
     @Modifying
-    void deleteByCustomerIdAndProductId(Long customerId, Long productId);
+    void deleteByCustomerAndProduct(Long customerId, Long productId);
 
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/security/CustomUserDetails.java b/src/main/java/com/manir/springbootecommercerestapi/security/CustomUserDetails.java
new file mode 100644
index 0000000..0fe8a10
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/security/CustomUserDetails.java
@@ -0,0 +1,53 @@
+package com.manir.springbootecommercerestapi.security;
+
+import com.manir.springbootecommercerestapi.model.User;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import java.util.Collection;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class CustomUserDetails implements UserDetails {
+    private User user;
+    @Override
+    public Collection<? extends GrantedAuthority> getAuthorities() {
+        return null;
+    }
+
+    @Override
+    public String getPassword() {
+        return null;
+    }
+
+    @Override
+    public String getUsername() {
+        return user.getEmail();
+    }
+
+    @Override
+    public boolean isAccountNonExpired() {
+        return false;
+    }
+
+    @Override
+    public boolean isAccountNonLocked() {
+        return false;
+    }
+
+    @Override
+    public boolean isCredentialsNonExpired() {
+        return false;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return false;
+    }
+
+
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/ShoppingCartServiceImpl.java b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/ShoppingCartServiceImpl.java
index a35796b..cc98060 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/ShoppingCartServiceImpl.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/ShoppingCartServiceImpl.java
@@ -38,9 +38,9 @@ public class ShoppingCartServiceImpl implements ShoppingCartService {
     @Resource
     private CommonService commonService;
     @Override
-    public CartItemResponse findByCustomerId(Long customerId) {
+    public CartItemResponse findByCustomer(User customer) {
 
-        List<CartItem> cartItems = cartItemRepository.findByCustomerId(customerId);
+        List<CartItem> cartItems = cartItemRepository.findByCustomer(customer);
 
         if (cartItems.size() == 0){
             throw new EcommerceApiException("User has no product in cart item", HttpStatus.BAD_REQUEST);
@@ -58,18 +58,17 @@ public CartItemResponse findByCustomerId(Long customerId) {
     }
 
     @Override
-    public CartItemResponse addCartItem(Long customerId, Long productId, Integer quantity) {
+    public CartItemResponse addCartItem(User customer, Long productId, Integer quantity) {
         Integer addedQuantity = quantity;
-        User user = findCustomerById(customerId);
         Product product = findProductById(productId);
 
-        CartItem cartItem = cartItemRepository.findByCustomerIdAndProductId(customerId, productId);
+        CartItem cartItem = cartItemRepository.findByCustomerAndProduct(customer, product);
         if(cartItem != null){
             addedQuantity = cartItem.getQuantity() + quantity;
             cartItem.setQuantity(addedQuantity);
         }else {
             cartItem = new CartItem();
-            cartItem.setCustomer(user);
+            cartItem.setCustomer(customer);
             cartItem.setProduct(product);
             cartItem.setQuantity(quantity);
         }
@@ -82,9 +81,9 @@ public CartItemResponse addCartItem(Long customerId, Long productId, Integer qua
     }
 
     @Override
-    public CartItemResponse updateItemQuantity(Long customerId, Long productId, Integer quantity) {
-
-        CartItem cartItem = cartItemRepository.findByCustomerIdAndProductId(customerId, productId);
+    public CartItemResponse updateItemQuantity(User customer, Long productId, Integer quantity) {
+        Product product = findProductById(productId);
+        CartItem cartItem = cartItemRepository.findByCustomerAndProduct(customer, product);
         if (cartItem == null){
             throw new EcommerceApiException("Product is not in the cart item", HttpStatus.BAD_REQUEST);
         }
@@ -98,13 +97,13 @@ public CartItemResponse updateItemQuantity(Long customerId, Long productId, Inte
 
     @Override
     @Transactional
-    public void deleteItemProduct(Long customerId, Long productId) {
-
-        CartItem cartItem = cartItemRepository.findByCustomerIdAndProductId(customerId, productId);
+    public void deleteItemProduct(User customer, Long productId) {
+        Product product = findProductById(productId);
+        CartItem cartItem = cartItemRepository.findByCustomerAndProduct(customer, product);
         if (cartItem == null){
             throw new EcommerceApiException("Product is not in the cart item", HttpStatus.BAD_REQUEST);
         }
-        cartItemRepository.deleteByCustomerIdAndProductId(customerId, productId);
+        cartItemRepository.deleteByCustomerAndProduct(customer.getId(), productId);
     }
 
     //map to dto
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/ShoppingCartService.java b/src/main/java/com/manir/springbootecommercerestapi/service/ShoppingCartService.java
index 7520bbf..f657c98 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/ShoppingCartService.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/ShoppingCartService.java
@@ -1,14 +1,15 @@
 package com.manir.springbootecommercerestapi.service;
 
+import com.manir.springbootecommercerestapi.model.User;
 import com.manir.springbootecommercerestapi.response.CartItemResponse;
 
 public interface ShoppingCartService {
 
-    CartItemResponse findByCustomerId(Long customerId);
+    CartItemResponse findByCustomer(User customer);
 
-    CartItemResponse addCartItem(Long customerId, Long productId, Integer quantity);
+    CartItemResponse addCartItem(User customer, Long productId, Integer quantity);
 
-    CartItemResponse updateItemQuantity(Long customerId, Long productId, Integer quantity);
+    CartItemResponse updateItemQuantity(User customer, Long productId, Integer quantity);
 
-    void deleteItemProduct(Long customerId, Long productId);
+    void deleteItemProduct(User customer, Long productId);
 }

From 4f28a19f8aa7b48ebad094d840bbbfb0013b970a Mon Sep 17 00:00:00 2001
From: manirDev <mhtnourmhtmjr@gmail.com>
Date: Tue, 15 Nov 2022 14:52:20 +0300
Subject: [PATCH 07/16] order and ordered products is implemented successfully.

---
 .../controller/OrderController.java           | 63 +++++++++++++
 .../controller/ShoppingCartController.java    |  9 +-
 .../dto/OrderDto.java                         |  3 +
 .../dto/OrderProductsDto.java                 | 13 +++
 .../model/Order.java                          |  7 +-
 .../model/User.java                           |  7 +-
 .../repository/CartItemRepository.java        |  4 +
 .../repository/OrderProductsRepository.java   |  8 ++
 .../repository/OrderRepository.java           | 12 +++
 .../Impl/OrderProductsServiceImpl.java        | 22 +++++
 .../service/Impl/OrderServiceImpl.java        | 94 +++++++++++++++++++
 .../service/OrderProductsService.java         |  7 ++
 .../service/OrderService.java                 | 13 +++
 13 files changed, 254 insertions(+), 8 deletions(-)
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/controller/OrderController.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/dto/OrderProductsDto.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/repository/OrderProductsRepository.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/repository/OrderRepository.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/service/Impl/OrderProductsServiceImpl.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/service/Impl/OrderServiceImpl.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/service/OrderProductsService.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/service/OrderService.java

diff --git a/src/main/java/com/manir/springbootecommercerestapi/controller/OrderController.java b/src/main/java/com/manir/springbootecommercerestapi/controller/OrderController.java
new file mode 100644
index 0000000..0805dee
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/controller/OrderController.java
@@ -0,0 +1,63 @@
+package com.manir.springbootecommercerestapi.controller;
+
+import com.manir.springbootecommercerestapi.dto.OrderDto;
+import com.manir.springbootecommercerestapi.exception.EcommerceApiException;
+import com.manir.springbootecommercerestapi.model.Order;
+import com.manir.springbootecommercerestapi.model.User;
+import com.manir.springbootecommercerestapi.repository.UserRepository;
+import com.manir.springbootecommercerestapi.service.OrderService;
+import com.manir.springbootecommercerestapi.utils.isAuthenticatedAsAdminOrUser;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController
+@RequestMapping(value = "api/v1/order")
+public class OrderController {
+
+    @Autowired
+    private UserRepository userRepository;
+    @Autowired
+    private OrderService orderService;
+
+    //place order complete order api
+    @isAuthenticatedAsAdminOrUser
+    @PostMapping("/placeOrder")
+    public ResponseEntity<?> placeOrder(@AuthenticationPrincipal Authentication authentication){
+        authentication = SecurityContextHolder.getContext().getAuthentication();
+        if (!(authentication instanceof AnonymousAuthenticationToken)){
+            String currentUserEmail = authentication.getName();
+            User customer = userRepository.findByEmail(currentUserEmail).orElseThrow(() -> new UsernameNotFoundException("Customer Not found"));
+            orderService.placeOrder(customer);
+            return new ResponseEntity<>("Order placed successfully", HttpStatus.CREATED);
+        }else{
+            throw new EcommerceApiException("User not authenticated", HttpStatus.BAD_REQUEST);
+        }
+    }
+
+    //find order by customer api
+    @isAuthenticatedAsAdminOrUser
+    @GetMapping("/findByCustomer")
+    public List<OrderDto> listOrdersByCustomer(@AuthenticationPrincipal Authentication authentication){
+        authentication = SecurityContextHolder.getContext().getAuthentication();
+        if (!(authentication instanceof AnonymousAuthenticationToken)){
+            String currentUserEmail = authentication.getName();
+            User customer = userRepository.findByEmail(currentUserEmail).orElseThrow(() -> new UsernameNotFoundException("Customer Not found"));
+            List<OrderDto> customerOrders =  orderService.listOrdersByCustomer(customer);
+            return customerOrders;
+        }else{
+            throw new EcommerceApiException("User not authenticated", HttpStatus.BAD_REQUEST);
+        }
+    }
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/controller/ShoppingCartController.java b/src/main/java/com/manir/springbootecommercerestapi/controller/ShoppingCartController.java
index 593ffe0..7cac9e1 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/controller/ShoppingCartController.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/controller/ShoppingCartController.java
@@ -4,6 +4,7 @@
 import com.manir.springbootecommercerestapi.model.User;
 import com.manir.springbootecommercerestapi.repository.UserRepository;
 import com.manir.springbootecommercerestapi.response.CartItemResponse;
+import com.manir.springbootecommercerestapi.service.OrderService;
 import com.manir.springbootecommercerestapi.service.ShoppingCartService;
 import com.manir.springbootecommercerestapi.utils.isAuthenticatedAsAdminOrUser;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -24,10 +25,10 @@ public class ShoppingCartController {
 
     @Resource
     private ShoppingCartService shoppingCartService;
-
     @Autowired
     private UserRepository userRepository;
-
+    @Autowired
+    private OrderService orderService;
 
     //find by customer api
     @isAuthenticatedAsAdminOrUser
@@ -96,4 +97,8 @@ public ResponseEntity<String> deleteItemProduct(@AuthenticationPrincipal Authent
             throw new EcommerceApiException("User not authenticated", HttpStatus.BAD_REQUEST);
         }
     }
+
+
+
+
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/dto/OrderDto.java b/src/main/java/com/manir/springbootecommercerestapi/dto/OrderDto.java
index 7f2cee5..a5c1332 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/dto/OrderDto.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/dto/OrderDto.java
@@ -1,5 +1,6 @@
 package com.manir.springbootecommercerestapi.dto;
 
+import com.manir.springbootecommercerestapi.model.User;
 import lombok.Data;
 
 @Data
@@ -12,4 +13,6 @@ public class OrderDto {
     private double totalPrice;
     private String note;
     private  String status;
+
+    private User customer;
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/dto/OrderProductsDto.java b/src/main/java/com/manir/springbootecommercerestapi/dto/OrderProductsDto.java
new file mode 100644
index 0000000..166dad0
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/dto/OrderProductsDto.java
@@ -0,0 +1,13 @@
+package com.manir.springbootecommercerestapi.dto;
+
+import lombok.Data;
+
+@Data
+public class OrderProductsDto {
+    private Long id;
+    private double productPrice;
+    private Integer productQuantity;
+    private double totalPrice;
+    private String note;
+    private String status;
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/model/Order.java b/src/main/java/com/manir/springbootecommercerestapi/model/Order.java
index 56054d5..73e9f68 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/model/Order.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/model/Order.java
@@ -1,15 +1,14 @@
 package com.manir.springbootecommercerestapi.model;
 
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import lombok.NoArgsConstructor;
+import lombok.*;
 
 import javax.persistence.*;
 import java.util.Set;
 
 @AllArgsConstructor
 @NoArgsConstructor
-@Data
+@Getter
+@Setter
 @Entity
 @Table(name = "orders")
 public class Order {
diff --git a/src/main/java/com/manir/springbootecommercerestapi/model/User.java b/src/main/java/com/manir/springbootecommercerestapi/model/User.java
index 7489091..edc66b9 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/model/User.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/model/User.java
@@ -1,11 +1,14 @@
 package com.manir.springbootecommercerestapi.model;
 
-import lombok.Data;
+import lombok.*;
 
 import javax.persistence.*;
 import java.util.Set;
 
-@Data
+@Getter
+@Setter
+@AllArgsConstructor
+@NoArgsConstructor
 @Entity
 @Table(name = "users", uniqueConstraints = {@UniqueConstraint(columnNames = {"userName"}),
         @UniqueConstraint(columnNames = {"email"})
diff --git a/src/main/java/com/manir/springbootecommercerestapi/repository/CartItemRepository.java b/src/main/java/com/manir/springbootecommercerestapi/repository/CartItemRepository.java
index 0f1c119..5ff677a 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/repository/CartItemRepository.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/repository/CartItemRepository.java
@@ -23,4 +23,8 @@ public interface CartItemRepository extends JpaRepository<CartItem, Long> {
     @Modifying
     void deleteByCustomerAndProduct(Long customerId, Long productId);
 
+    @Query("DELETE FROM CartItem c WHERE c.customer.id = ?1")
+    @Modifying
+    void deleteByCustomerId(Long customerId);
+
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/repository/OrderProductsRepository.java b/src/main/java/com/manir/springbootecommercerestapi/repository/OrderProductsRepository.java
new file mode 100644
index 0000000..4a4037b
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/repository/OrderProductsRepository.java
@@ -0,0 +1,8 @@
+package com.manir.springbootecommercerestapi.repository;
+
+import com.manir.springbootecommercerestapi.model.OrderProducts;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface OrderProductsRepository extends JpaRepository<OrderProducts, Long> {
+
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/repository/OrderRepository.java b/src/main/java/com/manir/springbootecommercerestapi/repository/OrderRepository.java
new file mode 100644
index 0000000..d0e3573
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/repository/OrderRepository.java
@@ -0,0 +1,12 @@
+package com.manir.springbootecommercerestapi.repository;
+
+import com.manir.springbootecommercerestapi.model.Order;
+import com.manir.springbootecommercerestapi.model.User;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+
+public interface OrderRepository extends JpaRepository<Order, Long> {
+
+    List<Order> findByCustomer(User customer);
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/OrderProductsServiceImpl.java b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/OrderProductsServiceImpl.java
new file mode 100644
index 0000000..a7fd767
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/OrderProductsServiceImpl.java
@@ -0,0 +1,22 @@
+package com.manir.springbootecommercerestapi.service.Impl;
+
+import com.manir.springbootecommercerestapi.model.OrderProducts;
+import com.manir.springbootecommercerestapi.repository.OrderProductsRepository;
+import com.manir.springbootecommercerestapi.service.OrderProductsService;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+
+@Service
+@AllArgsConstructor
+public class OrderProductsServiceImpl implements OrderProductsService {
+
+    @Resource(name = "orderProductsRepository")
+    private final OrderProductsRepository orderProductsRepository;
+
+    @Override
+    public void addOrderProducts(OrderProducts orderProducts) {
+        orderProductsRepository.save(orderProducts);
+    }
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/OrderServiceImpl.java b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/OrderServiceImpl.java
new file mode 100644
index 0000000..45d1970
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/OrderServiceImpl.java
@@ -0,0 +1,94 @@
+package com.manir.springbootecommercerestapi.service.Impl;
+
+import com.manir.springbootecommercerestapi.dto.CartItemDto;
+import com.manir.springbootecommercerestapi.dto.OrderDto;
+import com.manir.springbootecommercerestapi.exception.EcommerceApiException;
+import com.manir.springbootecommercerestapi.model.Order;
+import com.manir.springbootecommercerestapi.model.OrderProducts;
+import com.manir.springbootecommercerestapi.model.User;
+import com.manir.springbootecommercerestapi.repository.CartItemRepository;
+import com.manir.springbootecommercerestapi.repository.OrderRepository;
+import com.manir.springbootecommercerestapi.response.CartItemResponse;
+import com.manir.springbootecommercerestapi.service.OrderProductsService;
+import com.manir.springbootecommercerestapi.service.OrderService;
+import com.manir.springbootecommercerestapi.service.ShoppingCartService;
+import lombok.AllArgsConstructor;
+import org.modelmapper.ModelMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Service;
+
+import javax.transaction.Transactional;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+@AllArgsConstructor
+public class OrderServiceImpl implements OrderService {
+
+    @Autowired
+    private final OrderRepository orderRepository;
+    @Autowired
+    private final OrderProductsService orderProductsService;
+    @Autowired
+    private final CartItemRepository cartItemRepository;
+    @Autowired
+    private final ShoppingCartService shoppingCartService;
+    @Autowired
+    private final ModelMapper modelMapper;
+
+    @Override
+    @Transactional
+    public void placeOrder(User customer) {
+        CartItemResponse cartItemDto = shoppingCartService.findByCustomer(customer);
+        OrderDto orderDto = new OrderDto();
+        orderDto.setTotalPrice(cartItemDto.getTotalCost());
+        orderDto.setEmail(customer.getEmail());
+        orderDto.setName(customer.getName());
+        OrderDto savedOrder = saveOrder(orderDto, customer);
+        List<CartItemDto> cartItemDtoList = cartItemDto.getContent();
+        for(CartItemDto cartItem : cartItemDtoList){
+            OrderProducts orderProducts = new OrderProducts();
+            orderProducts.setCustomer(customer);
+            orderProducts.setProduct(cartItem.getProduct());
+            orderProducts.setOrder(mapToEntity(savedOrder));
+            orderProducts.setProductPrice(cartItem.getProduct().getPrice());
+            orderProducts.setProductQuantity(cartItem.getQuantity());
+            orderProducts.setTotalPrice(cartItemDto.getTotalCost());
+            orderProductsService.addOrderProducts(orderProducts);
+        }
+        cartItemRepository.deleteByCustomerId(customer.getId());
+    }
+
+    @Override
+    public OrderDto saveOrder(OrderDto orderDto, User customer) {
+        //convert to entity
+        Order order = mapToEntity(orderDto);
+        //save order to db
+        Order placedOrder = orderRepository.save(order);
+        return mapToDto(placedOrder);
+    }
+
+    @Override
+    public List<OrderDto> listOrdersByCustomer(User customer) {
+        List<Order> orders = orderRepository.findByCustomer(customer);
+        if (orders.size() == 0){
+            throw new EcommerceApiException("User has no order", HttpStatus.BAD_REQUEST);
+        }
+        List<OrderDto> orderDtoList = orders.stream()
+                                                    .map(order -> mapToDto(order))
+                                                    .collect(Collectors.toList());
+        return orderDtoList;
+    }
+
+    //map to Entity
+    private Order mapToEntity(OrderDto orderDto){
+        Order order = modelMapper.map(orderDto, Order.class);
+        return order;
+    }
+    //map to Dto
+    private OrderDto mapToDto(Order order){
+        OrderDto orderDto = modelMapper.map(order, OrderDto.class);
+        return orderDto;
+    }
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/OrderProductsService.java b/src/main/java/com/manir/springbootecommercerestapi/service/OrderProductsService.java
new file mode 100644
index 0000000..c198a83
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/OrderProductsService.java
@@ -0,0 +1,7 @@
+package com.manir.springbootecommercerestapi.service;
+
+import com.manir.springbootecommercerestapi.model.OrderProducts;
+
+public interface OrderProductsService {
+    void addOrderProducts(OrderProducts orderProducts);
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/OrderService.java b/src/main/java/com/manir/springbootecommercerestapi/service/OrderService.java
new file mode 100644
index 0000000..e2d9da9
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/OrderService.java
@@ -0,0 +1,13 @@
+package com.manir.springbootecommercerestapi.service;
+
+import com.manir.springbootecommercerestapi.dto.OrderDto;
+import com.manir.springbootecommercerestapi.model.User;
+
+import java.util.List;
+
+public interface OrderService {
+
+    void placeOrder(User customer);
+    OrderDto saveOrder(OrderDto orderDto, User customer);
+    List<OrderDto> listOrdersByCustomer(User customer);
+}

From b02e91b2ddc40c2efb68a8d49498e90fd5768c32 Mon Sep 17 00:00:00 2001
From: manirDev <mhtnourmhtmjr@gmail.com>
Date: Wed, 16 Nov 2022 11:05:37 +0300
Subject: [PATCH 08/16] Find Customer order and ordered items are done.

---
 .../config/SecurityConfig.java                |  6 ----
 .../controller/OrderController.java           | 20 +++++++++++-
 .../dto/OrderDto.java                         |  2 ++
 .../repository/OrderProductsRepository.java   |  5 ++-
 .../Impl/OrderProductsServiceImpl.java        | 31 +++++++++++++++++++
 .../service/Impl/OrderServiceImpl.java        |  3 +-
 .../service/OrderProductsService.java         |  5 +++
 7 files changed, 63 insertions(+), 9 deletions(-)

diff --git a/src/main/java/com/manir/springbootecommercerestapi/config/SecurityConfig.java b/src/main/java/com/manir/springbootecommercerestapi/config/SecurityConfig.java
index baf3107..8314f0e 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/config/SecurityConfig.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/config/SecurityConfig.java
@@ -11,14 +11,8 @@
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
-import org.springframework.security.core.userdetails.User;
-import org.springframework.security.core.userdetails.UserDetails;
-import org.springframework.security.core.userdetails.UserDetailsService;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.security.crypto.password.PasswordEncoder;
-import org.springframework.security.provisioning.InMemoryUserDetailsManager;
-
-import javax.annotation.Resource;
 
 @Configuration
 @EnableWebSecurity
diff --git a/src/main/java/com/manir/springbootecommercerestapi/controller/OrderController.java b/src/main/java/com/manir/springbootecommercerestapi/controller/OrderController.java
index 0805dee..172436f 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/controller/OrderController.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/controller/OrderController.java
@@ -1,10 +1,11 @@
 package com.manir.springbootecommercerestapi.controller;
 
 import com.manir.springbootecommercerestapi.dto.OrderDto;
+import com.manir.springbootecommercerestapi.dto.OrderProductsDto;
 import com.manir.springbootecommercerestapi.exception.EcommerceApiException;
-import com.manir.springbootecommercerestapi.model.Order;
 import com.manir.springbootecommercerestapi.model.User;
 import com.manir.springbootecommercerestapi.repository.UserRepository;
+import com.manir.springbootecommercerestapi.service.OrderProductsService;
 import com.manir.springbootecommercerestapi.service.OrderService;
 import com.manir.springbootecommercerestapi.utils.isAuthenticatedAsAdminOrUser;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -30,6 +31,8 @@ public class OrderController {
     private UserRepository userRepository;
     @Autowired
     private OrderService orderService;
+    @Autowired
+    private OrderProductsService orderProductsService;
 
     //place order complete order api
     @isAuthenticatedAsAdminOrUser
@@ -60,4 +63,19 @@ public List<OrderDto> listOrdersByCustomer(@AuthenticationPrincipal Authenticati
             throw new EcommerceApiException("User not authenticated", HttpStatus.BAD_REQUEST);
         }
     }
+
+    //find ordered items by Customer
+    @isAuthenticatedAsAdminOrUser
+    @GetMapping("/findOrderedItemsByCustomer")
+    public List<OrderProductsDto> findOrderedItemsByCustomer(@AuthenticationPrincipal Authentication authentication){
+        authentication = SecurityContextHolder.getContext().getAuthentication();
+        if (!(authentication instanceof AnonymousAuthenticationToken)){
+            String currentUserEmail = authentication.getName();
+            User customer = userRepository.findByEmail(currentUserEmail).orElseThrow(() -> new UsernameNotFoundException("Customer Not found"));
+            List<OrderProductsDto> customerOrderedItems =  orderProductsService.findOrderItemsByCustomer(customer);
+            return customerOrderedItems;
+        }else{
+            throw new EcommerceApiException("User not authenticated", HttpStatus.BAD_REQUEST);
+        }
+    }
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/dto/OrderDto.java b/src/main/java/com/manir/springbootecommercerestapi/dto/OrderDto.java
index a5c1332..e4c5156 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/dto/OrderDto.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/dto/OrderDto.java
@@ -1,5 +1,6 @@
 package com.manir.springbootecommercerestapi.dto;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.manir.springbootecommercerestapi.model.User;
 import lombok.Data;
 
@@ -14,5 +15,6 @@ public class OrderDto {
     private String note;
     private  String status;
 
+    @JsonIgnore
     private User customer;
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/repository/OrderProductsRepository.java b/src/main/java/com/manir/springbootecommercerestapi/repository/OrderProductsRepository.java
index 4a4037b..03d331d 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/repository/OrderProductsRepository.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/repository/OrderProductsRepository.java
@@ -1,8 +1,11 @@
 package com.manir.springbootecommercerestapi.repository;
 
 import com.manir.springbootecommercerestapi.model.OrderProducts;
+import com.manir.springbootecommercerestapi.model.User;
 import org.springframework.data.jpa.repository.JpaRepository;
 
-public interface OrderProductsRepository extends JpaRepository<OrderProducts, Long> {
+import java.util.List;
 
+public interface OrderProductsRepository extends JpaRepository<OrderProducts, Long> {
+    List<OrderProducts> findByCustomer(User customer);
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/OrderProductsServiceImpl.java b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/OrderProductsServiceImpl.java
index a7fd767..0a3e532 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/OrderProductsServiceImpl.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/OrderProductsServiceImpl.java
@@ -1,12 +1,20 @@
 package com.manir.springbootecommercerestapi.service.Impl;
 
+import com.manir.springbootecommercerestapi.dto.OrderProductsDto;
+import com.manir.springbootecommercerestapi.exception.EcommerceApiException;
 import com.manir.springbootecommercerestapi.model.OrderProducts;
+import com.manir.springbootecommercerestapi.model.User;
 import com.manir.springbootecommercerestapi.repository.OrderProductsRepository;
 import com.manir.springbootecommercerestapi.service.OrderProductsService;
 import lombok.AllArgsConstructor;
+import org.modelmapper.ModelMapper;
+import org.modelmapper.convention.MatchingStrategies;
+import org.springframework.http.HttpStatus;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
+import java.util.List;
+import java.util.stream.Collectors;
 
 @Service
 @AllArgsConstructor
@@ -14,9 +22,32 @@ public class OrderProductsServiceImpl implements OrderProductsService {
 
     @Resource(name = "orderProductsRepository")
     private final OrderProductsRepository orderProductsRepository;
+    @Resource(name = "modelMapper")
+    private final ModelMapper modelMapper;
 
     @Override
     public void addOrderProducts(OrderProducts orderProducts) {
         orderProductsRepository.save(orderProducts);
     }
+
+    @Override
+    public List<OrderProductsDto> findOrderItemsByCustomer(User customer) {
+
+        List<OrderProducts> orderProducts = orderProductsRepository.findByCustomer(customer);
+        if (orderProducts.size() == 0){
+            throw new EcommerceApiException("User has no ordered products", HttpStatus.BAD_REQUEST);
+        }
+        List<OrderProductsDto> orderProductsDtoList =  orderProducts.stream()
+                                                                            .map(orderProduct -> mapToDto(orderProduct))
+                                                                            .collect(Collectors.toList());
+        return orderProductsDtoList;
+    }
+
+    //map to dto
+    private OrderProductsDto mapToDto(OrderProducts orderProducts){
+        modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
+        OrderProductsDto orderProductsDto = modelMapper.map(orderProducts, OrderProductsDto.class);
+
+        return orderProductsDto;
+    }
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/OrderServiceImpl.java b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/OrderServiceImpl.java
index 45d1970..8b58f61 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/OrderServiceImpl.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/OrderServiceImpl.java
@@ -45,6 +45,7 @@ public void placeOrder(User customer) {
         orderDto.setTotalPrice(cartItemDto.getTotalCost());
         orderDto.setEmail(customer.getEmail());
         orderDto.setName(customer.getName());
+        orderDto.setCustomer(customer);
         OrderDto savedOrder = saveOrder(orderDto, customer);
         List<CartItemDto> cartItemDtoList = cartItemDto.getContent();
         for(CartItemDto cartItem : cartItemDtoList){
@@ -54,7 +55,7 @@ public void placeOrder(User customer) {
             orderProducts.setOrder(mapToEntity(savedOrder));
             orderProducts.setProductPrice(cartItem.getProduct().getPrice());
             orderProducts.setProductQuantity(cartItem.getQuantity());
-            orderProducts.setTotalPrice(cartItemDto.getTotalCost());
+            orderProducts.setTotalPrice(cartItem.getProduct().getPrice()*cartItem.getQuantity());
             orderProductsService.addOrderProducts(orderProducts);
         }
         cartItemRepository.deleteByCustomerId(customer.getId());
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/OrderProductsService.java b/src/main/java/com/manir/springbootecommercerestapi/service/OrderProductsService.java
index c198a83..2fec5dc 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/OrderProductsService.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/OrderProductsService.java
@@ -1,7 +1,12 @@
 package com.manir.springbootecommercerestapi.service;
 
+import com.manir.springbootecommercerestapi.dto.OrderProductsDto;
 import com.manir.springbootecommercerestapi.model.OrderProducts;
+import com.manir.springbootecommercerestapi.model.User;
+
+import java.util.List;
 
 public interface OrderProductsService {
     void addOrderProducts(OrderProducts orderProducts);
+    List<OrderProductsDto> findOrderItemsByCustomer(User customer);
 }

From b3e8afdda41c3c768c916142dc1a2cff82c5ce56 Mon Sep 17 00:00:00 2001
From: manirDev <mhtnourmhtmjr@gmail.com>
Date: Wed, 16 Nov 2022 12:48:26 +0300
Subject: [PATCH 09/16] JWT token provider is implemented.

---
 pom.xml                                       |  7 ++
 .../config/SecurityConfig.java                | 30 ++++++++-
 .../controller/AuthController.java            | 12 +++-
 .../response/JWTAuthResponse.java             | 15 +++++
 .../security/JwtAuthenticationEntryPoint.java | 21 ++++++
 .../security/JwtAuthenticationFilter.java     | 57 ++++++++++++++++
 .../security/JwtTokenProvider.java            | 66 +++++++++++++++++++
 src/main/resources/application.properties     |  3 +
 8 files changed, 207 insertions(+), 4 deletions(-)
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/response/JWTAuthResponse.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/security/JwtAuthenticationEntryPoint.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/security/JwtAuthenticationFilter.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/security/JwtTokenProvider.java

diff --git a/pom.xml b/pom.xml
index 1cd8705..083ab16 100644
--- a/pom.xml
+++ b/pom.xml
@@ -52,6 +52,13 @@
 			<groupId>org.springframework.boot</groupId>
 			<artifactId>spring-boot-starter-security</artifactId>
 		</dependency>
+		<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
+		<dependency>
+			<groupId>io.jsonwebtoken</groupId>
+			<artifactId>jjwt</artifactId>
+			<version>0.9.1</version>
+		</dependency>
+
 		<dependency>
 			<groupId>org.springframework.boot</groupId>
 			<artifactId>spring-boot-starter-test</artifactId>
diff --git a/src/main/java/com/manir/springbootecommercerestapi/config/SecurityConfig.java b/src/main/java/com/manir/springbootecommercerestapi/config/SecurityConfig.java
index 8314f0e..cb723b4 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/config/SecurityConfig.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/config/SecurityConfig.java
@@ -1,6 +1,8 @@
 package com.manir.springbootecommercerestapi.config;
 
 import com.manir.springbootecommercerestapi.security.CustomUserDetailsService;
+import com.manir.springbootecommercerestapi.security.JwtAuthenticationEntryPoint;
+import com.manir.springbootecommercerestapi.security.JwtAuthenticationFilter;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
@@ -11,8 +13,10 @@
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.http.SessionCreationPolicy;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
 
 @Configuration
 @EnableWebSecurity
@@ -25,21 +29,43 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
 
     @Autowired
     private CustomUserDetailsService customUserDetailsService;
+    @Autowired
+    private JwtAuthenticationEntryPoint authenticationEntryPoint;
 
     @Override
     protected void configure(HttpSecurity http) throws Exception {
         http
                 .csrf().disable()
+                /*-------------------------JWT Starts------------------------------*/
+                .exceptionHandling()
+                .authenticationEntryPoint(authenticationEntryPoint)
+                .and()
+                .sessionManagement()
+                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+                /*-------------------------JWT ends------------------------------*/
+                .and()
                 .authorizeRequests()
                 //to permit all get request and secure post put and delete methods
                 .antMatchers(HttpMethod.GET, "/api/**").permitAll()
                 //authorize singIn and signUp
                 .antMatchers("/api/v1/auth/**").permitAll()
                 .anyRequest()
-                .authenticated()
-                .and()
+                .authenticated();
+
+                /**
+                  Basic auth used before JWT implementation
+                 .and()
                 .httpBasic();
+                 **/
+        http
+                .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
+
+    }
 
+    //Jwt auth filter method
+    @Bean
+    public JwtAuthenticationFilter jwtAuthenticationFilter(){
+        return new JwtAuthenticationFilter();
     }
 
     //In memory Auth
diff --git a/src/main/java/com/manir/springbootecommercerestapi/controller/AuthController.java b/src/main/java/com/manir/springbootecommercerestapi/controller/AuthController.java
index 2bf09f2..672f51a 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/controller/AuthController.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/controller/AuthController.java
@@ -3,6 +3,8 @@
 import com.manir.springbootecommercerestapi.dto.LoginDto;
 import com.manir.springbootecommercerestapi.dto.SignUpDto;
 import com.manir.springbootecommercerestapi.repository.UserRepository;
+import com.manir.springbootecommercerestapi.response.JWTAuthResponse;
+import com.manir.springbootecommercerestapi.security.JwtTokenProvider;
 import com.manir.springbootecommercerestapi.service.UserRegisterService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
@@ -28,10 +30,12 @@ public class AuthController {
     private UserRepository userRepository;
     @Autowired
     private UserRegisterService userRegisterService;
+    @Autowired
+    private JwtTokenProvider tokenProvider;
 
     //login api
     @PostMapping("/login")
-    public ResponseEntity<String> authenticateUser(@RequestBody LoginDto loginDto){
+    public ResponseEntity<JWTAuthResponse> authenticateUser(@RequestBody LoginDto loginDto){
 
         Authentication authentication = authenticationManager.authenticate(
                                     new UsernamePasswordAuthenticationToken(
@@ -40,7 +44,11 @@ public ResponseEntity<String> authenticateUser(@RequestBody LoginDto loginDto){
                                     )
                                 );
         SecurityContextHolder.getContext().setAuthentication(authentication);
-        return new ResponseEntity<>("User sign-In successfully", HttpStatus.OK);
+
+        //get token from token provider
+        String token = tokenProvider.generateToken(authentication);
+
+        return new ResponseEntity<>(new JWTAuthResponse(token), HttpStatus.OK);
     }
 
     //register api
diff --git a/src/main/java/com/manir/springbootecommercerestapi/response/JWTAuthResponse.java b/src/main/java/com/manir/springbootecommercerestapi/response/JWTAuthResponse.java
new file mode 100644
index 0000000..52c7ae9
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/response/JWTAuthResponse.java
@@ -0,0 +1,15 @@
+package com.manir.springbootecommercerestapi.response;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class JWTAuthResponse {
+    private String accessToken;
+    private String tokenType = "Bearer";
+
+    public JWTAuthResponse(String accessToken) {
+        this.accessToken = accessToken;
+    }
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/security/JwtAuthenticationEntryPoint.java b/src/main/java/com/manir/springbootecommercerestapi/security/JwtAuthenticationEntryPoint.java
new file mode 100644
index 0000000..5b485e4
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/security/JwtAuthenticationEntryPoint.java
@@ -0,0 +1,21 @@
+package com.manir.springbootecommercerestapi.security;
+
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+@Component
+public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
+    @Override
+    public void commence(HttpServletRequest request,
+                         HttpServletResponse response,
+                         AuthenticationException authException) throws IOException, ServletException {
+
+        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());
+    }
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/security/JwtAuthenticationFilter.java b/src/main/java/com/manir/springbootecommercerestapi/security/JwtAuthenticationFilter.java
new file mode 100644
index 0000000..620ef9e
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/security/JwtAuthenticationFilter.java
@@ -0,0 +1,57 @@
+package com.manir.springbootecommercerestapi.security;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.util.StringUtils;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+public class JwtAuthenticationFilter extends OncePerRequestFilter {
+
+    @Autowired
+    private JwtTokenProvider tokenProvider;
+    @Autowired
+    private CustomUserDetailsService userDetailsService;
+
+    @Override
+    protected void doFilterInternal(HttpServletRequest request,
+                                    HttpServletResponse response,
+                                    FilterChain filterChain) throws ServletException, IOException {
+
+        //get jwt token from http request
+        String token = getJWTFromToken(request);
+        //validate token
+        if (StringUtils.hasText(token) && tokenProvider.validateToken(token)){
+            //retrieve user form token
+            String userName = tokenProvider.getUserNameFromToken(token);
+            //load user associated with the token
+            UserDetails userDetails = userDetailsService.loadUserByUsername(userName);
+            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
+                    userDetails, null, userDetails.getAuthorities()
+            );
+            //set spring security
+            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
+        }
+        filterChain.doFilter(request, response);
+    }
+
+    //get jwt from token
+    //Bearer <accessToken>
+    private String getJWTFromToken(HttpServletRequest request){
+
+        String bearerToken = request.getHeader("Authorization");
+        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")){
+            return bearerToken.substring(7, bearerToken.length());
+        }
+        return null;
+    }
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/security/JwtTokenProvider.java b/src/main/java/com/manir/springbootecommercerestapi/security/JwtTokenProvider.java
new file mode 100644
index 0000000..5e18a26
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/security/JwtTokenProvider.java
@@ -0,0 +1,66 @@
+package com.manir.springbootecommercerestapi.security;
+
+import com.manir.springbootecommercerestapi.exception.EcommerceApiException;
+import io.jsonwebtoken.*;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.core.Authentication;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+
+@Component
+public class JwtTokenProvider {
+
+    @Value("${app.jwt-secret}")
+    private String jwtSecret;
+    @Value("${app.jwt-expiration-milliseconds}")
+    private int jwtExpirationTime;
+
+    //generate token method
+
+    public String generateToken(Authentication authentication){
+        String userName = authentication.getName();
+        Date currentDate = new Date();
+        Date expireDate = new Date(currentDate.getTime() + jwtExpirationTime);
+
+        String token = Jwts.builder()
+                                    .setSubject(userName)
+                                    .setIssuedAt(new Date())
+                                    .setExpiration(expireDate)
+                                    .signWith(SignatureAlgorithm.HS512, jwtSecret)
+                                    .compact();
+
+        return token;
+    }
+
+    //get username from the token, retrieve username from generated token
+    public String getUserNameFromToken(String token){
+        Claims claims = Jwts.parser()
+                                    .setSigningKey(jwtSecret)
+                                    .parseClaimsJws(token)
+                                    .getBody();
+
+        return claims.getSubject();
+    }
+
+    //validate JWT token
+    public boolean validateToken(String token){
+            try {
+                Jwts.parser()
+                            .setSigningKey(jwtSecret)
+                            .parseClaimsJws(token);
+                return true;
+            }catch (SignatureException e){
+                throw new EcommerceApiException("Invalid JWT signature", HttpStatus.BAD_REQUEST);
+            }catch (MalformedJwtException e){
+                throw new EcommerceApiException("Invalid JWT token", HttpStatus.BAD_REQUEST);
+            }catch (ExpiredJwtException e){
+                throw new EcommerceApiException("Expired JWT token", HttpStatus.BAD_REQUEST);
+            }catch (UnsupportedJwtException e){
+                throw new EcommerceApiException("Unsupported JWT token", HttpStatus.BAD_REQUEST);
+            }catch (IllegalArgumentException e){
+                throw new EcommerceApiException("JWT claims string is empty", HttpStatus.BAD_REQUEST);
+            }
+    }
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 8c3a84b..bf443c7 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -13,3 +13,6 @@ logging.level.org.springframework.security=DEBUG
 #spring.security.customer.name= customer
 #spring.security.customer.roles= ADMIN
 
+#JWT properties
+app.jwt-secret = JWTSecretKey
+app.jwt-expiration-milliseconds = 604800000

From fe3785411599e58c630ffeb55187b7bdf173316b Mon Sep 17 00:00:00 2001
From: manirDev <mhtnourmhtmjr@gmail.com>
Date: Wed, 16 Nov 2022 15:12:17 +0300
Subject: [PATCH 10/16] Some code refactors are done on getting authenticated
 user.

---
 .../controller/OrderController.java           | 51 ++++++---------
 .../controller/ShoppingCartController.java    | 63 +++++--------------
 .../service/CommonService.java                |  5 ++
 .../service/Impl/CommonServiceImpl.java       | 30 +++++++++
 .../service/Impl/OrderServiceImpl.java        | 20 +++---
 .../service/OrderService.java                 |  2 +-
 6 files changed, 83 insertions(+), 88 deletions(-)

diff --git a/src/main/java/com/manir/springbootecommercerestapi/controller/OrderController.java b/src/main/java/com/manir/springbootecommercerestapi/controller/OrderController.java
index 172436f..302422b 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/controller/OrderController.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/controller/OrderController.java
@@ -2,20 +2,17 @@
 
 import com.manir.springbootecommercerestapi.dto.OrderDto;
 import com.manir.springbootecommercerestapi.dto.OrderProductsDto;
-import com.manir.springbootecommercerestapi.exception.EcommerceApiException;
 import com.manir.springbootecommercerestapi.model.User;
 import com.manir.springbootecommercerestapi.repository.UserRepository;
+import com.manir.springbootecommercerestapi.service.CommonService;
 import com.manir.springbootecommercerestapi.service.OrderProductsService;
 import com.manir.springbootecommercerestapi.service.OrderService;
 import com.manir.springbootecommercerestapi.utils.isAuthenticatedAsAdminOrUser;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.authentication.AnonymousAuthenticationToken;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.annotation.AuthenticationPrincipal;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.core.userdetails.UsernameNotFoundException;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -27,55 +24,43 @@
 @RequestMapping(value = "api/v1/order")
 public class OrderController {
 
-    @Autowired
-    private UserRepository userRepository;
     @Autowired
     private OrderService orderService;
     @Autowired
     private OrderProductsService orderProductsService;
+    @Autowired
+    private CommonService commonService;
 
     //place order complete order api
     @isAuthenticatedAsAdminOrUser
     @PostMapping("/placeOrder")
     public ResponseEntity<?> placeOrder(@AuthenticationPrincipal Authentication authentication){
-        authentication = SecurityContextHolder.getContext().getAuthentication();
-        if (!(authentication instanceof AnonymousAuthenticationToken)){
-            String currentUserEmail = authentication.getName();
-            User customer = userRepository.findByEmail(currentUserEmail).orElseThrow(() -> new UsernameNotFoundException("Customer Not found"));
-            orderService.placeOrder(customer);
-            return new ResponseEntity<>("Order placed successfully", HttpStatus.CREATED);
-        }else{
-            throw new EcommerceApiException("User not authenticated", HttpStatus.BAD_REQUEST);
-        }
+        User customer = commonService.getCurrentAuthenticatedUser(authentication);
+        orderService.placeOrder(customer);
+        return new ResponseEntity<>("Order placed successfully", HttpStatus.CREATED);
     }
 
     //find order by customer api
     @isAuthenticatedAsAdminOrUser
     @GetMapping("/findByCustomer")
     public List<OrderDto> listOrdersByCustomer(@AuthenticationPrincipal Authentication authentication){
-        authentication = SecurityContextHolder.getContext().getAuthentication();
-        if (!(authentication instanceof AnonymousAuthenticationToken)){
-            String currentUserEmail = authentication.getName();
-            User customer = userRepository.findByEmail(currentUserEmail).orElseThrow(() -> new UsernameNotFoundException("Customer Not found"));
-            List<OrderDto> customerOrders =  orderService.listOrdersByCustomer(customer);
-            return customerOrders;
-        }else{
-            throw new EcommerceApiException("User not authenticated", HttpStatus.BAD_REQUEST);
-        }
+
+        User customer = commonService.getCurrentAuthenticatedUser(authentication);
+
+        List<OrderDto> customerOrders =  orderService.listOrdersByCustomer(customer);
+        return customerOrders;
     }
 
+
+
     //find ordered items by Customer
     @isAuthenticatedAsAdminOrUser
     @GetMapping("/findOrderedItemsByCustomer")
     public List<OrderProductsDto> findOrderedItemsByCustomer(@AuthenticationPrincipal Authentication authentication){
-        authentication = SecurityContextHolder.getContext().getAuthentication();
-        if (!(authentication instanceof AnonymousAuthenticationToken)){
-            String currentUserEmail = authentication.getName();
-            User customer = userRepository.findByEmail(currentUserEmail).orElseThrow(() -> new UsernameNotFoundException("Customer Not found"));
-            List<OrderProductsDto> customerOrderedItems =  orderProductsService.findOrderItemsByCustomer(customer);
-            return customerOrderedItems;
-        }else{
-            throw new EcommerceApiException("User not authenticated", HttpStatus.BAD_REQUEST);
-        }
+        User customer = commonService.getCurrentAuthenticatedUser(authentication);
+        List<OrderProductsDto> customerOrderedItems =  orderProductsService.findOrderItemsByCustomer(customer);
+        return customerOrderedItems;
     }
+
+
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/controller/ShoppingCartController.java b/src/main/java/com/manir/springbootecommercerestapi/controller/ShoppingCartController.java
index 7cac9e1..c3bdfc5 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/controller/ShoppingCartController.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/controller/ShoppingCartController.java
@@ -1,20 +1,15 @@
 package com.manir.springbootecommercerestapi.controller;
 
-import com.manir.springbootecommercerestapi.exception.EcommerceApiException;
 import com.manir.springbootecommercerestapi.model.User;
-import com.manir.springbootecommercerestapi.repository.UserRepository;
 import com.manir.springbootecommercerestapi.response.CartItemResponse;
-import com.manir.springbootecommercerestapi.service.OrderService;
+import com.manir.springbootecommercerestapi.service.CommonService;
 import com.manir.springbootecommercerestapi.service.ShoppingCartService;
 import com.manir.springbootecommercerestapi.utils.isAuthenticatedAsAdminOrUser;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.authentication.AnonymousAuthenticationToken;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.annotation.AuthenticationPrincipal;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.core.userdetails.UsernameNotFoundException;
 import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
@@ -26,26 +21,16 @@ public class ShoppingCartController {
     @Resource
     private ShoppingCartService shoppingCartService;
     @Autowired
-    private UserRepository userRepository;
-    @Autowired
-    private OrderService orderService;
+    private CommonService commonService;
 
     //find by customer api
     @isAuthenticatedAsAdminOrUser
     @GetMapping("/findByCustomer")
     public CartItemResponse findByCustomerId(@AuthenticationPrincipal Authentication authentication){
-        authentication = SecurityContextHolder.getContext().getAuthentication();
-        if (!(authentication instanceof AnonymousAuthenticationToken)) {
-            String currentUserEmail = authentication.getName();
-            //System.out.println("Name:" + currentUserEmail);
-            User customer = userRepository.findByEmail(currentUserEmail).orElseThrow(()-> new UsernameNotFoundException("Customer not found"));
-            CartItemResponse responseCartItems = shoppingCartService.findByCustomer(customer);
-            return responseCartItems;
-
-        }else{
-            throw new EcommerceApiException("User not authenticated", HttpStatus.BAD_REQUEST);
-        }
 
+        User customer = commonService.getCurrentAuthenticatedUser(authentication);
+        CartItemResponse responseCartItems = shoppingCartService.findByCustomer(customer);
+        return responseCartItems;
     }
 
     //add item to the cart api
@@ -54,15 +39,10 @@ public CartItemResponse findByCustomerId(@AuthenticationPrincipal Authentication
     public ResponseEntity<CartItemResponse> addCartItem(@AuthenticationPrincipal Authentication authentication,
                                                         @PathVariable Long productId,
                                                         @PathVariable Integer quantity){
-        authentication = SecurityContextHolder.getContext().getAuthentication();
-        if (!(authentication instanceof AnonymousAuthenticationToken)){
-            String currentUserEmail = authentication.getName();
-            User customer = userRepository.findByEmail(currentUserEmail).orElseThrow(() -> new UsernameNotFoundException("Customer not found"));
-            CartItemResponse responseCartItem = shoppingCartService.addCartItem(customer, productId, quantity);
-            return new ResponseEntity<>(responseCartItem, HttpStatus.CREATED);
-        }else {
-            throw new EcommerceApiException("User not authenticated", HttpStatus.BAD_REQUEST);
-        }
+
+        User customer = commonService.getCurrentAuthenticatedUser(authentication);
+        CartItemResponse responseCartItem = shoppingCartService.addCartItem(customer, productId, quantity);
+        return new ResponseEntity<>(responseCartItem, HttpStatus.CREATED);
     }
 
     //update item quantity api
@@ -71,15 +51,9 @@ public ResponseEntity<CartItemResponse> addCartItem(@AuthenticationPrincipal Aut
     public ResponseEntity<CartItemResponse> updateItemQuantity(@AuthenticationPrincipal Authentication authentication,
                                                                @PathVariable Long productId,
                                                                @PathVariable Integer quantity){
-        authentication = SecurityContextHolder.getContext().getAuthentication();
-        if (!(authentication instanceof AnonymousAuthenticationToken)){
-            String currentUserEmail = authentication.getName();
-            User customer = userRepository.findByEmail(currentUserEmail).orElseThrow(() -> new UsernameNotFoundException("Customer Not found"));
-            CartItemResponse responseCartItem = shoppingCartService.updateItemQuantity(customer, productId, quantity);
-            return  new ResponseEntity<>(responseCartItem, HttpStatus.OK);
-        }else{
-            throw new EcommerceApiException("User not authenticated", HttpStatus.BAD_REQUEST);
-        }
+        User customer = commonService.getCurrentAuthenticatedUser(authentication);
+        CartItemResponse responseCartItem = shoppingCartService.updateItemQuantity(customer, productId, quantity);
+        return  new ResponseEntity<>(responseCartItem, HttpStatus.OK);
     }
 
     //delete item product api
@@ -87,15 +61,10 @@ public ResponseEntity<CartItemResponse> updateItemQuantity(@AuthenticationPrinci
     @DeleteMapping("/deleteItemProduct/{productId}")
     public ResponseEntity<String> deleteItemProduct(@AuthenticationPrincipal Authentication authentication,
                                                     @PathVariable Long productId){
-        authentication = SecurityContextHolder.getContext().getAuthentication();
-        if (!(authentication instanceof AnonymousAuthenticationToken)){
-            String currentUserEmail = authentication.getName();
-            User customer = userRepository.findByEmail(currentUserEmail).orElseThrow(() -> new UsernameNotFoundException("Customer Not found"));
-            shoppingCartService.deleteItemProduct(customer, productId);
-            return ResponseEntity.ok("Product with id = " + productId +" is deleted successfully from your shopping cart");
-        }else{
-            throw new EcommerceApiException("User not authenticated", HttpStatus.BAD_REQUEST);
-        }
+
+        User customer = commonService.getCurrentAuthenticatedUser(authentication);
+        shoppingCartService.deleteItemProduct(customer, productId);
+        return ResponseEntity.ok("Product with id = " + productId +" is deleted successfully from your shopping cart");
     }
 
 
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/CommonService.java b/src/main/java/com/manir/springbootecommercerestapi/service/CommonService.java
index 1dfce02..e2156a2 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/CommonService.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/CommonService.java
@@ -1,9 +1,11 @@
 package com.manir.springbootecommercerestapi.service;
 
 import com.manir.springbootecommercerestapi.dto.CartItemDto;
+import com.manir.springbootecommercerestapi.model.User;
 import com.manir.springbootecommercerestapi.response.CartItemResponse;
 import com.manir.springbootecommercerestapi.response.CommonResponse;
 import org.springframework.data.domain.Page;
+import org.springframework.security.core.Authentication;
 
 import java.util.List;
 
@@ -14,4 +16,7 @@ public interface CommonService<T> {
 
     //cart iem response handler
     CartItemResponse getResponse(CartItemDto cartItemDto);
+
+    //get current authenticated user
+    User getCurrentAuthenticatedUser(Authentication authentication);
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/CommonServiceImpl.java b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/CommonServiceImpl.java
index de3fec7..e8cfb9c 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/CommonServiceImpl.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/CommonServiceImpl.java
@@ -1,24 +1,39 @@
 package com.manir.springbootecommercerestapi.service.Impl;
 
 import com.manir.springbootecommercerestapi.dto.CartItemDto;
+import com.manir.springbootecommercerestapi.exception.EcommerceApiException;
+import com.manir.springbootecommercerestapi.model.User;
+import com.manir.springbootecommercerestapi.repository.UserRepository;
 import com.manir.springbootecommercerestapi.response.CartItemResponse;
 import com.manir.springbootecommercerestapi.response.CommonResponse;
 import com.manir.springbootecommercerestapi.service.CommonService;
+import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.data.domain.Page;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
 import org.springframework.stereotype.Service;
 
+
+import javax.annotation.Resource;
 import java.util.ArrayList;
 import java.util.List;
 
 @Service
 @Slf4j
+@AllArgsConstructor
 public class CommonServiceImpl implements CommonService{
 
     private static Logger logger = LoggerFactory.getLogger(CategoryServiceImpl.class);
 
+    @Resource(name = "userRepository")
+    private final UserRepository userRepository;
+
     @Override
     public CommonResponse getResponseContent(Page page, List dtoList) {
 
@@ -45,4 +60,19 @@ public CartItemResponse getResponse(CartItemDto cartItemDto) {
         cartItemResponse.setTotalCost(totalPrice);
         return cartItemResponse;
     }
+
+    @Override
+    public User getCurrentAuthenticatedUser(Authentication authentication) {
+        authentication = SecurityContextHolder.getContext().getAuthentication();
+        if (authentication == null || authentication instanceof AnonymousAuthenticationToken){
+            throw new EcommerceApiException("User not authenticated", HttpStatus.BAD_REQUEST);
+        }
+        String currentUserEmail = authentication.getName();
+        User currentUser = userRepository.findByEmail(currentUserEmail)
+                                         .orElseThrow(
+                                                () -> new UsernameNotFoundException("User Not found")
+                                         );
+
+        return currentUser;
+    }
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/OrderServiceImpl.java b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/OrderServiceImpl.java
index 8b58f61..40527be 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/OrderServiceImpl.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/OrderServiceImpl.java
@@ -41,12 +41,9 @@ public class OrderServiceImpl implements OrderService {
     @Transactional
     public void placeOrder(User customer) {
         CartItemResponse cartItemDto = shoppingCartService.findByCustomer(customer);
-        OrderDto orderDto = new OrderDto();
-        orderDto.setTotalPrice(cartItemDto.getTotalCost());
-        orderDto.setEmail(customer.getEmail());
-        orderDto.setName(customer.getName());
-        orderDto.setCustomer(customer);
-        OrderDto savedOrder = saveOrder(orderDto, customer);
+        OrderDto orderDto = setFields(cartItemDto, customer);
+        //save order to the db
+        OrderDto savedOrder = saveOrder(orderDto);
         List<CartItemDto> cartItemDtoList = cartItemDto.getContent();
         for(CartItemDto cartItem : cartItemDtoList){
             OrderProducts orderProducts = new OrderProducts();
@@ -62,7 +59,7 @@ public void placeOrder(User customer) {
     }
 
     @Override
-    public OrderDto saveOrder(OrderDto orderDto, User customer) {
+    public OrderDto saveOrder(OrderDto orderDto) {
         //convert to entity
         Order order = mapToEntity(orderDto);
         //save order to db
@@ -70,6 +67,15 @@ public OrderDto saveOrder(OrderDto orderDto, User customer) {
         return mapToDto(placedOrder);
     }
 
+    private OrderDto setFields(CartItemResponse cartItemDto, User customer){
+        OrderDto orderDto = new OrderDto();
+        orderDto.setTotalPrice(cartItemDto.getTotalCost());
+        orderDto.setEmail(customer.getEmail());
+        orderDto.setName(customer.getName());
+        orderDto.setCustomer(customer);
+
+        return orderDto;
+    }
     @Override
     public List<OrderDto> listOrdersByCustomer(User customer) {
         List<Order> orders = orderRepository.findByCustomer(customer);
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/OrderService.java b/src/main/java/com/manir/springbootecommercerestapi/service/OrderService.java
index e2d9da9..2ff968d 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/OrderService.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/OrderService.java
@@ -8,6 +8,6 @@
 public interface OrderService {
 
     void placeOrder(User customer);
-    OrderDto saveOrder(OrderDto orderDto, User customer);
+    OrderDto saveOrder(OrderDto orderDto);
     List<OrderDto> listOrdersByCustomer(User customer);
 }

From 4ebd51faf7c6d9d51cb20e5906c0cd429c5aebf7 Mon Sep 17 00:00:00 2001
From: manirDev <mhtnourmhtmjr@gmail.com>
Date: Wed, 16 Nov 2022 17:08:38 +0300
Subject: [PATCH 11/16] Auditing and Comment code refactors.

---
 ...SpringBootECommerceRestApiApplication.java |  2 +
 .../audit/AuditAwareImpl.java                 | 15 ++++++++
 .../controller/CommentController.java         | 27 +++++++++++--
 .../model/BaseEntity.java                     | 38 +++++++++++++++++++
 .../model/CartItem.java                       |  2 +-
 .../model/Category.java                       |  2 +-
 .../model/Comment.java                        |  7 +++-
 .../model/ImageData.java                      |  2 +-
 .../model/Order.java                          |  2 +-
 .../model/OrderProducts.java                  |  2 +-
 .../model/Product.java                        |  2 +-
 .../model/Role.java                           |  2 +-
 .../model/User.java                           |  8 +++-
 .../repository/CommentRepository.java         |  2 +
 .../service/CommentService.java               |  4 +-
 .../service/Impl/CommentServiceImpl.java      | 25 +++++++++++-
 .../service/Impl/OrderServiceImpl.java        |  1 +
 17 files changed, 128 insertions(+), 15 deletions(-)
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/audit/AuditAwareImpl.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/model/BaseEntity.java

diff --git a/src/main/java/com/manir/springbootecommercerestapi/SpringBootECommerceRestApiApplication.java b/src/main/java/com/manir/springbootecommercerestapi/SpringBootECommerceRestApiApplication.java
index 4c07638..6b03645 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/SpringBootECommerceRestApiApplication.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/SpringBootECommerceRestApiApplication.java
@@ -4,8 +4,10 @@
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.context.annotation.Bean;
+import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
 
 @SpringBootApplication
+@EnableJpaAuditing(auditorAwareRef = "auditAwareImpl")
 public class SpringBootECommerceRestApiApplication {
 
 	public static void main(String[] args) {
diff --git a/src/main/java/com/manir/springbootecommercerestapi/audit/AuditAwareImpl.java b/src/main/java/com/manir/springbootecommercerestapi/audit/AuditAwareImpl.java
new file mode 100644
index 0000000..2ba6d01
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/audit/AuditAwareImpl.java
@@ -0,0 +1,15 @@
+package com.manir.springbootecommercerestapi.audit;
+
+import org.springframework.data.domain.AuditorAware;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Component;
+
+import java.util.Optional;
+
+@Component("auditAwareImpl")
+public class AuditAwareImpl implements AuditorAware<String> {
+    @Override
+    public Optional<String> getCurrentAuditor() {
+        return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication().getName());
+    }
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/controller/CommentController.java b/src/main/java/com/manir/springbootecommercerestapi/controller/CommentController.java
index b10c997..b119ef8 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/controller/CommentController.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/controller/CommentController.java
@@ -1,9 +1,15 @@
 package com.manir.springbootecommercerestapi.controller;
 
 import com.manir.springbootecommercerestapi.dto.CommentDto;
+import com.manir.springbootecommercerestapi.model.User;
 import com.manir.springbootecommercerestapi.service.CommentService;
+import com.manir.springbootecommercerestapi.service.CommonService;
+import com.manir.springbootecommercerestapi.utils.isAuthenticatedAsAdminOrUser;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
 import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
@@ -13,17 +19,30 @@
 @RequestMapping(value = "api/v1/products")
 public class CommentController {
 
-    @Resource
+    @Autowired
     private CommentService commentService;
+    @Autowired
+    private CommonService commonService;
 
     //create comment api
+    @isAuthenticatedAsAdminOrUser
     @PostMapping("/{productId}/createComment")
-    public ResponseEntity<CommentDto> createComment(@PathVariable Long productId,
+    public ResponseEntity<CommentDto> createComment(@AuthenticationPrincipal Authentication authentication,
+                                                    @PathVariable Long productId,
                                                     @RequestBody CommentDto commentDto){
-        CommentDto responseComment = commentService.createComment(productId, commentDto);
+        User customer = commonService.getCurrentAuthenticatedUser(authentication);
+        CommentDto responseComment = commentService.createComment(customer, productId, commentDto);
         return new ResponseEntity<>(responseComment, HttpStatus.CREATED);
     }
 
+    //get comment by user
+    @isAuthenticatedAsAdminOrUser
+    @GetMapping("/comment/findByUser")
+    public List<CommentDto> findByUser(@AuthenticationPrincipal Authentication authentication){
+        User customer = commonService.getCurrentAuthenticatedUser(authentication);
+        return commentService.findCommentByCustomer(customer);
+    }
+
     //get all comments api
     @GetMapping("/getAllComments")
     public List<CommentDto> getAllComments(){
@@ -58,4 +77,6 @@ public ResponseEntity<String> deleteComment(@PathVariable Long productId, @PathV
         commentService.deleteComment(productId, commentId);
         return ResponseEntity.ok("Comment with id: "+commentId+" is successfully:)");
     }
+
+
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/model/BaseEntity.java b/src/main/java/com/manir/springbootecommercerestapi/model/BaseEntity.java
new file mode 100644
index 0000000..e9e620f
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/model/BaseEntity.java
@@ -0,0 +1,38 @@
+package com.manir.springbootecommercerestapi.model;
+
+import lombok.Data;
+import org.springframework.data.annotation.CreatedBy;
+import org.springframework.data.annotation.CreatedDate;
+import org.springframework.data.annotation.LastModifiedBy;
+import org.springframework.data.annotation.LastModifiedDate;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+import javax.persistence.Column;
+import javax.persistence.EntityListeners;
+import javax.persistence.MappedSuperclass;
+import java.time.LocalDateTime;
+
+@Data
+@MappedSuperclass
+/**
+  Auditing allows us : to see who created data and who updated
+ then we should enable jpa auditing...
+ **/
+@EntityListeners(AuditingEntityListener.class)
+public abstract class BaseEntity {
+
+    @CreatedDate
+    @Column(name = "created_at", updatable = false)
+    private LocalDateTime createdAt;
+    @CreatedBy
+    @Column(updatable = false)
+    private String createdBy;
+
+    @LastModifiedDate
+    @Column(name = "updated_at", insertable = false)
+    private LocalDateTime updatedAt;
+    @LastModifiedBy
+    @Column(insertable = false)
+    private String updatedBy;
+
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/model/CartItem.java b/src/main/java/com/manir/springbootecommercerestapi/model/CartItem.java
index 70155da..5a1c4ea 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/model/CartItem.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/model/CartItem.java
@@ -11,7 +11,7 @@
 @Data
 @Entity
 @Table(name = "cart_item")
-public class CartItem {
+public class CartItem extends BaseEntity{
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
diff --git a/src/main/java/com/manir/springbootecommercerestapi/model/Category.java b/src/main/java/com/manir/springbootecommercerestapi/model/Category.java
index 6d4e01c..3862154 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/model/Category.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/model/Category.java
@@ -14,7 +14,7 @@
 @Entity
 @Table(name = "categories")
 @JsonIgnoreProperties(value = {"children"})
-public class Category {
+public class Category extends BaseEntity{
 
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
diff --git a/src/main/java/com/manir/springbootecommercerestapi/model/Comment.java b/src/main/java/com/manir/springbootecommercerestapi/model/Comment.java
index 986eaa7..cd69ca0 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/model/Comment.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/model/Comment.java
@@ -9,7 +9,7 @@
 @Getter
 @Entity
 @Table(name = "product_comments")
-public class Comment {
+public class Comment extends BaseEntity{
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
@@ -23,4 +23,9 @@ public class Comment {
     @ManyToOne(fetch = FetchType.LAZY)
     @JoinColumn(name = "product_id")
     private Product product;
+
+    //relation with user
+    @ManyToOne()
+    @JoinColumn(name = "customer_id")
+    private User customer;
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/model/ImageData.java b/src/main/java/com/manir/springbootecommercerestapi/model/ImageData.java
index 2a76534..45b3d3d 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/model/ImageData.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/model/ImageData.java
@@ -10,7 +10,7 @@
 @NoArgsConstructor
 @Entity
 @Table(name = "product_image_gallery")
-public class ImageData {
+public class ImageData extends BaseEntity{
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
diff --git a/src/main/java/com/manir/springbootecommercerestapi/model/Order.java b/src/main/java/com/manir/springbootecommercerestapi/model/Order.java
index 73e9f68..189875c 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/model/Order.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/model/Order.java
@@ -11,7 +11,7 @@
 @Setter
 @Entity
 @Table(name = "orders")
-public class Order {
+public class Order extends BaseEntity{
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
diff --git a/src/main/java/com/manir/springbootecommercerestapi/model/OrderProducts.java b/src/main/java/com/manir/springbootecommercerestapi/model/OrderProducts.java
index b9362c5..084ea70 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/model/OrderProducts.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/model/OrderProducts.java
@@ -11,7 +11,7 @@
 @Data
 @Entity
 @Table(name = "order_products")
-public class OrderProducts {
+public class OrderProducts extends BaseEntity{
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
diff --git a/src/main/java/com/manir/springbootecommercerestapi/model/Product.java b/src/main/java/com/manir/springbootecommercerestapi/model/Product.java
index 78c7f3b..1333432 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/model/Product.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/model/Product.java
@@ -12,7 +12,7 @@
 @Setter
 @Entity
 @Table(name = "products")
-public class Product {
+public class Product extends BaseEntity{
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
diff --git a/src/main/java/com/manir/springbootecommercerestapi/model/Role.java b/src/main/java/com/manir/springbootecommercerestapi/model/Role.java
index 3ad1a0e..4f792f4 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/model/Role.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/model/Role.java
@@ -7,7 +7,7 @@
 @Data
 @Entity
 @Table(name = "roles")
-public class Role {
+public class Role extends BaseEntity{
 
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
diff --git a/src/main/java/com/manir/springbootecommercerestapi/model/User.java b/src/main/java/com/manir/springbootecommercerestapi/model/User.java
index edc66b9..dcda1f3 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/model/User.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/model/User.java
@@ -13,7 +13,7 @@
 @Table(name = "users", uniqueConstraints = {@UniqueConstraint(columnNames = {"userName"}),
         @UniqueConstraint(columnNames = {"email"})
 })
-public class User {
+public class User extends BaseEntity{
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     private Long id;
@@ -46,4 +46,10 @@ public class User {
             fetch = FetchType.LAZY, orphanRemoval = true,
             mappedBy = "customer")
     private Set<OrderProducts> orderProducts;
+
+    //relation with comment or review
+    @OneToMany(cascade = CascadeType.ALL,
+            fetch = FetchType.LAZY, orphanRemoval = true,
+            mappedBy = "customer")
+    private Set<Comment> comments;
  }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/repository/CommentRepository.java b/src/main/java/com/manir/springbootecommercerestapi/repository/CommentRepository.java
index 74fb923..8fa17c5 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/repository/CommentRepository.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/repository/CommentRepository.java
@@ -1,10 +1,12 @@
 package com.manir.springbootecommercerestapi.repository;
 
 import com.manir.springbootecommercerestapi.model.Comment;
+import com.manir.springbootecommercerestapi.model.User;
 import org.springframework.data.jpa.repository.JpaRepository;
 
 import java.util.List;
 
 public interface CommentRepository extends JpaRepository<Comment, Long> {
     List<Comment> findByProductId(Long productId);
+    List<Comment> findByCustomer(User customer);
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/CommentService.java b/src/main/java/com/manir/springbootecommercerestapi/service/CommentService.java
index 1741fed..575ad74 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/CommentService.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/CommentService.java
@@ -2,13 +2,15 @@
 
 
 import com.manir.springbootecommercerestapi.dto.CommentDto;
+import com.manir.springbootecommercerestapi.model.User;
 
 import java.util.List;
 
 public interface CommentService {
 
-    CommentDto createComment(Long productId, CommentDto commentDto);
+    CommentDto createComment(User customer, Long productId, CommentDto commentDto);
 
+    List<CommentDto> findCommentByCustomer(User customer);
     List<CommentDto> getAllComments();
     List<CommentDto> getAllCommentsByProductId(Long productId);
     CommentDto getCommentById(Long productId, Long commentId);
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/CommentServiceImpl.java b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/CommentServiceImpl.java
index fcbb875..f3ade8d 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/CommentServiceImpl.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/CommentServiceImpl.java
@@ -3,10 +3,12 @@
 import com.manir.springbootecommercerestapi.dto.CommentDto;
 import com.manir.springbootecommercerestapi.exception.EcommerceApiException;
 import com.manir.springbootecommercerestapi.exception.ResourceNotFoundException;
+import com.manir.springbootecommercerestapi.model.User;
 import com.manir.springbootecommercerestapi.repository.CommentRepository;
 import com.manir.springbootecommercerestapi.repository.ProductRepository;
 import com.manir.springbootecommercerestapi.model.Comment;
 import com.manir.springbootecommercerestapi.model.Product;
+import com.manir.springbootecommercerestapi.repository.UserRepository;
 import com.manir.springbootecommercerestapi.service.CommentService;
 import org.modelmapper.ModelMapper;
 import org.springframework.http.HttpStatus;
@@ -25,15 +27,18 @@ public class CommentServiceImpl implements CommentService {
     private CommentRepository commentRepository;
     @Resource
     private ProductRepository productRepository;
+    @Resource
+    private UserRepository userRepository;
 
     @Override
-    public CommentDto createComment(Long productId, CommentDto commentDto) {
-
+    public CommentDto createComment(User customer, Long productId, CommentDto commentDto) {
+        User user = findCustomerById(customer.getId());
         Product product = findProductById(productId);
         //convert to entity
         Comment comment = mapToEntity(commentDto);
         //save to db
         comment.setProduct(product);
+        comment.setCustomer(user);
         Comment createdComment = commentRepository.save(comment);
         //convert to dto
         CommentDto responseComment = mapToDto(createdComment);
@@ -41,6 +46,18 @@ public CommentDto createComment(Long productId, CommentDto commentDto) {
         return responseComment;
     }
 
+    @Override
+    public List<CommentDto> findCommentByCustomer(User customer) {
+        List<Comment> comments = commentRepository.findByCustomer(customer);
+        if (comments.size() == 0){
+            throw new EcommerceApiException("User has no comment or review", HttpStatus.BAD_REQUEST);
+        }
+        List<CommentDto> commentDtoList = comments.stream()
+                                                            .map(comment -> mapToDto(comment))
+                                                            .collect(Collectors.toList());
+        return commentDtoList;
+    }
+
     @Override
     public List<CommentDto> getAllComments() {
         List<Comment> comments = commentRepository.findAll();
@@ -120,4 +137,8 @@ private Comment findCommentById(Long commentId){
         return comment;
     }
 
+    private User findCustomerById(Long customerId){
+        User customer = userRepository.findById(customerId).orElseThrow(()->new ResourceNotFoundException("Customer", customerId));
+        return customer;
+    }
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/OrderServiceImpl.java b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/OrderServiceImpl.java
index 40527be..72a6138 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/OrderServiceImpl.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/OrderServiceImpl.java
@@ -41,6 +41,7 @@ public class OrderServiceImpl implements OrderService {
     @Transactional
     public void placeOrder(User customer) {
         CartItemResponse cartItemDto = shoppingCartService.findByCustomer(customer);
+        //set order fields
         OrderDto orderDto = setFields(cartItemDto, customer);
         //save order to the db
         OrderDto savedOrder = saveOrder(orderDto);

From 942ccfff6714bb964d1d1ad4aaed07bf3894ff6a Mon Sep 17 00:00:00 2001
From: manirDev <mhtnourmhtmjr@gmail.com>
Date: Thu, 17 Nov 2022 16:13:26 +0300
Subject: [PATCH 12/16] Setting, Contact profile are implemented

---
 .../config/SecurityConfig.java                |  3 +-
 .../controller/ContactController.java         | 33 ++++++++
 .../controller/ProductController.java         |  6 ++
 .../controller/SettingController.java         | 41 ++++++++++
 .../dto/ContactDto.java                       | 15 ++++
 .../dto/FaqDto.java                           | 11 +++
 .../dto/ProfileDto.java                       | 11 +++
 .../dto/SettingDto.java                       | 27 +++++++
 .../model/Contact.java                        | 26 +++++++
 .../springbootecommercerestapi/model/Faq.java | 22 ++++++
 .../model/Profile.java                        | 27 +++++++
 .../model/Setting.java                        | 38 ++++++++++
 .../model/User.java                           |  4 +
 .../repository/ContactRepository.java         |  7 ++
 .../repository/ProductRepository.java         | 12 +++
 .../repository/SettingRepository.java         |  7 ++
 .../service/ContactService.java               | 11 +++
 .../service/Impl/ContactServiceImpl.java      | 57 ++++++++++++++
 .../service/Impl/ProductServiceImpl.java      | 14 ++++
 .../service/Impl/SettingServiceImpl.java      | 75 +++++++++++++++++++
 .../service/ProductService.java               |  2 +
 .../service/SettingService.java               |  8 ++
 .../utils/RequestClientIP.java                | 41 ++++++++++
 23 files changed, 497 insertions(+), 1 deletion(-)
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/controller/ContactController.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/controller/SettingController.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/dto/ContactDto.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/dto/FaqDto.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/dto/ProfileDto.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/dto/SettingDto.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/model/Contact.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/model/Faq.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/model/Profile.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/model/Setting.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/repository/ContactRepository.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/repository/SettingRepository.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/service/ContactService.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/service/Impl/ContactServiceImpl.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/service/Impl/SettingServiceImpl.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/service/SettingService.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/utils/RequestClientIP.java

diff --git a/src/main/java/com/manir/springbootecommercerestapi/config/SecurityConfig.java b/src/main/java/com/manir/springbootecommercerestapi/config/SecurityConfig.java
index cb723b4..e2a691e 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/config/SecurityConfig.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/config/SecurityConfig.java
@@ -48,7 +48,8 @@ protected void configure(HttpSecurity http) throws Exception {
                 //to permit all get request and secure post put and delete methods
                 .antMatchers(HttpMethod.GET, "/api/**").permitAll()
                 //authorize singIn and signUp
-                .antMatchers("/api/v1/auth/**").permitAll()
+                .antMatchers("/api/v*/auth/**").permitAll()
+                .antMatchers(HttpMethod.POST, "/api/v*/contact/**").permitAll()
                 .anyRequest()
                 .authenticated();
 
diff --git a/src/main/java/com/manir/springbootecommercerestapi/controller/ContactController.java b/src/main/java/com/manir/springbootecommercerestapi/controller/ContactController.java
new file mode 100644
index 0000000..d440aed
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/controller/ContactController.java
@@ -0,0 +1,33 @@
+package com.manir.springbootecommercerestapi.controller;
+
+import com.manir.springbootecommercerestapi.dto.ContactDto;
+import com.manir.springbootecommercerestapi.service.ContactService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+
+@RestController
+@RequestMapping(value = "api/v1/contact")
+public class ContactController {
+    @Autowired
+    private ContactService contactService;
+
+    @PostMapping("/sendMessage")
+    public ResponseEntity<ContactDto> sendMessage(@RequestBody ContactDto contactDto,
+                                                  HttpServletRequest request){
+        ContactDto responseMessage =  contactService.sendMessage(contactDto, request);
+        return new ResponseEntity<>(responseMessage, HttpStatus.CREATED);
+    }
+
+    //get messages api
+    @PreAuthorize("hasRole('ADMIN')")
+    @GetMapping("/allMessages")
+    public List<ContactDto> getAllMessages(){
+        return contactService.getMessages();
+    }
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/controller/ProductController.java b/src/main/java/com/manir/springbootecommercerestapi/controller/ProductController.java
index d5e2605..b34e2ed 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/controller/ProductController.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/controller/ProductController.java
@@ -77,4 +77,10 @@ public  ResponseEntity<String> deleteProduct(@PathVariable Long productId){
         return new ResponseEntity<>("Product with id: "+ productId +" is deleted successfully:)", HttpStatus.OK);
     }
 
+    //search product api
+    @GetMapping("/search")
+    public List<ProductDto> searchProduct(@RequestParam(value = "query") String query){
+        return productService.searchProduct(query);
+    }
+
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/controller/SettingController.java b/src/main/java/com/manir/springbootecommercerestapi/controller/SettingController.java
new file mode 100644
index 0000000..053edbd
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/controller/SettingController.java
@@ -0,0 +1,41 @@
+package com.manir.springbootecommercerestapi.controller;
+
+import com.manir.springbootecommercerestapi.dto.SettingDto;
+import com.manir.springbootecommercerestapi.model.Setting;
+import com.manir.springbootecommercerestapi.repository.SettingRepository;
+import com.manir.springbootecommercerestapi.service.SettingService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Optional;
+
+@RestController
+@RequestMapping(value = "api/v1/setting")
+public class SettingController {
+    @Autowired
+    private SettingService settingService;
+    @Autowired
+    private SettingRepository settingRepository;
+
+    //post first setting api
+    @PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/createSetting")
+    public ResponseEntity<SettingDto> createSetting(@RequestBody SettingDto settingDto){
+        SettingDto createdSetting = settingService.addSettingFirstTime(settingDto);
+        return new ResponseEntity<>(createdSetting, HttpStatus.CREATED);
+    }
+
+    //edit existing setting api
+    @PreAuthorize("hasRole('ADMIN')")
+    @PutMapping("/editSetting")
+    public ResponseEntity<SettingDto> editSetting(@RequestBody SettingDto settingDto){
+        Optional<Setting> existingSetting = settingRepository.findAll().stream().findFirst();
+        SettingDto editedSetting = settingService.updateSetting(settingDto, existingSetting.get().getId());
+
+        return ResponseEntity.ok(editedSetting);
+    }
+
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/dto/ContactDto.java b/src/main/java/com/manir/springbootecommercerestapi/dto/ContactDto.java
new file mode 100644
index 0000000..338891d
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/dto/ContactDto.java
@@ -0,0 +1,15 @@
+package com.manir.springbootecommercerestapi.dto;
+
+import lombok.Data;
+
+@Data
+public class ContactDto {
+    private Long id;
+    private String name;
+    private String email;
+    private String phone;
+    private String subject;
+    private String message;
+    private String ipAddress;
+    private String status;
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/dto/FaqDto.java b/src/main/java/com/manir/springbootecommercerestapi/dto/FaqDto.java
new file mode 100644
index 0000000..32586c8
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/dto/FaqDto.java
@@ -0,0 +1,11 @@
+package com.manir.springbootecommercerestapi.dto;
+
+import lombok.Data;
+
+@Data
+public class FaqDto {
+    private Long id;
+    private String question;
+    private String answer;
+    private String status;
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/dto/ProfileDto.java b/src/main/java/com/manir/springbootecommercerestapi/dto/ProfileDto.java
new file mode 100644
index 0000000..ae0c350
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/dto/ProfileDto.java
@@ -0,0 +1,11 @@
+package com.manir.springbootecommercerestapi.dto;
+
+import lombok.Data;
+
+@Data
+public class ProfileDto {
+    private Long id;
+    private String image;
+    private String address;
+    private String phone;
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/dto/SettingDto.java b/src/main/java/com/manir/springbootecommercerestapi/dto/SettingDto.java
new file mode 100644
index 0000000..676cbc7
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/dto/SettingDto.java
@@ -0,0 +1,27 @@
+package com.manir.springbootecommercerestapi.dto;
+
+import lombok.Data;
+
+@Data
+public class SettingDto {
+    private Long id;
+    private String title;
+    private String keywords;
+    private String description;
+    private String company;
+    private String address;
+    private String phone;
+    private String fax;
+    private String email;
+    private String smtpServer;
+    private String smtpEmail;
+    private String smtpPassword;
+    private Integer smtpPort;
+    private String facebook;
+    private String instagram;
+    private String twitter;
+    private String aboutUs;
+    private String contact;
+    private String reference;
+    private String status;
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/model/Contact.java b/src/main/java/com/manir/springbootecommercerestapi/model/Contact.java
new file mode 100644
index 0000000..e321566
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/model/Contact.java
@@ -0,0 +1,26 @@
+package com.manir.springbootecommercerestapi.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.*;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+@Entity
+@Table(name = "contact")
+public class Contact extends BaseEntity{
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String name;
+    private String email;
+    private String phone;
+    private String subject;
+    private String message;
+    private String ipAddress;
+    private String status;
+
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/model/Faq.java b/src/main/java/com/manir/springbootecommercerestapi/model/Faq.java
new file mode 100644
index 0000000..369f411
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/model/Faq.java
@@ -0,0 +1,22 @@
+package com.manir.springbootecommercerestapi.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.*;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+@Entity
+@Table(name = "faqs")
+public class Faq extends BaseEntity{
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String question;
+    private String answer;
+    private String status;
+
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/model/Profile.java b/src/main/java/com/manir/springbootecommercerestapi/model/Profile.java
new file mode 100644
index 0000000..da592e4
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/model/Profile.java
@@ -0,0 +1,27 @@
+package com.manir.springbootecommercerestapi.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.*;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+@Entity
+@Table(name = "user_profile")
+public class Profile extends BaseEntity{
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String image;
+    private String address;
+    private String phone;
+
+    //relation with user
+    @OneToOne(cascade = CascadeType.ALL)
+    @JoinColumn(name = "user_id", referencedColumnName = "id")
+    private User user;
+
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/model/Setting.java b/src/main/java/com/manir/springbootecommercerestapi/model/Setting.java
new file mode 100644
index 0000000..ea839f9
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/model/Setting.java
@@ -0,0 +1,38 @@
+package com.manir.springbootecommercerestapi.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.*;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+@Entity
+@Table(name = "settings")
+public class Setting extends BaseEntity{
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+    private String title;
+    private String keywords;
+    private String description;
+    private String company;
+    private String address;
+    private String phone;
+    private String fax;
+    private String email;
+    private String smtpServer;
+    private String smtpEmail;
+    private String smtpPassword;
+    private Integer smtpPort;
+    private String facebook;
+    private String instagram;
+    private String twitter;
+    private String aboutUs;
+    private String contact;
+    private String reference;
+    private String status;
+
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/model/User.java b/src/main/java/com/manir/springbootecommercerestapi/model/User.java
index dcda1f3..5ca2a50 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/model/User.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/model/User.java
@@ -52,4 +52,8 @@ public class User extends BaseEntity{
             fetch = FetchType.LAZY, orphanRemoval = true,
             mappedBy = "customer")
     private Set<Comment> comments;
+
+    //relation with profile
+    @OneToOne(mappedBy = "user")
+    private Profile user_profile;
  }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/repository/ContactRepository.java b/src/main/java/com/manir/springbootecommercerestapi/repository/ContactRepository.java
new file mode 100644
index 0000000..3e2911a
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/repository/ContactRepository.java
@@ -0,0 +1,7 @@
+package com.manir.springbootecommercerestapi.repository;
+
+import com.manir.springbootecommercerestapi.model.Contact;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface ContactRepository extends JpaRepository<Contact, Long> {
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/repository/ProductRepository.java b/src/main/java/com/manir/springbootecommercerestapi/repository/ProductRepository.java
index 2e29f5b..419dd56 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/repository/ProductRepository.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/repository/ProductRepository.java
@@ -2,7 +2,19 @@
 
 import com.manir.springbootecommercerestapi.model.Product;
 import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+
+import java.util.List;
 
 public interface ProductRepository extends JpaRepository<Product, Long> {
 
+    @Query(
+            "SELECT p FROM Product p WHERE " +
+            "p.title LIKE CONCAT('%', :query, '%') " +
+            "or p.description LIKE CONCAT('%', :query, '%')" +
+            "or p.keywords LIKE CONCAT('%', :query, '%')" +
+            "or p.detail LIKE CONCAT('%', :query, '%')"
+          )
+    List<Product> searchProduct(String query);
+
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/repository/SettingRepository.java b/src/main/java/com/manir/springbootecommercerestapi/repository/SettingRepository.java
new file mode 100644
index 0000000..94b6cb4
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/repository/SettingRepository.java
@@ -0,0 +1,7 @@
+package com.manir.springbootecommercerestapi.repository;
+
+import com.manir.springbootecommercerestapi.model.Setting;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface SettingRepository extends JpaRepository<Setting, Long> {
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/ContactService.java b/src/main/java/com/manir/springbootecommercerestapi/service/ContactService.java
new file mode 100644
index 0000000..65223b0
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/ContactService.java
@@ -0,0 +1,11 @@
+package com.manir.springbootecommercerestapi.service;
+
+import com.manir.springbootecommercerestapi.dto.ContactDto;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+
+public interface ContactService {
+    ContactDto sendMessage(ContactDto contactDto, HttpServletRequest request);
+    List<ContactDto> getMessages();
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/ContactServiceImpl.java b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/ContactServiceImpl.java
new file mode 100644
index 0000000..db39d0a
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/ContactServiceImpl.java
@@ -0,0 +1,57 @@
+package com.manir.springbootecommercerestapi.service.Impl;
+
+import com.manir.springbootecommercerestapi.dto.ContactDto;
+import com.manir.springbootecommercerestapi.model.Contact;
+import com.manir.springbootecommercerestapi.repository.ContactRepository;
+import com.manir.springbootecommercerestapi.service.ContactService;
+import com.manir.springbootecommercerestapi.utils.RequestClientIP;
+import lombok.AllArgsConstructor;
+import org.modelmapper.ModelMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+@AllArgsConstructor
+public class ContactServiceImpl implements ContactService {
+
+    @Resource(name = "contactRepository")
+    private final ContactRepository contactRepository;
+    @Resource(name = "modelMapper")
+    private final ModelMapper modelMapper;
+    @Override
+    public ContactDto sendMessage(ContactDto contactDto, HttpServletRequest request) {
+
+        Contact contact = mapToEntity(contactDto);
+        String ipAddress = RequestClientIP.getClientIpAddress(request);
+        contact.setIpAddress(ipAddress);
+        Contact createdMessage = contactRepository.save(contact);
+
+        return mapToDto(createdMessage);
+    }
+
+    @Override
+    public List<ContactDto> getMessages() {
+        List<Contact> messages = contactRepository.findAll();
+
+        return messages.stream()
+                                .map(message -> mapToDto(message))
+                                .collect(Collectors.toList());
+    }
+
+    //map to entity
+    private Contact mapToEntity(ContactDto contactDto){
+        Contact contact = modelMapper.map(contactDto, Contact.class);
+        return contact;
+    }
+    //map to dto
+    private ContactDto mapToDto(Contact contact){
+        ContactDto contactDto = modelMapper.map(contact, ContactDto.class);
+        return contactDto;
+    }
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/ProductServiceImpl.java b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/ProductServiceImpl.java
index 2f4fd98..9b3dcb6 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/ProductServiceImpl.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/ProductServiceImpl.java
@@ -1,6 +1,7 @@
 package com.manir.springbootecommercerestapi.service.Impl;
 
 import com.manir.springbootecommercerestapi.dto.ProductDto;
+import com.manir.springbootecommercerestapi.exception.EcommerceApiException;
 import com.manir.springbootecommercerestapi.exception.ResourceNotFoundException;
 import com.manir.springbootecommercerestapi.repository.CategoryRepository;
 import com.manir.springbootecommercerestapi.repository.ProductRepository;
@@ -17,6 +18,7 @@
 import org.springframework.data.domain.PageRequest;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.domain.Sort;
+import org.springframework.http.HttpStatus;
 import org.springframework.stereotype.Service;
 import org.springframework.util.StringUtils;
 import org.springframework.web.multipart.MultipartFile;
@@ -131,6 +133,18 @@ public ProductDto saveProductByCategoryId(Long categoryId, ProductDto productDto
         return responseProduct;
     }
 
+    @Override
+    public List<ProductDto> searchProduct(String query) {
+        List<Product> products = productRepository.searchProduct(query);
+        if (products.size() == 0){
+            throw new EcommerceApiException("No product is found", HttpStatus.BAD_REQUEST);
+        }
+        List<ProductDto> productDtoList = products.stream()
+                                                          .map(product -> mapToDto(product))
+                                                          .collect(Collectors.toList());
+        return productDtoList;
+    }
+
     //upload image
     private String uploadProductImage(MultipartFile file){
         ProductDto productDto = new ProductDto();
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/SettingServiceImpl.java b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/SettingServiceImpl.java
new file mode 100644
index 0000000..b697560
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/SettingServiceImpl.java
@@ -0,0 +1,75 @@
+package com.manir.springbootecommercerestapi.service.Impl;
+
+import com.manir.springbootecommercerestapi.dto.SettingDto;
+import com.manir.springbootecommercerestapi.exception.EcommerceApiException;
+import com.manir.springbootecommercerestapi.exception.ResourceNotFoundException;
+import com.manir.springbootecommercerestapi.model.Setting;
+import com.manir.springbootecommercerestapi.repository.SettingRepository;
+import com.manir.springbootecommercerestapi.service.SettingService;
+import lombok.AllArgsConstructor;
+import org.modelmapper.ModelMapper;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.Optional;
+
+@Service
+@AllArgsConstructor
+public class SettingServiceImpl implements SettingService {
+    @Resource(name = "settingRepository")
+    private final SettingRepository settingRepository;
+    @Resource(name = "modelMapper")
+    private final ModelMapper modelMapper;
+
+    @Override
+    public SettingDto addSettingFirstTime(SettingDto settingDto) {
+
+        Optional<Setting> setting = settingRepository.findAll().stream().findFirst();
+        if (!setting.isPresent()){
+           Setting firstSetting = mapToEntity(settingDto);
+           Setting saveSetting = settingRepository.save(firstSetting);
+           return mapToDto(saveSetting);
+        }else {
+            throw new EcommerceApiException("Setting already exists, please edit it only", HttpStatus.BAD_REQUEST);
+        }
+    }
+
+    @Override
+    public SettingDto updateSetting(SettingDto settingDto, Long id) {
+        Setting setting = settingRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Setting", id));
+        setting.setTitle(settingDto.getTitle());
+        setting.setKeywords(settingDto.getKeywords());
+        setting.setDescription(settingDto.getDescription());
+        setting.setCompany(settingDto.getCompany());
+        setting.setAddress(settingDto.getAddress());
+        setting.setPhone(settingDto.getPhone());
+        setting.setFax(settingDto.getFax());
+        setting.setEmail(settingDto.getEmail());
+        setting.setSmtpServer(settingDto.getSmtpServer());
+        setting.setSmtpEmail(settingDto.getSmtpEmail());
+        setting.setSmtpPassword(settingDto.getSmtpPassword());
+        setting.setSmtpPort(settingDto.getSmtpPort());
+        setting.setFacebook(settingDto.getFacebook());
+        setting.setInstagram(settingDto.getInstagram());
+        setting.setTwitter(settingDto.getTwitter());
+        setting.setAboutUs(settingDto.getAboutUs());
+        setting.setContact(settingDto.getContact());
+        setting.setContact(settingDto.getReference());
+        //save setting changes
+        Setting editedSetting = settingRepository.save(setting);
+
+        return mapToDto(editedSetting);
+    }
+
+    //map to dto
+    private SettingDto mapToDto(Setting setting){
+        SettingDto settingDto = modelMapper.map(setting, SettingDto.class);
+        return settingDto;
+    }
+    //map to entity
+    private Setting mapToEntity(SettingDto settingDto){
+        Setting setting = modelMapper.map(settingDto, Setting.class);
+        return setting;
+    }
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/ProductService.java b/src/main/java/com/manir/springbootecommercerestapi/service/ProductService.java
index 9853f05..803e650 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/ProductService.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/ProductService.java
@@ -15,4 +15,6 @@ public interface ProductService {
     void deleteProduct(Long productId);
 
     ProductDto saveProductByCategoryId(Long categoryId, ProductDto productDto);
+
+    List<ProductDto> searchProduct(String query);
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/SettingService.java b/src/main/java/com/manir/springbootecommercerestapi/service/SettingService.java
new file mode 100644
index 0000000..05c010e
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/SettingService.java
@@ -0,0 +1,8 @@
+package com.manir.springbootecommercerestapi.service;
+
+import com.manir.springbootecommercerestapi.dto.SettingDto;
+
+public interface SettingService {
+    SettingDto addSettingFirstTime(SettingDto settingDto);
+    SettingDto updateSetting(SettingDto settingDto, Long id);
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/utils/RequestClientIP.java b/src/main/java/com/manir/springbootecommercerestapi/utils/RequestClientIP.java
new file mode 100644
index 0000000..5920839
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/utils/RequestClientIP.java
@@ -0,0 +1,41 @@
+package com.manir.springbootecommercerestapi.utils;
+
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+
+public class RequestClientIP {
+
+    private static final String[] IP_HEADER_CANDIDATES = {
+            "X-Forwarded-For",
+            "Proxy-Client-IP",
+            "WL-Proxy-Client-IP",
+            "HTTP_X_FORWARDED_FOR",
+            "HTTP_X_FORWARDED",
+            "HTTP_X_CLUSTER_CLIENT_IP",
+            "HTTP_CLIENT_IP",
+            "HTTP_FORWARDED_FOR",
+            "HTTP_FORWARDED",
+            "HTTP_VIA",
+            "REMOTE_ADDR"
+    };
+
+    public static String getClientIpAddress(HttpServletRequest request) {
+
+        if (RequestContextHolder.getRequestAttributes() == null) {
+            return "0.0.0.0";
+        }
+
+        request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+        for (String header: IP_HEADER_CANDIDATES) {
+            String ipList = request.getHeader(header);
+            if (ipList != null && ipList.length() != 0 && !"unknown".equalsIgnoreCase(ipList)) {
+                String ip = ipList.split(",")[0];
+                return ip;
+            }
+        }
+
+        return request.getRemoteAddr();
+    }
+}

From 36847a17309793b5e99a9c2306f86047000268f6 Mon Sep 17 00:00:00 2001
From: manirDev <mhtnourmhtmjr@gmail.com>
Date: Thu, 17 Nov 2022 16:56:56 +0300
Subject: [PATCH 13/16] Faq CRUD is done.

---
 .../controller/FaqController.java             | 57 ++++++++++++++++
 .../repository/FaqRepository.java             |  7 ++
 .../service/FaqService.java                   | 13 ++++
 .../service/Impl/FaqServiceImpl.java          | 68 +++++++++++++++++++
 4 files changed, 145 insertions(+)
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/controller/FaqController.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/repository/FaqRepository.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/service/FaqService.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/service/Impl/FaqServiceImpl.java

diff --git a/src/main/java/com/manir/springbootecommercerestapi/controller/FaqController.java b/src/main/java/com/manir/springbootecommercerestapi/controller/FaqController.java
new file mode 100644
index 0000000..4c3ea0a
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/controller/FaqController.java
@@ -0,0 +1,57 @@
+package com.manir.springbootecommercerestapi.controller;
+
+import com.manir.springbootecommercerestapi.dto.FaqDto;
+import com.manir.springbootecommercerestapi.service.FaqService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("api/v1/faq")
+public class FaqController {
+    @Autowired
+    private FaqService faqService;
+
+    //add faq api
+    @PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/addFaq")
+    public ResponseEntity<FaqDto> addFaq(@RequestBody FaqDto faqDto){
+        FaqDto addedFaq = faqService.addFaq(faqDto);
+        return new ResponseEntity<>(addedFaq, HttpStatus.CREATED);
+    }
+    //list all faqs api
+    @PreAuthorize("hasRole('ADMIN')")
+    @GetMapping("/getAllFaqs")
+    public List<FaqDto> listAllFaqs(){
+        List<FaqDto> faqs = faqService.listAllFaqs();
+        return faqs;
+    }
+
+    //get faq by id api
+    @PreAuthorize("hasRole('ADMIN')")
+    @GetMapping("/{id}")
+    public ResponseEntity<FaqDto> getFaqById(@PathVariable Long id){
+        FaqDto faq = faqService.getFaqById(id);
+        return new ResponseEntity<>(faq, HttpStatus.OK);
+    }
+
+    //update faq api
+    @PreAuthorize("hasRole('ADMIN')")
+    @PutMapping("/updateFaq/{id}")
+    public ResponseEntity<FaqDto> updateFaq(@RequestBody FaqDto faqDto, @PathVariable Long id){
+        FaqDto updatedFaq = faqService.updateFaq(faqDto, id);
+        return new ResponseEntity<>(updatedFaq, HttpStatus.OK);
+    }
+
+    //delete faq api
+    @PreAuthorize("hasRole('ADMIN')")
+    @DeleteMapping("/deleteFaq/{id}")
+    public ResponseEntity<String> deleteFaq(@PathVariable Long id){
+        faqService.deleteFaq(id);
+        return new ResponseEntity<>("Faq with id: "+id+ " is deleted successfully", HttpStatus.OK);
+    }
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/repository/FaqRepository.java b/src/main/java/com/manir/springbootecommercerestapi/repository/FaqRepository.java
new file mode 100644
index 0000000..3fc9ed1
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/repository/FaqRepository.java
@@ -0,0 +1,7 @@
+package com.manir.springbootecommercerestapi.repository;
+
+import com.manir.springbootecommercerestapi.model.Faq;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface FaqRepository extends JpaRepository<Faq, Long> {
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/FaqService.java b/src/main/java/com/manir/springbootecommercerestapi/service/FaqService.java
new file mode 100644
index 0000000..acfe24e
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/FaqService.java
@@ -0,0 +1,13 @@
+package com.manir.springbootecommercerestapi.service;
+
+import com.manir.springbootecommercerestapi.dto.FaqDto;
+
+import java.util.List;
+
+public interface FaqService {
+    FaqDto addFaq(FaqDto faqDto);
+    List<FaqDto> listAllFaqs();
+    FaqDto getFaqById(Long id);
+    FaqDto updateFaq(FaqDto faqDto, Long id);
+    void deleteFaq(Long id);
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/FaqServiceImpl.java b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/FaqServiceImpl.java
new file mode 100644
index 0000000..d8fba99
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/FaqServiceImpl.java
@@ -0,0 +1,68 @@
+package com.manir.springbootecommercerestapi.service.Impl;
+
+import com.manir.springbootecommercerestapi.dto.FaqDto;
+import com.manir.springbootecommercerestapi.exception.ResourceNotFoundException;
+import com.manir.springbootecommercerestapi.model.Faq;
+import com.manir.springbootecommercerestapi.repository.FaqRepository;
+import com.manir.springbootecommercerestapi.service.FaqService;
+import lombok.AllArgsConstructor;
+import org.modelmapper.ModelMapper;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+@AllArgsConstructor
+public class FaqServiceImpl implements FaqService {
+    @Resource(name = "faqRepository")
+    private final FaqRepository faqRepository;
+    @Resource(name = "modelMapper")
+    private final ModelMapper modelMapper;
+
+    @Override
+    public FaqDto addFaq(FaqDto faqDto) {
+        Faq faq = mapToEntity(faqDto);
+        Faq addedFaq = faqRepository.save(faq);
+
+        return mapToDto(addedFaq);
+    }
+
+    @Override
+    public List<FaqDto> listAllFaqs() {
+        List<Faq> faqs = faqRepository.findAll();
+        return faqs.stream().map(faq -> mapToDto(faq)).collect(Collectors.toList());
+    }
+
+    @Override
+    public FaqDto getFaqById(Long id) {
+        Faq faq = faqRepository.findById(id).orElseThrow(()->new ResourceNotFoundException("Faq", id));
+        return mapToDto(faq);
+    }
+
+    @Override
+    public FaqDto updateFaq(FaqDto faqDto, Long id) {
+        Faq faq = faqRepository.findById(id).orElseThrow(()->new ResourceNotFoundException("Faq", id));
+        faq.setQuestion(faqDto.getQuestion());
+        faq.setAnswer(faqDto.getAnswer());
+        Faq updatedFaq = faqRepository.save(faq);
+        return mapToDto(updatedFaq);
+    }
+
+    @Override
+    public void deleteFaq(Long id) {
+        Faq faq = faqRepository.findById(id).orElseThrow(()->new ResourceNotFoundException("Faq", id));
+        faqRepository.delete(faq);
+    }
+
+    private FaqDto mapToDto(Faq faq){
+        FaqDto faqDto = modelMapper.map(faq, FaqDto.class);
+        return faqDto;
+    }
+    //map to entity
+    private Faq mapToEntity(FaqDto faqDto){
+        Faq faq = modelMapper.map(faqDto, Faq.class);
+        return faq;
+    }
+}

From 57849c8a4d0080d2e3eef6dd1e7bb0d56ceab884 Mon Sep 17 00:00:00 2001
From: manirDev <mhtnourmhtmjr@gmail.com>
Date: Fri, 18 Nov 2022 15:16:57 +0300
Subject: [PATCH 14/16] Code Refactor

---
 .../springbootecommercerestapi/dto/FaqDto.java  |  3 ++-
 .../service/CommonService.java                  |  6 ++++++
 .../service/Impl/CommonServiceImpl.java         | 17 +++++++++++++++++
 .../service/Impl/FaqServiceImpl.java            |  3 +++
 4 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/src/main/java/com/manir/springbootecommercerestapi/dto/FaqDto.java b/src/main/java/com/manir/springbootecommercerestapi/dto/FaqDto.java
index 32586c8..a476603 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/dto/FaqDto.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/dto/FaqDto.java
@@ -1,9 +1,10 @@
 package com.manir.springbootecommercerestapi.dto;
 
+import com.manir.springbootecommercerestapi.model.BaseEntity;
 import lombok.Data;
 
 @Data
-public class FaqDto {
+public class FaqDto extends BaseEntity {
     private Long id;
     private String question;
     private String answer;
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/CommonService.java b/src/main/java/com/manir/springbootecommercerestapi/service/CommonService.java
index e2156a2..f973fb0 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/CommonService.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/CommonService.java
@@ -19,4 +19,10 @@ public interface CommonService<T> {
 
     //get current authenticated user
     User getCurrentAuthenticatedUser(Authentication authentication);
+
+    //entity mapper
+    T mapToEntity(T type);
+
+    //dto mapper
+    T mapToDto(T type);
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/CommonServiceImpl.java b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/CommonServiceImpl.java
index e8cfb9c..bc633ab 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/CommonServiceImpl.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/CommonServiceImpl.java
@@ -9,6 +9,7 @@
 import com.manir.springbootecommercerestapi.service.CommonService;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.modelmapper.ModelMapper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.data.domain.Page;
@@ -17,6 +18,7 @@
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Service;
 
 
@@ -27,12 +29,15 @@
 @Service
 @Slf4j
 @AllArgsConstructor
+@Component("commonService")
 public class CommonServiceImpl implements CommonService{
 
     private static Logger logger = LoggerFactory.getLogger(CategoryServiceImpl.class);
 
     @Resource(name = "userRepository")
     private final UserRepository userRepository;
+    @Resource(name = "modelMapper")
+    private final ModelMapper modelMapper;
 
     @Override
     public CommonResponse getResponseContent(Page page, List dtoList) {
@@ -75,4 +80,16 @@ public User getCurrentAuthenticatedUser(Authentication authentication) {
 
         return currentUser;
     }
+
+    @Override
+    public Object mapToEntity(Object type) {
+        Object entityObject = modelMapper.map(type, Object.class);
+        return entityObject;
+    }
+
+    @Override
+    public Object mapToDto(Object type) {
+        Object dtoObject = modelMapper.map(type, Object.class);
+        return dtoObject;
+    }
 }
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/FaqServiceImpl.java b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/FaqServiceImpl.java
index d8fba99..6ea6cb8 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/FaqServiceImpl.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/FaqServiceImpl.java
@@ -4,6 +4,7 @@
 import com.manir.springbootecommercerestapi.exception.ResourceNotFoundException;
 import com.manir.springbootecommercerestapi.model.Faq;
 import com.manir.springbootecommercerestapi.repository.FaqRepository;
+import com.manir.springbootecommercerestapi.service.CommonService;
 import com.manir.springbootecommercerestapi.service.FaqService;
 import lombok.AllArgsConstructor;
 import org.modelmapper.ModelMapper;
@@ -20,6 +21,8 @@ public class FaqServiceImpl implements FaqService {
     private final FaqRepository faqRepository;
     @Resource(name = "modelMapper")
     private final ModelMapper modelMapper;
+    @Resource(name = "commonService")
+    private final CommonService commonService;
 
     @Override
     public FaqDto addFaq(FaqDto faqDto) {

From e7907878183fff1336bc089ae45f9abd8d64e9e2 Mon Sep 17 00:00:00 2001
From: manirDev <mhtnourmhtmjr@gmail.com>
Date: Fri, 18 Nov 2022 17:30:20 +0300
Subject: [PATCH 15/16] ModelMapper code refactor using java generic

---
 .../service/Impl/FaqServiceImpl.java          |  9 ++---
 .../service/MapperService.java                |  9 +++++
 .../service/MapperServiceImpl.java            | 36 +++++++++++++++++++
 3 files changed, 50 insertions(+), 4 deletions(-)
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/service/MapperService.java
 create mode 100644 src/main/java/com/manir/springbootecommercerestapi/service/MapperServiceImpl.java

diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/FaqServiceImpl.java b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/FaqServiceImpl.java
index 6ea6cb8..567dc11 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/FaqServiceImpl.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/FaqServiceImpl.java
@@ -6,6 +6,7 @@
 import com.manir.springbootecommercerestapi.repository.FaqRepository;
 import com.manir.springbootecommercerestapi.service.CommonService;
 import com.manir.springbootecommercerestapi.service.FaqService;
+import com.manir.springbootecommercerestapi.service.MapperService;
 import lombok.AllArgsConstructor;
 import org.modelmapper.ModelMapper;
 import org.springframework.stereotype.Service;
@@ -21,15 +22,15 @@ public class FaqServiceImpl implements FaqService {
     private final FaqRepository faqRepository;
     @Resource(name = "modelMapper")
     private final ModelMapper modelMapper;
-    @Resource(name = "commonService")
-    private final CommonService commonService;
+    @Resource(name = "mapperService")
+    private final MapperService<Faq, FaqDto> mapperService;
 
     @Override
     public FaqDto addFaq(FaqDto faqDto) {
-        Faq faq = mapToEntity(faqDto);
+        Faq faq = mapperService.mapToEntity(faqDto);
         Faq addedFaq = faqRepository.save(faq);
 
-        return mapToDto(addedFaq);
+        return mapperService.mapToDto(addedFaq);
     }
 
     @Override
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/MapperService.java b/src/main/java/com/manir/springbootecommercerestapi/service/MapperService.java
new file mode 100644
index 0000000..33a4150
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/MapperService.java
@@ -0,0 +1,9 @@
+package com.manir.springbootecommercerestapi.service;
+
+public interface MapperService<E, D>{
+    //entity mapper
+    E mapToEntity(D type);
+
+    //dto mapper
+    D mapToDto(E type);
+}
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/MapperServiceImpl.java b/src/main/java/com/manir/springbootecommercerestapi/service/MapperServiceImpl.java
new file mode 100644
index 0000000..2eb67f5
--- /dev/null
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/MapperServiceImpl.java
@@ -0,0 +1,36 @@
+package com.manir.springbootecommercerestapi.service;
+
+import lombok.AllArgsConstructor;
+import org.modelmapper.ModelMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+
+@AllArgsConstructor
+@Service
+@Component("mapperService")
+public class MapperServiceImpl<E, D> implements MapperService<E, D> {
+
+
+    @Resource(name = "modelMapper")
+    private final ModelMapper modelMapper;
+    @Resource
+    private final Class<E> entityClass;
+    @Resource
+    private final Class<D> dtoClass;
+
+
+    @Override
+    public E mapToEntity(D type) {
+        E model = modelMapper.map(type, entityClass);
+        return model;
+    }
+
+    @Override
+    public D mapToDto(E type) {
+        D dto = modelMapper.map(type, dtoClass);
+        return dto;
+    }
+}

From 3b990a9004305917bb42b2b6d378357ffe87079c Mon Sep 17 00:00:00 2001
From: manirDev <mhtnourmhtmjr@gmail.com>
Date: Mon, 21 Nov 2022 14:06:53 +0300
Subject: [PATCH 16/16] model mapper refactor

---
 .../service/Impl/FaqServiceImpl.java          |  2 +-
 .../service/MapperServiceImpl.java            | 22 +++++++++++--------
 2 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/FaqServiceImpl.java b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/FaqServiceImpl.java
index 567dc11..1732722 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/Impl/FaqServiceImpl.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/Impl/FaqServiceImpl.java
@@ -36,7 +36,7 @@ public FaqDto addFaq(FaqDto faqDto) {
     @Override
     public List<FaqDto> listAllFaqs() {
         List<Faq> faqs = faqRepository.findAll();
-        return faqs.stream().map(faq -> mapToDto(faq)).collect(Collectors.toList());
+        return faqs.stream().map(faq -> mapperService.mapToDto(faq)).collect(Collectors.toList());
     }
 
     @Override
diff --git a/src/main/java/com/manir/springbootecommercerestapi/service/MapperServiceImpl.java b/src/main/java/com/manir/springbootecommercerestapi/service/MapperServiceImpl.java
index 2eb67f5..9857292 100644
--- a/src/main/java/com/manir/springbootecommercerestapi/service/MapperServiceImpl.java
+++ b/src/main/java/com/manir/springbootecommercerestapi/service/MapperServiceImpl.java
@@ -1,36 +1,40 @@
 package com.manir.springbootecommercerestapi.service;
 
-import lombok.AllArgsConstructor;
+
+import lombok.Data;
 import org.modelmapper.ModelMapper;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
 
-@AllArgsConstructor
+
 @Service
 @Component("mapperService")
+@Data
 public class MapperServiceImpl<E, D> implements MapperService<E, D> {
 
 
     @Resource(name = "modelMapper")
     private final ModelMapper modelMapper;
-    @Resource
-    private final Class<E> entityClass;
-    @Resource
-    private final Class<D> dtoClass;
 
+    private Class<E> entityClass;
+
+    private Class<D> dtoClass;
+
+    public MapperServiceImpl(ModelMapper modelMapper) {
+        this.modelMapper = modelMapper;
+    }
 
     @Override
     public E mapToEntity(D type) {
-        E model = modelMapper.map(type, entityClass);
+        E model = modelMapper.map(type, getEntityClass());
         return model;
     }
 
     @Override
     public D mapToDto(E type) {
-        D dto = modelMapper.map(type, dtoClass);
+        D dto = modelMapper.map(type, getDtoClass());
         return dto;
     }
 }