Skip to content

Commit

Permalink
#882 Create a shared project
Browse files Browse the repository at this point in the history
  • Loading branch information
nashtech-huyphamphu committed Sep 17, 2024
1 parent 3a7e601 commit dc18046
Show file tree
Hide file tree
Showing 22 changed files with 231 additions and 205 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,19 @@ public class IntegrationTestConfiguration {
@Bean(destroyMethod = "stop")
public PostgreSQLContainer<?> postgresContainer() {
return new PostgreSQLContainer<>("postgres:16")
.withReuse(true);
.withReuse(true);
}

@Bean(destroyMethod = "stop")
public KeycloakContainer keycloakContainer(DynamicPropertyRegistry registry) {
KeycloakContainer keycloak = new KeycloakContainer()
.withRealmImportFiles("/test-realm.json")
.withReuse(true);
.withRealmImportFiles("/test-realm.json")
.withReuse(true);

registry.add("spring.security.oauth2.resourceserver.jwt.issuer-uri",
() -> keycloak.getAuthServerUrl() + "/realms/quarkus");
() -> keycloak.getAuthServerUrl() + "/realms/quarkus");
registry.add("spring.security.oauth2.resourceserver.jwt.jwk-set-uri",
() -> keycloak.getAuthServerUrl() + "/realms/quarkus/protocol/openid-connect/certs");
() -> keycloak.getAuthServerUrl() + "/realms/quarkus/protocol/openid-connect/certs");
return keycloak;
}
}
45 changes: 24 additions & 21 deletions cart/src/it/java/com/yas/cart/service/CartServiceIT.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
package com.yas.cart.service;

import static com.yas.cart.util.SecurityContextUtils.setUpSecurityContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.when;

import com.yas.cart.config.IntegrationTestConfiguration;
import com.yas.cart.model.Cart;
import com.yas.cart.model.CartItem;
import com.yas.cart.repository.CartItemRepository;
import com.yas.cart.repository.CartRepository;
import com.yas.cart.viewmodel.*;
import com.yas.cart.viewmodel.CartGetDetailVm;
import com.yas.cart.viewmodel.CartItemPutVm;
import com.yas.cart.viewmodel.CartItemVm;
import com.yas.cart.viewmodel.CartListVm;
import com.yas.cart.viewmodel.ProductThumbnailVm;
import com.yas.commonlibrary.exception.BadRequestException;
import com.yas.commonlibrary.exception.NotFoundException;
import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand All @@ -17,14 +28,6 @@
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;

import java.util.List;
import java.util.Set;

import static com.yas.cart.util.SecurityContextUtils.setUpSecurityContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.when;

@SpringBootTest
@Import(IntegrationTestConfiguration.class)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
Expand All @@ -46,9 +49,9 @@ class CartServiceIT {
void setUp() {

cart1 = cartRepository.save(Cart
.builder().customerId(customerId1).build());
.builder().customerId(customerId1).build());
cart2 = cartRepository.save(Cart
.builder().customerId("customer-2").build());
.builder().customerId("customer-2").build());

CartItem cartItem1 = new CartItem();
cartItem1.setProductId(1L);
Expand Down Expand Up @@ -122,7 +125,7 @@ void removeCartItemListByProductIdList_SomeProductsExist_RemovesThem() {
void removeCartItemListByProductIdList_NoCartItems_ThrowBadRequestException() {

Cart cart3 = cartRepository.save(Cart
.builder().customerId("customer-3").build());
.builder().customerId("customer-3").build());

cartRepository.save(cart3);

Expand All @@ -134,7 +137,7 @@ void removeCartItemListByProductIdList_NoCartItems_ThrowBadRequestException() {

assertThat(thrownException).isInstanceOf(BadRequestException.class);
assertThat(thrownException.getMessage())
.isEqualTo("There is no cart item in current cart to update!");
.isEqualTo("There is no cart item in current cart to update!");

}

Expand All @@ -149,7 +152,7 @@ void removeCartItemListByProductIdList_NotMatchProductId_ThrowNotFoundException(

assertThat(thrownException).isInstanceOf(NotFoundException.class);
assertThat(thrownException.getMessage())
.isEqualTo("There is no product with ID: 4 in the current cart");
.isEqualTo("There is no product with ID: 4 in the current cart");

}

Expand All @@ -170,7 +173,7 @@ void countNumberItemInCart_NoCart_ReturnsZero() {
void countNumberItemInCart_EmptyCart_ReturnsZero() {

Cart cart3 = cartRepository.save(Cart
.builder().customerId("customer-3").build());
.builder().customerId("customer-3").build());

cartRepository.save(cart3);

Expand All @@ -188,11 +191,11 @@ void countNumberItemInCart_NonEmptyCart_ReturnsCorrectCount() {
void addToCart_ProductsExist_AddsItemsToNewCart() {

List<CartItemVm> cartItemVms = List.of(
new CartItemVm(1L, 2, 10L)
new CartItemVm(1L, 2, 10L)
);

List<ProductThumbnailVm> productThumbnails = List.of(
new ProductThumbnailVm(1L, "A21", "A22", "A23")
new ProductThumbnailVm(1L, "A21", "A22", "A23")
);

when(productService.getProducts(List.of(1L))).thenReturn(productThumbnails);
Expand All @@ -208,11 +211,11 @@ void addToCart_ProductsExist_AddsItemsToNewCart() {
@Test
void addToCart_ProductsExist_AddsItemsToExistingCart() {
List<CartItemVm> cartItemVms = List.of(
new CartItemVm(3L, 2, 10L)
new CartItemVm(3L, 2, 10L)
);

List<ProductThumbnailVm> productThumbnails = List.of(
new ProductThumbnailVm(3L, "A21", "A22", "A23")
new ProductThumbnailVm(3L, "A21", "A22", "A23")
);

when(productService.getProducts(List.of(3L))).thenReturn(productThumbnails);
Expand All @@ -228,7 +231,7 @@ void addToCart_ProductsExist_AddsItemsToExistingCart() {
@Test
void addToCart_SomeProductsDoNotExist_ThrowsNotFoundException() {
List<CartItemVm> cartItemVms = List.of(
new CartItemVm(2L, 2, 10L)
new CartItemVm(2L, 2, 10L)
);

when(productService.getProducts(List.of(2L))).thenReturn(List.of());
Expand All @@ -239,7 +242,7 @@ void addToCart_SomeProductsDoNotExist_ThrowsNotFoundException() {

assertThat(thrownException).isInstanceOf(NotFoundException.class);
assertThat(thrownException.getMessage())
.isEqualTo("Not found product [2]");
.isEqualTo("Not found product [2]");

}

Expand Down
13 changes: 6 additions & 7 deletions cart/src/it/java/com/yas/cart/service/ProductServiceIT.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
package com.yas.cart.service;

import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.verify;

import io.github.resilience4j.circuitbreaker.CallNotPermittedException;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
Expand All @@ -11,13 +17,6 @@
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import java.util.List;

import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.verify;

@SpringBootTest
@Testcontainers
class ProductServiceIT {
Expand Down
2 changes: 1 addition & 1 deletion cart/src/main/java/com/yas/cart/config/CorsConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public WebMvcConfigurer corsConfigure() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("*").allowedOrigins("*")
.allowedHeaders("*");
.allowedHeaders("*");
}
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
package com.yas.cart.config;

import java.util.Optional;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.AuditorAware;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.security.core.Authentication;


import java.util.Optional;
import org.springframework.security.core.context.SecurityContextHolder;

@Configuration
Expand Down
29 changes: 14 additions & 15 deletions cart/src/main/java/com/yas/cart/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.yas.cart.config;

import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
Expand All @@ -9,10 +12,6 @@
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;

import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
Expand All @@ -21,15 +20,15 @@ public class SecurityConfig {
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

return http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/prometheus", "/actuator/health/**",
"/swagger-ui", "/swagger-ui/**", "/error", "/v3/api-docs/**").permitAll()
.requestMatchers("/storefront/carts", "/storefront/carts/**").hasRole("CUSTOMER")
.requestMatchers("/storefront/**").permitAll()
.requestMatchers("/backoffice/**").hasRole("ADMIN")
.anyRequest().authenticated())
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
.build();
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/prometheus", "/actuator/health/**",
"/swagger-ui", "/swagger-ui/**", "/error", "/v3/api-docs/**").permitAll()
.requestMatchers("/storefront/carts", "/storefront/carts/**").hasRole("CUSTOMER")
.requestMatchers("/storefront/**").permitAll()
.requestMatchers("/backoffice/**").hasRole("ADMIN")
.anyRequest().authenticated())
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
.build();
}

@Bean
Expand All @@ -38,8 +37,8 @@ public JwtAuthenticationConverter jwtAuthenticationConverterForKeycloak() {
Map<String, Collection<String>> realmAccess = jwt.getClaim("realm_access");
Collection<String> roles = realmAccess.get("roles");
return roles.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
.collect(Collectors.toList());
.map(role -> new SimpleGrantedAuthority("ROLE_" + role))
.collect(Collectors.toList());
};

var jwtAuthenticationConverter = new JwtAuthenticationConverter();
Expand Down
19 changes: 11 additions & 8 deletions cart/src/main/java/com/yas/cart/config/SwaggerConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.info.Info;

import io.swagger.v3.oas.annotations.security.*;
import io.swagger.v3.oas.annotations.security.OAuthFlow;
import io.swagger.v3.oas.annotations.security.OAuthFlows;
import io.swagger.v3.oas.annotations.security.OAuthScope;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
import io.swagger.v3.oas.annotations.servers.Server;

@OpenAPIDefinition(info = @Info(title = "Product Service API", description = "Product API documentation",
version = "1.0"), security = @SecurityRequirement(name = "oauth2_bearer"),
servers = {@Server(url = "${server.servlet.context-path}", description = "Default Server URL")})
version = "1.0"), security = @SecurityRequirement(name = "oauth2_bearer"),
servers = {@Server(url = "${server.servlet.context-path}", description = "Default Server URL")})
@SecurityScheme(name = "oauth2_bearer", type = SecuritySchemeType.OAUTH2,
flows = @OAuthFlows(authorizationCode = @OAuthFlow(
authorizationUrl = "${springdoc.oauthflow.authorization-url}",
tokenUrl = "${springdoc.oauthflow.token-url}", scopes = {
@OAuthScope(name = "openid", description = "openid")})))
flows = @OAuthFlows(authorizationCode = @OAuthFlow(
authorizationUrl = "${springdoc.oauthflow.authorization-url}",
tokenUrl = "${springdoc.oauthflow.token-url}", scopes = {
@OAuthScope(name = "openid", description = "openid")})))

Check warning on line 20 in cart/src/main/java/com/yas/cart/config/SwaggerConfig.java

View workflow job for this annotation

GitHub Actions / Checkstyle

com.puppycrawl.tools.checkstyle.checks.indentation.IndentationCheck

'annotation array initialization' child has incorrect indentation level 8, expected level should be 12.
public class SwaggerConfig {
}
32 changes: 21 additions & 11 deletions cart/src/main/java/com/yas/cart/controller/CartController.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@
package com.yas.cart.controller;

import com.yas.cart.service.CartService;
import com.yas.cart.viewmodel.*;
import com.yas.cart.viewmodel.CartGetDetailVm;
import com.yas.cart.viewmodel.CartItemPutVm;
import com.yas.cart.viewmodel.CartItemVm;
import com.yas.cart.viewmodel.CartListVm;
import com.yas.cart.viewmodel.ErrorVm;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import java.security.Principal;
import java.util.List;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.security.Principal;
import java.util.List;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@Validated
@RestController
Expand Down Expand Up @@ -49,12 +59,12 @@ public ResponseEntity<CartGetDetailVm> getLastCart(Principal principal) {
@PostMapping(path = "/storefront/carts")
@Operation(summary = "Add product to shopping cart. When no cart exists, this will create a new cart.")
@ApiResponses(value = {
@ApiResponse(responseCode = "201", description = "Add to cart successfully",
content = @Content(schema = @Schema(implementation = CartGetDetailVm.class))),
@ApiResponse(responseCode = "404", description = "Not found",
content = @Content(schema = @Schema(implementation = ErrorVm.class))),
@ApiResponse(responseCode = "400", description = "Bad request",
content = @Content(schema = @Schema(implementation = ErrorVm.class)))})
@ApiResponse(responseCode = "201", description = "Add to cart successfully",
content = @Content(schema = @Schema(implementation = CartGetDetailVm.class))),
@ApiResponse(responseCode = "404", description = "Not found",
content = @Content(schema = @Schema(implementation = ErrorVm.class))),
@ApiResponse(responseCode = "400", description = "Bad request",
content = @Content(schema = @Schema(implementation = ErrorVm.class)))})
public ResponseEntity<CartGetDetailVm> createCart(@Valid @RequestBody @NotEmpty List<CartItemVm> cartItemVms) {
CartGetDetailVm cartGetDetailVm = cartService.addToCart(cartItemVms);
return new ResponseEntity<>(cartGetDetailVm, HttpStatus.CREATED);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.yas.cart.viewmodel.ErrorVm;
import com.yas.commonlibrary.exception.BadRequestException;
import com.yas.commonlibrary.exception.NotFoundException;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand All @@ -12,8 +13,6 @@
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;

import java.util.List;

@ControllerAdvice
@Slf4j
public class ApiExceptionHandler {
Expand All @@ -38,10 +37,10 @@ protected ResponseEntity<ErrorVm> handleMethodArgumentNotValid(MethodArgumentNot
HttpStatus status = HttpStatus.BAD_REQUEST;

List<String> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(error -> error.getField() + " " + error.getDefaultMessage())
.toList();
.getFieldErrors()
.stream()
.map(error -> error.getField() + " " + error.getDefaultMessage())
.toList();

return buildErrorResponse(status, "Request information is not valid", errors, ex, null, 0);
}
Expand Down Expand Up @@ -69,7 +68,7 @@ private ResponseEntity<ErrorVm> handleBadRequest(Exception ex, WebRequest reques
private ResponseEntity<ErrorVm> buildErrorResponse(HttpStatus status, String message, List<String> errors,
Exception ex, WebRequest request, int statusCode) {
ErrorVm errorVm =
new ErrorVm(status.toString(), status.getReasonPhrase(), message, errors);
new ErrorVm(status.toString(), status.getReasonPhrase(), message, errors);

if (request != null) {
log.error(ERROR_LOG_FORMAT, this.getServletPath(request), statusCode, message);
Expand Down
Loading

0 comments on commit dc18046

Please sign in to comment.