├── retail-store-webapp ├── src │ ├── main │ │ ├── resources │ │ │ ├── static │ │ │ │ ├── css │ │ │ │ │ └── styles.css │ │ │ │ ├── images │ │ │ │ │ └── books.png │ │ │ │ └── js │ │ │ │ │ ├── orders.js │ │ │ │ │ └── orderDetails.js │ │ │ └── templates │ │ │ │ ├── fragments │ │ │ │ ├── inventoryPagination.html │ │ │ │ └── productsPagination.html │ │ │ │ ├── login.html │ │ │ │ └── orders.html │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── retailstore │ │ │ └── webapp │ │ │ ├── clients │ │ │ ├── order │ │ │ │ ├── OrderConfirmationDTO.java │ │ │ │ ├── OrderItemExternal.java │ │ │ │ ├── OrderItemRequest.java │ │ │ │ ├── OrderItemResponse.java │ │ │ │ ├── Address.java │ │ │ │ ├── OrderRequestExternal.java │ │ │ │ ├── CreateOrderRequest.java │ │ │ │ └── OrderServiceClient.java │ │ │ ├── customer │ │ │ │ ├── CustomerResponse.java │ │ │ │ ├── CustomerRequest.java │ │ │ │ └── CustomerServiceClient.java │ │ │ ├── inventory │ │ │ │ ├── InventoryResponse.java │ │ │ │ ├── InventoryUpdateRequest.java │ │ │ │ └── InventoryServiceClient.java │ │ │ ├── PagedResult.java │ │ │ └── catalog │ │ │ │ ├── ProductResponse.java │ │ │ │ ├── ProductRequest.java │ │ │ │ └── CatalogServiceClient.java │ │ │ ├── config │ │ │ ├── ApplicationProperties.java │ │ │ ├── SecurityConstants.java │ │ │ └── WebConfig.java │ │ │ ├── exception │ │ │ ├── InvalidRequestException.java │ │ │ └── ResourceNotFoundException.java │ │ │ ├── RetailStoreWebappApplication.java │ │ │ └── web │ │ │ ├── controller │ │ │ └── LoginController.java │ │ │ └── model │ │ │ └── request │ │ │ └── RegistrationRequest.java │ └── test │ │ ├── resources │ │ └── application-test.yml │ │ └── java │ │ └── com │ │ └── example │ │ └── retailstore │ │ └── webapp │ │ ├── ApplicationIntegrationTest.java │ │ ├── clients │ │ └── order │ │ │ └── CreateOrderRequestTest.java │ │ └── common │ │ └── AbstractIntegrationTest.java ├── .mvn │ └── wrapper │ │ └── maven-wrapper.properties ├── docker │ └── docker-compose.yml ├── .gitignore └── sonar-project.properties ├── api-gateway ├── src │ ├── test │ │ ├── resources │ │ │ ├── junit-platform.properties │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── api │ │ │ │ └── gateway │ │ │ │ ├── filter │ │ │ │ ├── CorrelationIdFilterIntegrationTest │ │ │ │ │ └── correlation-test.json │ │ │ │ └── LoggingFilterIntegrationTest │ │ │ │ │ └── logging-test.json │ │ │ │ └── config │ │ │ │ ├── ApiGatewayConfigurationIntegrationTest │ │ │ │ ├── get-mapping.json │ │ │ │ └── test-routing.json │ │ │ │ ├── RateLimiterConfigurationIntegrationTest │ │ │ │ └── mocks-config.json │ │ │ │ └── CircuitBreakerConfigurationIntegrationTest │ │ │ │ └── circuit-breaker.json │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── api │ │ │ └── gateway │ │ │ ├── TestAPIGatewayApplication.java │ │ │ ├── APIGatewayApplicationIntegrationTest.java │ │ │ └── config │ │ │ └── ContainerConfig.java │ └── main │ │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── api │ │ │ └── gateway │ │ │ ├── model │ │ │ ├── ServiceResult.java │ │ │ ├── UsernameDTO.java │ │ │ ├── ServiceType.java │ │ │ └── GenerationResponse.java │ │ │ ├── APIGatewayApplication.java │ │ │ ├── bootstrap │ │ │ └── DataInitializer.java │ │ │ ├── config │ │ │ ├── WebClientConfiguration.java │ │ │ └── WebFluxConfig.java │ │ │ └── web │ │ │ └── controller │ │ │ ├── HomeController.java │ │ │ └── GatewayInventoryFallback.java │ │ └── resources │ │ └── logback-spring.xml ├── license-header ├── .mvn │ └── wrapper │ │ └── maven-wrapper.properties ├── build-config │ └── checkstyle │ │ └── suppressions.xml └── sonar-project.properties ├── images ├── swagger.jpg ├── zipkin.jpg ├── gatewayArchitecture.JPG └── microservicesArchitecture.png ├── catalog-service ├── license-header ├── src │ ├── main │ │ ├── resources │ │ │ ├── db │ │ │ │ └── changelog │ │ │ │ │ └── db.changelog-master.yaml │ │ │ └── logback-spring.xml │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── catalogservice │ │ │ ├── model │ │ │ ├── response │ │ │ │ ├── InventoryResponse.java │ │ │ │ ├── PagedResult.java │ │ │ │ └── ProductResponse.java │ │ │ ├── payload │ │ │ │ └── ProductDto.java │ │ │ └── request │ │ │ │ └── ProductRequest.java │ │ │ ├── config │ │ │ ├── SwaggerConfig.java │ │ │ ├── logging │ │ │ │ ├── Loggable.java │ │ │ │ └── LogWriter.java │ │ │ ├── Initializer.java │ │ │ └── WebFluxConfig.java │ │ │ ├── utils │ │ │ └── AppConstants.java │ │ │ ├── CatalogServiceApplication.java │ │ │ ├── exception │ │ │ ├── CustomResponseStatusException.java │ │ │ └── ProductAlreadyExistsException.java │ │ │ └── mapper │ │ │ └── ProductMapper.java │ └── test │ │ ├── resources │ │ └── logback-test.xml │ │ └── java │ │ └── com │ │ └── example │ │ └── catalogservice │ │ ├── TestCatalogServiceApplication.java │ │ ├── common │ │ ├── SQLContainerConfig.java │ │ └── AbstractCircuitBreakerTest.java │ │ └── config │ │ └── TestKafkaListenerConfig.java ├── .mvn │ └── wrapper │ │ └── maven-wrapper.properties ├── build-config │ └── checkstyle │ │ └── suppressions.xml ├── .yo-rc.json ├── Jenkinsfile ├── docker │ └── docker-compose-app.yml ├── .github │ └── workflows │ │ ├── maven-main.yml │ │ └── maven-dev.yml ├── .gitignore ├── sonar-project.properties └── README.md ├── order-service ├── license-header ├── src │ ├── main │ │ ├── resources │ │ │ ├── db │ │ │ │ └── changelog │ │ │ │ │ └── db.changelog-master.yaml │ │ │ ├── application-h2.yml │ │ │ └── logback-spring.xml │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ ├── orderservice │ │ │ ├── entities │ │ │ │ └── OrderStatus.java │ │ │ ├── exception │ │ │ │ ├── OrderNotFoundException.java │ │ │ │ └── ProductNotFoundException.java │ │ │ ├── repositories │ │ │ │ └── OrderItemRepository.java │ │ │ ├── model │ │ │ │ ├── response │ │ │ │ │ ├── OrderItemResponse.java │ │ │ │ │ ├── OrderResponse.java │ │ │ │ │ └── PagedResult.java │ │ │ │ ├── Address.java │ │ │ │ └── request │ │ │ │ │ ├── OrderRequest.java │ │ │ │ │ └── OrderItemRequest.java │ │ │ ├── config │ │ │ │ ├── ObservedAspectConfiguration.java │ │ │ │ ├── AuditConfiguration.java │ │ │ │ ├── logging │ │ │ │ │ ├── Loggable.java │ │ │ │ │ └── LogWriter.java │ │ │ │ ├── ApplicationProperties.java │ │ │ │ ├── SwaggerConfig.java │ │ │ │ ├── Initializer.java │ │ │ │ ├── HttpClientConfig.java │ │ │ │ └── WebMvcConfig.java │ │ │ ├── OrderServiceApplication.java │ │ │ ├── services │ │ │ │ └── CatalogServiceProxy.java │ │ │ └── utils │ │ │ │ └── AppConstants.java │ │ │ └── common │ │ │ └── dtos │ │ │ ├── OrderItemDto.java │ │ │ └── OrderDto.java │ └── test │ │ ├── resources │ │ └── logback-test.xml │ │ └── java │ │ └── com │ │ └── example │ │ └── orderservice │ │ ├── TestOrderServiceApplication.java │ │ └── common │ │ ├── PostGreSQLContainer.java │ │ └── ContainersConfig.java ├── .mvn │ └── wrapper │ │ └── maven-wrapper.properties ├── build-config │ └── checkstyle │ │ └── suppressions.xml ├── .yo-rc.json ├── Jenkinsfile ├── docker │ └── docker-compose-app.yml ├── .github │ └── workflows │ │ ├── maven-main.yml │ │ └── maven-dev.yml ├── .gitignore └── sonar-project.properties ├── inventory-service ├── license-header ├── .mvn │ └── wrapper │ │ └── maven-wrapper.properties ├── src │ ├── main │ │ ├── resources │ │ │ ├── db │ │ │ │ └── changelog │ │ │ │ │ └── db.changelog-master.yaml │ │ │ ├── application-local.properties │ │ │ ├── application.yml │ │ │ └── logback-spring.xml │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ ├── inventoryservice │ │ │ ├── model │ │ │ │ ├── payload │ │ │ │ │ └── ProductDto.java │ │ │ │ ├── request │ │ │ │ │ └── InventoryRequest.java │ │ │ │ └── response │ │ │ │ │ └── PagedResult.java │ │ │ ├── repositories │ │ │ │ ├── InventoryRepository.java │ │ │ │ └── InventoryJOOQRepository.java │ │ │ ├── config │ │ │ │ ├── SwaggerConfig.java │ │ │ │ ├── RestTemplateConfig.java │ │ │ │ ├── logging │ │ │ │ │ ├── Loggable.java │ │ │ │ │ └── LogWriter.java │ │ │ │ └── WebMvcConfig.java │ │ │ ├── InventoryServiceApplication.java │ │ │ ├── mapper │ │ │ │ └── InventoryMapper.java │ │ │ └── utils │ │ │ │ └── AppConstants.java │ │ │ └── common │ │ │ └── dtos │ │ │ └── OrderItemDto.java │ └── test │ │ ├── resources │ │ └── logback-test.xml │ │ └── java │ │ └── com │ │ └── example │ │ └── inventoryservice │ │ ├── util │ │ └── MockTestData.java │ │ ├── InventoryServiceApplicationIntegrationTest.java │ │ ├── TestInventoryApplication.java │ │ ├── common │ │ ├── SQLContainersConfig.java │ │ └── NonSQLContainersConfig.java │ │ ├── config │ │ └── TestStockOrderListenerConfig.java │ │ └── repositories │ │ └── InventoryRepositoryTest.java ├── build-config │ └── checkstyle │ │ └── suppressions.xml ├── Jenkinsfile ├── .yo-rc.json ├── docker │ └── docker-compose-app.yml ├── .github │ └── workflows │ │ ├── maven-main.yml │ │ └── maven-dev.yml ├── .gitignore ├── sonar-project.properties └── README.md ├── config-server ├── src │ ├── main │ │ ├── resources │ │ │ ├── config-repository │ │ │ │ ├── order-service-docker.properties │ │ │ │ ├── application-native.yml │ │ │ │ ├── naming-server-docker.yml │ │ │ │ ├── api-gateway.properties │ │ │ │ ├── application-docker.yml │ │ │ │ ├── catalog-service-docker.properties │ │ │ │ └── inventory-service.properties │ │ │ └── application.properties │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── configserver │ │ │ ├── ConfigServerApplication.java │ │ │ └── config │ │ │ └── SecurityConfig.java │ └── test │ │ └── java │ │ └── com │ │ └── example │ │ └── configserver │ │ └── ConfigServerApplicationTests.java ├── .mvn │ └── wrapper │ │ └── maven-wrapper.properties ├── sonar-project.properties └── ReadMe.md ├── .mvn └── wrapper │ └── maven-wrapper.properties ├── gatling-tests ├── .mvn │ └── wrapper │ │ └── maven-wrapper.properties ├── src │ └── test │ │ ├── java │ │ └── simulation │ │ │ ├── InventoryResponseDTO.java │ │ │ └── GatlingHelper.java │ │ └── resources │ │ └── logback-test.xml └── run-optimized-test.cmd ├── service-registry ├── .mvn │ └── wrapper │ │ └── maven-wrapper.properties ├── src │ ├── test │ │ └── java │ │ │ └── org │ │ │ └── service │ │ │ └── registry │ │ │ ├── NamingServerApplicationTests.java │ │ │ └── TestNamingServerApplication.java │ └── main │ │ ├── resources │ │ └── application.properties │ │ └── java │ │ └── org │ │ └── service │ │ └── registry │ │ └── NamingServerApplication.java ├── docker-compose.yml ├── sonar-project.properties └── ReadMe.md ├── payment-service ├── src │ ├── main │ │ ├── resources │ │ │ ├── db │ │ │ │ └── changelog │ │ │ │ │ ├── db.changelog-master.yaml │ │ │ │ │ └── migration │ │ │ │ │ └── 00-create_payment_schema.xml │ │ │ ├── application-local.properties │ │ │ ├── application.properties │ │ │ └── logback-spring.xml │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ ├── paymentservice │ │ │ ├── model │ │ │ │ ├── query │ │ │ │ │ └── FindCustomersQuery.java │ │ │ │ ├── response │ │ │ │ │ ├── CustomerResponse.java │ │ │ │ │ └── PagedResult.java │ │ │ │ └── request │ │ │ │ │ └── CustomerRequest.java │ │ │ ├── exception │ │ │ │ └── CustomerNotFoundException.java │ │ │ ├── config │ │ │ │ ├── SwaggerConfig.java │ │ │ │ ├── RestTemplateConfig.java │ │ │ │ ├── logging │ │ │ │ │ ├── Loggable.java │ │ │ │ │ └── LogWriter.java │ │ │ │ └── WebMvcConfig.java │ │ │ ├── PaymentApplication.java │ │ │ ├── repositories │ │ │ │ └── CustomerRepository.java │ │ │ └── utils │ │ │ │ └── AppConstants.java │ │ │ └── common │ │ │ └── dtos │ │ │ ├── OrderItemDto.java │ │ │ └── OrderDto.java │ └── test │ │ ├── resources │ │ ├── application-test.properties │ │ └── logback-test.xml │ │ └── java │ │ └── com │ │ └── example │ │ └── paymentservice │ │ ├── TestPaymentApplication.java │ │ ├── util │ │ └── TestData.java │ │ └── common │ │ ├── SQLContainerConfig.java │ │ └── NonSQLContainerConfig.java ├── .mvn │ └── wrapper │ │ └── maven-wrapper.properties ├── Jenkinsfile ├── .yo-rc.json ├── docker │ └── docker-compose-app.yml ├── .gitignore ├── .github │ └── workflows │ │ ├── maven-main.yml │ │ └── maven-dev.yml ├── sonar-project.properties └── README.md ├── deployment └── config │ ├── grafana │ └── provisioning │ │ └── dashboards │ │ └── dashboard.yml │ ├── alert-manager │ └── config │ │ └── alertmanager.yml │ ├── promtail │ └── promtail.yml │ ├── prometheus │ └── config │ │ └── alert-rules.yml │ └── tempo │ └── tempo.yml ├── renovate.json ├── .github ├── workflows │ ├── end-to-end-tests.yml │ ├── greetings.yml │ ├── label.yml │ └── gatling-tests.yml └── dependabot.yml ├── .vscode └── settings.json ├── run-circuit-test.sh ├── .gitignore ├── .gitattributes ├── install.sh ├── LICENSE ├── pom.xml └── .devcontainer └── devcontainer.json /retail-store-webapp/src/main/resources/static/css/styles.css: -------------------------------------------------------------------------------- 1 | #app { 2 | padding-top: 90px; 3 | } -------------------------------------------------------------------------------- /api-gateway/src/test/resources/junit-platform.properties: -------------------------------------------------------------------------------- 1 | junit.jupiter.testinstance.lifecycle.default=per_class -------------------------------------------------------------------------------- /images/swagger.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajadilipkolli/spring-boot-microservices-series-v2/HEAD/images/swagger.jpg -------------------------------------------------------------------------------- /images/zipkin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajadilipkolli/spring-boot-microservices-series-v2/HEAD/images/zipkin.jpg -------------------------------------------------------------------------------- /api-gateway/license-header: -------------------------------------------------------------------------------- 1 | /*** 2 |
3 | Licensed under MIT License Copyright (c) $YEAR Raja Kolli. 4 |
5 | ***/ 6 | 7 | -------------------------------------------------------------------------------- /catalog-service/license-header: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) $YEAR Raja Kolli. 4 |
5 | ***/ 6 | 7 | -------------------------------------------------------------------------------- /order-service/license-header: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) $YEAR Raja Kolli. 4 |
5 | ***/ 6 | 7 | -------------------------------------------------------------------------------- /inventory-service/license-header: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) $YEAR Raja Kolli. 4 |
5 | ***/ 6 | 7 | -------------------------------------------------------------------------------- /images/gatewayArchitecture.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajadilipkolli/spring-boot-microservices-series-v2/HEAD/images/gatewayArchitecture.JPG -------------------------------------------------------------------------------- /images/microservicesArchitecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajadilipkolli/spring-boot-microservices-series-v2/HEAD/images/microservicesArchitecture.png -------------------------------------------------------------------------------- /config-server/src/main/resources/config-repository/order-service-docker.properties: -------------------------------------------------------------------------------- 1 | application.catalog-service-url=http://catalog-service:18080/catalog-service 2 | 3 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionType=only-script 2 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip 3 | -------------------------------------------------------------------------------- /config-server/src/main/resources/config-repository/application-native.yml: -------------------------------------------------------------------------------- 1 | 2 | spring: 3 | config.import: optional:configserver:http://localhost:8888/ 4 | threads.virtual.enabled: true 5 | -------------------------------------------------------------------------------- /api-gateway/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionType=only-script 2 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip 3 | -------------------------------------------------------------------------------- /config-server/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionType=only-script 2 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip 3 | -------------------------------------------------------------------------------- /gatling-tests/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionType=only-script 2 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip 3 | -------------------------------------------------------------------------------- /retail-store-webapp/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionType=only-script 2 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip 3 | -------------------------------------------------------------------------------- /retail-store-webapp/src/main/resources/static/images/books.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajadilipkolli/spring-boot-microservices-series-v2/HEAD/retail-store-webapp/src/main/resources/static/images/books.png -------------------------------------------------------------------------------- /service-registry/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionType=only-script 2 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip 3 | -------------------------------------------------------------------------------- /catalog-service/src/main/resources/db/changelog/db.changelog-master.yaml: -------------------------------------------------------------------------------- 1 | databaseChangeLog: 2 | - includeAll: 3 | path: migration/ 4 | errorIfMissingOrEmpty: true 5 | relativeToChangelogFile: true -------------------------------------------------------------------------------- /order-service/src/main/resources/db/changelog/db.changelog-master.yaml: -------------------------------------------------------------------------------- 1 | databaseChangeLog: 2 | - includeAll: 3 | path: /migration 4 | relativeToChangelogFile: true 5 | errorsOnMissingFile: true 6 | -------------------------------------------------------------------------------- /payment-service/src/main/resources/db/changelog/db.changelog-master.yaml: -------------------------------------------------------------------------------- 1 | databaseChangeLog: 2 | - includeAll: 3 | path: /migration 4 | relativeToChangelogFile: true 5 | errorsOnMissingFile: true 6 | -------------------------------------------------------------------------------- /deployment/config/grafana/provisioning/dashboards/dashboard.yml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | providers: 4 | - name: 'Default' 5 | folder: 'Applications' 6 | options: 7 | path: /etc/grafana/provisioning/dashboards -------------------------------------------------------------------------------- /catalog-service/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | wrapperVersion=3.3.4 2 | distributionType=only-script 3 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip 4 | -------------------------------------------------------------------------------- /order-service/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | wrapperVersion=3.3.4 2 | distributionType=only-script 3 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip 4 | -------------------------------------------------------------------------------- /payment-service/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | wrapperVersion=3.3.4 2 | distributionType=only-script 3 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip 4 | -------------------------------------------------------------------------------- /inventory-service/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | wrapperVersion=3.3.4 2 | distributionType=only-script 3 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip 4 | -------------------------------------------------------------------------------- /inventory-service/src/main/resources/db/changelog/db.changelog-master.yaml: -------------------------------------------------------------------------------- 1 | databaseChangeLog: 2 | - includeAll: 3 | path: migration/ 4 | errorIfMissingOrEmpty: true 5 | relativeToChangelogFile: true 6 | -------------------------------------------------------------------------------- /config-server/src/main/resources/config-repository/naming-server-docker.yml: -------------------------------------------------------------------------------- 1 | eureka: 2 | instance: 3 | hostname: localhost 4 | client: 5 | serviceUrl: 6 | defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ -------------------------------------------------------------------------------- /config-server/src/main/resources/config-repository/api-gateway.properties: -------------------------------------------------------------------------------- 1 | #spring.cloud.gateway.discovery.locator.enabled=true 2 | #spring.cloud.gateway.discovery.locator.lower-case-service-id=true 3 | 4 | management.endpoints.web.exposure.include=* -------------------------------------------------------------------------------- /retail-store-webapp/src/main/java/com/example/retailstore/webapp/clients/order/OrderConfirmationDTO.java: -------------------------------------------------------------------------------- 1 | package com.example.retailstore.webapp.clients.order; 2 | 3 | public record OrderConfirmationDTO(Long orderId, Long customerId, String status) {} 4 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:recommended" 4 | ], 5 | "packageRules": [ 6 | { 7 | "matchUpdateTypes": [ 8 | "minor", 9 | "patch", 10 | "pin" 11 | ], 12 | "automerge": true 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /retail-store-webapp/src/main/java/com/example/retailstore/webapp/clients/order/OrderItemExternal.java: -------------------------------------------------------------------------------- 1 | package com.example.retailstore.webapp.clients.order; 2 | 3 | import java.math.BigDecimal; 4 | 5 | record OrderItemExternal(String productCode, int quantity, BigDecimal productPrice) {} 6 | -------------------------------------------------------------------------------- /retail-store-webapp/src/main/java/com/example/retailstore/webapp/clients/order/OrderItemRequest.java: -------------------------------------------------------------------------------- 1 | package com.example.retailstore.webapp.clients.order; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public record OrderItemRequest(String productCode, int quantity, BigDecimal price) {} 6 | -------------------------------------------------------------------------------- /api-gateway/build-config/checkstyle/suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 |3 | Licensed under MIT License Copyright (c) 2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.api.gateway.model; 8 | 9 | /** Internal result type for service calls */ 10 | public record ServiceResult(int status, String response) {} 11 | -------------------------------------------------------------------------------- /api-gateway/src/main/java/com/example/api/gateway/model/UsernameDTO.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.api.gateway.model; 8 | 9 | import java.io.Serializable; 10 | 11 | public record UsernameDTO(String username) implements Serializable {} 12 | -------------------------------------------------------------------------------- /retail-store-webapp/src/main/java/com/example/retailstore/webapp/config/ApplicationProperties.java: -------------------------------------------------------------------------------- 1 | package com.example.retailstore.webapp.config; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | @ConfigurationProperties(prefix = "retailstore") 6 | public record ApplicationProperties(String apiGatewayUrl) {} 7 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/entities/OrderStatus.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2023 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.orderservice.entities; 8 | 9 | public enum OrderStatus { 10 | NEW, 11 | ACCEPT, 12 | REJECTED, 13 | CONFIRMED, 14 | FAILED, 15 | ROLLBACK 16 | } 17 | -------------------------------------------------------------------------------- /retail-store-webapp/src/main/java/com/example/retailstore/webapp/clients/customer/CustomerResponse.java: -------------------------------------------------------------------------------- 1 | /*** Licensed under MIT License Copyright (c) 2023 Raja Kolli. ***/ 2 | package com.example.retailstore.webapp.clients.customer; 3 | 4 | public record CustomerResponse( 5 | Long customerId, String name, String email, String phone, String address, int amountAvailable) {} 6 | -------------------------------------------------------------------------------- /order-service/.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-springboot": { 3 | "appName": "order-service", 4 | "packageName": "com.example.orderservice", 5 | "databaseType": "postgresql", 6 | "dbMigrationTool": "flywaydb", 7 | "features": [], 8 | "buildTool": "maven", 9 | "packageFolder": "com/example/orderservice", 10 | "flywayMigrationCounter": 2 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /order-service/Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent any 3 | 4 | triggers { 5 | pollSCM('* * * * *') 6 | } 7 | 8 | environment { 9 | APPLICATION_NAME = 'order-service' 10 | } 11 | 12 | stages { 13 | stage('Build') { 14 | steps { 15 | sh './mvnw clean verify' 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /catalog-service/.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-springboot": { 3 | "appName": "catalogservice", 4 | "packageName": "com.example.catalogservice", 5 | "databaseType": "postgresql", 6 | "dbMigrationTool": "flywaydb", 7 | "features": [], 8 | "buildTool": "maven", 9 | "packageFolder": "com/example/catalogservice", 10 | "flywayMigrationCounter": 2 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /catalog-service/Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent any 3 | 4 | triggers { 5 | pollSCM('* * * * *') 6 | } 7 | 8 | environment { 9 | APPLICATION_NAME = 'catalogservice' 10 | } 11 | 12 | stages { 13 | stage('Build') { 14 | steps { 15 | sh './mvnw clean verify' 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /payment-service/Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent any 3 | 4 | triggers { 5 | pollSCM('* * * * *') 6 | } 7 | 8 | environment { 9 | APPLICATION_NAME = 'payment-service' 10 | } 11 | 12 | stages { 13 | stage('Build') { 14 | steps { 15 | sh './mvnw clean verify' 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /inventory-service/Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent any 3 | 4 | triggers { 5 | pollSCM('* * * * *') 6 | } 7 | 8 | environment { 9 | APPLICATION_NAME = 'inventory-service' 10 | } 11 | 12 | stages { 13 | stage('Build') { 14 | steps { 15 | sh './mvnw clean verify' 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /payment-service/.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-springboot": { 3 | "appName": "payment-service", 4 | "packageName": "com.example.paymentservice", 5 | "databaseType": "postgresql", 6 | "dbMigrationTool": "liquibase", 7 | "features": [], 8 | "buildTool": "maven", 9 | "packageFolder": "com/example/paymentservice", 10 | "liquibaseMigrationCounter": 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /service-registry/src/test/java/org/service/registry/NamingServerApplicationTests.java: -------------------------------------------------------------------------------- 1 | /* Licensed under Apache-2.0 2021-2023 */ 2 | package org.service.registry; 3 | 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | @SpringBootTest 8 | class NamingServerApplicationTests { 9 | 10 | @Test 11 | void contextLoads() {} 12 | } 13 | -------------------------------------------------------------------------------- /config-server/src/test/java/com/example/configserver/ConfigServerApplicationTests.java: -------------------------------------------------------------------------------- 1 | /* Licensed under Apache-2.0 2021-2022 */ 2 | package com.example.configserver; 3 | 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | @SpringBootTest 8 | class ConfigServerApplicationTests { 9 | 10 | @Test 11 | void contextLoads() {} 12 | } 13 | -------------------------------------------------------------------------------- /deployment/config/alert-manager/config/alertmanager.yml: -------------------------------------------------------------------------------- 1 | route: 2 | receiver: app-down-alert-manager 3 | repeat_interval: 1m 4 | receivers: 5 | - name: 'app-down-alert-manager' 6 | email_configs: 7 | - to: 'dockertmt@gmail.com' 8 | from: 'no-reply-retail@gmail.com' 9 | smarthost: 'smtp.gmail.com:587' 10 | auth_username: 'username' 11 | auth_password: 'password' -------------------------------------------------------------------------------- /inventory-service/.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-springboot": { 3 | "appName": "inventory-service", 4 | "packageName": "com.example.inventoryservice", 5 | "databaseType": "postgresql", 6 | "dbMigrationTool": "liquibase", 7 | "features": [], 8 | "buildTool": "maven", 9 | "packageFolder": "com/example/inventoryservice", 10 | "liquibaseMigrationCounter": 2 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /service-registry/src/test/java/org/service/registry/TestNamingServerApplication.java: -------------------------------------------------------------------------------- 1 | /* Licensed under Apache-2.0 2023 */ 2 | package org.service.registry; 3 | 4 | import org.springframework.boot.SpringApplication; 5 | 6 | public class TestNamingServerApplication { 7 | public static void main(String[] args) { 8 | SpringApplication.from(NamingServerApplication::main).run(args); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /catalog-service/src/main/java/com/example/catalogservice/model/response/InventoryResponse.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2023 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.catalogservice.model.response; 8 | 9 | import java.io.Serializable; 10 | 11 | public record InventoryResponse(String productCode, Integer availableQuantity) 12 | implements Serializable {} 13 | -------------------------------------------------------------------------------- /.github/workflows/end-to-end-tests.yml: -------------------------------------------------------------------------------- 1 | name: End-to-End Tests (Manual) 2 | on: 3 | workflow_dispatch: # Only allow manual triggering of the workflow 4 | 5 | jobs: 6 | e2e-tests: 7 | name: Run End-to-End Tests 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v6 11 | 12 | - name: Run End to End Tests 13 | run: ./test-em-all.sh --detailed-logs start stop 14 | -------------------------------------------------------------------------------- /retail-store-webapp/src/main/java/com/example/retailstore/webapp/clients/inventory/InventoryResponse.java: -------------------------------------------------------------------------------- 1 | package com.example.retailstore.webapp.clients.inventory; 2 | 3 | public record InventoryResponse(Long id, String productCode, Integer availableQuantity, Integer reservedItems) { 4 | public InventoryUpdateRequest createInventoryUpdateRequest() { 5 | return InventoryUpdateRequest.fromInventoryResponse(this); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /payment-service/src/main/java/com/example/paymentservice/model/response/CustomerResponse.java: -------------------------------------------------------------------------------- 1 | /*** Licensed under MIT License Copyright (c) 2023-2025 Raja Kolli. ***/ 2 | package com.example.paymentservice.model.response; 3 | 4 | public record CustomerResponse( 5 | Long customerId, 6 | String name, 7 | String email, 8 | String phone, 9 | String address, 10 | double amountAvailable) {} 11 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/exception/OrderNotFoundException.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2022-2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.orderservice.exception; 8 | 9 | public class OrderNotFoundException extends RuntimeException { 10 | public OrderNotFoundException(Long orderId) { 11 | super("Order with Id " + orderId + " not found"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.github/workflows/greetings.yml: -------------------------------------------------------------------------------- 1 | name: Greetings 2 | 3 | on: [pull_request, issues] 4 | 5 | permissions: 6 | issues: write 7 | pull-requests: write 8 | 9 | jobs: 10 | greeting: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/first-interaction@v3 14 | with: 15 | repo_token: ${{ secrets.GITHUB_TOKEN }} 16 | issue_message: 'Thanks for reporting issues' 17 | pr_message: 'Thanks for sending your prs' 18 | -------------------------------------------------------------------------------- /config-server/src/main/resources/config-repository/application-docker.yml: -------------------------------------------------------------------------------- 1 | 2 | spring: 3 | datasource: 4 | password: secret 5 | url: jdbc:postgresql://postgresql:5432/appdb 6 | username: appuser 7 | kafka.bootstrap-servers: 8 | - kafka:29092 9 | threads.virtual.enabled: true 10 | eureka.client.service-url.defaultZone: http://naming-server:8761/eureka/ 11 | management.tracing.export.zipkin.endpoint: http://zipkin-server:9411/api/v2/spans 12 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/repositories/OrderItemRepository.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2022-2023 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.orderservice.repositories; 8 | 9 | import com.example.orderservice.entities.OrderItem; 10 | import org.springframework.data.jpa.repository.JpaRepository; 11 | 12 | public interface OrderItemRepository extends JpaRepository3 | Licensed under MIT License Copyright (c) 2022-2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.orderservice.exception; 8 | 9 | import java.util.List; 10 | 11 | public class ProductNotFoundException extends RuntimeException { 12 | 13 | public ProductNotFoundException(List3 | Licensed under MIT License Copyright (c) 2023-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.api.gateway; 8 | 9 | import com.example.api.gateway.config.ContainerConfig; 10 | import org.springframework.boot.SpringApplication; 11 | 12 | public class TestAPIGatewayApplication { 13 | 14 | public static void main(String[] args) { 15 | SpringApplication.from(APIGatewayApplication::main).with(ContainerConfig.class).run(args); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /catalog-service/.github/workflows/maven-main.yml: -------------------------------------------------------------------------------- 1 | name: Main Branch CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build: 10 | name: Run Unit & Integration Tests 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v6 14 | 15 | - name: Set up JDK 21 16 | uses: actions/setup-java@v5.1.0 17 | with: 18 | java-version: 21 19 | distribution: 'temurin' 20 | cache: 'maven' 21 | 22 | - name: Build with Maven 23 | run: ./mvn clean install 24 | -------------------------------------------------------------------------------- /inventory-service/.github/workflows/maven-main.yml: -------------------------------------------------------------------------------- 1 | name: Main Branch CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build: 10 | name: Run Unit & Integration Tests 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v6 14 | 15 | - name: Set up JDK 21 16 | uses: actions/setup-java@v5.1.0 17 | with: 18 | java-version: 21 19 | distribution: 'temurin' 20 | cache: 'maven' 21 | 22 | - name: Build with Maven 23 | run: ./mvn clean install 24 | -------------------------------------------------------------------------------- /order-service/.github/workflows/maven-main.yml: -------------------------------------------------------------------------------- 1 | name: Main Branch CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build: 10 | name: Run Unit & Integration Tests 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v6 14 | 15 | - name: Set up JDK 21 16 | uses: actions/setup-java@v5.1.0 17 | with: 18 | java-version: 21 19 | distribution: 'temurin' 20 | cache: 'maven' 21 | 22 | - name: Build with Maven 23 | run: ./mvn clean install 24 | -------------------------------------------------------------------------------- /api-gateway/src/main/java/com/example/api/gateway/model/ServiceType.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.api.gateway.model; 8 | 9 | /** Service identifiers for logging and error handling */ 10 | public enum ServiceType { 11 | CATALOG("catalog"), 12 | INVENTORY("inventory"); 13 | 14 | private final String id; 15 | 16 | ServiceType(String id) { 17 | this.id = id; 18 | } 19 | 20 | public String getId() { 21 | return id; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /catalog-service/src/main/java/com/example/catalogservice/model/payload/ProductDto.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2021-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.catalogservice.model.payload; 8 | 9 | import jakarta.validation.constraints.NotBlank; 10 | import jakarta.validation.constraints.Positive; 11 | 12 | public record ProductDto( 13 | @NotBlank(message = "Product code can't be blank") String code, 14 | String productName, 15 | String description, 16 | @Positive Double price) {} 17 | -------------------------------------------------------------------------------- /inventory-service/src/main/java/com/example/inventoryservice/model/payload/ProductDto.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2021-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.inventoryservice.model.payload; 8 | 9 | import jakarta.validation.constraints.NotBlank; 10 | import jakarta.validation.constraints.Positive; 11 | 12 | public record ProductDto( 13 | @NotBlank(message = "Product code can't be blank") String code, 14 | String productName, 15 | String description, 16 | @Positive Double price) {} 17 | -------------------------------------------------------------------------------- /retail-store-webapp/src/main/java/com/example/retailstore/webapp/clients/order/OrderItemResponse.java: -------------------------------------------------------------------------------- 1 | package com.example.retailstore.webapp.clients.order; 2 | 3 | import com.fasterxml.jackson.annotation.JsonFormat; 4 | import java.math.BigDecimal; 5 | 6 | public record OrderItemResponse( 7 | Long itemId, 8 | String productId, 9 | int quantity, 10 | @JsonFormat(shape = JsonFormat.Shape.NUMBER_FLOAT, pattern = "0.00") BigDecimal productPrice, 11 | @JsonFormat(shape = JsonFormat.Shape.NUMBER_FLOAT, pattern = "0.00") BigDecimal price) {} 12 | -------------------------------------------------------------------------------- /inventory-service/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 |3 | Licensed under MIT License Copyright (c) 2021-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.catalogservice.model.request; 8 | 9 | import jakarta.validation.constraints.NotBlank; 10 | import jakarta.validation.constraints.Positive; 11 | 12 | public record ProductRequest( 13 | @NotBlank(message = "Product code can't be blank") String productCode, 14 | String productName, 15 | String description, 16 | String imageUrl, 17 | @Positive Double price) {} 18 | -------------------------------------------------------------------------------- /order-service/src/main/resources/application-h2.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | cloud: 3 | config: 4 | enabled: false 5 | discovery: 6 | enabled: false 7 | datasource: 8 | url: jdbc:h2:file:/data/demo 9 | username: sa 10 | password: password 11 | driverClassName: org.h2.Driver 12 | jpa: 13 | spring: 14 | jpa: 15 | database-platform: org.hibernate.dialect.H2Dialect 16 | config: 17 | import: optional:configserver:${CONFIG_SERVER:http://localhost:8888}/ 18 | application: 19 | catalog-service-url: http://localhost:18080/catalog-service 20 | byPassCircuitBreaker: true 21 | -------------------------------------------------------------------------------- /retail-store-webapp/src/main/java/com/example/retailstore/webapp/RetailStoreWebappApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.retailstore.webapp; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.context.properties.ConfigurationPropertiesScan; 6 | 7 | @SpringBootApplication 8 | @ConfigurationPropertiesScan 9 | public class RetailStoreWebappApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(RetailStoreWebappApplication.class, args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /payment-service/.github/workflows/maven-main.yml: -------------------------------------------------------------------------------- 1 | name: Main Branch CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build: 10 | name: Build 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v6 14 | 15 | - name: Set up JDK 21 16 | uses: actions/setup-java@v5.1.0 17 | with: 18 | java-version: 21 19 | distribution: 'temurin' 20 | cache: 'maven' 21 | 22 | - name: Grant execute permission for mvnw 23 | run: chmod +x mvnw 24 | 25 | - name: Build with Maven 26 | run: ./mvnw clean verify 27 | -------------------------------------------------------------------------------- /retail-store-webapp/src/main/java/com/example/retailstore/webapp/config/SecurityConstants.java: -------------------------------------------------------------------------------- 1 | package com.example.retailstore.webapp.config; 2 | 3 | public final class SecurityConstants { 4 | private SecurityConstants() {} // Prevent instantiation 5 | 6 | public static final String[] PUBLIC_URLS = { 7 | "/js/**", 8 | "/css/**", 9 | "/images/**", 10 | "/error", 11 | "/webjars/**", 12 | "/", 13 | "/actuator/**", 14 | "/products/**", 15 | "/api/products/**", 16 | "/api/register", 17 | "/registration", 18 | "/login" 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /api-gateway/src/test/resources/com/example/api/gateway/config/ApiGatewayConfigurationIntegrationTest/test-routing.json: -------------------------------------------------------------------------------- 1 | { 2 | "mappings": [ 3 | { 4 | "request": { 5 | "method": "GET", 6 | "url": "/example" 7 | }, 8 | "response": { 9 | "status": 200, 10 | "headers": { 11 | "Content-Type": "application/json" 12 | }, 13 | "jsonBody": { 14 | "message": "test route response" 15 | } 16 | } 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /api-gateway/src/test/resources/com/example/api/gateway/filter/LoggingFilterIntegrationTest/logging-test.json: -------------------------------------------------------------------------------- 1 | { 2 | "mappings": [ 3 | { 4 | "request": { 5 | "method": "GET", 6 | "urlPattern": "/test/endpoint" 7 | }, 8 | "response": { 9 | "status": 200, 10 | "headers": { 11 | "Content-Type": "application/json" 12 | }, 13 | "jsonBody": { 14 | "message": "test response" 15 | } 16 | } 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /payment-service/src/main/java/com/example/common/dtos/OrderItemDto.java: -------------------------------------------------------------------------------- 1 | /*** Licensed under MIT License Copyright (c) 2021-2025 Raja Kolli. ***/ 2 | package com.example.common.dtos; 3 | 4 | import java.io.Serial; 5 | import java.io.Serializable; 6 | import java.math.BigDecimal; 7 | 8 | public record OrderItemDto(Long itemId, String productId, int quantity, BigDecimal productPrice) 9 | implements Serializable { 10 | 11 | @Serial private static final long serialVersionUID = 1L; 12 | 13 | public BigDecimal getPrice() { 14 | return this.productPrice().multiply(BigDecimal.valueOf(this.quantity())); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /retail-store-webapp/src/main/java/com/example/retailstore/webapp/clients/catalog/ProductResponse.java: -------------------------------------------------------------------------------- 1 | /*** 2 | *3 | * Licensed under MIT License Copyright (c) 2024 Raja Kolli. 4 | *
5 | ***/ 6 | package com.example.retailstore.webapp.clients.catalog; 7 | 8 | import com.fasterxml.jackson.annotation.JsonFormat; 9 | 10 | public record ProductResponse( 11 | Long id, 12 | String productCode, 13 | String productName, 14 | String description, 15 | String imageUrl, 16 | @JsonFormat(shape = JsonFormat.Shape.NUMBER_FLOAT, pattern = "0.00") Double price, 17 | boolean inStock) {} 18 | -------------------------------------------------------------------------------- /inventory-service/src/main/java/com/example/inventoryservice/repositories/InventoryRepository.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2021-2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.inventoryservice.repositories; 8 | 9 | import com.example.inventoryservice.entities.Inventory; 10 | import java.util.List; 11 | import org.springframework.data.jpa.repository.JpaRepository; 12 | 13 | public interface InventoryRepository extends JpaRepository3 | Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.inventoryservice.model.request; 8 | 9 | import jakarta.validation.constraints.NotBlank; 10 | import jakarta.validation.constraints.PositiveOrZero; 11 | import java.io.Serializable; 12 | 13 | public record InventoryRequest( 14 | @NotBlank(message = "ProductCode can't be blank") String productCode, 15 | @PositiveOrZero(message = "Quantity can't be negative") Integer availableQuantity) 16 | implements Serializable {} 17 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/common/dtos/OrderItemDto.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2021-2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.common.dtos; 8 | 9 | import java.io.Serial; 10 | import java.io.Serializable; 11 | import java.math.BigDecimal; 12 | 13 | public record OrderItemDto(Long itemId, String productId, int quantity, BigDecimal productPrice) 14 | implements Serializable { 15 | 16 | @Serial private static final long serialVersionUID = 1L; 17 | 18 | public BigDecimal getPrice() { 19 | return this.productPrice().multiply(BigDecimal.valueOf(this.quantity())); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /inventory-service/src/main/java/com/example/common/dtos/OrderItemDto.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2021-2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.common.dtos; 8 | 9 | import java.io.Serial; 10 | import java.io.Serializable; 11 | import java.math.BigDecimal; 12 | 13 | public record OrderItemDto(Long itemId, String productId, int quantity, BigDecimal productPrice) 14 | implements Serializable { 15 | 16 | @Serial private static final long serialVersionUID = 1L; 17 | 18 | public BigDecimal getPrice() { 19 | return this.productPrice().multiply(BigDecimal.valueOf(this.quantity())); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /retail-store-webapp/src/main/java/com/example/retailstore/webapp/clients/catalog/ProductRequest.java: -------------------------------------------------------------------------------- 1 | /*** 2 | *3 | * Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli. 4 | *
5 | ***/ 6 | package com.example.retailstore.webapp.clients.catalog; 7 | 8 | import jakarta.validation.constraints.NotBlank; 9 | import jakarta.validation.constraints.Positive; 10 | 11 | public record ProductRequest( 12 | @NotBlank(message = "Product code can't be blank") String productCode, 13 | @NotBlank(message = "Product name can't be blank") String productName, 14 | String description, 15 | String imageUrl, 16 | @Positive Double price) {} 17 | -------------------------------------------------------------------------------- /api-gateway/src/main/java/com/example/api/gateway/APIGatewayApplication.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.api.gateway; 8 | 9 | import org.springframework.boot.SpringApplication; 10 | import org.springframework.boot.autoconfigure.SpringBootApplication; 11 | import org.springframework.boot.context.properties.ConfigurationPropertiesScan; 12 | 13 | @ConfigurationPropertiesScan 14 | @SpringBootApplication 15 | public class APIGatewayApplication { 16 | 17 | public static void main(String[] args) { 18 | SpringApplication.run(APIGatewayApplication.class, args); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /inventory-service/src/test/java/com/example/inventoryservice/util/MockTestData.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2024-2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.inventoryservice.util; 8 | 9 | import com.example.common.dtos.OrderDto; 10 | import com.example.common.dtos.OrderItemDto; 11 | import java.math.BigDecimal; 12 | import java.util.List; 13 | 14 | public class MockTestData { 15 | public static OrderDto getOrderDto(String source) { 16 | OrderItemDto orderItemDto = new OrderItemDto(1L, "JUNIT_000", 10, BigDecimal.TEN); 17 | return new OrderDto(151L, 1001L, "NEW", source, List.of(orderItemDto)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/model/response/OrderItemResponse.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2023 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.orderservice.model.response; 8 | 9 | import com.fasterxml.jackson.annotation.JsonFormat; 10 | import java.math.BigDecimal; 11 | 12 | public record OrderItemResponse( 13 | Long itemId, 14 | String productId, 15 | int quantity, 16 | @JsonFormat(shape = JsonFormat.Shape.NUMBER_FLOAT, pattern = "0.00") 17 | BigDecimal productPrice, 18 | @JsonFormat(shape = JsonFormat.Shape.NUMBER_FLOAT, pattern = "0.00") BigDecimal price) {} 19 | -------------------------------------------------------------------------------- /retail-store-webapp/src/main/java/com/example/retailstore/webapp/clients/order/Address.java: -------------------------------------------------------------------------------- 1 | package com.example.retailstore.webapp.clients.order; 2 | 3 | import jakarta.validation.constraints.NotBlank; 4 | import java.io.Serializable; 5 | 6 | public record Address( 7 | @NotBlank(message = "AddressLine1 is required") String addressLine1, 8 | String addressLine2, 9 | @NotBlank(message = "City is required") String city, 10 | @NotBlank(message = "State is required") String state, 11 | @NotBlank(message = "ZipCode is required") String zipCode, 12 | @NotBlank(message = "Country is required") String country) 13 | implements Serializable {} 14 | -------------------------------------------------------------------------------- /catalog-service/src/main/java/com/example/catalogservice/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2022-2023 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.catalogservice.config; 8 | 9 | import io.swagger.v3.oas.annotations.OpenAPIDefinition; 10 | import io.swagger.v3.oas.annotations.info.Info; 11 | import io.swagger.v3.oas.annotations.servers.Server; 12 | import org.springframework.context.annotation.Configuration; 13 | 14 | @Configuration 15 | @OpenAPIDefinition( 16 | info = @Info(title = "catalog-service", version = "v1"), 17 | servers = @Server(url = "${spring.webflux.base-path}")) 18 | public class SwaggerConfig {} 19 | -------------------------------------------------------------------------------- /inventory-service/src/main/java/com/example/inventoryservice/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2022-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.inventoryservice.config; 8 | 9 | import io.swagger.v3.oas.annotations.OpenAPIDefinition; 10 | import io.swagger.v3.oas.annotations.info.Info; 11 | import io.swagger.v3.oas.annotations.servers.Server; 12 | import org.springframework.context.annotation.Configuration; 13 | 14 | @Configuration 15 | @OpenAPIDefinition( 16 | info = @Info(title = "inventory-service", version = "v1"), 17 | servers = @Server(url = "${server.servlet.contextPath}")) 18 | class SwaggerConfig {} 19 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/model/Address.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.orderservice.model; 8 | 9 | import jakarta.validation.constraints.NotBlank; 10 | 11 | public record Address( 12 | @NotBlank(message = "AddressLine1 is required") String addressLine1, 13 | String addressLine2, 14 | @NotBlank(message = "City is required") String city, 15 | @NotBlank(message = "State is required") String state, 16 | @NotBlank(message = "ZipCode is required") String zipCode, 17 | @NotBlank(message = "Country is required") String country) {} 18 | -------------------------------------------------------------------------------- /.github/workflows/label.yml: -------------------------------------------------------------------------------- 1 | # This workflow will triage pull requests and apply a label based on the 2 | # paths that are modified in the pull request. 3 | # 4 | # To use this workflow, you will need to set up a .github/labeler.yml 5 | # file with configuration. For more information, see: 6 | # https://github.com/actions/labeler 7 | 8 | name: "Pull Request Labeler" 9 | on: 10 | - pull_request_target 11 | 12 | jobs: 13 | triage: 14 | permissions: 15 | contents: read 16 | pull-requests: write 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/labeler@v6 20 | with: 21 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 22 | sync-labels: true 23 | -------------------------------------------------------------------------------- /retail-store-webapp/src/main/java/com/example/retailstore/webapp/exception/ResourceNotFoundException.java: -------------------------------------------------------------------------------- 1 | package com.example.retailstore.webapp.exception; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | @ResponseStatus(HttpStatus.NOT_FOUND) 7 | public class ResourceNotFoundException extends RuntimeException { 8 | public ResourceNotFoundException(String message) { 9 | super(message); 10 | } 11 | 12 | public ResourceNotFoundException(String resourceName, String fieldName, Object fieldValue) { 13 | super("%s not found with %s : '%s'".formatted(resourceName, fieldName, fieldValue)); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /retail-store-webapp/src/main/java/com/example/retailstore/webapp/clients/customer/CustomerRequest.java: -------------------------------------------------------------------------------- 1 | /*** Licensed under MIT License Copyright (c) 2023 Raja Kolli. ***/ 2 | package com.example.retailstore.webapp.clients.customer; 3 | 4 | import jakarta.validation.constraints.Email; 5 | import jakarta.validation.constraints.NotBlank; 6 | 7 | public record CustomerRequest( 8 | @NotBlank(message = "Name cannot be Blank") String name, 9 | @NotBlank(message = "Email cannot be Blank") @Email(message = "supplied email is not valid") String email, 10 | @NotBlank(message = "Customer Phone number is required") String phone, 11 | String address, 12 | int amountAvailable) {} 13 | -------------------------------------------------------------------------------- /api-gateway/src/main/java/com/example/api/gateway/model/GenerationResponse.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.api.gateway.model; 8 | 9 | import io.swagger.v3.oas.annotations.media.Schema; 10 | import java.util.Map; 11 | 12 | @Schema(description = "Response containing data generation results") 13 | public record GenerationResponse( 14 | @Schema(description = "Status of the generation process") String status, 15 | @Schema(description = "Detailed message about the generation process") String message, 16 | @Schema(description = "Map of service responses") Map3 | * Licensed under MIT License Copyright (c) 2023-2024 Raja Kolli. 4 | *
5 | ***/ 6 | package com.example.retailstore.webapp.clients.order; 7 | 8 | import jakarta.validation.Valid; 9 | import jakarta.validation.constraints.NotEmpty; 10 | import jakarta.validation.constraints.Positive; 11 | import java.util.List; 12 | 13 | public record OrderRequestExternal( 14 | @Positive(message = "CustomerId should be positive") Long customerId, 15 | @NotEmpty(message = "Order without items not valid") List3 | Licensed under MIT License Copyright (c) 2023-2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.orderservice.model.request; 8 | 9 | import com.example.orderservice.model.Address; 10 | import jakarta.validation.Valid; 11 | import jakarta.validation.constraints.NotEmpty; 12 | import jakarta.validation.constraints.Positive; 13 | import java.util.List; 14 | 15 | public record OrderRequest( 16 | @Positive(message = "CustomerId should be positive") Long customerId, 17 | @Valid @NotEmpty(message = "Order without items not valid") List3 | Licensed under MIT License Copyright (c) 2023-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.orderservice.config; 8 | 9 | import io.micrometer.observation.ObservationRegistry; 10 | import io.micrometer.observation.aop.ObservedAspect; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.Configuration; 13 | 14 | @Configuration(proxyBeanMethods = false) 15 | class ObservedAspectConfiguration { 16 | 17 | @Bean 18 | ObservedAspect observedAspect(ObservationRegistry observationRegistry) { 19 | return new ObservedAspect(observationRegistry); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /api-gateway/src/test/resources/com/example/api/gateway/config/RateLimiterConfigurationIntegrationTest/mocks-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "mappings": [ 3 | { 4 | "request": { 5 | "method": "GET", 6 | "urlPattern": "/order-service/api/1" 7 | }, 8 | "response": { 9 | "status": 200, 10 | "headers": { 11 | "Content-Type": "application/json" 12 | }, 13 | "jsonBody": { 14 | "orderId": 1, 15 | "customerId" : 1, 16 | "status" : "COMPLETED", 17 | "items" : [ 18 | { 19 | "itemId" : 1, 20 | "productId" : "P001" 21 | } 22 | ] 23 | } 24 | } 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /config-server/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=configserver 2 | server.port=8888 3 | management.endpoints.web.exposure.include=* 4 | management.endpoint.health.probes.enabled=true 5 | management.endpoint.health.show-details=always 6 | management.endpoint.health.show-components=always 7 | 8 | spring.profiles.include=native 9 | spring.output.ansi.enabled= ALWAYS 10 | spring.cloud.config.server.native.search-locations=classpath:/config-repository 11 | spring.threads.virtual.enabled=true 12 | #Disable Refresh for native build 13 | #spring.cloud.refresh.enabled=false 14 | 15 | #Enabling Spring security 16 | spring.security.user.name=dev-usr 17 | spring.security.user.password=dev-pass 18 | logging.level.org.springframework.security=INFO -------------------------------------------------------------------------------- /retail-store-webapp/src/main/resources/static/js/orderDetails.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('alpine:init', () => { 2 | Alpine.data('initData', (orderNumber) => ({ 3 | orderNumber: orderNumber, 4 | orderDetails: { 5 | items: [], 6 | customer: {}, 7 | deliveryAddress: {} 8 | }, 9 | init() { 10 | updateCartItemCount(); 11 | this.getOrderDetails(this.orderNumber) 12 | }, 13 | getOrderDetails(orderNumber) { 14 | $.getJSON("/api/orders/"+ orderNumber, (data) => { 15 | //console.log("Get Order Resp:", data) 16 | this.orderDetails = data 17 | }); 18 | } 19 | })) 20 | }); 21 | -------------------------------------------------------------------------------- /payment-service/src/main/java/com/example/paymentservice/config/RestTemplateConfig.java: -------------------------------------------------------------------------------- 1 | /*** Licensed under MIT License Copyright (c) 2022-2024 Raja Kolli. ***/ 2 | package com.example.paymentservice.config; 3 | 4 | import org.springframework.boot.restclient.RestTemplateBuilder; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.web.client.RestTemplate; 8 | 9 | @Configuration(proxyBeanMethods = false) 10 | class RestTemplateConfig { 11 | 12 | // IMPORTANT! To instrument RestTemplate you must inject the RestTemplateBuilder 13 | @Bean 14 | RestTemplate restTemplate(RestTemplateBuilder builder) { 15 | return builder.build(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /catalog-service/src/main/java/com/example/catalogservice/utils/AppConstants.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.catalogservice.utils; 8 | 9 | public final class AppConstants { 10 | public static final String PROFILE_LOCAL = "local"; 11 | public static final String PROFILE_PROD = "prod"; 12 | public static final String PROFILE_NOT_PROD = "!" + PROFILE_PROD; 13 | public static final String PROFILE_TEST = "test"; 14 | 15 | public static final String DEFAULT_PAGE_NUMBER = "0"; 16 | public static final String DEFAULT_PAGE_SIZE = "10"; 17 | public static final String DEFAULT_SORT_BY = "id"; 18 | public static final String DEFAULT_SORT_DIRECTION = "asc"; 19 | } 20 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/OrderServiceApplication.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2021-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.orderservice; 8 | 9 | import com.example.orderservice.config.ApplicationProperties; 10 | import org.springframework.boot.SpringApplication; 11 | import org.springframework.boot.autoconfigure.SpringBootApplication; 12 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 13 | 14 | @SpringBootApplication 15 | @EnableConfigurationProperties({ApplicationProperties.class}) 16 | class OrderServiceApplication { 17 | 18 | public static void main(String[] args) { 19 | SpringApplication.run(OrderServiceApplication.class, args); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /api-gateway/src/main/java/com/example/api/gateway/bootstrap/DataInitializer.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2021-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.api.gateway.bootstrap; 8 | 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.boot.context.event.ApplicationReadyEvent; 12 | import org.springframework.context.event.EventListener; 13 | import org.springframework.stereotype.Component; 14 | 15 | @Component 16 | class DataInitializer { 17 | 18 | private static final Logger log = LoggerFactory.getLogger(DataInitializer.class); 19 | 20 | @EventListener(ApplicationReadyEvent.class) 21 | public void init() { 22 | log.info("start data initialization..."); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | 35 | ### Misc ### 36 | *.log 37 | .DS_Store 38 | logs/ 39 | LOG_FILE_IS_UNDEFINED 40 | catalog-service/logs/ 41 | inventory-service/logs/ 42 | inventory-service/LOG_FILE_IS_UNDEFINED 43 | order-service/logs/ 44 | order-service/LOG_FILE_IS_UNDEFINED 45 | -------------------------------------------------------------------------------- /inventory-service/src/main/java/com/example/inventoryservice/config/RestTemplateConfig.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2022-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.inventoryservice.config; 8 | 9 | import org.springframework.boot.restclient.RestTemplateBuilder; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.web.client.RestTemplate; 13 | 14 | @Configuration(proxyBeanMethods = false) 15 | class RestTemplateConfig { 16 | 17 | // IMPORTANT! To instrument RestTemplate you must inject the RestTemplateBuilder 18 | @Bean 19 | RestTemplate restTemplate(RestTemplateBuilder builder) { 20 | return builder.build(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/model/request/OrderItemRequest.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2023-2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.orderservice.model.request; 8 | 9 | import jakarta.validation.constraints.DecimalMin; 10 | import jakarta.validation.constraints.NotBlank; 11 | import jakarta.validation.constraints.Positive; 12 | import java.math.BigDecimal; 13 | 14 | public record OrderItemRequest( 15 | @NotBlank(message = "Product code must be provided") String productCode, 16 | @Positive(message = "Quantity should be greater than zero") int quantity, 17 | @DecimalMin(value = "0.01", inclusive = true, message = "Price should be greater than zero") 18 | BigDecimal productPrice) {} 19 | -------------------------------------------------------------------------------- /payment-service/src/test/java/com/example/paymentservice/TestPaymentApplication.java: -------------------------------------------------------------------------------- 1 | /*** Licensed under MIT License Copyright (c) 2023-2024 Raja Kolli. ***/ 2 | package com.example.paymentservice; 3 | 4 | import com.example.paymentservice.common.NonSQLContainerConfig; 5 | import com.example.paymentservice.common.SQLContainerConfig; 6 | import com.example.paymentservice.utils.AppConstants; 7 | import org.springframework.boot.SpringApplication; 8 | 9 | public class TestPaymentApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.from(PaymentApplication::main) 13 | .with(SQLContainerConfig.class, NonSQLContainerConfig.class) 14 | .withAdditionalProfiles(AppConstants.PROFILE_LOCAL) 15 | .run(args); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /catalog-service/src/main/java/com/example/catalogservice/CatalogServiceApplication.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.catalogservice; 8 | 9 | import com.example.catalogservice.config.ApplicationProperties; 10 | import org.springframework.boot.SpringApplication; 11 | import org.springframework.boot.autoconfigure.SpringBootApplication; 12 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 13 | 14 | @SpringBootApplication 15 | @EnableConfigurationProperties({ApplicationProperties.class}) 16 | public class CatalogServiceApplication { 17 | 18 | public static void main(String[] args) { 19 | SpringApplication.run(CatalogServiceApplication.class, args); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /inventory-service/src/main/java/com/example/inventoryservice/InventoryServiceApplication.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2021-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.inventoryservice; 8 | 9 | import com.example.inventoryservice.config.ApplicationProperties; 10 | import org.springframework.boot.SpringApplication; 11 | import org.springframework.boot.autoconfigure.SpringBootApplication; 12 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 13 | 14 | @SpringBootApplication 15 | @EnableConfigurationProperties({ApplicationProperties.class}) 16 | class InventoryServiceApplication { 17 | 18 | public static void main(String[] args) { 19 | SpringApplication.run(InventoryServiceApplication.class, args); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /inventory-service/src/test/java/com/example/inventoryservice/InventoryServiceApplicationIntegrationTest.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.inventoryservice; 8 | 9 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 10 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 11 | 12 | import com.example.inventoryservice.common.AbstractIntegrationTest; 13 | import org.junit.jupiter.api.Test; 14 | 15 | class InventoryServiceApplicationIntegrationTest extends AbstractIntegrationTest { 16 | 17 | @Test 18 | void contextLoads() throws Exception { 19 | this.mockMvc.perform(get("/actuator")).andExpect(status().isOk()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /payment-service/sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.sourceEncoding=UTF-8 2 | sonar.projectKey=rajadilipkolli-microserviesv2-payment-service 3 | sonar.organization=rajadilipkolli 4 | sonar.host.url=https://sonarcloud.io 5 | 6 | sonar.sources=src/main/java 7 | sonar.tests=src/test/java 8 | sonar.exclusions=src/main/java/**/config/**,src/main/java/**/entities/*.*,src/main/java/**/models/*.*,src/main/java/**/exceptions/*.*,src/main/java/**/utils/*.*,src/main/java/**/common/dtos/*.*,src/main/java/**/*Application.* 9 | sonar.test.inclusions=**/*Test.java,**/*IntegrationTest.java,**/*IT.java 10 | sonar.java.codeCoveragePlugin=jacoco 11 | sonar.coverage.jacoco.xmlReportPaths=target/jacoco/test/jacoco.xml,target/jacoco/integrationTest/jacoco.xml 12 | sonar.junit.reportPaths=target/test-results/test,target/test-results/integrationTest 13 | 14 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/model/response/OrderResponse.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2023-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.orderservice.model.response; 8 | 9 | import com.example.orderservice.model.Address; 10 | import com.fasterxml.jackson.annotation.JsonFormat; 11 | import java.math.BigDecimal; 12 | import java.time.LocalDateTime; 13 | import java.util.List; 14 | 15 | public record OrderResponse( 16 | Long orderId, 17 | Long customerId, 18 | String status, 19 | String source, 20 | Address deliveryAddress, 21 | LocalDateTime createdDate, 22 | @JsonFormat(shape = JsonFormat.Shape.NUMBER_FLOAT, pattern = "0.00") BigDecimal totalPrice, 23 | List3 | Licensed under MIT License Copyright (c) 2023-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.orderservice; 8 | 9 | import com.example.orderservice.common.ContainersConfig; 10 | import com.example.orderservice.common.PostGreSQLContainer; 11 | import com.example.orderservice.utils.AppConstants; 12 | import org.springframework.boot.SpringApplication; 13 | 14 | public class TestOrderServiceApplication { 15 | 16 | public static void main(String[] args) { 17 | SpringApplication.from(OrderServiceApplication::main) 18 | .with(ContainersConfig.class, PostGreSQLContainer.class) 19 | .withAdditionalProfiles(AppConstants.PROFILE_LOCAL) 20 | .run(args); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /payment-service/src/main/java/com/example/paymentservice/config/logging/Loggable.java: -------------------------------------------------------------------------------- 1 | /*** Licensed under MIT License Copyright (c) 2021-2024 Raja Kolli. ***/ 2 | package com.example.paymentservice.config.logging; 3 | 4 | import static java.lang.annotation.ElementType.METHOD; 5 | import static java.lang.annotation.ElementType.TYPE; 6 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 7 | 8 | import java.lang.annotation.Inherited; 9 | import java.lang.annotation.Retention; 10 | import java.lang.annotation.Target; 11 | import org.springframework.boot.logging.LogLevel; 12 | 13 | @Target({TYPE, METHOD}) 14 | @Retention(RUNTIME) 15 | @Inherited 16 | public @interface Loggable { 17 | 18 | boolean params() default true; 19 | 20 | boolean result() default true; 21 | 22 | LogLevel value() default LogLevel.DEBUG; 23 | } 24 | -------------------------------------------------------------------------------- /catalog-service/src/test/java/com/example/catalogservice/TestCatalogServiceApplication.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2023-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.catalogservice; 8 | 9 | import com.example.catalogservice.common.ContainersConfig; 10 | import com.example.catalogservice.common.SQLContainerConfig; 11 | import com.example.catalogservice.utils.AppConstants; 12 | import org.springframework.boot.SpringApplication; 13 | 14 | public class TestCatalogServiceApplication { 15 | 16 | public static void main(String[] args) { 17 | SpringApplication.from(CatalogServiceApplication::main) 18 | .with(ContainersConfig.class, SQLContainerConfig.class) 19 | .withAdditionalProfiles(AppConstants.PROFILE_TEST) 20 | .run(args); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/config/AuditConfiguration.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2023-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.orderservice.config; 8 | 9 | import java.util.Optional; 10 | import org.jspecify.annotations.NonNull; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.Configuration; 13 | import org.springframework.data.domain.AuditorAware; 14 | import org.springframework.data.jpa.repository.config.EnableJpaAuditing; 15 | 16 | @Configuration(proxyBeanMethods = false) 17 | @EnableJpaAuditing(auditorAwareRef = "auditorProvider") 18 | class AuditConfiguration { 19 | 20 | @Bean 21 | AuditorAware<@NonNull String> auditorProvider() { 22 | return () -> Optional.of("AppUser"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /api-gateway/src/main/java/com/example/api/gateway/config/WebClientConfiguration.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2023-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.api.gateway.config; 8 | 9 | import org.springframework.cloud.client.loadbalancer.LoadBalanced; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.web.reactive.function.client.WebClient; 13 | 14 | @Configuration(proxyBeanMethods = false) 15 | class WebClientConfiguration { 16 | 17 | @Bean 18 | @LoadBalanced 19 | WebClient.Builder loadBalancedWebClientBuilder() { 20 | return WebClient.builder(); 21 | } 22 | 23 | @Bean 24 | WebClient webClient(WebClient.Builder builder) { 25 | return builder.build(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/config/logging/Loggable.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2021-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.orderservice.config.logging; 8 | 9 | import static java.lang.annotation.ElementType.METHOD; 10 | import static java.lang.annotation.ElementType.TYPE; 11 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 12 | 13 | import java.lang.annotation.Inherited; 14 | import java.lang.annotation.Retention; 15 | import java.lang.annotation.Target; 16 | import org.springframework.boot.logging.LogLevel; 17 | 18 | @Target({TYPE, METHOD}) 19 | @Retention(RUNTIME) 20 | @Inherited 21 | public @interface Loggable { 22 | 23 | boolean params() default true; 24 | 25 | boolean result() default true; 26 | 27 | LogLevel value() default LogLevel.DEBUG; 28 | } 29 | -------------------------------------------------------------------------------- /catalog-service/src/main/java/com/example/catalogservice/config/logging/Loggable.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2021-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.catalogservice.config.logging; 8 | 9 | import static java.lang.annotation.ElementType.METHOD; 10 | import static java.lang.annotation.ElementType.TYPE; 11 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 12 | 13 | import java.lang.annotation.Inherited; 14 | import java.lang.annotation.Retention; 15 | import java.lang.annotation.Target; 16 | import org.springframework.boot.logging.LogLevel; 17 | 18 | @Target({TYPE, METHOD}) 19 | @Retention(RUNTIME) 20 | @Inherited 21 | public @interface Loggable { 22 | 23 | boolean params() default true; 24 | 25 | boolean result() default true; 26 | 27 | LogLevel value() default LogLevel.DEBUG; 28 | } 29 | -------------------------------------------------------------------------------- /inventory-service/src/test/java/com/example/inventoryservice/TestInventoryApplication.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2023-2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.inventoryservice; 8 | 9 | import com.example.inventoryservice.common.NonSQLContainersConfig; 10 | import com.example.inventoryservice.common.SQLContainersConfig; 11 | import com.example.inventoryservice.utils.AppConstants; 12 | import org.springframework.boot.SpringApplication; 13 | 14 | public class TestInventoryApplication { 15 | 16 | public static void main(String[] args) { 17 | SpringApplication.from(InventoryServiceApplication::main) 18 | .with(NonSQLContainersConfig.class, SQLContainersConfig.class) 19 | .withAdditionalProfiles(AppConstants.PROFILE_TEST) 20 | .run(args); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /inventory-service/src/main/java/com/example/inventoryservice/config/logging/Loggable.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2021-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.inventoryservice.config.logging; 8 | 9 | import static java.lang.annotation.ElementType.METHOD; 10 | import static java.lang.annotation.ElementType.TYPE; 11 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 12 | 13 | import java.lang.annotation.Inherited; 14 | import java.lang.annotation.Retention; 15 | import java.lang.annotation.Target; 16 | import org.springframework.boot.logging.LogLevel; 17 | 18 | @Target({TYPE, METHOD}) 19 | @Retention(RUNTIME) 20 | @Inherited 21 | public @interface Loggable { 22 | 23 | boolean params() default true; 24 | 25 | boolean result() default true; 26 | 27 | LogLevel value() default LogLevel.DEBUG; 28 | } 29 | -------------------------------------------------------------------------------- /payment-service/src/test/java/com/example/paymentservice/common/SQLContainerConfig.java: -------------------------------------------------------------------------------- 1 | /*** Licensed under MIT License Copyright (c) 2023-2025 Raja Kolli. ***/ 2 | package com.example.paymentservice.common; 3 | 4 | import org.springframework.boot.test.context.TestConfiguration; 5 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection; 6 | import org.springframework.context.annotation.Bean; 7 | import org.testcontainers.postgresql.PostgreSQLContainer; 8 | import org.testcontainers.utility.DockerImageName; 9 | 10 | @TestConfiguration(proxyBeanMethods = false) 11 | public class SQLContainerConfig { 12 | 13 | @Bean 14 | @ServiceConnection 15 | PostgreSQLContainer postgreSQLContainer() { 16 | return new PostgreSQLContainer(DockerImageName.parse("postgres").withTag("18-alpine")) 17 | .withReuse(true); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /catalog-service/src/main/java/com/example/catalogservice/exception/CustomResponseStatusException.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2023 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.catalogservice.exception; 8 | 9 | import org.springframework.http.HttpStatus; 10 | import org.springframework.http.HttpStatusCode; 11 | import org.springframework.http.ProblemDetail; 12 | import org.springframework.web.ErrorResponseException; 13 | 14 | public class CustomResponseStatusException extends ErrorResponseException { 15 | 16 | public CustomResponseStatusException(HttpStatusCode status, String message) { 17 | super(status, problemDetail(message), null); 18 | } 19 | 20 | private static ProblemDetail problemDetail(String message) { 21 | return ProblemDetail.forStatusAndDetail(HttpStatus.SERVICE_UNAVAILABLE, message); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /inventory-service/src/test/java/com/example/inventoryservice/common/SQLContainersConfig.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2023-2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.inventoryservice.common; 8 | 9 | import org.springframework.boot.test.context.TestConfiguration; 10 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection; 11 | import org.springframework.context.annotation.Bean; 12 | import org.testcontainers.postgresql.PostgreSQLContainer; 13 | import org.testcontainers.utility.DockerImageName; 14 | 15 | @TestConfiguration(proxyBeanMethods = false) 16 | public class SQLContainersConfig { 17 | 18 | @Bean 19 | @ServiceConnection 20 | PostgreSQLContainer postgreSQLContainer() { 21 | return new PostgreSQLContainer(DockerImageName.parse("postgres:18-alpine")).withReuse(true); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /retail-store-webapp/src/main/java/com/example/retailstore/webapp/clients/catalog/CatalogServiceClient.java: -------------------------------------------------------------------------------- 1 | package com.example.retailstore.webapp.clients.catalog; 2 | 3 | import com.example.retailstore.webapp.clients.PagedResult; 4 | import org.springframework.web.bind.annotation.RequestBody; 5 | import org.springframework.web.bind.annotation.RequestParam; 6 | import org.springframework.web.service.annotation.GetExchange; 7 | import org.springframework.web.service.annotation.HttpExchange; 8 | import org.springframework.web.service.annotation.PostExchange; 9 | 10 | @HttpExchange("/catalog-service") 11 | public interface CatalogServiceClient { 12 | 13 | @GetExchange("/api/catalog") 14 | PagedResult3 | Licensed under MIT License Copyright (c) 2023-2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.orderservice.common; 8 | 9 | import org.springframework.boot.test.context.TestConfiguration; 10 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection; 11 | import org.springframework.context.annotation.Bean; 12 | import org.testcontainers.postgresql.PostgreSQLContainer; 13 | import org.testcontainers.utility.DockerImageName; 14 | 15 | @TestConfiguration(proxyBeanMethods = false) 16 | public class PostGreSQLContainer { 17 | 18 | @ServiceConnection 19 | @Bean 20 | PostgreSQLContainer postgreSQLContainer() { 21 | return new PostgreSQLContainer(DockerImageName.parse("postgres").withTag("18-alpine")) 22 | .withReuse(true); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /retail-store-webapp/sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.sourceEncoding=UTF-8 2 | sonar.projectKey=rajadilipkolli-microservicesv2-retail-store-webapp 3 | sonar.organization=rajadilipkolli 4 | sonar.host.url=https://sonarcloud.io 5 | 6 | sonar.sources=src/main/java 7 | sonar.tests=src/test/java 8 | sonar.coverage.exclusions=src/main/java/**/config/**,src/main/java/**/entities/*.*,src/main/java/**/models/*.*,src/main/java/**/exceptions/*.*,src/main/java/**/utils/*.*,src/main/java/**/*Application.* 9 | sonar.test.inclusions=**/*Test.java,**/*IntegrationTest.java,**/*IT.java 10 | sonar.java.codeCoveragePlugin=jacoco 11 | sonar.coverage.jacoco.xmlReportPaths=target/jacoco/test/jacoco.xml,target/jacoco/integrationTest/jacoco.xml 12 | sonar.junit.reportPaths=target/test-results/test,target/test-results/integrationTest 13 | sonar.java.checkstyle.reportPaths=target/checkstyle-result.xml 14 | sonar.java.pmd.reportPaths=target/pmd/main.xml 15 | -------------------------------------------------------------------------------- /catalog-service/src/main/java/com/example/catalogservice/config/Initializer.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2021-2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.catalogservice.config; 8 | 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.boot.CommandLineRunner; 12 | import org.springframework.stereotype.Component; 13 | 14 | @Component 15 | public class Initializer implements CommandLineRunner { 16 | 17 | private final Logger log = LoggerFactory.getLogger(this.getClass()); 18 | 19 | private final ApplicationProperties properties; 20 | 21 | public Initializer(ApplicationProperties properties) { 22 | this.properties = properties; 23 | } 24 | 25 | @Override 26 | public void run(String... args) { 27 | log.info("Running Initializer.....{}", properties.inventoryServiceUrl()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /inventory-service/sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.sourceEncoding=UTF-8 2 | sonar.projectKey=rajadilipkolli-microserviesv2-inventory-service 3 | sonar.organization=rajadilipkolli 4 | sonar.host.url=https://sonarcloud.io 5 | 6 | sonar.sources=src/main/java 7 | sonar.tests=src/test/java 8 | sonar.exclusions=src/main/java/**/config/**,src/main/java/**/entities/*.*,src/main/java/**/models/*.*,src/main/java/**/exceptions/*.*,src/main/java/**/utils/*.*,src/main/java/**/common/dtos/*.*,src/main/java/**/*Application.* 9 | sonar.test.inclusions=**/*Test.java,**/*IntegrationTest.java,**/*IT.java 10 | sonar.java.codeCoveragePlugin=jacoco 11 | sonar.coverage.jacoco.xmlReportPaths=target/jacoco/test/jacoco.xml,target/jacoco/integrationTest/jacoco.xml 12 | sonar.junit.reportPaths=target/test-results/test,target/test-results/integrationTest 13 | sonar.java.checkstyle.reportPaths=target/checkstyle-result.xml 14 | sonar.java.pmd.reportPaths=target/pmd/main.xml 15 | -------------------------------------------------------------------------------- /payment-service/src/main/java/com/example/paymentservice/config/logging/LogWriter.java: -------------------------------------------------------------------------------- 1 | /*** Licensed under MIT License Copyright (c) 2024 Raja Kolli. ***/ 2 | package com.example.paymentservice.config.logging; 3 | 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.boot.logging.LogLevel; 7 | 8 | class LogWriter { 9 | 10 | public static void write(Class> originClass, LogLevel logLevel, String message) { 11 | Logger logger = LoggerFactory.getLogger(originClass); 12 | switch (logLevel) { 13 | case TRACE -> logger.trace(message); 14 | case DEBUG -> logger.debug(message); 15 | case INFO -> logger.info(message); 16 | case WARN -> logger.warn(message); 17 | case ERROR, FATAL -> logger.error(message); 18 | default -> logger.info("Defaulting to INFO level for message: {}", message); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /inventory-service/src/main/java/com/example/inventoryservice/repositories/InventoryJOOQRepository.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2023-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.inventoryservice.repositories; 8 | 9 | import com.example.inventoryservice.entities.Inventory; 10 | import java.util.List; 11 | import java.util.Optional; 12 | import org.springframework.data.domain.Page; 13 | import org.springframework.data.domain.Pageable; 14 | import org.springframework.stereotype.Repository; 15 | 16 | @Repository 17 | public interface InventoryJOOQRepository { 18 | 19 | Optional3 | Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.orderservice.config; 8 | 9 | import jakarta.validation.Valid; 10 | import jakarta.validation.constraints.NotBlank; 11 | import org.springframework.boot.context.properties.ConfigurationProperties; 12 | import org.springframework.boot.context.properties.NestedConfigurationProperty; 13 | import org.springframework.validation.annotation.Validated; 14 | 15 | @ConfigurationProperties("application") 16 | @Validated 17 | public record ApplicationProperties( 18 | @NotBlank(message = "CatalogServiceUrl Cant be Blank") String catalogServiceUrl, 19 | boolean byPassCircuitBreaker, 20 | @NestedConfigurationProperty @Valid Cors cors) { 21 | 22 | public ApplicationProperties { 23 | cors = new Cors(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /retail-store-webapp/src/test/java/com/example/retailstore/webapp/ApplicationIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.example.retailstore.webapp; 2 | 3 | import com.example.retailstore.webapp.common.AbstractIntegrationTest; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.http.HttpStatus; 6 | 7 | class ApplicationIntegrationTest extends AbstractIntegrationTest { 8 | 9 | @Test 10 | void shouldReturnHealthyStatusFromActuatorEndpoint() { 11 | mockMvcTester 12 | .get() 13 | .uri("/actuator/health") 14 | .assertThat() 15 | .hasContentType("application/vnd.spring-boot.actuator.v3+json") 16 | .hasHeader("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate") 17 | .hasStatus(HttpStatus.OK) 18 | .bodyJson() 19 | .extractingPath("$.status") 20 | .isEqualTo("UP"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /inventory-service/src/main/java/com/example/inventoryservice/mapper/InventoryMapper.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.inventoryservice.mapper; 8 | 9 | import com.example.inventoryservice.entities.Inventory; 10 | import com.example.inventoryservice.model.request.InventoryRequest; 11 | import org.mapstruct.InheritConfiguration; 12 | import org.mapstruct.Mapper; 13 | import org.mapstruct.Mapping; 14 | import org.mapstruct.MappingTarget; 15 | 16 | @Mapper(componentModel = "spring") 17 | public interface InventoryMapper { 18 | 19 | @Mapping(target = "id", ignore = true) 20 | @Mapping(target = "reservedItems", ignore = true) 21 | Inventory toEntity(InventoryRequest inventoryRequest); 22 | 23 | @InheritConfiguration 24 | void updateInventoryFromRequest( 25 | InventoryRequest inventoryRequest, @MappingTarget Inventory inventory); 26 | } 27 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/config/logging/LogWriter.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.orderservice.config.logging; 8 | 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.boot.logging.LogLevel; 12 | 13 | public class LogWriter { 14 | 15 | public static void write(Class> originClass, LogLevel logLevel, String message) { 16 | Logger logger = LoggerFactory.getLogger(originClass); 17 | switch (logLevel) { 18 | case TRACE -> logger.trace(message); 19 | case DEBUG -> logger.debug(message); 20 | case INFO -> logger.info(message); 21 | case WARN -> logger.warn(message); 22 | case ERROR, FATAL -> logger.error(message); 23 | default -> logger.warn("No suitable logLevel found"); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /retail-store-webapp/src/main/java/com/example/retailstore/webapp/clients/customer/CustomerServiceClient.java: -------------------------------------------------------------------------------- 1 | package com.example.retailstore.webapp.clients.customer; 2 | 3 | import org.springframework.web.bind.annotation.PathVariable; 4 | import org.springframework.web.bind.annotation.RequestBody; 5 | import org.springframework.web.service.annotation.GetExchange; 6 | import org.springframework.web.service.annotation.HttpExchange; 7 | import org.springframework.web.service.annotation.PostExchange; 8 | 9 | @HttpExchange("/payment-service") 10 | public interface CustomerServiceClient { 11 | 12 | @PostExchange("/api/customers") 13 | CustomerResponse getOrCreateCustomer(@RequestBody CustomerRequest customer); 14 | 15 | @GetExchange("/api/customers/name/{name}") 16 | CustomerResponse getCustomerByName(@PathVariable String name); 17 | 18 | @GetExchange("/api/customers/{id}") 19 | CustomerResponse getCustomerById(@PathVariable Long id); 20 | } 21 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto-detect text files and normalize 2 | * text=auto 3 | 4 | # Force LF for shell scripts and common config/source files 5 | *.sh text eol=lf 6 | *.yml text eol=lf 7 | *.yaml text eol=lf 8 | *.properties text eol=lf 9 | *.xml text eol=lf 10 | *.java text eol=lf 11 | *.gradle text eol=lf 12 | pom.xml text eol=lf 13 | *.md text eol=lf 14 | Dockerfile text eol=lf 15 | deployment/** text eol=lf 16 | 17 | # Keep Windows script files in CRLF form (helpful for PowerShell/Batch authors) 18 | *.ps1 text eol=crlf 19 | *.bat text eol=crlf 20 | 21 | # Treat common binary files as binary to avoid EOL conversion 22 | *.png binary 23 | *.jpg binary 24 | *.jpeg binary 25 | *.gif binary 26 | *.jar binary 27 | *.war binary 28 | *.zip binary 29 | *.gz binary 30 | 31 | # Ensure the attributes file itself uses LF 32 | .gitattributes text eol=lf 33 | *.xml text eol=lf 34 | *.yaml text eol=lf 35 | *.properties text eol=lf 36 | /mvnw text eol=lf 37 | *.cmd text eol=crlf 38 | -------------------------------------------------------------------------------- /inventory-service/src/main/java/com/example/inventoryservice/config/logging/LogWriter.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.inventoryservice.config.logging; 8 | 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.boot.logging.LogLevel; 12 | 13 | public class LogWriter { 14 | 15 | public static void write(Class> originClass, LogLevel logLevel, String message) { 16 | Logger logger = LoggerFactory.getLogger(originClass); 17 | switch (logLevel) { 18 | case TRACE -> logger.trace(message); 19 | case DEBUG -> logger.debug(message); 20 | case INFO -> logger.info(message); 21 | case WARN -> logger.warn(message); 22 | case ERROR, FATAL -> logger.error(message); 23 | default -> logger.warn("No suitable logLevel found"); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /catalog-service/src/test/java/com/example/catalogservice/common/SQLContainerConfig.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2023-2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.catalogservice.common; 8 | 9 | import org.springframework.boot.test.context.TestConfiguration; 10 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection; 11 | import org.springframework.context.annotation.Bean; 12 | import org.testcontainers.postgresql.PostgreSQLContainer; 13 | import org.testcontainers.utility.DockerImageName; 14 | 15 | @TestConfiguration(proxyBeanMethods = false) 16 | public class SQLContainerConfig { 17 | 18 | @Bean 19 | @ServiceConnection 20 | PostgreSQLContainer postgreSQLContainer() { 21 | return new PostgreSQLContainer(DockerImageName.parse("postgres").withTag("18-alpine")) 22 | .withDatabaseName("catalog-service") 23 | .withReuse(true); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /catalog-service/src/main/java/com/example/catalogservice/config/logging/LogWriter.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.catalogservice.config.logging; 8 | 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | import org.springframework.boot.logging.LogLevel; 12 | 13 | public class LogWriter { 14 | 15 | public static void write(Class> originClass, LogLevel logLevel, String message) { 16 | Logger logger = LoggerFactory.getLogger(originClass); 17 | switch (logLevel) { 18 | case TRACE -> logger.trace(message); 19 | case DEBUG -> logger.debug(message); 20 | case INFO -> logger.info(message); 21 | case WARN -> logger.warn(message); 22 | case ERROR, FATAL -> logger.error(message); 23 | default -> logger.info("Defaulting to INFO level for message: {}", message); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /deployment/config/promtail/promtail.yml: -------------------------------------------------------------------------------- 1 | # https://grafana.com/docs/loki/latest/clients/promtail/configuration/ 2 | # https://docs.docker.com/engine/api/v1.41/#operation/ContainerList 3 | server: 4 | http_listen_port: 9080 5 | grpc_listen_port: 0 6 | 7 | positions: 8 | filename: /tmp/positions.yaml 9 | 10 | clients: 11 | - url: http://loki:3100/loki/api/v1/push 12 | 13 | scrape_configs: 14 | - job_name: flog_scrape 15 | docker_sd_configs: 16 | - host: unix:///var/run/docker.sock 17 | refresh_interval: 5s 18 | filters: 19 | - name: label 20 | values: ["logging=promtail"] 21 | relabel_configs: 22 | - source_labels: ['__meta_docker_container_name'] 23 | regex: '/(.*)' 24 | target_label: 'container' 25 | - source_labels: ['__meta_docker_container_log_stream'] 26 | target_label: 'logstream' 27 | - source_labels: ['__meta_docker_container_label_logging_jobname'] 28 | target_label: 'job' -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Download mod cli 4 | curl --request GET \ 5 | --output mod.tar.gz \ 6 | --url 'https://api.app.moderne.io/cli/download?operatingSystem=linux-tar&environment=stable' \ 7 | --header 'Authorization: Bearer ************************************************' # IGNORE 8 | 9 | # Extract the archive 10 | tar -xzf mod.tar.gz 11 | 12 | # Make the binary executable 13 | chmod +x "${PWD}/mod" 14 | 15 | # Create .moderne directory in user's home 16 | mkdir -p $HOME/.moderne/bin 17 | 18 | # Copy mod to .moderne/bin directory 19 | cp "${PWD}/mod" "$HOME/.moderne/bin/mod" 20 | 21 | # Add .moderne/bin to PATH if not already present 22 | if [[ $PATH != *".moderne/bin"* ]]; then 23 | echo "export PATH=\$PATH:\$HOME/.moderne/bin" >> $HOME/.bashrc 24 | echo "source <(mod generate-completion)" >> $HOME/.bashrc 25 | source $HOME/.bashrc 26 | fi 27 | 28 | # Clean up the downloaded files 29 | rm mod.tar.gz mod 30 | 31 | echo "mod CLI has been installed successfully!" 32 | -------------------------------------------------------------------------------- /payment-service/src/main/java/com/example/paymentservice/repositories/CustomerRepository.java: -------------------------------------------------------------------------------- 1 | /*** Licensed under MIT License Copyright (c) 2022-2024 Raja Kolli. ***/ 2 | package com.example.paymentservice.repositories; 3 | 4 | import com.example.paymentservice.entities.Customer; 5 | import com.example.paymentservice.model.response.CustomerResponse; 6 | import java.util.List; 7 | import java.util.Optional; 8 | import org.springframework.data.domain.Page; 9 | import org.springframework.data.domain.Pageable; 10 | 11 | public interface CustomerRepository { 12 | 13 | Optional3 | Licensed under MIT License Copyright (c) 2023-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.api.gateway.config; 8 | 9 | import org.jspecify.annotations.NonNull; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.web.reactive.config.CorsRegistry; 12 | import org.springframework.web.reactive.config.WebFluxConfigurer; 13 | 14 | @Configuration(proxyBeanMethods = false) 15 | class WebFluxConfig implements WebFluxConfigurer { 16 | 17 | @Override 18 | public void addCorsMappings(@NonNull CorsRegistry registry) { 19 | registry.addMapping("/**") 20 | .allowedMethods("GET", "POST", "OPTIONS", "HEAD", "PUT") 21 | .allowedHeaders("Access-Control-Allow-Origin") 22 | .allowedOrigins("http://localhost:8765", "mytrustedwebsite.com") 23 | .allowCredentials(true); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /service-registry/ReadMe.md: -------------------------------------------------------------------------------- 1 | #service-registry 2 | 3 | A service registry is a central directory that maintains a list of all the available microservices in a system. It allows services to discover and communicate with each other, enabling them to work together to achieve a common goal. The registry typically contains information about the location, availability, and capabilities of each service. It may also include details such as the version of the service, the protocols it uses to communicate, and any dependencies it has on other services. Service registries are an important component of microservice architecture as they enable services to be flexible and scalable, while still being able to interact with each other in a consistent and reliable manner. 4 | 5 | ### Run tests 6 | ```shell 7 | ./mvnw clean verify 8 | ``` 9 | 10 | ### Run locally 11 | ```shell 12 | docker compose -f docker-compose.yml up -d 13 | ./mvnw spring-boot:run 14 | ``` 15 | 16 | ### Useful Links 17 | * Accessing Eureka Server via http://localhost:8761/ 18 | -------------------------------------------------------------------------------- /retail-store-webapp/src/main/java/com/example/retailstore/webapp/clients/inventory/InventoryServiceClient.java: -------------------------------------------------------------------------------- 1 | package com.example.retailstore.webapp.clients.inventory; 2 | 3 | import com.example.retailstore.webapp.clients.PagedResult; 4 | import org.springframework.web.bind.annotation.PathVariable; 5 | import org.springframework.web.bind.annotation.RequestBody; 6 | import org.springframework.web.bind.annotation.RequestParam; 7 | import org.springframework.web.service.annotation.GetExchange; 8 | import org.springframework.web.service.annotation.HttpExchange; 9 | import org.springframework.web.service.annotation.PutExchange; 10 | 11 | @HttpExchange("/inventory-service") 12 | public interface InventoryServiceClient { 13 | 14 | @GetExchange("/api/inventory") 15 | PagedResult3 | Licensed under MIT License Copyright (c) 2023 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.api.gateway.web.controller; 8 | 9 | import static org.springframework.web.reactive.function.server.RequestPredicates.GET; 10 | import static org.springframework.web.reactive.function.server.RouterFunctions.route; 11 | 12 | import java.net.URI; 13 | import org.springframework.context.annotation.Bean; 14 | import org.springframework.context.annotation.Configuration; 15 | import org.springframework.web.reactive.function.server.RouterFunction; 16 | import org.springframework.web.reactive.function.server.ServerResponse; 17 | 18 | @Configuration(proxyBeanMethods = false) 19 | public class HomeController { 20 | 21 | @Bean 22 | RouterFunction3 | Licensed under MIT License Copyright (c) 2023-2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.api.gateway.web.controller; 8 | 9 | import org.springframework.web.bind.annotation.GetMapping; 10 | import org.springframework.web.bind.annotation.PathVariable; 11 | import org.springframework.web.bind.annotation.RequestMapping; 12 | import org.springframework.web.bind.annotation.RestController; 13 | import reactor.core.publisher.Mono; 14 | 15 | @RestController 16 | @RequestMapping("/fallback/api/inventory") 17 | public class GatewayInventoryFallback { 18 | 19 | @GetMapping("/{id}") 20 | public Mono3 | Licensed under MIT License Copyright (c) 2022-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.orderservice.services; 8 | 9 | import com.example.orderservice.config.logging.Loggable; 10 | import java.util.List; 11 | import org.springframework.http.MediaType; 12 | import org.springframework.web.bind.annotation.RequestParam; 13 | import org.springframework.web.service.annotation.GetExchange; 14 | import org.springframework.web.service.annotation.HttpExchange; 15 | 16 | // @HttpExchange("lb://catalog-service/") 17 | // @HttpExchange("http://localhost:18080/catalog-service") 18 | @HttpExchange( 19 | // url = "${application.catalog-service-url}", 20 | accept = MediaType.APPLICATION_JSON_VALUE, 21 | contentType = MediaType.APPLICATION_JSON_VALUE) 22 | @Loggable 23 | public interface CatalogServiceProxy { 24 | 25 | @GetExchange("/api/catalog/exists") 26 | boolean productsExistsByCodes(@RequestParam List3 | Licensed under MIT License Copyright (c) 2021-2023 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.inventoryservice.utils; 8 | 9 | public final class AppConstants { 10 | public static final String PROFILE_LOCAL = "local"; 11 | public static final String PROFILE_PROD = "prod"; 12 | public static final String PROFILE_NOT_PROD = "!" + PROFILE_PROD; 13 | public static final String PROFILE_TEST = "test"; 14 | public static final String ORDERS_TOPIC = "orders"; 15 | public static final String STOCK_ORDERS_TOPIC = "stock-orders"; 16 | public static final String SOURCE = "INVENTORY"; 17 | public static final String ROLLBACK = "ROLLBACK"; 18 | public static final String DEFAULT_PAGE_NUMBER = "0"; 19 | public static final String DEFAULT_PAGE_SIZE = "10"; 20 | public static final String DEFAULT_SORT_BY = "id"; 21 | public static final String DEFAULT_SORT_DIRECTION = "asc"; 22 | public static final String PRODUCT_TOPIC = "productTopic"; 23 | } 24 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/utils/AppConstants.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2021-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.orderservice.utils; 8 | 9 | public final class AppConstants { 10 | public static final String PROFILE_LOCAL = "local"; 11 | public static final String PROFILE_PROD = "prod"; 12 | public static final String PROFILE_NOT_PROD = "!" + PROFILE_PROD; 13 | public static final String PROFILE_TEST = "test"; 14 | public static final String ORDERS_TOPIC = "orders"; 15 | public static final String PAYMENT_ORDERS_TOPIC = "payment-orders"; 16 | public static final String STOCK_ORDERS_TOPIC = "stock-orders"; 17 | public static final String RECOVER_DLQ_TOPIC = "recovererDLQ"; 18 | public static final String ROLLBACK = "ROLLBACK"; 19 | public static final String DEFAULT_PAGE_NUMBER = "0"; 20 | public static final String DEFAULT_PAGE_SIZE = "10"; 21 | public static final String DEFAULT_SORT_BY = "id"; 22 | public static final String DEFAULT_SORT_DIRECTION = "asc"; 23 | } 24 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2022-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.orderservice.config; 8 | 9 | import io.swagger.v3.oas.annotations.OpenAPIDefinition; 10 | import io.swagger.v3.oas.annotations.info.Info; 11 | import io.swagger.v3.oas.annotations.servers.Server; 12 | import org.springframework.context.annotation.Bean; 13 | import org.springframework.context.annotation.Configuration; 14 | import org.springframework.web.filter.ForwardedHeaderFilter; 15 | 16 | @Configuration(proxyBeanMethods = false) 17 | @OpenAPIDefinition( 18 | info = 19 | @Info( 20 | title = "order-service", 21 | version = "v1", 22 | description = "APIs related to Orders"), 23 | servers = @Server(url = "${server.servlet.contextPath}")) 24 | class SwaggerConfig { 25 | 26 | @Bean 27 | ForwardedHeaderFilter forwardedHeaderFilter() { 28 | return new ForwardedHeaderFilter(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /retail-store-webapp/src/main/java/com/example/retailstore/webapp/clients/order/CreateOrderRequest.java: -------------------------------------------------------------------------------- 1 | package com.example.retailstore.webapp.clients.order; 2 | 3 | import com.example.retailstore.webapp.clients.customer.CustomerRequest; 4 | import jakarta.validation.Valid; 5 | import jakarta.validation.constraints.NotEmpty; 6 | import java.io.Serializable; 7 | import java.util.List; 8 | 9 | public record CreateOrderRequest( 10 | @NotEmpty(message = "Items cannot be empty.") List3 | Licensed under MIT License Copyright (c) 2021-2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.common.dtos; 8 | 9 | import jakarta.validation.constraints.NotEmpty; 10 | import jakarta.validation.constraints.Positive; 11 | import java.io.Serial; 12 | import java.io.Serializable; 13 | import java.util.List; 14 | import java.util.Objects; 15 | 16 | public record OrderDto( 17 | Long orderId, 18 | @Positive(message = "CustomerId should be positive") Long customerId, 19 | String status, 20 | String source, 21 | @NotEmpty(message = "Order without items not valid") List3 | Licensed under MIT License Copyright (c) 2023 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.orderservice.model.response; 8 | 9 | import com.fasterxml.jackson.annotation.JsonProperty; 10 | import java.util.List; 11 | import org.springframework.data.domain.Page; 12 | 13 | public record PagedResult3 | Licensed under MIT License Copyright (c) 2023 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.catalogservice.model.response; 8 | 9 | import com.fasterxml.jackson.annotation.JsonProperty; 10 | import java.util.List; 11 | import org.springframework.data.domain.Page; 12 | 13 | public record PagedResult3 | Licensed under MIT License Copyright (c) 2023 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.inventoryservice.model.response; 8 | 9 | import com.fasterxml.jackson.annotation.JsonProperty; 10 | import java.util.List; 11 | import org.springframework.data.domain.Page; 12 | 13 | public record PagedResult3 | Licensed under MIT License Copyright (c) 2023-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.catalogservice.config; 8 | 9 | import java.util.concurrent.CountDownLatch; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.boot.test.context.TestConfiguration; 13 | import org.springframework.kafka.annotation.KafkaListener; 14 | import org.springframework.messaging.handler.annotation.Payload; 15 | 16 | @TestConfiguration(proxyBeanMethods = false) 17 | public class TestKafkaListenerConfig { 18 | 19 | private static final Logger log = LoggerFactory.getLogger(TestKafkaListenerConfig.class); 20 | 21 | private final CountDownLatch latch = new CountDownLatch(10); 22 | 23 | @KafkaListener(id = "products", topics = "productTopic", groupId = "product") 24 | public void onSaveProductEvent(@Payload String productDto) { 25 | log.info("Received Product: {}", productDto); 26 | latch.countDown(); 27 | } 28 | 29 | public CountDownLatch getLatch() { 30 | return latch; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /api-gateway/src/test/resources/com/example/api/gateway/config/CircuitBreakerConfigurationIntegrationTest/circuit-breaker.json: -------------------------------------------------------------------------------- 1 | { 2 | "mappings": [ 3 | { 4 | "request": { 5 | "urlPath": "/failing-service", 6 | "method": "GET" 7 | }, 8 | "response": { 9 | "status": 500, 10 | "headers": { 11 | "Content-Type": "application/json" 12 | }, 13 | "jsonBody": { 14 | "error": "Internal Server Error" 15 | } 16 | } 17 | }, 18 | { 19 | "request": { 20 | "urlPath": "/slow-service", 21 | "method": "GET" 22 | }, 23 | "response": { 24 | "status": 200, 25 | "headers": { 26 | "Content-Type": "application/json" 27 | }, 28 | "fixedDelayMilliseconds": 6000, 29 | "jsonBody": { 30 | "message": "Delayed Response" 31 | } 32 | } 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/config/Initializer.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2021-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.orderservice.config; 8 | 9 | import com.example.orderservice.services.OrderService; 10 | import org.jobrunr.scheduling.BackgroundJob; 11 | import org.jobrunr.scheduling.cron.Cron; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | import org.springframework.boot.CommandLineRunner; 15 | import org.springframework.stereotype.Component; 16 | 17 | @Component 18 | class Initializer implements CommandLineRunner { 19 | 20 | private final Logger log = LoggerFactory.getLogger(this.getClass()); 21 | 22 | private final OrderService orderService; 23 | 24 | public Initializer(OrderService orderService) { 25 | this.orderService = orderService; 26 | } 27 | 28 | @Override 29 | public void run(String... args) { 30 | log.info("Running Initializer....."); 31 | BackgroundJob.scheduleRecurrently(Cron.minutely(), orderService::retryNewOrders); 32 | log.info("Completed Scheduling Recurrently BackgroundJob with 2 retries"); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /api-gateway/src/test/java/com/example/api/gateway/APIGatewayApplicationIntegrationTest.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2021-2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.api.gateway; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | 11 | import com.example.api.gateway.config.AbstractIntegrationTest; 12 | import org.junit.jupiter.api.Test; 13 | 14 | class APIGatewayApplicationIntegrationTest extends AbstractIntegrationTest { 15 | 16 | @Test 17 | void actuatorHealth() { 18 | webTestClient 19 | .get() 20 | .uri("/actuator/health") 21 | .exchange() 22 | .expectStatus() 23 | .isOk() 24 | .expectBody(String.class) 25 | .consumeWith( 26 | res -> 27 | assertThat(res.getResponseBody()) 28 | .isEqualToIgnoringWhitespace( 29 | """ 30 | {"groups":["liveness","readiness"],"status":"UP"} 31 | """)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /catalog-service/src/main/java/com/example/catalogservice/model/response/ProductResponse.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2023-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.catalogservice.model.response; 8 | 9 | import com.fasterxml.jackson.annotation.JsonFormat; 10 | import com.fasterxml.jackson.annotation.JsonIgnore; 11 | 12 | public record ProductResponse( 13 | Long id, 14 | String productCode, 15 | String productName, 16 | String description, 17 | String imageUrl, 18 | @JsonFormat(shape = JsonFormat.Shape.NUMBER_FLOAT, pattern = "0.00") double price, 19 | boolean inStock) { 20 | 21 | @JsonIgnore 22 | public ProductResponse withInStock(final boolean inStock) { 23 | return this.inStock == inStock 24 | ? this 25 | : new ProductResponse( 26 | this.id, 27 | this.productCode, 28 | this.productName, 29 | this.description, 30 | this.imageUrl, 31 | this.price, 32 | inStock); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /retail-store-webapp/src/main/resources/templates/fragments/inventoryPagination.html: -------------------------------------------------------------------------------- 1 |3 | Licensed under MIT License Copyright (c) 2023-2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.api.gateway.config; 8 | 9 | import com.redis.testcontainers.RedisContainer; 10 | import java.time.Duration; 11 | import org.springframework.boot.test.context.TestConfiguration; 12 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection; 13 | import org.springframework.context.annotation.Bean; 14 | import org.testcontainers.grafana.LgtmStackContainer; 15 | import org.testcontainers.utility.DockerImageName; 16 | 17 | @TestConfiguration(proxyBeanMethods = false) 18 | public class ContainerConfig { 19 | 20 | @Bean 21 | @ServiceConnection 22 | LgtmStackContainer lgtmContainer() { 23 | return new LgtmStackContainer(DockerImageName.parse("grafana/otel-lgtm:0.12.0")) 24 | .withStartupTimeout(Duration.ofMinutes(2)); 25 | } 26 | 27 | @Bean 28 | @ServiceConnection(name = "redis") 29 | RedisContainer redisContainer() { 30 | return new RedisContainer(DockerImageName.parse("redis").withTag("8.4.0-alpine")) 31 | .withReuse(true); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /catalog-service/src/main/java/com/example/catalogservice/mapper/ProductMapper.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2021-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.catalogservice.mapper; 8 | 9 | import com.example.catalogservice.entities.Product; 10 | import com.example.catalogservice.model.payload.ProductDto; 11 | import com.example.catalogservice.model.request.ProductRequest; 12 | import com.example.catalogservice.model.response.ProductResponse; 13 | import org.mapstruct.Mapper; 14 | import org.mapstruct.Mapping; 15 | import org.mapstruct.MappingTarget; 16 | 17 | @Mapper(componentModel = "spring") 18 | public interface ProductMapper { 19 | 20 | @Mapping(target = "inStock", ignore = true) 21 | @Mapping(target = "withInStock", ignore = true) 22 | ProductResponse toProductResponse(Product product); 23 | 24 | @Mapping(target = "id", ignore = true) 25 | Product toEntity(ProductRequest productRequest); 26 | 27 | @Mapping(target = "code", source = "productCode") 28 | ProductDto toProductDto(ProductRequest productRequest); 29 | 30 | @Mapping(target = "id", ignore = true) 31 | void mapProductWithRequest(ProductRequest productRequest, @MappingTarget Product product); 32 | } 33 | -------------------------------------------------------------------------------- /inventory-service/src/test/java/com/example/inventoryservice/common/NonSQLContainersConfig.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2023-2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.inventoryservice.common; 8 | 9 | import java.time.Duration; 10 | import org.springframework.boot.test.context.TestConfiguration; 11 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection; 12 | import org.springframework.context.annotation.Bean; 13 | import org.testcontainers.grafana.LgtmStackContainer; 14 | import org.testcontainers.kafka.KafkaContainer; 15 | import org.testcontainers.utility.DockerImageName; 16 | 17 | @TestConfiguration(proxyBeanMethods = false) 18 | public class NonSQLContainersConfig { 19 | 20 | @Bean 21 | @ServiceConnection 22 | LgtmStackContainer lgtmContainer() { 23 | return new LgtmStackContainer(DockerImageName.parse("grafana/otel-lgtm:0.12.0")) 24 | .withStartupTimeout(Duration.ofMinutes(2)); 25 | } 26 | 27 | @Bean 28 | @ServiceConnection 29 | KafkaContainer kafkaContainer() { 30 | return new KafkaContainer(DockerImageName.parse("apache/kafka-native").withTag("4.1.1")) 31 | .withReuse(true); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /inventory-service/src/test/java/com/example/inventoryservice/config/TestStockOrderListenerConfig.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.inventoryservice.config; 8 | 9 | import com.example.common.dtos.OrderDto; 10 | import java.util.concurrent.CountDownLatch; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | import org.springframework.boot.test.context.TestConfiguration; 14 | import org.springframework.kafka.annotation.KafkaListener; 15 | import org.springframework.messaging.handler.annotation.Payload; 16 | 17 | @TestConfiguration(proxyBeanMethods = false) 18 | public class TestStockOrderListenerConfig { 19 | 20 | private static final Logger log = LoggerFactory.getLogger(TestStockOrderListenerConfig.class); 21 | 22 | private final CountDownLatch countDownLatch = new CountDownLatch(1); 23 | 24 | public CountDownLatch getCountDownLatch() { 25 | return countDownLatch; 26 | } 27 | 28 | @KafkaListener(id = "stocks", topics = "stock-orders", groupId = "inventory") 29 | public void onOrderEvent(@Payload OrderDto orderDto) { 30 | log.info("Received Order: {}", orderDto); 31 | countDownLatch.countDown(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /retail-store-webapp/src/main/java/com/example/retailstore/webapp/web/model/request/RegistrationRequest.java: -------------------------------------------------------------------------------- 1 | package com.example.retailstore.webapp.web.model.request; 2 | 3 | import jakarta.validation.constraints.Email; 4 | import jakarta.validation.constraints.NotBlank; 5 | import jakarta.validation.constraints.Pattern; 6 | import jakarta.validation.constraints.Size; 7 | 8 | public record RegistrationRequest( 9 | @NotBlank @Pattern( 10 | regexp = "^[a-zA-Z0-9._-]{3,20}$", 11 | message = 12 | "Username must be 3-20 characters and contain only letters, numbers, dots, underscores or hyphens") 13 | String username, 14 | @NotBlank @Email String email, 15 | @NotBlank @Size(min = 1, max = 50) String firstName, 16 | @NotBlank @Size(min = 1, max = 50) String lastName, 17 | @NotBlank @Pattern( 18 | regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\\S+$).{8,}$", 19 | message = 20 | "Password must be at least 8 characters and include uppercase, lowercase, numbers and special characters") 21 | String password, 22 | Long phone, 23 | String address) {} 24 | -------------------------------------------------------------------------------- /inventory-service/src/test/java/com/example/inventoryservice/repositories/InventoryRepositoryTest.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2023-2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.inventoryservice.repositories; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | 11 | import com.example.inventoryservice.common.SQLContainersConfig; 12 | import com.zaxxer.hikari.HikariDataSource; 13 | import javax.sql.DataSource; 14 | import org.junit.jupiter.api.Test; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest; 17 | import org.springframework.boot.jdbc.test.autoconfigure.AutoConfigureTestDatabase; 18 | import org.springframework.context.annotation.Import; 19 | 20 | @DataJpaTest( 21 | properties = { 22 | "spring.jpa.hibernate.ddl-auto=validate", 23 | "spring.cloud.config.enabled=false" 24 | }) 25 | @Import(SQLContainersConfig.class) 26 | @AutoConfigureTestDatabase 27 | class InventoryRepositoryTest { 28 | 29 | @Autowired private DataSource datasource; 30 | 31 | @Test 32 | void dataSource() { 33 | assertThat(datasource).isNotNull().isInstanceOf(HikariDataSource.class); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /payment-service/README.md: -------------------------------------------------------------------------------- 1 | # payment-service 2 | 3 | ### Run tests 4 | `$ ./mvnw clean verify` 5 | 6 | ### Run locally 7 | 8 | ```shell 9 | docker-compose -f docker/docker-compose.yml up -d 10 | ./mvnw spring-boot:run -Dspring-boot.run.profiles=local 11 | ``` 12 | 13 | ### Using Testcontainers at Development Time 14 | You can run `TestPaymentApplication.java` from your IDE directly. 15 | You can also run the application using Maven as follows: 16 | 17 | ```shell 18 | ./mvnw spotless:apply spring-boot:test-run 19 | ``` 20 | 21 | ### Running only this Service Locally - Tips 22 | 23 | To run only the Payment Service locally with clean logs, you can follow these steps: 24 | 25 | 26 | 1. start the Kafka and Postgres servers by using below command(You should be inside appropriate directory and docker setup should be done :- ) : 27 | ```shell 28 | docker compose up kafka postgres 29 | ``` 30 | 31 | 2. In IntelIj Open Modify Run Configuration from Main class: 32 | `com.example.paymentservice.TestPaymentApplication` 33 | Set the Environment variable value to 34 | ```text 35 | SPRING_PROFILES_ACTIVE=local 36 | ``` 37 | 38 | 39 | ### Useful Links 40 | * Swagger UI: http://localhost:18085/payment-service/swagger-ui.html 41 | * Actuator Endpoint: http://localhost:18085/payment-service/actuator 42 | -------------------------------------------------------------------------------- /retail-store-webapp/src/main/resources/templates/fragments/productsPagination.html: -------------------------------------------------------------------------------- 1 |3 | Licensed under MIT License Copyright (c) 2022-2024 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.orderservice.config; 8 | 9 | import com.example.orderservice.services.CatalogServiceProxy; 10 | import io.micrometer.observation.ObservationRegistry; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.Configuration; 13 | import org.springframework.web.client.support.RestClientHttpServiceGroupConfigurer; 14 | import org.springframework.web.service.registry.ImportHttpServices; 15 | 16 | @Configuration(proxyBeanMethods = false) 17 | @ImportHttpServices(CatalogServiceProxy.class) 18 | class HttpClientConfig { 19 | 20 | @Bean 21 | RestClientHttpServiceGroupConfigurer groupConfigurer( 22 | ObservationRegistry observationRegistry, ApplicationProperties applicationProperties) { 23 | return groups -> 24 | groups.forEachClient( 25 | (_, builder) -> 26 | builder.baseUrl(applicationProperties.catalogServiceUrl()) 27 | .observationRegistry(observationRegistry) 28 | .build()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /order-service/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 |3 | Licensed under MIT License Copyright (c) 2023-2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.orderservice.common; 8 | 9 | import java.time.Duration; 10 | import org.springframework.boot.devtools.restart.RestartScope; 11 | import org.springframework.boot.test.context.TestConfiguration; 12 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection; 13 | import org.springframework.context.annotation.Bean; 14 | import org.testcontainers.grafana.LgtmStackContainer; 15 | import org.testcontainers.kafka.KafkaContainer; 16 | import org.testcontainers.utility.DockerImageName; 17 | 18 | @TestConfiguration(proxyBeanMethods = false) 19 | public class ContainersConfig { 20 | 21 | @Bean 22 | @ServiceConnection 23 | @RestartScope 24 | KafkaContainer kafkaContainer() { 25 | return new KafkaContainer(DockerImageName.parse("apache/kafka-native").withTag("4.1.1")) 26 | .withReuse(true); 27 | } 28 | 29 | @Bean 30 | @ServiceConnection 31 | LgtmStackContainer lgtmContainer() { 32 | return new LgtmStackContainer(DockerImageName.parse("grafana/otel-lgtm:0.12.0")) 33 | .withStartupTimeout(Duration.ofMinutes(2)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /catalog-service/src/main/java/com/example/catalogservice/config/WebFluxConfig.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2021-2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.catalogservice.config; 8 | 9 | import org.jspecify.annotations.NonNull; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.web.reactive.config.CorsRegistry; 12 | import org.springframework.web.reactive.config.WebFluxConfigurer; 13 | 14 | @Configuration(proxyBeanMethods = false) 15 | public class WebFluxConfig implements WebFluxConfigurer { 16 | 17 | private final ApplicationProperties properties; 18 | 19 | public WebFluxConfig(ApplicationProperties properties) { 20 | this.properties = properties; 21 | } 22 | 23 | @Override 24 | public void addCorsMappings(@NonNull CorsRegistry registry) { 25 | ApplicationProperties.Cors cors = properties.cors(); 26 | registry.addMapping(cors.getPathPattern()) 27 | .allowedMethods(cors.getAllowedMethods().split(",")) 28 | .allowedHeaders(cors.getAllowedHeaders().split(",")) 29 | .allowedOriginPatterns(cors.getAllowedOriginPatterns().split(",")) 30 | .allowCredentials(cors.isAllowCredentials()) 31 | .maxAge(3600); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /payment-service/src/main/java/com/example/paymentservice/config/WebMvcConfig.java: -------------------------------------------------------------------------------- 1 | /*** Licensed under MIT License Copyright (c) 2021-2025 Raja Kolli. ***/ 2 | package com.example.paymentservice.config; 3 | 4 | import org.jspecify.annotations.NonNull; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 7 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 8 | 9 | @Configuration(proxyBeanMethods = false) 10 | class WebMvcConfig implements WebMvcConfigurer { 11 | 12 | private final ApplicationProperties properties; 13 | 14 | public WebMvcConfig(ApplicationProperties properties) { 15 | this.properties = properties; 16 | } 17 | 18 | @Override 19 | public void addCorsMappings(@NonNull CorsRegistry registry) { 20 | ApplicationProperties.Cors cors = properties.getCors(); 21 | registry.addMapping(cors.getPathPattern()) 22 | .allowedMethods(cors.getAllowedMethods().split(",")) 23 | .allowedHeaders(cors.getAllowedHeaders().split(",")) 24 | .allowedOriginPatterns(cors.getAllowedOriginPatterns().split(",")) 25 | .allowCredentials(cors.isAllowCredentials()) 26 | .maxAge(3600); // Cache preflight response for 1 hour 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /catalog-service/src/main/java/com/example/catalogservice/exception/ProductAlreadyExistsException.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2022-2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.catalogservice.exception; 8 | 9 | import java.net.URI; 10 | import java.time.Instant; 11 | import org.springframework.http.HttpStatus; 12 | import org.springframework.http.ProblemDetail; 13 | import org.springframework.web.ErrorResponseException; 14 | 15 | public class ProductAlreadyExistsException extends ErrorResponseException { 16 | public ProductAlreadyExistsException(String productCode) { 17 | super( 18 | HttpStatus.CONFLICT, 19 | asProblemDetail("Product with code " + productCode + " already exists"), 20 | null); 21 | } 22 | 23 | private static ProblemDetail asProblemDetail(String message) { 24 | ProblemDetail problemDetail = 25 | ProblemDetail.forStatusAndDetail(HttpStatus.CONFLICT, message); 26 | problemDetail.setTitle("Product Already Exists"); 27 | problemDetail.setType(URI.create("https://api.microservices.com/errors/already-exists")); 28 | problemDetail.setProperty("errorCategory", "Generic"); 29 | problemDetail.setProperty("timestamp", Instant.now()); 30 | return problemDetail; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /payment-service/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 |Don't have an account? Register here
34 |3 | Licensed under MIT License Copyright (c) 2021-2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.inventoryservice.config; 8 | 9 | import com.example.inventoryservice.config.ApplicationProperties.Cors; 10 | import org.jspecify.annotations.NonNull; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 13 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 14 | 15 | @Configuration(proxyBeanMethods = false) 16 | class WebMvcConfig implements WebMvcConfigurer { 17 | 18 | private final ApplicationProperties properties; 19 | 20 | WebMvcConfig(ApplicationProperties properties) { 21 | this.properties = properties; 22 | } 23 | 24 | @Override 25 | public void addCorsMappings(@NonNull CorsRegistry registry) { 26 | Cors cors = properties.getCors(); 27 | registry.addMapping(cors.getPathPattern()) 28 | .allowedMethods(cors.getAllowedMethods().split(",")) 29 | .allowedHeaders(cors.getAllowedHeaders().split(",")) 30 | .allowedOriginPatterns(cors.getAllowedOriginPatterns().split(",")) 31 | .allowCredentials(cors.isAllowCredentials()) 32 | .maxAge(3600); // Cache preflight response for 1 hour 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /order-service/src/main/java/com/example/orderservice/config/WebMvcConfig.java: -------------------------------------------------------------------------------- 1 | /*** 2 |3 | Licensed under MIT License Copyright (c) 2021-2025 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.orderservice.config; 8 | 9 | import org.jspecify.annotations.NonNull; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.scheduling.annotation.EnableAsync; 12 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 13 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 14 | 15 | @Configuration(proxyBeanMethods = false) 16 | @EnableAsync 17 | class WebMvcConfig implements WebMvcConfigurer { 18 | 19 | private final ApplicationProperties properties; 20 | 21 | public WebMvcConfig(ApplicationProperties properties) { 22 | this.properties = properties; 23 | } 24 | 25 | @Override 26 | public void addCorsMappings(@NonNull CorsRegistry registry) { 27 | Cors cors = properties.cors(); 28 | registry.addMapping(cors.getPathPattern()) 29 | .allowedMethods(cors.getAllowedMethods().split(",")) 30 | .allowedHeaders(cors.getAllowedHeaders().split(",")) 31 | .allowedOriginPatterns(cors.getAllowedOriginPatterns().split(",")) 32 | .allowCredentials(cors.isAllowCredentials()) 33 | .maxAge(3600); // Cache preflight response for 1 hour 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /catalog-service/README.md: -------------------------------------------------------------------------------- 1 | # catalogservice 2 | 3 | ### Run locally 4 | 5 | ```shell 6 | docker-compose -f docker/docker-compose.yml up -d 7 | ./mvnw spring-boot:run -Dspring-boot.run.profiles=local 8 | ``` 9 | 10 | ### Using Testcontainers at Development Time 11 | You can run `TestCatalogServiceApplication.java` from your IDE directly. 12 | You can also run the application using Maven as follows: 13 | 14 | ```shell 15 | ./mvnw spotless:apply spring-boot:test-run 16 | ``` 17 | 18 | ### Run tests 19 | 20 | ```shell 21 | ./mvnw clean verify 22 | ``` 23 | 24 | ### Running only this Service Locally - Tips 25 | 26 | To run only the Catalog Service locally with clean logs, you can follow these steps: 27 | 28 | 29 | 1. start the Kafka and Postgres servers by using below command(You should be inside appropriate directory and docker setup should be done :-) 30 | ```shell 31 | docker compose up kafka postgres 32 | ``` 33 | 2. In IntelIj Open Modify Run Configuration from Main class: 34 | `com.example.catalogservice.CatalogServiceApplication` 35 | Set the Environment variable value to 36 | ```text 37 | SPRING_PROFILES_ACTIVE=local 38 | ``` 39 | 40 | 41 | ### Useful Links 42 | * Swagger UI: http://localhost:18080/catalog-service/swagger-ui.html 43 | * Actuator Endpoint: http://localhost:18080/catalog-service/actuator 44 | * Actuator Health Endpoint : http://localhost:18080/catalog-service/actuator/health 45 | * Zipkin Server: http://localhost:9411 46 | -------------------------------------------------------------------------------- /catalog-service/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 || Order ID | 18 |Status | 19 |Created Date | 20 |
|---|---|---|
| OrderNumber | 26 |status | 27 |createdDate | 28 |
3 | Licensed under MIT License Copyright (c) 2023 Raja Kolli. 4 |
5 | ***/ 6 | 7 | package com.example.catalogservice.common; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | 11 | import io.github.resilience4j.circuitbreaker.CircuitBreaker; 12 | 13 | public abstract class AbstractCircuitBreakerTest extends AbstractIntegrationTest { 14 | 15 | protected void checkHealthStatus(String circuitBreakerName, CircuitBreaker.State state) { 16 | CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker(circuitBreakerName); 17 | assertThat(circuitBreaker.getState()).isEqualTo(state); 18 | } 19 | 20 | protected void transitionToOpenState(String circuitBreakerName) { 21 | CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker(circuitBreakerName); 22 | circuitBreaker.transitionToOpenState(); 23 | } 24 | 25 | protected void transitionToClosedState(String circuitBreakerName) { 26 | CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker(circuitBreakerName); 27 | circuitBreaker.transitionToClosedState(); 28 | } 29 | 30 | protected void transitionToHalfOpenState(String circuitBreakerName) { 31 | CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker(circuitBreakerName); 32 | circuitBreaker.transitionToHalfOpenState(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /api-gateway/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 |