diff --git a/gateway/pom.xml b/gateway/pom.xml index 8bea519..97cfbe4 100644 --- a/gateway/pom.xml +++ b/gateway/pom.xml @@ -1,43 +1,36 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - - - dev.snowdrop - snowdrop-dependencies - 2.2.6.SP1-redhat-00001 - - + io.konveyor.demo gateway 2.0.0-SNAPSHOT - 11 - 11 - 1.4.0.Final - 1.18.2 - latest - 2.2.6.RELEASE - registry.access.redhat.com/redhat-openjdk-18/openjdk18-openshift:${openjdk18-openshift.version} + 3.13.0 + 17 + 3.2.5 + 3.2.4 + 2023.0.1 + 2.1.2 - org.jboss.arquillian - arquillian-bom - ${arquillian.version} + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} pom import - org.arquillian.cube - arquillian-cube-bom - ${arquillian-cube.version} - import + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} pom + import @@ -47,48 +40,44 @@ org.springframework.boot spring-boot-starter-web - - org.springframework.boot - spring-boot-starter-tomcat - - org.springframework.boot spring-boot-starter-actuator org.springframework.boot - spring-boot-starter-data-jpa + spring-boot-starter-aop - org.springframework.cloud - spring-cloud-starter-netflix-hystrix - 2.1.5.RELEASE + org.springframework.data + spring-data-commons - ch.qos.logback - logback-core + io.micrometer + micrometer-registry-prometheus + + - ch.qos.logback - logback-classic + io.micrometer + micrometer-tracing-bridge-otel + - org.projectlombok - lombok + io.opentelemetry + opentelemetry-exporter-otlp + - io.opentracing.contrib - opentracing-spring-jaeger-web-starter + org.springframework.cloud + spring-cloud-starter-circuitbreaker-resilience4j - io.opentracing.contrib - opentracing-spring-cloud-starter - 0.5.4 + org.projectlombok + lombok + true + org.springframework.boot @@ -96,25 +85,21 @@ test - com.h2database - h2 - test - - - io.rest-assured - rest-assured - test - - - org.awaitility - awaitility + org.assertj + assertj-core test - org.assertj - assertj-core + org.springframework.cloud + spring-cloud-starter-contract-stub-runner test + + + + + + @@ -123,27 +108,18 @@ org.springframework.boot spring-boot-maven-plugin - - local - exec + + + org.projectlombok + lombok + + - - - - repackage - - - - - redhat-early-access - Red Hat Early Access Repository - https://maven.repository.redhat.com/earlyaccess/all/ - redhat-ga Red Hat GA Repository @@ -151,100 +127,10 @@ - - redhat-early-access - Red Hat Early Access Repository - https://maven.repository.redhat.com/earlyaccess/all/ - redhat-ga Red Hat GA Repository https://maven.repository.redhat.com/ga/ - - - local - - - - - - openshift - - - org.postgresql - postgresql - runtime - - - - true - - - - openshift-manual - - - org.postgresql - postgresql - runtime - - - - - - - io.fabric8 - fabric8-maven-plugin - - - fmp - package - - resource - build - - - - - - - - - openshift-it - - - - org.apache.maven.plugins - maven-failsafe-plugin - - - - integration-test - verify - - - - - - - - - - io.fabric8 - kubernetes-client - 1.4.34 - test - - - io.fabric8 - openshift-client - 1.4.34 - test - - - - - \ No newline at end of file diff --git a/gateway/src/main/java/META-INF/MANIFEST.MF b/gateway/src/main/java/META-INF/MANIFEST.MF deleted file mode 100644 index 5e94951..0000000 --- a/gateway/src/main/java/META-INF/MANIFEST.MF +++ /dev/null @@ -1,3 +0,0 @@ -Manifest-Version: 1.0 -Class-Path: - diff --git a/gateway/src/main/java/io/konveyor/demo/gateway/Application.java b/gateway/src/main/java/io/konveyor/demo/gateway/Application.java index 35afaec..b36379c 100644 --- a/gateway/src/main/java/io/konveyor/demo/gateway/Application.java +++ b/gateway/src/main/java/io/konveyor/demo/gateway/Application.java @@ -2,37 +2,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; -import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; -import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; -import org.springframework.context.annotation.Bean; -import org.springframework.web.client.RestTemplate; -import com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect; - -@SpringBootApplication(exclude = { - DataSourceAutoConfiguration.class, - DataSourceTransactionManagerAutoConfiguration.class, - HibernateJpaAutoConfiguration.class - }) -@EnableCircuitBreaker -public class Application -{ - +@SpringBootApplication +public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); - } - - - @Bean - public RestTemplate restTemplate() { - return new RestTemplate(); - } - - @Bean - public HystrixCommandAspect hystrixAspect() { - return new HystrixCommandAspect(); - } - + } } diff --git a/gateway/src/main/java/io/konveyor/demo/gateway/command/ProductCommand.java b/gateway/src/main/java/io/konveyor/demo/gateway/command/ProductCommand.java deleted file mode 100644 index 072f23d..0000000 --- a/gateway/src/main/java/io/konveyor/demo/gateway/command/ProductCommand.java +++ /dev/null @@ -1,49 +0,0 @@ -package io.konveyor.demo.gateway.command; - -import io.konveyor.demo.gateway.model.OrderItem; -import io.konveyor.demo.gateway.model.Product; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; - -import com.netflix.hystrix.HystrixCommand; -import com.netflix.hystrix.HystrixCommandGroupKey; -import com.netflix.hystrix.HystrixThreadPoolKey; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class ProductCommand extends HystrixCommand{ - private OrderItem item; - - String inventoryServiceURL; - - RestTemplate restTemplate; - - public ProductCommand(OrderItem item, String inventoryServiceURL, RestTemplate restTemplate){ - super( HystrixCommandGroupKey.Factory.asKey( "Products" ), HystrixThreadPoolKey.Factory.asKey( "ProductsThreads" ) ); - this.item = item; - this.inventoryServiceURL = inventoryServiceURL; - this.restTemplate = restTemplate; - } - - @Override - protected OrderItem run() throws Exception { - UriComponentsBuilder builder = UriComponentsBuilder - .fromHttpUrl(inventoryServiceURL) - .pathSegment( "{product}"); - Product p = restTemplate.getForObject( - builder.buildAndExpand(item.getProduct().getId()).toUriString(), - Product.class); - if (p != null) { - item.setProduct(p); - } - return item; - } - - @Override - protected OrderItem getFallback() { - log.warn( "Failed to obtain product, " + getFailedExecutionException().getMessage() + " for order line " + item ); - return item; - } - -} diff --git a/gateway/src/main/java/io/konveyor/demo/gateway/controller/CustomersController.java b/gateway/src/main/java/io/konveyor/demo/gateway/controller/CustomersController.java index b36d2b7..8c0cd9f 100644 --- a/gateway/src/main/java/io/konveyor/demo/gateway/controller/CustomersController.java +++ b/gateway/src/main/java/io/konveyor/demo/gateway/controller/CustomersController.java @@ -1,52 +1,40 @@ package io.konveyor.demo.gateway.controller; -import io.konveyor.demo.gateway.exception.ResourceNotFoundException; -import io.konveyor.demo.gateway.model.Customer; -import io.konveyor.demo.gateway.service.CustomersService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.MediaType; -import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import io.opentracing.Span; -import io.opentracing.Tracer; +import io.konveyor.demo.gateway.exception.ResourceNotFoundException; +import io.konveyor.demo.gateway.model.Customer; +import io.konveyor.demo.gateway.service.CustomersService; import lombok.extern.slf4j.Slf4j; @RestController -@RequestMapping("/customers") -@Component +@RequestMapping(path = "/customers", produces = MediaType.APPLICATION_JSON_VALUE) @Slf4j public class CustomersController { - @Autowired private CustomersService customerService; - - @Autowired - Tracer tracer; - - @GetMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE) - public Customer getById(@PathVariable("id") Long id) { - Customer c; - Span span = tracer.buildSpan("getById").start(); - try{ - log.debug("Entering CustomerController.getById()"); - c = customerService.getById(id); - if (c == null) { - throw new ResourceNotFoundException("Requested customer doesn't exist"); - } - log.debug("Returning element: " + c); - } finally { - span.finish(); + + @GetMapping("/{id}") + public Customer getById(@PathVariable("id") Long id) { + log.debug("Entering CustomerController.getById()"); + var c = customerService.getById(id); + + if (c == null) { + throw new ResourceNotFoundException("Requested customer doesn't exist"); } + + log.debug("Returning element: " + c); return c; } - @RequestMapping + @GetMapping public Page findAll(Pageable pageable){ return customerService.findAll(pageable); } diff --git a/gateway/src/main/java/io/konveyor/demo/gateway/controller/InventoryController.java b/gateway/src/main/java/io/konveyor/demo/gateway/controller/InventoryController.java index 051fbfe..1969be6 100644 --- a/gateway/src/main/java/io/konveyor/demo/gateway/controller/InventoryController.java +++ b/gateway/src/main/java/io/konveyor/demo/gateway/controller/InventoryController.java @@ -1,52 +1,42 @@ package io.konveyor.demo.gateway.controller; -import io.konveyor.demo.gateway.exception.ResourceNotFoundException; -import io.konveyor.demo.gateway.model.Product; -import io.konveyor.demo.gateway.service.InventoryService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.MediaType; -import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import io.opentracing.Span; -import io.opentracing.Tracer; +import io.konveyor.demo.gateway.exception.ResourceNotFoundException; +import io.konveyor.demo.gateway.model.Product; +import io.konveyor.demo.gateway.service.InventoryService; import lombok.extern.slf4j.Slf4j; @RestController -@RequestMapping("/products") -@Component +@RequestMapping(path = "/products", produces = MediaType.APPLICATION_JSON_VALUE) @Slf4j public class InventoryController { - + @Autowired private InventoryService inventoryService; - - @Autowired - Tracer tracer; - - @GetMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE) - public Product getById(@PathVariable("id") Long id) { - Product p; - Span span = tracer.buildSpan("getById").start(); - try{ - log.debug("Entering InventoryController.getById()"); - p = inventoryService.getById(id); - if (p == null) { - throw new ResourceNotFoundException("Requested product doesn't exist"); - } - log.debug("Returning element: " + p); - } finally { - span.finish(); + + + @GetMapping("/{id}") + public Product getById(@PathVariable("id") Long id) { + log.debug("Entering InventoryController.getById()"); + var p = inventoryService.getById(id); + + if (p == null) { + throw new ResourceNotFoundException("Requested product doesn't exist"); } + + log.debug("Returning element: " + p); return p; } - - @RequestMapping + + @GetMapping public Page findAll(Pageable pageable){ return inventoryService.findAll(pageable); } diff --git a/gateway/src/main/java/io/konveyor/demo/gateway/controller/OrdersController.java b/gateway/src/main/java/io/konveyor/demo/gateway/controller/OrdersController.java index 82f9864..31c4f44 100644 --- a/gateway/src/main/java/io/konveyor/demo/gateway/controller/OrdersController.java +++ b/gateway/src/main/java/io/konveyor/demo/gateway/controller/OrdersController.java @@ -1,52 +1,41 @@ package io.konveyor.demo.gateway.controller; -import io.konveyor.demo.gateway.exception.ResourceNotFoundException; -import io.konveyor.demo.gateway.model.Order; -import io.konveyor.demo.gateway.service.OrdersService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.MediaType; -import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import io.opentracing.Span; -import io.opentracing.Tracer; +import io.konveyor.demo.gateway.exception.ResourceNotFoundException; +import io.konveyor.demo.gateway.model.Order; +import io.konveyor.demo.gateway.service.OrdersService; import lombok.extern.slf4j.Slf4j; @RestController -@RequestMapping("/orders") -@Component +@RequestMapping(path = "/orders", produces = MediaType.APPLICATION_JSON_VALUE) @Slf4j public class OrdersController { @Autowired private OrdersService orderService; - - @Autowired - Tracer tracer; - - @GetMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE) - public Order getById(@PathVariable("id") Long id) { - Order o; - Span span = tracer.buildSpan("getById").start(); - try{ - log.debug("Entering OrderController.getById()"); - o = orderService.getById(id); - if (o == null) { - throw new ResourceNotFoundException("Requested order doesn't exist"); - } - log.debug("Returning element: " + o); - } finally { - span.finish(); + + @GetMapping("/{id}") + public Order getById(@PathVariable("id") Long id) { + log.debug("Entering OrderController.getById()"); + var o = orderService.getById(id); + + if (o == null) { + throw new ResourceNotFoundException("Requested order doesn't exist"); } + + log.debug("Returning element: " + o); return o; } - - @RequestMapping + + @GetMapping public Page findAll(Pageable pageable){ return orderService.findAll(pageable); } diff --git a/gateway/src/main/java/io/konveyor/demo/gateway/exception/handler/ExceptionHandlingController.java b/gateway/src/main/java/io/konveyor/demo/gateway/exception/handler/ExceptionHandlingController.java index 5a96490..9326bf7 100644 --- a/gateway/src/main/java/io/konveyor/demo/gateway/exception/handler/ExceptionHandlingController.java +++ b/gateway/src/main/java/io/konveyor/demo/gateway/exception/handler/ExceptionHandlingController.java @@ -1,19 +1,18 @@ package io.konveyor.demo.gateway.exception.handler; -import io.konveyor.demo.gateway.exception.ResourceNotFoundException; import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import io.konveyor.demo.gateway.exception.ResourceNotFoundException; import lombok.extern.slf4j.Slf4j; -@ControllerAdvice +@RestControllerAdvice @Slf4j public class ExceptionHandlingController { - @ResponseStatus(value = HttpStatus.NOT_FOUND, - reason = "Order not found") + @ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Order not found") @ExceptionHandler(ResourceNotFoundException.class) public void resourceNotFound(ResourceNotFoundException e) { log.warn("Resource not found: " + e.getMessage()); diff --git a/gateway/src/main/java/io/konveyor/demo/gateway/model/Customer.java b/gateway/src/main/java/io/konveyor/demo/gateway/model/Customer.java index baed07b..8ba3957 100644 --- a/gateway/src/main/java/io/konveyor/demo/gateway/model/Customer.java +++ b/gateway/src/main/java/io/konveyor/demo/gateway/model/Customer.java @@ -1,16 +1,17 @@ package io.konveyor.demo.gateway.model; -import java.io.Serializable; - import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; @Data @JsonIgnoreProperties(ignoreUnknown = true) -public class Customer implements Serializable{ - - private static final long serialVersionUID = -647167746768043097L; +@NoArgsConstructor +@AllArgsConstructor +@Builder(toBuilder = true) +public class Customer { private Long id; private String username; private String name; @@ -19,5 +20,4 @@ public class Customer implements Serializable{ private String zipCode; private String city; private String country; - } diff --git a/gateway/src/main/java/io/konveyor/demo/gateway/model/Order.java b/gateway/src/main/java/io/konveyor/demo/gateway/model/Order.java index 4eea5bc..f5b1cea 100644 --- a/gateway/src/main/java/io/konveyor/demo/gateway/model/Order.java +++ b/gateway/src/main/java/io/konveyor/demo/gateway/model/Order.java @@ -1,31 +1,24 @@ package io.konveyor.demo.gateway.model; -import java.io.Serializable; import java.math.BigDecimal; import java.util.Date; import java.util.List; import java.util.function.Function; -import io.konveyor.demo.gateway.serialization.CustomerDeserializer; - import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - +import io.konveyor.demo.gateway.serialization.CustomerDeserializer; import lombok.Data; -import lombok.ToString; -@ToString(exclude = "items") @Data @JsonIgnoreProperties(ignoreUnknown = true) -public class Order implements Serializable { - - private static final long serialVersionUID = -1703065930151811237L; +public class Order { private Long id; private Customer customer; - @JsonFormat - (shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy", timezone="CET") + + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy", timezone="CET") private Date date; private List items; @@ -39,7 +32,7 @@ public BigDecimal getTotalAmmount() { return new BigDecimal(0); } } - + @JsonProperty("customer") public Customer getCustomer() { return this.customer; diff --git a/gateway/src/main/java/io/konveyor/demo/gateway/model/OrderItem.java b/gateway/src/main/java/io/konveyor/demo/gateway/model/OrderItem.java index 9c68fc6..ef89892 100644 --- a/gateway/src/main/java/io/konveyor/demo/gateway/model/OrderItem.java +++ b/gateway/src/main/java/io/konveyor/demo/gateway/model/OrderItem.java @@ -1,27 +1,26 @@ package io.konveyor.demo.gateway.model; -import java.io.Serializable; import java.math.BigDecimal; -import io.konveyor.demo.gateway.serialization.ProductDeserializer; - import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - +import io.konveyor.demo.gateway.serialization.ProductDeserializer; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; @Data @JsonIgnoreProperties(ignoreUnknown = true) -public class OrderItem implements Serializable { - - private static final long serialVersionUID = 95662333489566465L; - +@NoArgsConstructor +@AllArgsConstructor +@Builder(toBuilder = true) +public class OrderItem { private Product product; private Integer quantity; private BigDecimal price; - - + @JsonProperty("product") public Product getProduct() { return this.product; diff --git a/gateway/src/main/java/io/konveyor/demo/gateway/model/OrderSummary.java b/gateway/src/main/java/io/konveyor/demo/gateway/model/OrderSummary.java index cc74959..2dddba6 100644 --- a/gateway/src/main/java/io/konveyor/demo/gateway/model/OrderSummary.java +++ b/gateway/src/main/java/io/konveyor/demo/gateway/model/OrderSummary.java @@ -1,14 +1,11 @@ package io.konveyor.demo.gateway.model; -import java.io.Serializable; import java.util.Date; import lombok.Data; @Data -public class OrderSummary implements Serializable{ - - private static final long serialVersionUID = 5290825002214978384L; +public class OrderSummary { private long id; private String customerName; private Date date; diff --git a/gateway/src/main/java/io/konveyor/demo/gateway/model/Product.java b/gateway/src/main/java/io/konveyor/demo/gateway/model/Product.java index 8689f53..16972f4 100644 --- a/gateway/src/main/java/io/konveyor/demo/gateway/model/Product.java +++ b/gateway/src/main/java/io/konveyor/demo/gateway/model/Product.java @@ -1,16 +1,17 @@ package io.konveyor.demo.gateway.model; -import java.io.Serializable; - import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; @JsonIgnoreProperties(ignoreUnknown = true) @Data -public class Product implements Serializable{ - - private static final long serialVersionUID = 1647872601694471121L; +@AllArgsConstructor +@NoArgsConstructor +@Builder(toBuilder = true) +public class Product { private Long id; private String name; private String description; diff --git a/gateway/src/main/java/io/konveyor/demo/gateway/repository/CustomerRepository.java b/gateway/src/main/java/io/konveyor/demo/gateway/repository/CustomerRepository.java index bf91a26..ebe03d9 100644 --- a/gateway/src/main/java/io/konveyor/demo/gateway/repository/CustomerRepository.java +++ b/gateway/src/main/java/io/konveyor/demo/gateway/repository/CustomerRepository.java @@ -3,82 +3,72 @@ import java.util.ArrayList; import java.util.List; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.ParameterizedTypeReference; import org.springframework.data.domain.Pageable; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; - -import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; -import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Repository; +import org.springframework.web.client.RestClient; +import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker; +import io.github.resilience4j.retry.annotation.Retry; import io.konveyor.demo.gateway.model.Customer; import io.konveyor.demo.util.PaginatedResponse; -import io.opentracing.Span; -import io.opentracing.Tracer; +import io.micrometer.tracing.annotation.NewSpan; +import io.micrometer.tracing.annotation.SpanTag; import lombok.extern.slf4j.Slf4j; -@Component +@Repository @Slf4j -public class CustomerRepository extends GenericRepository{ - - @Autowired - Tracer tracer; - - @Autowired - RestTemplate restTemplate; - - @Value("${services.customers.url}") - String customersServiceURL; - - @HystrixCommand(commandKey = "Customers", fallbackMethod = "getFallbackCustomer", commandProperties = { - @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000") - }) - public Customer getCustomerById(Long id) { - Span span = tracer.buildSpan("getCustomerById").start(); +public class CustomerRepository extends GenericRepository { + private final RestClient restClient; + + public CustomerRepository(RestClient.Builder restClientBuilder, @Value("${services.customers.url}") String customersServiceURL) { + this.restClient = restClientBuilder.baseUrl(customersServiceURL) + .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) + .build(); + } + + @CircuitBreaker(name = "Customers", fallbackMethod = "getFallbackCustomer") + @Retry(name = "Customers", fallbackMethod = "getFallbackCustomer") + @NewSpan + public Customer getCustomerById(@SpanTag Long id) { log.debug("Entering OrdersService.getCustomerById()"); - UriComponentsBuilder builder = UriComponentsBuilder - .fromHttpUrl(customersServiceURL) - .pathSegment( "{customer}"); - Customer c = restTemplate.getForObject( - builder.buildAndExpand(id).toUriString(), - Customer.class); + + var c = this.restClient.get() + .uri("/{id}", id) + .retrieve() + .body(Customer.class); + //Trigger fallback if no result is obtained. if (c == null) { throw new RuntimeException(); } + log.debug(c.toString()); - span.finish(); return c; } - - @HystrixCommand(commandKey = "AllCustomers", fallbackMethod = "getFallbackCustomers", commandProperties = { - @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000") - }) - public List findAll(Pageable pageable) { - Span span = tracer.buildSpan("findAll").start(); + + @CircuitBreaker(name = "AllCustomers", fallbackMethod = "getFallbackCustomers") + @Retry(name = "AllCustomers", fallbackMethod = "getFallbackCustomer") + @NewSpan + public List findAll(@SpanTag Pageable pageable) { log.debug("Entering CustomerRepository.findAll()"); - UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(customersServiceURL) + + return this.restClient.get() + .uri(uriBuilder -> uriBuilder .queryParam("page", pageable.getPageNumber()) .queryParam("size", pageable.getPageSize()) - .queryParam("sort", getSortString(pageable)); - ResponseEntity> responseEntity = - restTemplate.exchange( - builder.toUriString(), - HttpMethod.GET, - null, - new ParameterizedTypeReference>() {} - ); - List customers = responseEntity.getBody().getContent(); - span.finish(); - return customers; + .queryParam("sort", getSortString(pageable)) + .build() + ) + .retrieve() + .body(new ParameterizedTypeReference>() {}) + .getContent(); } - public Customer getFallbackCustomer(Long id, Throwable e) { + public Customer getFallbackCustomer(Long id, Exception e) { log.warn("Failed to obtain Customer, " + e.getMessage() + " for customer with id " + id); Customer c = new Customer(); c.setId(id); @@ -92,9 +82,8 @@ public Customer getFallbackCustomer(Long id, Throwable e) { return c; } - public List getFallbackCustomers(Pageable pageable, Throwable e) { + public List getFallbackCustomers(Pageable pageable, Exception e) { log.warn("Failed to obtain Customers, " + e.getMessage()); - return new ArrayList(); + return new ArrayList<>(); } - } diff --git a/gateway/src/main/java/io/konveyor/demo/gateway/repository/InventoryRepository.java b/gateway/src/main/java/io/konveyor/demo/gateway/repository/InventoryRepository.java index 862e27e..9e3c8ee 100644 --- a/gateway/src/main/java/io/konveyor/demo/gateway/repository/InventoryRepository.java +++ b/gateway/src/main/java/io/konveyor/demo/gateway/repository/InventoryRepository.java @@ -1,129 +1,122 @@ package io.konveyor.demo.gateway.repository; import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import io.konveyor.demo.gateway.command.ProductCommand; -import io.konveyor.demo.gateway.model.OrderItem; -import io.konveyor.demo.gateway.model.Product; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationContext; import org.springframework.core.ParameterizedTypeReference; import org.springframework.data.domain.Pageable; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Repository; +import org.springframework.web.client.RestClient; -import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; -import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; - -import io.opentracing.Span; -import io.opentracing.Tracer; +import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker; +import io.github.resilience4j.retry.annotation.Retry; +import io.konveyor.demo.gateway.model.OrderItem; +import io.konveyor.demo.gateway.model.Product; +import io.micrometer.tracing.annotation.NewSpan; +import io.micrometer.tracing.annotation.SpanTag; import lombok.extern.slf4j.Slf4j; -import rx.Observable; -@Component +@Repository @Slf4j -public class InventoryRepository extends GenericRepository{ - - @Autowired - Tracer tracer; - - @Autowired - RestTemplate restTemplate; - - @Value("${hystrix.threadpool.ProductsThreads.coreSize}") - private int threadSize; - - @Value("${services.inventory.url}") - String inventoryServiceURL; - - public List getProductDetails(List items){ - Span span = tracer.buildSpan("getProductDetails").start(); +public class InventoryRepository extends GenericRepository { + private final ApplicationContext applicationContext; + private final RestClient restClient; + + public InventoryRepository(ApplicationContext applicationContext, RestClient.Builder restClientBuilder, @Value("${services.inventory.url}") String inventoryServiceURL) { + this.applicationContext = applicationContext; + this.restClient = restClientBuilder.baseUrl(inventoryServiceURL) + .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) + .build(); + } + + @CircuitBreaker(name = "ProductDetails", fallbackMethod = "getFallbackProduct") + @Retry(name = "ProductDetails", fallbackMethod = "getFallbackProduct") + public Product getProduct(OrderItem item) { + return this.restClient.get() + .uri("/{id}", item.getProduct().getId()) + .retrieve() + .body(Product.class); + } + + @NewSpan + public List getProductDetails(@SpanTag List items) { log.debug("Entering InventoryRepository.getProductDetails()"); - List detailedItems = new ArrayList<>(); - for(int index= 0; index < items.size();) { - List> observables = new ArrayList<>(); - int batchLimit = Math.min( index + threadSize, items.size() ); - for( int batchIndex = index; batchIndex < batchLimit; batchIndex++ ) - { - observables.add( new ProductCommand( items.get(batchIndex), inventoryServiceURL, restTemplate ).toObservable() ); - } - log.info("Will get product detail from " + observables.size() + " items"); - Observable zipped = Observable.zip( observables, objects-> - { - OrderItem[] detailed = new OrderItem[objects.length]; - for( int batchIndex = 0; batchIndex < objects.length; batchIndex++ ) - { - detailed[batchIndex] = (OrderItem)objects[batchIndex]; + + return items.stream() + .map(item -> { + // Need to do this so that the annotations work + // If its just a local method call then none of the AOP stuff takes effect + var product = this.applicationContext.getBean(InventoryRepository.class).getProduct(item); + var i = item.toBuilder(); + + if (product != null) { + i.product(product); } - return detailed; - } ); - Collections.addAll( detailedItems, zipped.toBlocking().first() ); - index += threadSize; - } - span.finish(); - return detailedItems; + + return i.build(); + + }) + .toList(); } - - @HystrixCommand(commandKey = "AllProducts", fallbackMethod = "getFallbackProducts", commandProperties = { - @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000") - }) - public List findAll(Pageable pageable) { - Span span = tracer.buildSpan("findAll").start(); + + @CircuitBreaker(name = "AllProducts", fallbackMethod = "getFallbackProducts") + @Retry(name = "AllProducts", fallbackMethod = "getFallbackProducts") + @NewSpan + public List findAll(@SpanTag Pageable pageable) { log.debug("Entering InventoryRepository.findAll()"); - UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(inventoryServiceURL) + + return this.restClient.get() + .uri(uriBuilder -> uriBuilder .queryParam("page", pageable.getPageNumber()) .queryParam("size", pageable.getPageSize()) - .queryParam("sort", getSortString(pageable)); - ResponseEntity> responseEntity = - restTemplate.exchange( - builder.toUriString(), - HttpMethod.GET, - null, - new ParameterizedTypeReference>() {} - ); - List orders = responseEntity.getBody(); - span.finish(); - return orders; + .queryParam("sort", getSortString(pageable)) + .build() + ) + .retrieve() + .body(new ParameterizedTypeReference<>() {}); } - - @HystrixCommand(commandKey = "Products", fallbackMethod = "getFallbackProduct", commandProperties = { - @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000") - }) - public Product getProductById(Long id) { - Span span = tracer.buildSpan("getProductById").start(); + + @CircuitBreaker(name = "Products", fallbackMethod = "getFallbackProduct") + @Retry(name = "Products", fallbackMethod = "getFallbackProduct") + @NewSpan + public Product getProductById(@SpanTag Long id) { log.debug("Entering InventoryRepository.getProductById()"); - UriComponentsBuilder builder = UriComponentsBuilder - .fromHttpUrl(inventoryServiceURL) - .pathSegment( "{product}"); - Product p = restTemplate.getForObject( - builder.buildAndExpand(id).toUriString(), - Product.class); + + var p = this.restClient.get() + .uri("/{id}", id) + .retrieve() + .body(Product.class); + //Trigger fallback if no result is obtained. if (p == null) { throw new RuntimeException(); } log.debug(p.toString()); - span.finish(); return p; } - - public List getFallbackProducts(Pageable pageable, Throwable e) { + + public List getFallbackProducts(Pageable pageable, Exception e) { log.warn("Failed to obtain Products, " + e.getMessage()); - return new ArrayList(); + return new ArrayList<>(); } - - public Product getFallbackProduct(Long id, Throwable e) { + + public Product getFallbackProduct(Long id, Exception e) { log.warn("Failed to obtain Product, " + e.getMessage() + " for Product with id " + id); - Product p = new Product(); - p.setId(id); - p.setName("Unknown"); - p.setDescription("Unknown"); - return p; + + return Product.builder() + .id(id) + .name("Unknown") + .description("Unknown") + .build(); } + public Product getFallbackProduct(OrderItem item, Exception e) { + return Product.builder() + .id(item.getProduct().getId()) + .build(); + } } diff --git a/gateway/src/main/java/io/konveyor/demo/gateway/repository/OrderRepository.java b/gateway/src/main/java/io/konveyor/demo/gateway/repository/OrderRepository.java index cf4a300..f22e802 100644 --- a/gateway/src/main/java/io/konveyor/demo/gateway/repository/OrderRepository.java +++ b/gateway/src/main/java/io/konveyor/demo/gateway/repository/OrderRepository.java @@ -3,87 +3,81 @@ import java.util.ArrayList; import java.util.List; -import io.konveyor.demo.gateway.model.Order; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.ParameterizedTypeReference; import org.springframework.data.domain.Pageable; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; - -import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; -import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Repository; +import org.springframework.web.client.RestClient; -import io.opentracing.Span; -import io.opentracing.Tracer; +import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker; +import io.github.resilience4j.retry.annotation.Retry; +import io.konveyor.demo.gateway.model.Order; +import io.micrometer.tracing.annotation.NewSpan; +import io.micrometer.tracing.annotation.SpanTag; import lombok.extern.slf4j.Slf4j; -@Component +@Repository @Slf4j public class OrderRepository extends GenericRepository { - @Autowired - Tracer tracer; - - @Autowired - RestTemplate restTemplate; - - @Value("${services.orders.url}") - String ordersServiceURL; - - @HystrixCommand(commandKey = "Orders", fallbackMethod = "getFallbackOrder", commandProperties = { - @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000") - }) - public Order getOrderById(Long id) { - Span span = tracer.buildSpan("getOrderById").start(); + private final RestClient restClient; + + public OrderRepository(RestClient.Builder restClientBuilder, @Value("${services.orders.url}") String ordersServiceURL) { + this.restClient = restClientBuilder.baseUrl(ordersServiceURL) + .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) + .build(); + } + + @CircuitBreaker(name = "Orders", fallbackMethod = "getFallbackOrder") + @Retry(name = "Orders", fallbackMethod = "getFallbackOrder") + @NewSpan + public Order getOrderById(@SpanTag Long id) { log.debug("Entering OrderRepository.getOrderById()"); - UriComponentsBuilder builder = UriComponentsBuilder - .fromHttpUrl(ordersServiceURL) - .pathSegment( "{order}"); - Order o = restTemplate.getForObject( - builder.buildAndExpand(id).toUriString(), - Order.class); - if (o == null) - log.debug("Obtained null order"); - else - log.debug(o.toString()); - span.finish(); + + var o = this.restClient.get() + .uri("/{id}", id) + .retrieve() + .body(Order.class); + + if (log.isDebugEnabled()) { + if (o == null) { + log.debug("Obtained null order"); + } + else { + log.debug(o.toString()); + } + } + return o; } - @HystrixCommand(commandKey = "AllOrders", fallbackMethod = "getFallbackOrders", commandProperties = { - @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000") - }) - public List findAll(Pageable pageable) { - Span span = tracer.buildSpan("findAll").start(); + @CircuitBreaker(name = "AllOrders", fallbackMethod = "getFallbackOrders") + @Retry(name = "AllOrders", fallbackMethod = "getFallbackOrders") + @NewSpan + public List findAll(@SpanTag Pageable pageable) { log.debug("Entering OrderRepository.findAll()"); - UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(ordersServiceURL) + + return this.restClient.get() + .uri(uriBuilder -> uriBuilder .queryParam("page", pageable.getPageNumber()) .queryParam("size", pageable.getPageSize()) - .queryParam("sort", getSortString(pageable)); - ResponseEntity> responseEntity = - restTemplate.exchange( - builder.toUriString(), - HttpMethod.GET, - null, - new ParameterizedTypeReference>() {} - ); - List orders = responseEntity.getBody(); - span.finish(); - return orders; + .queryParam("sort", getSortString(pageable)) + .build() + ) + .retrieve() + .body(new ParameterizedTypeReference<>() {}); } - public Order getFallbackOrder(Long id, Throwable e) { + public Order getFallbackOrder(Long id, Exception e) { log.warn("Failed to obtain Order, " + e.getMessage() + " for order with id " + id); return null; } - public List getFallbackOrders(Pageable pageable, Throwable e) { + public List getFallbackOrders(Pageable pageable, Exception e) { log.warn("Failed to obtain Orders, " + e.getMessage()); - return new ArrayList(); + return new ArrayList<>(); } } diff --git a/gateway/src/main/java/io/konveyor/demo/gateway/service/CustomersService.java b/gateway/src/main/java/io/konveyor/demo/gateway/service/CustomersService.java index cf8f6d1..bb5b6ed 100644 --- a/gateway/src/main/java/io/konveyor/demo/gateway/service/CustomersService.java +++ b/gateway/src/main/java/io/konveyor/demo/gateway/service/CustomersService.java @@ -1,43 +1,34 @@ package io.konveyor.demo.gateway.service; -import java.util.List; - -import io.konveyor.demo.gateway.model.Customer; -import io.konveyor.demo.gateway.repository.CustomerRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; -import io.opentracing.Span; -import io.opentracing.Tracer; +import io.konveyor.demo.gateway.model.Customer; +import io.konveyor.demo.gateway.repository.CustomerRepository; +import io.micrometer.tracing.annotation.NewSpan; +import io.micrometer.tracing.annotation.SpanTag; import lombok.extern.slf4j.Slf4j; @Service @Slf4j public class CustomersService { - - @Autowired - private Tracer tracer; - @Autowired private CustomerRepository customerRepository; - - public Page findAll(Pageable pageable) { - Span span = tracer.buildSpan("findAll").start(); + + @NewSpan + public Page findAll(@SpanTag Pageable pageable) { log.debug("Entering OrdersService.findAll()"); - List orders = customerRepository.findAll(pageable); - span.finish(); + + var orders = customerRepository.findAll(pageable); return new PageImpl(orders, pageable, orders.size()); } - public Customer getById(Long id) { - Span span = tracer.buildSpan("getById").start(); + @NewSpan + public Customer getById(@SpanTag Long id) { log.debug("Entering CustomersService.getById()"); - Customer c = customerRepository.getCustomerById(id); - span.finish(); - return c; + return customerRepository.getCustomerById(id); } - } diff --git a/gateway/src/main/java/io/konveyor/demo/gateway/service/InventoryService.java b/gateway/src/main/java/io/konveyor/demo/gateway/service/InventoryService.java index 0502392..2a57a89 100644 --- a/gateway/src/main/java/io/konveyor/demo/gateway/service/InventoryService.java +++ b/gateway/src/main/java/io/konveyor/demo/gateway/service/InventoryService.java @@ -1,42 +1,34 @@ package io.konveyor.demo.gateway.service; -import java.util.List; - -import io.konveyor.demo.gateway.model.Product; -import io.konveyor.demo.gateway.repository.InventoryRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; -import io.opentracing.Span; -import io.opentracing.Tracer; +import io.konveyor.demo.gateway.model.Product; +import io.konveyor.demo.gateway.repository.InventoryRepository; +import io.micrometer.tracing.annotation.NewSpan; +import io.micrometer.tracing.annotation.SpanTag; import lombok.extern.slf4j.Slf4j; @Service @Slf4j public class InventoryService { - @Autowired - private Tracer tracer; - @Autowired private InventoryRepository inventoryRepository; - - public Page findAll(Pageable pageable) { - Span span = tracer.buildSpan("findAll").start(); + + @NewSpan + public Page findAll(@SpanTag Pageable pageable) { log.debug("Entering OrdersService.findAll()"); - List orders = inventoryRepository.findAll(pageable); - span.finish(); + + var orders = inventoryRepository.findAll(pageable); return new PageImpl(orders, pageable, orders.size()); } - public Product getById(Long id) { - Span span = tracer.buildSpan("getById").start(); + @NewSpan + public Product getById(@SpanTag Long id) { log.debug("Entering CustomersService.getById()"); - Product p = inventoryRepository.getProductById(id); - span.finish(); - return p; + return inventoryRepository.getProductById(id); } - } diff --git a/gateway/src/main/java/io/konveyor/demo/gateway/service/OrdersService.java b/gateway/src/main/java/io/konveyor/demo/gateway/service/OrdersService.java index 63642b9..742a171 100644 --- a/gateway/src/main/java/io/konveyor/demo/gateway/service/OrdersService.java +++ b/gateway/src/main/java/io/konveyor/demo/gateway/service/OrdersService.java @@ -1,28 +1,22 @@ package io.konveyor.demo.gateway.service; -import java.util.List; - -import io.konveyor.demo.gateway.model.Order; -import io.konveyor.demo.gateway.repository.CustomerRepository; -import io.konveyor.demo.gateway.repository.InventoryRepository; -import io.konveyor.demo.gateway.repository.OrderRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; -import io.opentracing.Span; -import io.opentracing.Tracer; +import io.konveyor.demo.gateway.model.Order; +import io.konveyor.demo.gateway.repository.CustomerRepository; +import io.konveyor.demo.gateway.repository.InventoryRepository; +import io.konveyor.demo.gateway.repository.OrderRepository; +import io.micrometer.tracing.annotation.NewSpan; +import io.micrometer.tracing.annotation.SpanTag; import lombok.extern.slf4j.Slf4j; @Service @Slf4j public class OrdersService { - - @Autowired - private Tracer tracer; - @Autowired private OrderRepository orderRepository; @@ -31,29 +25,30 @@ public class OrdersService { @Autowired private InventoryRepository inventoryRepository; - - public Order getById(Long id) { - Span span = tracer.buildSpan("getById").start(); + + @NewSpan + public Order getById(@SpanTag Long id) { log.debug("Entering OrdersService.getById()"); Order o = orderRepository.getOrderById(id); + if (o != null) { o.setCustomer(customerRepository.getCustomerById(o.getCustomer().getId())); o.setItems(inventoryRepository.getProductDetails(o.getItems())); } - span.finish(); + return o; } - public Page findAll(Pageable pageable) { - Span span = tracer.buildSpan("findAll").start(); + @NewSpan + public Page findAll(@SpanTag Pageable pageable) { log.debug("Entering OrdersService.findAll()"); - List orders = orderRepository.findAll(pageable); - for (Order o : orders) { + var orders = orderRepository.findAll(pageable); + + orders.forEach(o -> { o.setCustomer(customerRepository.getCustomerById(o.getCustomer().getId())); o.setItems(inventoryRepository.getProductDetails(o.getItems())); - } - span.finish(); - return new PageImpl(orders, pageable, orders.size()); - } + }); + return new PageImpl<>(orders, pageable, orders.size()); + } } diff --git a/gateway/src/main/java/io/konveyor/demo/util/PaginatedResponse.java b/gateway/src/main/java/io/konveyor/demo/util/PaginatedResponse.java index 8bb5c18..4e3052d 100644 --- a/gateway/src/main/java/io/konveyor/demo/util/PaginatedResponse.java +++ b/gateway/src/main/java/io/konveyor/demo/util/PaginatedResponse.java @@ -1,20 +1,17 @@ package io.konveyor.demo.util; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.JsonNode; +import java.util.ArrayList; +import java.util.List; + import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; -import java.util.ArrayList; -import java.util.List; -public class PaginatedResponse extends PageImpl { - /** - * - */ - private static final long serialVersionUID = -8776767744571311395L; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.JsonNode; +public class PaginatedResponse extends PageImpl { @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) public PaginatedResponse(@JsonProperty("content") List content, @JsonProperty("number") int number, diff --git a/gateway/src/main/resources/application.properties b/gateway/src/main/resources/application.properties index eadc1e9..1fd9de8 100644 --- a/gateway/src/main/resources/application.properties +++ b/gateway/src/main/resources/application.properties @@ -1,18 +1,17 @@ +spring.application.name=gateway + services.orders.url=http://orders:8080/orders services.inventory.url=http://inventory:8080/products services.customers.url=http://customers:8080/customers-tomcat-1.0.0-SNAPSHOT/customers -hystrix.command.ProductsCall.execution.isolation.thread.timeoutInMilliseconds=2000 -hystrix.threadpool.ProductsThreads.coreSize=20 -hystrix.threadpool.ProductsThreads.maxQueueSize=200 -hystrix.threadpool.ProductsThreads.queueSizeRejectionThreshold=200 +resilience4j.circuitbreaker.configs.default.register-health-indicator=true +resilience4j.timelimiter.configs.default.timeout-duration=1s +resilience4j.retry.configs.default.max-attempts=1 -# Disable Opentracing Spring Cloud Starter modules. Tracer registration is to be -# performed manually for Hystrix -opentracing.spring.cloud.jdbc.enabled=false -opentracing.spring.cloud.feign.enabled=false -opentracing.spring.cloud.jms.enabled=false -opentracing.spring.cloud.zuul.enabled=false -opentracing.spring.cloud.websocket.enabled=false -opentracing.spring.cloud.hystrix.strategy.enabled=true -opentracing.spring.cloud.async.enabled=false \ No newline at end of file +management.endponts.web.exposure.include=* +management.endpoint.health.show-details=always +management.health.circuitbreakers.enabled=true +management.info.git.mode=full +management.observations.annotations.enabled=true +management.otlp.tracing.endpoint=http://otel-collector:4318/v1/traces +management.tracing.sampling.probability=1.0 \ No newline at end of file diff --git a/gateway/src/test/java/io/konveyor/demo/gateway/controller/OrdersControllerTest.java b/gateway/src/test/java/io/konveyor/demo/gateway/controller/OrdersControllerTest.java index 657fb7b..43b4d20 100644 --- a/gateway/src/test/java/io/konveyor/demo/gateway/controller/OrdersControllerTest.java +++ b/gateway/src/test/java/io/konveyor/demo/gateway/controller/OrdersControllerTest.java @@ -1,41 +1,36 @@ package io.konveyor.demo.gateway.controller; -import static io.restassured.RestAssured.*; -import static org.hamcrest.Matchers.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import java.math.BigDecimal; -import java.util.ArrayList; import java.util.GregorianCalendar; import java.util.List; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import io.konveyor.demo.gateway.model.Customer; -import io.konveyor.demo.gateway.model.Order; -import io.konveyor.demo.gateway.model.OrderItem; -import io.konveyor.demo.gateway.model.Product; -import io.konveyor.demo.gateway.service.OrdersService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import org.springframework.beans.factory.annotation.Value; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; -import io.restassured.RestAssured; +import io.konveyor.demo.gateway.model.Customer; +import io.konveyor.demo.gateway.model.Order; +import io.konveyor.demo.gateway.model.OrderItem; +import io.konveyor.demo.gateway.model.Product; +import io.konveyor.demo.gateway.service.OrdersService; -@RunWith(SpringRunner.class) -@SpringBootTest( - webEnvironment = WebEnvironment.RANDOM_PORT) -@TestPropertySource( - locations = "classpath:application-test.properties") +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +@AutoConfigureMockMvc public class OrdersControllerTest { - - @Value("${local.server.port}") - private int port; - + @Autowired + private MockMvc mockMvc; + @MockBean private OrdersService service; @@ -47,14 +42,14 @@ public class OrdersControllerTest { private Customer customer; - @Before - public void setUp() { + @BeforeEach + void setUp() { order = new Order(); - order.setId(new Long(1)); + order.setId(1L); order.setDate(new GregorianCalendar(2018, 4, 30).getTime()); customer = new Customer(); - customer.setId(new Long(1)); + customer.setId(1L); customer.setName("Test Customer"); customer.setSurname("Test Customer"); customer.setUsername("testcustomer"); @@ -64,7 +59,7 @@ public void setUp() { customer.setAddress("Test Address"); product = new Product(); - product.setId(new Long(1)); + product.setId(1L); product.setName("Test Product"); product.setDescription("Test Description"); @@ -73,48 +68,50 @@ public void setUp() { item.setQuantity(3); item.setProduct(product); - List items = new ArrayList(); - items.add(item); - + List items = List.of(item); + order.setItems(items); order.setCustomer(customer); - - RestAssured.baseURI = String.format("http://localhost:%d/orders", port); } @Test - public void getByIdExisting() { - Mockito.when(service.getById(new Long (1))) + void getByIdExisting() throws Exception { + Mockito.when(service.getById(1L)) .thenReturn(order); - - when().get(new Long(1).toString()) - .then() - .statusCode(200) - .body("id", is(1)) - .body("date", is("30-05-2018")) - .body("customer.id", is(1)) - .body("customer.name", is("Test Customer")) - .body("customer.surname", is("Test Customer")) - .body("customer.username", is("testcustomer")) - .body("customer.zipCode", is("28080")) - .body("customer.city", is("Madrid")) - .body("customer.country", is("Spain")) - .body("customer.address", is("Test Address")) - .body("items.size()", is(1)) - .body("items.get(0).quantity", is(3)) - .body("items.get(0).price", is(30)) - .body("items.get(0).product.id", is(1)) - .body("items.get(0).product.name", is("Test Product")) - .body("items.get(0).product.description", is("Test Description")); + + this.mockMvc.perform(get("/orders/{id}", 1L)) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$.id").value(1L)) + .andExpect(jsonPath("$.date").value("30-05-2018")) + .andExpect(jsonPath("$.customer.id").value(1L)) + .andExpect(jsonPath("$.customer.name").value("Test Customer")) + .andExpect(jsonPath("$.customer.surname").value("Test Customer")) + .andExpect(jsonPath("$.customer.username").value("testcustomer")) + .andExpect(jsonPath("$.customer.zipCode").value("28080")) + .andExpect(jsonPath("$.customer.city").value("Madrid")) + .andExpect(jsonPath("$.customer.country").value("Spain")) + .andExpect(jsonPath("$.customer.address").value("Test Address")) + .andExpect(jsonPath("$.items.size()").value(1)) + .andExpect(jsonPath("$.items[0].quantity").value(3)) + .andExpect(jsonPath("$.items[0].price").value(30)) + .andExpect(jsonPath("$.items[0].product.id").value(1L)) + .andExpect(jsonPath("$.items[0].product.name").value("Test Product")) + .andExpect(jsonPath("$.items[0].product.description").value("Test Description")); + + Mockito.verify(service).getById(1L); + Mockito.verifyNoMoreInteractions(service); } @Test - public void getByIdNonExisting() { - Mockito.when(service.getById(new Long (1))) + void getByIdNonExisting() throws Exception { + Mockito.when(service.getById(1L)) .thenReturn(null); - - when().get(new Long(1).toString()) - .then() - .statusCode(404); + + this.mockMvc.perform(get("/orders/{id}", 1L)) + .andExpect(status().isNotFound()); + + Mockito.verify(service).getById(1L); + Mockito.verifyNoMoreInteractions(service); } } diff --git a/gateway/src/test/java/io/konveyor/demo/gateway/model/OrderTest.java b/gateway/src/test/java/io/konveyor/demo/gateway/model/OrderTest.java index 9746a30..80f0449 100644 --- a/gateway/src/test/java/io/konveyor/demo/gateway/model/OrderTest.java +++ b/gateway/src/test/java/io/konveyor/demo/gateway/model/OrderTest.java @@ -6,7 +6,7 @@ import java.util.ArrayList; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class OrderTest { diff --git a/gateway/src/test/java/io/konveyor/demo/gateway/repository/CustomerRepositoryTest.java b/gateway/src/test/java/io/konveyor/demo/gateway/repository/CustomerRepositoryTest.java index c47c486..e32996a 100644 --- a/gateway/src/test/java/io/konveyor/demo/gateway/repository/CustomerRepositoryTest.java +++ b/gateway/src/test/java/io/konveyor/demo/gateway/repository/CustomerRepositoryTest.java @@ -1,95 +1,122 @@ package io.konveyor.demo.gateway.repository; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.*; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; +import static org.springframework.test.web.client.response.MockRestResponseCreators.*; -import java.util.Properties; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; -import org.apache.commons.lang.SerializationUtils; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import io.konveyor.demo.gateway.model.Customer; -import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.web.client.RestTemplate; - -import io.jaegertracing.Configuration; -import io.opentracing.Tracer; - -@RunWith(SpringRunner.class) -public class CustomerRepositoryTest { - - @TestConfiguration - static class CustomerRepositoryTestContextConfiguration { - @Bean - public Tracer tracer() { - return new Configuration("gateway").getTracer(); - } - - @Bean - public static PropertySourcesPlaceholderConfigurer properties() { - final PropertySourcesPlaceholderConfigurer pspc = - new PropertySourcesPlaceholderConfigurer(); - final Properties properties = new Properties(); - properties.setProperty("services.customers.url", "http://customers.svc:8080/customers"); - pspc.setProperties(properties); - return pspc; - } - - @Bean - public CustomerRepository repository() { - return new CustomerRepository(); - } - } - - @MockBean - RestTemplate restTemplate; - +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.autoconfigure.aop.AopAutoConfiguration; +import org.springframework.boot.test.autoconfigure.actuate.observability.AutoConfigureObservability; +import org.springframework.boot.test.autoconfigure.web.client.RestClientTest; +import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JAutoConfiguration; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.test.web.client.MockRestServiceServer; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.github.resilience4j.springboot3.circuitbreaker.autoconfigure.CircuitBreakerAutoConfiguration; +import io.github.resilience4j.springboot3.circuitbreaker.autoconfigure.CircuitBreakerMetricsAutoConfiguration; +import io.github.resilience4j.springboot3.retry.autoconfigure.RetryAutoConfiguration; +import io.github.resilience4j.springboot3.timelimiter.autoconfigure.TimeLimiterAutoConfiguration; +import io.konveyor.demo.gateway.model.Customer; + +@RestClientTest( + components = CustomerRepository.class, + properties = "services.customers.url=/customers" +) +@ImportAutoConfiguration({ AopAutoConfiguration.class, Resilience4JAutoConfiguration.class, RetryAutoConfiguration.class, CircuitBreakerAutoConfiguration.class, CircuitBreakerMetricsAutoConfiguration.class, TimeLimiterAutoConfiguration.class }) +@AutoConfigureObservability +class CustomerRepositoryTest { + @Autowired + private MockRestServiceServer server; + + @Autowired + private CustomerRepository repository; + @Autowired - CustomerRepository repository; - - private Customer customer; - - @Before - public void setup() { - customer = new Customer(); - customer.setId(new Long(1)); - customer.setName("Test Customer"); - customer.setSurname("Test Customer"); - customer.setUsername("testcustomer"); - customer.setZipCode("28080"); - customer.setCountry("Spain"); - customer.setCity("Madrid"); - customer.setAddress("Test Address"); + private ObjectMapper objectMapper; + + private static Customer CUSTOMER; + private String customerJson; + + @BeforeAll + static void createCustomer() { + CUSTOMER = new Customer(); + CUSTOMER.setId(1L); + CUSTOMER.setName("Test Customer"); + CUSTOMER.setSurname("Test Customer"); + CUSTOMER.setUsername("testcustomer"); + CUSTOMER.setZipCode("28080"); + CUSTOMER.setCountry("Spain"); + CUSTOMER.setCity("Madrid"); + CUSTOMER.setAddress("Test Address"); + } + + @BeforeEach + void createCustomerJson() throws JsonProcessingException { + this.customerJson = this.objectMapper.writeValueAsString(CUSTOMER); + this.server.reset(); + } + + @Test + void getCustomerByIdExistingTest() { + this.server.expect(requestTo("/customers/1")) + .andExpect(method(HttpMethod.GET)) + .andRespond(withSuccess(customerJson, MediaType.APPLICATION_JSON)); + + var customer = repository.getCustomerById(1L); + + assertThat(customer) + .isNotNull() + .usingRecursiveComparison() + .isEqualTo(CUSTOMER); + + this.server.verify(); } - + @Test - public void getCustomerByIdExistingTest() { - Mockito.when(restTemplate.getForObject("http://customers.svc:8080/customers/1", Customer.class)) - .thenReturn((Customer)SerializationUtils.clone(customer)); - - Customer found = repository.getCustomerById(new Long(1)); - assertThat(found).isEqualTo(customer); + void getCustomerByIdNonExistingTest() { + this.server.expect(requestTo("/customers/1")) + .andExpect(method(HttpMethod.GET)) + .andRespond(withSuccess()); + + + var fallbackCustomer = repository.getFallbackCustomer(1L, new Exception()); + + assertThat(repository.getCustomerById(1L)) + .isNotNull() + .usingRecursiveComparison() + .isEqualTo(fallbackCustomer); + + this.server.verify(); } - - @Test(expected = RuntimeException.class) - public void getCustomerByIdNonExistingTest() { - - Mockito.when(restTemplate.getForObject("http://customers.svc:8080/customers/1", Customer.class)) - .thenReturn(null); - - repository.getCustomerById(new Long(1)); + + @Test + void fallsBack() { + var fallbackCustomer = repository.getFallbackCustomer(1L, new Exception()); + + this.server.expect(requestTo("/customers/1")) + .andExpect(method(HttpMethod.GET)) + .andRespond(withServerError()); + + assertThat(repository.getCustomerById(1L)) + .isNotNull() + .usingRecursiveComparison() + .isEqualTo(fallbackCustomer); + + this.server.verify(); } - + @Test - public void getFallbackCustomerTest() { + void getFallbackCustomerTest() { Customer c = new Customer(); - c.setId(new Long(1)); + c.setId(1L); c.setUsername("Unknown"); c.setName("Unknown"); c.setSurname("Unknown"); @@ -97,9 +124,11 @@ public void getFallbackCustomerTest() { c.setCity("Unknown"); c.setCountry("Unknown"); c.setZipCode("Unknown"); - - Customer found = repository.getFallbackCustomer(new Long(1), new Exception()); - - assertThat(found).isEqualTo(c); + + var customer = repository.getFallbackCustomer(1L, new Exception()); + + assertThat(customer) + .usingRecursiveComparison() + .isEqualTo(c); } } diff --git a/gateway/src/test/java/io/konveyor/demo/gateway/repository/InventoryRepositoryTest.java b/gateway/src/test/java/io/konveyor/demo/gateway/repository/InventoryRepositoryTest.java index 67325cc..a2dc018 100644 --- a/gateway/src/test/java/io/konveyor/demo/gateway/repository/InventoryRepositoryTest.java +++ b/gateway/src/test/java/io/konveyor/demo/gateway/repository/InventoryRepositoryTest.java @@ -1,63 +1,49 @@ package io.konveyor.demo.gateway.repository; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; +import static org.springframework.test.web.client.response.MockRestResponseCreators.*; import java.math.BigDecimal; -import java.util.ArrayList; import java.util.List; -import java.util.Properties; -import org.apache.commons.lang.SerializationUtils; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.autoconfigure.aop.AopAutoConfiguration; +import org.springframework.boot.test.autoconfigure.actuate.observability.AutoConfigureObservability; +import org.springframework.boot.test.autoconfigure.web.client.RestClientTest; +import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JAutoConfiguration; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.test.web.client.MockRestServiceServer; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.github.resilience4j.springboot3.circuitbreaker.autoconfigure.CircuitBreakerAutoConfiguration; +import io.github.resilience4j.springboot3.circuitbreaker.autoconfigure.CircuitBreakerMetricsAutoConfiguration; +import io.github.resilience4j.springboot3.retry.autoconfigure.RetryAutoConfiguration; +import io.github.resilience4j.springboot3.timelimiter.autoconfigure.TimeLimiterAutoConfiguration; import io.konveyor.demo.gateway.model.OrderItem; import io.konveyor.demo.gateway.model.Product; -import org.mockito.Mockito; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.web.client.RestClientException; -import org.springframework.web.client.RestTemplate; - -import io.jaegertracing.Configuration; -import io.opentracing.Tracer; - -@RunWith(SpringRunner.class) -public class InventoryRepositoryTest { - - @TestConfiguration - static class CustomerRepositoryTestContextConfiguration { - @Bean - public Tracer tracer() { - return new Configuration("gateway").getTracer(); - } - - @Bean - public static PropertySourcesPlaceholderConfigurer properties() { - final PropertySourcesPlaceholderConfigurer pspc = - new PropertySourcesPlaceholderConfigurer(); - final Properties properties = new Properties(); - properties.setProperty("services.inventory.url", "http://inventory.svc:8080/products"); - properties.setProperty("hystrix.threadpool.ProductsThreads.coreSize", "20"); - pspc.setProperties(properties); - return pspc; - } - - @Bean - public InventoryRepository repository() { - return new InventoryRepository(); - } - } - - @MockBean - RestTemplate restTemplate; + +@RestClientTest( + components = InventoryRepository.class, + properties = "services.inventory.url=/products" +) +@ImportAutoConfiguration({ AopAutoConfiguration.class, Resilience4JAutoConfiguration.class, RetryAutoConfiguration.class, CircuitBreakerAutoConfiguration.class, CircuitBreakerMetricsAutoConfiguration.class, TimeLimiterAutoConfiguration.class }) +@AutoConfigureObservability +class InventoryRepositoryTest { + @Autowired + private MockRestServiceServer server; @Autowired - InventoryRepository repository; + private InventoryRepository repository; + + @Autowired + private ObjectMapper objectMapper; private List items; @@ -65,42 +51,40 @@ public InventoryRepository repository() { private Product product2; - @Before - public void setup() { + @BeforeEach + void setup() { product1 = new Product(); - product1.setId(new Long(1)); + product1.setId(1L); product1.setName("Test Product 1"); product1.setDescription("Test Description 1"); product2 = new Product(); - product2.setId(new Long(2)); + product2.setId(2L); product2.setName("Test Product 2"); product2.setDescription("Test Description 2"); OrderItem item1 = new OrderItem(); item1.setPrice(new BigDecimal(30)); item1.setQuantity(3); - item1.setProduct((Product)SerializationUtils.clone(product1)); + item1.setProduct(new Product(product1.getId(), product1.getName(), product1.getDescription())); OrderItem item2 = new OrderItem(); item2.setPrice(new BigDecimal(50)); item2.setQuantity(2); - item2.setProduct((Product)SerializationUtils.clone(product2)); - - items = new ArrayList(); + item2.setProduct(new Product(product2.getId(), product2.getName(), product2.getDescription())); - items.add(item1); - items.add(item2); + items = List.of(item1, item2); + + this.server.reset(); } @Test - public void getProductDetailsExistingTest() { - + void getProductDetailsExistingTest() throws JsonProcessingException { Product p1 = new Product(); - p1.setId(new Long(1)); + p1.setId(1L); Product p2 = new Product(); - p2.setId(new Long(2)); + p2.setId(2L); OrderItem i1 = new OrderItem(); i1.setPrice(new BigDecimal(30)); @@ -112,30 +96,32 @@ public void getProductDetailsExistingTest() { i2.setQuantity(2); i2.setProduct(p2); - List incomplete = new ArrayList(); - - incomplete.add(i1); - incomplete.add(i2); - - Mockito.when(restTemplate.getForObject("http://inventory.svc:8080/products/1", Product.class)) - .thenReturn(product1); - - Mockito.when(restTemplate.getForObject("http://inventory.svc:8080/products/2", Product.class)) - .thenReturn(product2); - - List found = repository.getProductDetails(incomplete); + List incomplete = List.of(i1, i2); + + this.server.expect(requestTo("/products/1")) + .andExpect(method(HttpMethod.GET)) + .andRespond(withSuccess(objectMapper.writeValueAsString(product1), MediaType.APPLICATION_JSON)); + + this.server.expect(requestTo("/products/2")) + .andExpect(method(HttpMethod.GET)) + .andRespond(withSuccess(objectMapper.writeValueAsString(product2), MediaType.APPLICATION_JSON)); + + var found = repository.getProductDetails(incomplete); - assertThat(found).isEqualTo(items); + assertThat(found) + .usingRecursiveFieldByFieldElementComparator() + .isEqualTo(items); + + this.server.verify(); } @Test - public void getProductDetailsOneNonExistingTest() { - + void getProductDetailsOneNonExistingTest() throws JsonProcessingException { Product p1 = new Product(); - p1.setId(new Long(1)); + p1.setId(1L); Product p2 = new Product(); - p2.setId(new Long(2)); + p2.setId(2L); OrderItem i1 = new OrderItem(); i1.setPrice(new BigDecimal(30)); @@ -147,32 +133,34 @@ public void getProductDetailsOneNonExistingTest() { i2.setQuantity(2); i2.setProduct(p2); - List incomplete = new ArrayList(); - - incomplete.add(i1); - incomplete.add(i2); - - Mockito.when(restTemplate.getForObject("http://inventory.svc:8080/products/1", Product.class)) - .thenReturn(null); - - Mockito.when(restTemplate.getForObject("http://inventory.svc:8080/products/2", Product.class)) - .thenReturn(product2); - - List found = repository.getProductDetails(incomplete); + List incomplete = List.of(i1, i2); + + server.expect(requestTo("/products/1")) + .andExpect(method(HttpMethod.GET)) + .andRespond(withSuccess()); + + server.expect(requestTo("/products/2")) + .andExpect(method(HttpMethod.GET)) + .andRespond(withSuccess(objectMapper.writeValueAsString(product2), MediaType.APPLICATION_JSON)); + + var found = repository.getProductDetails(incomplete); items.get(0).setProduct(p1); - - assertThat(found).isEqualTo(items); + + assertThat(found) + .usingRecursiveFieldByFieldElementComparator() + .isEqualTo(items); + + server.verify(); } @Test - public void getProductDetailsRestExceptionTest() { - + void getProductDetailsRestExceptionTest() { Product p1 = new Product(); - p1.setId(new Long(1)); + p1.setId(1L); Product p2 = new Product(); - p2.setId(new Long(2)); + p2.setId(2L); OrderItem i1 = new OrderItem(); i1.setPrice(new BigDecimal(30)); @@ -184,21 +172,22 @@ public void getProductDetailsRestExceptionTest() { i2.setQuantity(2); i2.setProduct(p2); - List incomplete = new ArrayList(); - - incomplete.add(i1); - incomplete.add(i2); - - Mockito.when(restTemplate.getForObject("http://inventory.svc:8080/products/1", Product.class)) - .thenThrow(new RestClientException("Failed 1")); - - Mockito.when(restTemplate.getForObject("http://inventory.svc:8080/products/2", Product.class)) - .thenThrow(new RestClientException("Failed 2")); - + List incomplete = List.of(i1, i2); + + server.expect(requestTo("/products/1")) + .andExpect(method(HttpMethod.GET)) + .andRespond(withServerError()); + + server.expect(requestTo("/products/2")) + .andExpect(method(HttpMethod.GET)) + .andRespond(withServerError()); + List found = repository.getProductDetails(incomplete); - assertThat(found).isEqualTo(incomplete); + assertThat(found) + .usingRecursiveFieldByFieldElementComparator() + .isEqualTo(incomplete); + + server.verify(); } - - } diff --git a/gateway/src/test/java/io/konveyor/demo/gateway/repository/OrderRepositoryTest.java b/gateway/src/test/java/io/konveyor/demo/gateway/repository/OrderRepositoryTest.java index 08e6575..bb82837 100644 --- a/gateway/src/test/java/io/konveyor/demo/gateway/repository/OrderRepositoryTest.java +++ b/gateway/src/test/java/io/konveyor/demo/gateway/repository/OrderRepositoryTest.java @@ -1,113 +1,136 @@ package io.konveyor.demo.gateway.repository; import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; +import static org.springframework.test.web.client.response.MockRestResponseCreators.*; import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.GregorianCalendar; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.Date; import java.util.List; -import java.util.Properties; -import org.apache.commons.lang.SerializationUtils; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; +import org.springframework.boot.autoconfigure.aop.AopAutoConfiguration; +import org.springframework.boot.test.autoconfigure.actuate.observability.AutoConfigureObservability; +import org.springframework.boot.test.autoconfigure.web.client.RestClientTest; +import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JAutoConfiguration; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.test.web.client.MockRestServiceServer; + +import io.github.resilience4j.springboot3.circuitbreaker.autoconfigure.CircuitBreakerAutoConfiguration; +import io.github.resilience4j.springboot3.circuitbreaker.autoconfigure.CircuitBreakerMetricsAutoConfiguration; +import io.github.resilience4j.springboot3.retry.autoconfigure.RetryAutoConfiguration; +import io.github.resilience4j.springboot3.timelimiter.autoconfigure.TimeLimiterAutoConfiguration; import io.konveyor.demo.gateway.model.Customer; import io.konveyor.demo.gateway.model.Order; import io.konveyor.demo.gateway.model.OrderItem; import io.konveyor.demo.gateway.model.Product; -import org.mockito.Mockito; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.web.client.RestTemplate; - -import io.jaegertracing.Configuration; -import io.opentracing.Tracer; - -@RunWith(SpringRunner.class) -public class OrderRepositoryTest { - - @TestConfiguration - static class CustomerRepositoryTestContextConfiguration { - @Bean - public Tracer tracer() { - return new Configuration("gateway").getTracer(); - } - - @Bean - public static PropertySourcesPlaceholderConfigurer properties() { - final PropertySourcesPlaceholderConfigurer pspc = - new PropertySourcesPlaceholderConfigurer(); - final Properties properties = new Properties(); - properties.setProperty("services.orders.url", "http://orders.svc:8080/orders"); - pspc.setProperties(properties); - return pspc; - } - - @Bean - public OrderRepository repository() { - return new OrderRepository(); - } - } - - @MockBean - RestTemplate restTemplate; - + +@RestClientTest( + components = OrderRepository.class, + properties = "services.orders.url=/orders" +) +@ImportAutoConfiguration({ AopAutoConfiguration.class, Resilience4JAutoConfiguration.class, RetryAutoConfiguration.class, CircuitBreakerAutoConfiguration.class, CircuitBreakerMetricsAutoConfiguration.class, TimeLimiterAutoConfiguration.class }) +@AutoConfigureObservability +class OrderRepositoryTest { @Autowired - OrderRepository repository; + private MockRestServiceServer server; + @Autowired + private OrderRepository repository; + private Order order; - @Before - public void setup() { + @BeforeEach + void setup() { order = new Order(); - order.setId(new Long(1)); - order.setDate(new GregorianCalendar(2018, 4, 30).getTime()); + order.setId(11L); + order.setDate(Date.from(LocalDate.of(2018, 04, 30).atStartOfDay(ZoneId.of("CET")).toInstant())); Product p = new Product(); - p.setId(new Long(1)); + p.setId(1L); Customer c = new Customer(); - c.setId(new Long(1)); + c.setId(1L); OrderItem i = new OrderItem(); i.setPrice(new BigDecimal(30)); i.setQuantity(3); i.setProduct(p); - List items = new ArrayList(); - items.add(i); - + List items = List.of(i); + order.setItems(items); order.setCustomer(c); } @Test - public void getOrderByIdExistingTest() { - Mockito.when(restTemplate.getForObject("http://orders.svc:8080/orders/1", Order.class)) - .thenReturn((Order)SerializationUtils.clone(order)); - - Order found = repository.getOrderById(new Long(1)); + void getOrderByIdExistingTest() { + var json = """ + { + "id": 11, + "date": "30-04-2018", + "items": [ + { + "quantity": 3, + "price": 30, + "productUID": 1 + } + ], + "totalAmmount": 90, + "customerUID": 1 + } + """; + + this.server.expect(requestTo("/orders/1")) + .andExpect(method(HttpMethod.GET)) + .andRespond(withSuccess(json, MediaType.APPLICATION_JSON)); + + Order found = repository.getOrderById(1L); - assertThat(found).isEqualTo(order); + assertThat(found) + .usingRecursiveComparison() + .isEqualTo(order); + + this.server.verify(); } @Test - public void getOrderByIdNonExistingTest() { - Mockito.when(restTemplate.getForObject("http://orders.svc:8080/orders/1", Order.class)) - .thenReturn(null); - - Order found = repository.getOrderById(new Long(1)); + void getOrderByIdNonExistingTest() { + this.server.expect(requestTo("/orders/1")) + .andExpect(method(HttpMethod.GET)) + .andRespond(withSuccess()); + + Order found = repository.getOrderById(1L); - assertThat(found).isNull(); + assertThat(found) + .isNull(); + this.server.verify(); } @Test - public void getFallbackCustomerTest() { - assertThat(repository.getFallbackOrder(new Long(1), new Exception())).isNull(); + void getFallbackCustomerTest() { + assertThat(repository.getFallbackOrder(1L, new Exception())).isNull(); + } + + @Test + void fallsBack() { + var fallbackOrder = repository.getFallbackOrder(1L, new Exception()); + + this.server.expect(requestTo("/orders/1")) + .andExpect(method(HttpMethod.GET)) + .andRespond(withServerError()); + + assertThat(repository.getOrderById(1L)) + .usingRecursiveComparison() + .isEqualTo(fallbackOrder); + + this.server.verify(); } } diff --git a/gateway/src/test/java/io/konveyor/demo/gateway/service/OrdersServiceTest.java b/gateway/src/test/java/io/konveyor/demo/gateway/service/OrdersServiceTest.java index c171191..fe31767 100644 --- a/gateway/src/test/java/io/konveyor/demo/gateway/service/OrdersServiceTest.java +++ b/gateway/src/test/java/io/konveyor/demo/gateway/service/OrdersServiceTest.java @@ -3,14 +3,19 @@ import static org.assertj.core.api.Assertions.assertThat; import java.math.BigDecimal; -import java.util.ArrayList; import java.util.GregorianCalendar; import java.util.List; -import org.apache.commons.lang.SerializationUtils; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Bean; + import io.konveyor.demo.gateway.model.Customer; import io.konveyor.demo.gateway.model.Order; import io.konveyor.demo.gateway.model.OrderItem; @@ -18,60 +23,46 @@ import io.konveyor.demo.gateway.repository.CustomerRepository; import io.konveyor.demo.gateway.repository.InventoryRepository; import io.konveyor.demo.gateway.repository.OrderRepository; -import org.mockito.Mockito; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Bean; -import org.springframework.test.context.junit4.SpringRunner; -import io.jaegertracing.Configuration; -import io.opentracing.Tracer; +@SpringBootTest +class OrdersServiceTest { -@RunWith(SpringRunner.class) -public class OrdersServiceTest { - @TestConfiguration - static class OrdersServiceTestContextConfiguration { + static class OrdersServiceTestContextConfiguration { @Bean public OrdersService ordersService() { return new OrdersService(); } - - @Bean - public Tracer tracer() { - return new Configuration("gateway").getTracer(); - } } - + @Autowired - OrdersService ordersService; - + private OrdersService ordersService; + @MockBean private OrderRepository orderRepository; - + @MockBean private CustomerRepository customerRepository; - + @MockBean private InventoryRepository inventoryRepository; - + private Order order; - + private OrderItem item; - + private Product product; - + private Customer customer; - - @Before - public void setup() { + + @BeforeEach + void setup() { order = new Order(); - order.setId(new Long(1)); + order.setId(1L); order.setDate(new GregorianCalendar(2018, 5, 30).getTime()); - + customer = new Customer(); - customer.setId(new Long(1)); + customer.setId(1L); customer.setName("Test Customer"); customer.setSurname("Test Customer"); customer.setUsername("testcustomer"); @@ -79,71 +70,69 @@ public void setup() { customer.setCountry("Spain"); customer.setCity("Madrid"); customer.setAddress("Test Address"); - + product = new Product(); - product.setId(new Long(1)); + product.setId(1L); product.setName("Test Product"); product.setDescription("Test Description"); - + item = new OrderItem(); item.setPrice(new BigDecimal(30)); item.setQuantity(3); item.setProduct(product); - - List items = new ArrayList(); - items.add(item); - + + List items = List.of(item); order.setItems(items); order.setCustomer(customer); } - + @Test - public void getByIdWithExistingOrderTest() { + void getByIdWithExistingOrderTest() { Order o = new Order(); - o.setId(new Long(1)); + o.setId(1L); o.setDate(new GregorianCalendar(2018, 5, 30).getTime()); - + Product p = new Product(); - p.setId(new Long(1)); - + p.setId(1L); + Customer c = new Customer(); - c.setId(new Long(1)); - + c.setId(1L); + OrderItem i = new OrderItem(); i.setPrice(new BigDecimal(30)); i.setQuantity(3); i.setProduct(p); - - List items = new ArrayList(); - items.add(item); - - List detaileditems = new ArrayList(); - detaileditems.add((OrderItem)SerializationUtils.clone(item)); - + + List items = List.of(item); + + List detaileditems = List.of(new OrderItem(item.getProduct(), item.getQuantity(), item.getPrice())); + o.setItems(items); o.setCustomer(c); - - Mockito.when(orderRepository.getOrderById(new Long(1))) + + Mockito.when(orderRepository.getOrderById(1L)) .thenReturn(o); - - Mockito.when(customerRepository.getCustomerById(new Long(1))) - .thenReturn((Customer)SerializationUtils.clone(customer)); - + + Mockito.when(customerRepository.getCustomerById(1L)) + .thenReturn(customer.toBuilder().build()); + Mockito.when(inventoryRepository.getProductDetails(items)) - .thenReturn(detaileditems); - - Order found = ordersService.getById(new Long(1)); - - assertThat(found).isEqualTo(order); + .thenReturn(detaileditems); + + Order found = ordersService.getById(1L); + + assertThat(found) + .usingRecursiveComparison() + .isEqualTo(order); } - + @Test - public void getByIdWithNonExistingOrderTest() { - Mockito.when(orderRepository.getOrderById(new Long(1))) - .thenReturn(null); - - Order found = ordersService.getById(new Long(1)); - + void getByIdWithNonExistingOrderTest() { + Mockito.when(orderRepository.getOrderById(1L)) + .thenReturn(null); + + Order found = ordersService.getById(1L); + assertThat(found).isNull(); } } diff --git a/gateway/src/test/resources/application-test.properties b/gateway/src/test/resources/application-test.properties deleted file mode 100644 index 0a69bdf..0000000 --- a/gateway/src/test/resources/application-test.properties +++ /dev/null @@ -1,19 +0,0 @@ -services.orders.url=http://orders-rhoar.192.168.42.215.nip.io/orders -services.inventory.url=http://inventory-rhoar.192.168.42.215.nip.io/products -services.customers.url=http://customers-rhoar.192.168.42.215.nip.io/customers - -hystrix.command.ProductsCall.execution.isolation.thread.timeoutInMilliseconds=2000 - -hystrix.threadpool.ProductsThreads.coreSize=20 -hystrix.threadpool.ProductsThreads.maxQueueSize=200 -hystrix.threadpool.ProductsThreads.queueSizeRejectionThreshold=200 - -# Disable Opentracing Spring Cloud Starter modules. Tracer registration is to be -# performed manually for Hystrix -opentracing.spring.cloud.jdbc.enabled=false -opentracing.spring.cloud.feign.enabled=false -opentracing.spring.cloud.jms.enabled=false -opentracing.spring.cloud.zuul.enabled=false -opentracing.spring.cloud.websocket.enabled=false -opentracing.spring.cloud.hystrix.strategy.enabled=true -opentracing.spring.cloud.async.enabled=false \ No newline at end of file diff --git a/gateway/src/test/resources/bootstrap.properties b/gateway/src/test/resources/bootstrap.properties deleted file mode 100644 index 5312ef6..0000000 --- a/gateway/src/test/resources/bootstrap.properties +++ /dev/null @@ -1,4 +0,0 @@ -spring.application.name=gateway -#Point to a non existing configmap to force loading the test properties file specified -#in the integration tests. -spring.cloud.kubernetes.config.name=gateway-test-config diff --git a/inventory/pom.xml b/inventory/pom.xml index 61e7dc2..1258af4 100644 --- a/inventory/pom.xml +++ b/inventory/pom.xml @@ -1,8 +1,5 @@ - - + + 4.0.0 com.redhat.coolstore inventory @@ -34,7 +31,7 @@ io.quarkus - quarkus-resteasy-reactive-jackson + quarkus-rest-jackson org.springframework.boot diff --git a/orders/src/main/java/io/konveyor/demo/orders/config/OtelDataSourceConfig.java b/orders/src/main/java/io/konveyor/demo/orders/config/OtelDataSourceConfig.java new file mode 100644 index 0000000..1d60c5a --- /dev/null +++ b/orders/src/main/java/io/konveyor/demo/orders/config/OtelDataSourceConfig.java @@ -0,0 +1,30 @@ +package io.konveyor.demo.orders.config; + +import javax.sql.DataSource; + +import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.boot.jdbc.DataSourceBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.jdbc.datasource.JdbcTelemetry; + +/** + * This class is needed to allow tracing down to the JDBC level. + * Taken from https://opentelemetry.io/docs/languages/java/automatic/spring-boot/#jdbc-instrumentation + */ +@Configuration +public class OtelDataSourceConfig { + @Bean + public DataSource dataSource(DataSourceProperties dataSourceProperties, OpenTelemetry openTelemetry) { + var dataSource = DataSourceBuilder.create() + .driverClassName(dataSourceProperties.determineDriverClassName()) + .url(dataSourceProperties.determineUrl()) + .username(dataSourceProperties.getUsername()) + .password(dataSourceProperties.getPassword()) + .build(); + + return JdbcTelemetry.create(openTelemetry).wrap(dataSource); + } +} \ No newline at end of file diff --git a/orders/src/main/resources/application.properties b/orders/src/main/resources/application.properties index f7f17ce..55e2064 100644 --- a/orders/src/main/resources/application.properties +++ b/orders/src/main/resources/application.properties @@ -1,3 +1,5 @@ +spring.application.name=orders + spring.datasource.url=jdbc:h2:mem:orders;DB_CLOSE_ON_EXIT=FALSE spring.datasource.username=orders spring.datasource.password=orders @@ -10,5 +12,5 @@ management.endponts.web.exposure.include=* management.endpoint.health.show-details=always management.info.git.mode=full management.observations.annotations.enabled=true -management.otlp.tracing.endpoint=http://localhost:4318/traces +management.otlp.tracing.endpoint=http://otel-collector:4318/v1/traces management.tracing.sampling.probability=1.0 \ No newline at end of file